AngularJS blocks login access to inactive users - javascript

I'am trying to block users with unactivated email tokens from accessing. Currently unactivated users can still log in and simply get an alert. This method uses the satellizer auth system, with a promise, then and a catch.
Here is my LoginCtrl
'use strict';
angular.module('myapp').controller('LoginCtrl', function ($scope, alert, auth, $state, $auth, $timeout) {
$scope.submit = function () {
$auth.login({
email: $scope.email,
password: $scope.password
})
.then(function(res) {
var message = 'Thanks for coming back ' + res.data.user.email + '!';
if (!res.data.user.active)
message = 'Just a reminder, please activate your account soon :)';
alert('success', 'Welcome', message);
return null;
})
.then(function() {
$timeout(function() {
$state.go('main');
});
})
.catch(handleError);
}
function handleError(err) {
alert('warning', 'oops there is a problem!', err.message);
}
});
I like to display an alert message "please activate first" at the same time block login. I appreciate your help. Basically, I want to check if the user is active, then login authorized, if not, login will not be possible.

Related

Resent email verification link not changing verified to true

I'm setting up a custom authentication system in my meteor app.
When a user signs up, they are sent an email verification link. If they click on this, it changes the verification boolean to true as expected.
I have a button in the users account setting page that allows them to resend a verification email if needed. Clicking on this works ok and they receive another email with a different link.
When this new link is clicked, it redirects to the home page but it doesn't verify the email address.
My guess is that the users account isn't aware of the new token.
//server code
Meteor.methods({
'sendVerificationEmail' : function(userId, primaryEmail){
var userId = Meteor.user();
Accounts.sendVerificationEmail(userId, primaryEmail, function (error) {
if (! error) {
return alert('Verfication email sent');
} else {
return alert(error);
};
});
}
});
//Client code
Accounts.onEmailVerificationLink(function (token, done) {
Accounts.verifyEmail(token, function (error) {
if (! error) {
console.log('Account verified');
alert('Account verified');
}
done();
// show something if there was an error.
});
});
// Email verification route
Router.route('/verify-email/:token', {
name: 'verifyEmail'
});

Getting error Firebase. createUser failed: First argument must contain the key "password" Angularjs

I'm new to angular and I'm following a chatapp tutorial from online. I'm getting this the error "Firebase.createUser failed: First argument must contain the key "password" " when I try to register with an email and password. The app isn't complete yet, I just finished the auth part. Google answers suggested that I update to the latest angularfire, which I did ( 1.1.3). No idea what to do.
Register state in app.js:
.state('register', {
url: '/register',
templateUrl: 'auth/register.html',
controller:'AuthCtrl as authCtrl',
resolve:{
requireNoAuth: function($state,Auth){
return Auth.$requireAuth()
.then(function(auth){
$state.go('home');
},
function(error){
return;
});
}
}
})
authController.js
angular.module('chatApp')
.controller('AuthCtrl', function (Auth, $state) {
//Using 'Controller as syntax', instead of $scope, we use 'this' to make controller
var authCtrl = this;
//user object controller
authCtrl.user = {
email:'',
pass:''
};
//login object controller. Firebase provides functions. Using promises. ( either it's fufilled, or rejected)
authCtrl.login = function () {
Auth.authWithPassword(authCtrl.user)
// .then takes in 2 parameters( onSuccess, onFaliure)
//if successfull, go home
.then(function (auth) {
$state.go('home');
},
//if failed, set error in controller, so we can call it and display message later
function (error) {
authCtrl.error = error;
});
};
//registering user
authCtrl.register = function () {
Auth.$createUser(authCtrl.user)
// prompt user to login if successful
.then(function (user) {
authCtrl.login();
},
//else bring up error
function (error) {
authCtrl.error = error;
})
}
});
authFactory.js
angular.module('chatApp')
.factory('Auth',function($firebaseAuth,FirebaseUrl){
var declare= new Firebase(FirebaseUrl);
var auth=$firebaseAuth(declare);
return auth
});
It's password, not pass.
Secondly, you're incorrectly resolving the user in your route config. Rather than using the promise chain in resolve, you just need to return the promise.
.state('register', {
url: '/register',
templateUrl: 'auth/register.html',
controller:'AuthCtrl as authCtrl',
resolve:{
requireNoAuth: function($state,Auth){
return Auth.$requireAuth(); // return the promise
}
}
})
Then in the run() phase, you can listen for routing errors:
app.run(function($rootScope, $location) {
$rootScope.$on("$routeChangeError", function(event, next, previous, error) {
if (error === "AUTH_REQUIRED") {
$location.path("/home");
}
});
});
Check out the AngularFire docs on using Auth with Routing for more information.

AngularFire (Angular + Firebase) Authentication Error delay

I found out something weird. Please help!
$scope.login = function() {
ref.authWithPassword({
email: $scope.user.email,
password: $scope.user.password
}, function(error, authData) {
if (error) {
console.log("Login Failed!", error);
$scope.message = error.toString();
} else {
$location.path('/meetings');
console.log("Authenticated successfully with payload:", authData);
}
});
} //login
This is a login function and it works nicely.However, the thing is that I get error 3, 4 sec after I have submitted the login. I noticed that my {{message}} is not being updated immediately after I receive value at $scope.message . I thought Angular should show that value as soon as it changes. ?
After I click for the second time, I get the error showed.
This is where I am printing the value:
<p class="error formerror" ng-show="message">{{message}}</p>
You're calling authWithPassword, which is part of Firebase's regular JavaScript SDK. This API will start the authentication process and call your function when the authentication completes. Unfortunately at that point AngularJS is no longer aware of any updates you make to $scope.
To make AngularJS aware of the update, wrap your code in a $timeout call:
$scope.login = function() {
ref.authWithPassword({
email: $scope.user.email,
password: $scope.user.password
}, function(error, authData) {
$timeout(function() {
if (error) {
console.log("Login Failed!", error);
$scope.message = error.toString();
} else {
$location.path('/meetings');
console.log("Authenticated successfully with payload:", authData);
}
});
});
} //login
Note that precisely for this reason, AngularFire provides convenience wrappers around these authentication functions in its $firebaseAuth service. See the section in the AngularFire guide on logging users in.
The wrapper function you're looking for is $authWithPassword, read a sample of how to use $authWithPassword in its API documentation.

How to write unit test for controller with a service

I'm learning unit-testing in angularjs and i'm trying to test my authentication controller.
Currently the test is failing with Expected Function to equal '/dashboard'. The test does not seem to be getting into User.login from what I can tell.
Controller:
angular.module('foo').controller('LoginCtrl',function($scope, $rootScope, $http, $window, $location, User){
$scope.submit = function () {
User.login($scope.user,
function (user) { // Success
$window.sessionStorage.token = user.id;
$scope.message = 'Welcome';
// Redirect to dashboard
$location.path('/dashboard');
},
function (err) {
console.log(err);
// handle login errors
$scope.message = 'Error: Invalid user or password';
}
);
};
});
Test:
describe('LoginCtrl', function() {
beforeEach(module('foo'));
var scope, ctrl, location, window, user;
beforeEach(inject(function($rootScope, $controller, $location, $window, User) {
scope = $rootScope.$new();
location = $location;
window = $window;
user = User;
ctrl = $controller('LoginCtrl', {$scope: scope, User: user});
}));
it('should redirect upon successful login', function() {
console.log('before path = '+location.path());
scope.user = {
"username": "my_user",
"password": "my_pass"
};
scope.submit();
console.log('message = '+scope.message);
console.log('after path = '+location.path());
console.log(window.sessionStorage.getItem('token'));
expect(location.path).toEqual('/dashboard');
});
});
** EDIT **
User.login code:
module.factory(
"User",
['LoopBackResource', 'LoopBackAuth', '$injector', function(Resource, LoopBackAuth, $injector) {
var R = Resource(
urlBase + "/users/:id",
{ 'id': '#id' },
{
"login": {
url: urlBase + "/users/login",
method: "POST",
interceptor: {
response: function(response) {
var accessToken = response.data;
LoopBackAuth.currentUserId = accessToken.userId;
LoopBackAuth.accessTokenId = accessToken.id;
LoopBackAuth.rememberMe = response.config.params.rememberMe !== false;
LoopBackAuth.save();
return response.resource;
}
}
}
});
Your User.login function must be calling the callback method asynchronously, so when you call scope.submit();, your callback function is not called yet => the test fails.
To test this logic, you have to mock the User.login function:
it('should redirect upon successful login', function() {
console.log('before path = '+location.path());
scope.user = {
"username": "my_user",
"password": "my_pass"
};
//user here is user = User; in your beforeEach. I avoid pasting too much code.
//Jasmine 1.3: andCallFake
//Jasmine 2.0: and.callFake
spyOn(user, "login").andCallFake(function(userData,successCallback){
successCallback(userData); //simulate the success case.
}); //mock your User.login
scope.submit();
console.log('message = '+scope.message);
console.log('after path = '+location.path());
console.log(window.sessionStorage.getItem('token'));
expect(location.path()).toEqual('/dashboard'); //fix the problem with location.path
});
Explanation:
spyOn(user, "login").andCallFake replaces the actual function with our fake function.
In this test case, you're testing should redirect upon successful login, so the precondition is the login must be successful, by mocking the login function, we can ensure this precondition is always true in the test.
You could do this similarly to test a case like: set error message when login failed, in order to test this, you need to ensure the precondition login failed is always true in the test:
it('should redirect upon successful login', function() {
console.log('before path = '+location.path());
scope.user = {
"username": "my_user",
"password": "my_pass"
};
//user here is user = User; in your beforeEach. I avoid pasting too much code.
//Jasmine 1.3: andCallFake
//Jasmine 2.0: and.callFake
spyOn(user, "login").andCallFake(function(userData,successCallback,errorCallback){
errorCallback(userData); //simulate the error case.
}); //mock your User.login
scope.submit();
expect(scope.message).toEqual('Error: Invalid user or password'); //verify that the message was set correctly.
});
Expected Function to equal '/dashboard'
The test runner is telling you it expected a string '/dashboard', but instead got a reference to a function. That's because location.path is a reference to a function. Try this instead:
expect(location.path()).toEqual('/dashboard');

$scope changes in .success() not applying

Parse.User.logIn($scope.user.username, $scope.user.password, {
success: function (user) {
console.log("logged in");
$scope.User = user;
$scope.$apply();
},
error: function (user, error) {
console.log(error);
}
})
In the view, I have {{User.attributes.username}} that right after login shows nothing, but when someones already logged in and views the page, it works fine.
The scope doesn't change when someone just logs in. But above, I already have $scope.User = Parse.User.current() and that works fine. It seems to be an issue with this particular instance of changing the scope.
Try this:
success: function (user) {
console.log("logged in");
$scope.$apply(function() {
$scope.User = user;
});
},

Categories

Resources