Angular-UI and PassportJS Authentication - angularjs

So I'm trying to get local-auth working with passport and angular-ui but I think i've confused myself. I believe local-signup and local-login work because the redirects are working as expected.
I have a small Auth service that I want to inject and use to store the user information. it looks like this:
myApp.factory('Auth', function() {
var user;
return {
setUser: function(aUser) {
user = aUser;
},
isLoggedIn: function() {
return (user) ? user : false;
}
}
});
My routes look like this:
app.post('/login', function(req, res, next) {
passport.authenticate('local-login', function(err, user, info) {
if (err) {
console.log(err);
return next(err);
}
if (!user) {
console.log(user);
var param = 'There was an error with your login';
return res.redirect('/#/login?valid=' + param);
}
req.logIn(user, function(err) {
if (err) {
console.log(err);
return next(err);
}
//console.log(req);
console.log(user);
return res.redirect('/#/news');
});
})(req, res, next);
});
My states look like this:
myApp.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/news");
$stateProvider
.state('news', {
url: "/news",
templateUrl: "templates/news.html",
params: {
message: null
}
})
.state('people', {
url: "/people",
templateUrl: "templates/people.html",
resolve: {
authenticate: authenticate
},
})
.state('login', {
url: "/login",
templateUrl: "templates/login.html"
})
.state('signup', {
url: "/signup",
templateUrl: "templates/signup.html"
})
.state('preLogin', {
url: "/preLogin",
templateUrl: "templates/preLogin.html"
});
function authenticate($q, Auth, $state, $timeout) {
if (Auth.isLoggedIn()) {
return $q.when()
} else {
$timeout(function() {
$state.go('login')
})
return $q.reject()
}
}
});
I don't know how or when to store the user information in my Auth service. I assume I need to do this in the loginCtrl but I don't know how to tell when its a successful login besides the URL redirects.
Any help is appreciated.

Related

Sending data from a service to a factory

Hey guys I am trying to send a variable from a service from auth.service.js to a factory which is in app.js. I am kind of new to angular so I appreciate any help.
(function() {
'use strict';
angular
.module('app')
.service('authService', authService);
authService.$inject = ['$rootScope', 'lock', 'authManager', 'jwtHelper'];
function authService($rootScope, lock, authManager, jwtHelper) {
var userProfile = JSON.parse(localStorage.getItem('profile')) || {};
function login() {
lock.show();
}
// Logging out just requires removing the user's
// id_token and profile
function logout() {
localStorage.removeItem('id_token');
localStorage.removeItem('profile');
authManager.unauthenticate();
userProfile = {};
}
// Set up the logic for when a user authenticates
// This method is called from app.run.js
function registerAuthenticationListener() {
lock.on('authenticated', function(authResult) {
localStorage.setItem('id_token', authResult.idToken);
authManager.authenticate();
var pubkey = KEYUTIL.getKey('-----BEGIN CERTIFICATE-----MIIC8DCCAdigAwIBAgIJVUYCZUQdreDfMA0GCSqGSIb3DQEBBQUAMB8xHTAbBgNVBAMTFHN3aXRjaC1hcHAuYXV0aDAuY29tMB4XDTE2MTAwNTE5MzczM1oXDTMwMDYxNDE5MzczM1owHzEdMBsGA1UEAxMUc3dpdGNoLWFwcC5hdXRoMC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTG7oTPof81dqHqDKT8Mi/umpKgALMHJRwXSVIPBtPZGrgyOubi1oPsWZQrYOwla/3fMhstV/g6ZclVLGg9YHwNZl7uZYaOhAX1CjaTnDUe85R0lvFMRO42N5ZdbhXQASPPrMNZL7gov3eBQcj2n+Jb2k7OWYpN2mevw1fd6iah0eKAeoUcoWGjYwkB9DLmN7HizRsMHeVRyx3BJisI1PmFMkR+Ewbdu+HtOf7yavaVS6KmJti/U/192mXDgakRBLeODrZ+AxwedYcaF4CtGyS52SKkkHsbi6KsjDjfc9CbRRM+51VffVNzTsMTHYtADG34KHigGry/x/5QfsCAEXnAgMBAAGjLzAtMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFOUsGT48yyeHc6a/RdMAlasaM3p7MA0GCSqGSIb3DQEBBQUAA4IBAQAeqkTdeYqE1gSauYr/h30OSjxvHgskUOueWvFBnFveiEQWV3eVyu/psn2YG/2QgCeNSWUMsd8SXCAOBilFy6GL27bUmGKoZEDYsm0dUFTxZiTHgJZNMMQIpPtCLw48Ly1BVQQvi21DZnS9G5ZdWbTEwjNK4M+Fil5zgaiJaObRH4+oAXpgwngT+ZoEO3Z38urbs/Gcp1VKvHjEdY18JxyDChQfIDQNb6bc2zoOR62JTx75fC7khQesJ2jcxJhE1VLsvMRr1bVaVgBeEAdq+fC6WQsJA08209JmJfO4/OYscSe9RxnDEXa6UOQpNO34x5Tr8AImQTLy3jdFoNg1/fSL-----END CERTIFICATE-----');
var isValid = KJUR.jws.JWS.verifyJWT(authResult.idToken, pubkey, {alg: ['RS256']});
console.log(isValid);
// Used for decoding the message returned form auth0
var decoded = jwt_decode(authResult.idToken);
// The encoded message returned from auth0
console.log(authResult.idToken);
//The decoded message returned from auth0
console.log(decoded);
lock.hide();
// Redirect to default page
location.hash = '#/app/home';
lock.getProfile(authResult.idToken, function(error, profile) {
if (error) {
console.log(error);
}
localStorage.setItem('profile', JSON.stringify(profile));
});
});
}
function checkAuthOnRefresh() {
var token = localStorage.getItem('id_token');
if (token) {
if (!jwtHelper.isTokenExpired(token)) {
if (!$rootScope.isAuthenticated) {
authManager.authenticate();
}
}
}
}
return {
userProfile: userProfile,
login: login,
logout: logout,
registerAuthenticationListener: registerAuthenticationListener,
checkAuthOnRefresh: checkAuthOnRefresh
}
}
})();
I want to send from the function registeredAuthentificationListener() the authResult.idToken to a factory in
app.js
(function () {
var iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
if (iOS) {
var isApple = 'isApple';
}else{
var isApple = 'notApple';
}
'use strict';
var app = angular
.module('app', ['ionic', 'auth0.lock', 'angular-jwt'])
.config(config);
config.$inject = ['$stateProvider', '$urlRouterProvider', 'lockProvider', 'jwtOptionsProvider','$httpProvider'];
function factory() {
return {
request : function (config) {
config.headers['X-switch-using'] = isApple;
return config;
}
}
}
function config($stateProvider, $urlRouterProvider, lockProvider, jwtOptionsProvider,$httpProvider) {
$stateProvider
// setup an abstract state for the tabs directive
.state('app', {
url: '/app',
abstract: true,
templateUrl: 'components/menu/menu.html',
})
.state('app.home', {
url: '/home',
views: {
'menuContent': {
templateUrl: 'components/home/home.html'
}
}
})
.state('app.dashboard', {
url: '/dashboard',
views: {
'menuContent': {
templateUrl: 'components/template/template.html'
}
}
})
.state('app.signin', {
url: '/login',
views: {
'menuContent': {
templateUrl: 'components/login/login.html'
}
}
});
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/app/home');
$httpProvider.interceptors.push(factory);
lockProvider.init({
clientID: AUTH0_CLIENT_ID,
domain: AUTH0_DOMAIN,
options: {
auth: {
redirect: false,
params: {
scope: 'openid',
device: 'Mobile device'
}
}
}
});
// Configuration for angular-jwt
jwtOptionsProvider.config({
tokenGetter: function() {
return localStorage.getItem('id_token');
},
whiteListedDomains: ['localhost'],
unauthenticatedRedirectPath: '/login'
});
}
})();
Thank you for your help!
It's my bad, you can send the variable using localStorage.getItem('id_token') and use that straight in the app.js

Angularjs role based routing error

I am trying to implement role based authorization in beeman's loopback-angular-admin repo by using this https://github.com/Narzerus/angular-permission
so I am trying to change the users.config.js to like this -
(function () {
'use strict';
angular.module('com.module.users')
.run(function ($rootScope, gettextCatalog, Permission, AppAuth, User) {
$rootScope.addMenu(gettextCatalog.getString('Users'), 'app.users.list', 'fa-user');
Permission.defineRole('anonymous', function (stateParams) {
// If the returned value is *truthy* then the user has the role, otherwise they don't
return !AppAuth.hasOwnProperty('currentUser');
});
Permission.defineRole('student', function (stateParams) {
// If the returned value is *truthy* then the user has the role, otherwise they don't
AppAuth.ensureHasCurrentUser(function () {
//This call also serves to redirect a user to the login screen, via the interceptor in users.auth.js, if they are not authenticated.
console.log(User.getCurrent());
return !!(User.getCurrent().hasOwnProperty('role') && User.getCurrent().role == 'student');
});
}).defineRole('tutor', function (stateParams) {
// If the returned value is *truthy* then the user has the role, otherwise they don't
AppAuth.ensureHasCurrentUser(function () {
//This call also serves to redirect a user to the login screen, via the interceptor in users.auth.js, if they are not authenticated.
console.log(User.getCurrent());
return !!(User.getCurrent().hasOwnProperty('role') && User.getCurrent().role == 'tutor');
});
});
});
})();
So basically I am defining my roles with the help of AppAuth module that has the following code -
(function () {
'use strict';
/*jshint sub:true*/
/*jshint camelcase: false */
angular
.module('com.module.users')
.factory('AppAuth', function ($cookies, User, LoopBackAuth, $http) {
var self = {
login: function (data, cb) {
LoopBackAuth.currentUserId = LoopBackAuth.accessTokenId = null;
$http.post('/api/users/login?include=user', {
email: data.email,
password: data.password
})
.then(function (response) {
if (response.data && response.data.id) {
LoopBackAuth.currentUserId = response.data.userId;
LoopBackAuth.accessTokenId = response.data.id;
}
if (LoopBackAuth.currentUserId === null) {
delete $cookies['accessToken'];
LoopBackAuth.accessTokenId = null;
}
LoopBackAuth.save();
if (LoopBackAuth.currentUserId && response.data && response.data
.user) {
self.currentUser = response.data.user;
cb(self.currentUser);
} else {
cb({});
}
}, function () {
console.log('User.login() err', arguments);
LoopBackAuth.currentUserId = LoopBackAuth.accessTokenId =
null;
LoopBackAuth.save();
cb({});
});
},
logout: function (cb) {
//Destroy the access token.
User.logout({"access_token": LoopBackAuth.accessTokenId}, function () {
//Destory both cookies that get created.
delete $cookies["access_token"];
delete $cookies["accessToken"];
//Perform the Passport Logout
$http.post('/auth/logout');
});
self.currentUser = null;
cb();
},
ensureHasCurrentUser: function (cb) {
if ((!this.currentUser || this.currentUser.id === 'social') && $cookies.accessToken) {
LoopBackAuth.currentUserId = LoopBackAuth.accessTokenId = null;
$http.get('/auth/current')
.then(function (response) {
if (response.data.id) {
LoopBackAuth.currentUserId = response.data.id;
LoopBackAuth.accessTokenId = $cookies.accessToken.substring(
2, 66);
}
if (LoopBackAuth.currentUserId === null) {
delete $cookies['accessToken'];
LoopBackAuth.accessTokenId = null;
}
LoopBackAuth.save();
self.currentUser = response.data;
var profile = self.currentUser && self.currentUser.profiles &&
self.currentUser.profiles.length && self.currentUser.profiles[
0];
if (profile) {
self.currentUser.name = profile.profile.name;
}
cb(self.currentUser);
}, function () {
console.log('User.getCurrent() err', arguments);
LoopBackAuth.currentUserId = LoopBackAuth.accessTokenId =
null;
LoopBackAuth.save();
cb({});
});
} else {
if(self.currentUser){
console.log('Using cached current user.');
}
cb(self.currentUser);
}
}
};
return self;
});
})();
and then in my routes file I am doing -
$stateProvider
.state('router', {
url: '/router',
template: '<div class="lockscreen" style="height: 100%"></div>',
controller: 'RouteCtrl'
})
.state('error', {
url: '/error',
template: '<div class="text-center alert alert-danger" style="margin: 100px">An error occurred.</div>'
})
.state('app', {
abstract: true,
url: '/app',
templateUrl: 'modules/core/views/app.html',
controller: 'MainCtrl',
data: {
permissions: {
only: ['admin', 'student']
}
}
})
.state('app.home', {
url: '',
templateUrl: 'modules/core/views/home.html',
controller: 'HomeCtrl',
data: {
permissions: {
only: ['admin', 'student']
}
}
})
.state('tutor', {
abstract: true,
url: '/tutor',
templateUrl: 'modules/core/views/app.html',
controller: 'MainCtrl',
data: {
permissions: {
only: ['admin', 'tutor']
}
}
})
.state('tutor.home', {
url: '',
templateUrl: 'modules/core/views/home.html',
controller: 'HomeCtrl',
data: {
permissions: {
only: ['admin', 'tutor']
}
}
});
$urlRouterProvider.otherwise('/router');
but now I am getting the following error in the browser console -
Error: undefined role or invalid role validation
at Object.Permission._findMatchingRole (angular-permission.js:170)
at Object.Permission.resolveIfMatch (angular-permission.js:211)
at Object.Permission.authorize (angular-permission.js:239)
at angular-permission.js:45
at Scope.$broadcast (angular-scenario.js:24081)
at Object.transitionTo (angular-ui-router.js:3229)
at Array.<anonymous> (angular-ui-router.js:2346)
at Object.invoke (angular.js:4185)
at handleIfMatch (angular-ui-router.js:1836)
at angular-ui-router.js:1891
so how can I debug this error or implement client side role based authorization in my loopback app
[..] implement client side role based authorization in my loopback app
This doesn't mean anything, loopback is your server application.
Client-side authorization should be prohibited, since clients can be tampered with.
Authorization should take place server-side exclusively. Client-side you should just adapt the content in function of the role. That way if someone tampers with your client code at least its http requests to the API will fail and your data is safe

Error handling when data is not resolved in Angular UI router

I am using angular-ui-router's resolve to get data from server before moving a state. I want to inform user if the request has failed. I have service that will response error the request has failed. My question is how can I detect the failure in ui-router resolve and trigger some Modal service like pop up.
I have read some related posts online, but I am still confused how to make it happen. Thanks in advanced!
Config and Service:
angular.module('myApp',['ui.router', 'ngAnimate', 'ui.bootstrap'])
.config(function ($stateProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$stateProvider
.state('customer', {
url: '/customer',
templateUrl: '/customer.html',
controller: 'CustomerCtrl',
resolve: {
/*
* How to inject CustomerService here
* How to catch the server error
* How to trigger a popup
*/
data: cusomter_data
}
});
})
.service('CustomerService', function($http, $q) {
return ({
getGeneral: getGeneral
});
function getGeneral(customer_id) {
var request = $http({
method: "get",
url: '/customer',
params: {
customer_id: customer_id
}
});
return (request.then( handleSuccess, handleError));
}
function handleError (response){
if (!angular.isObject(response.data) || !response.data.message) {
return($q.reject("Failed to retrieve customer information."));
}
return($q.reject(response.data.message));
}
function handleSuccess(response) {
return (response.data);
}
});
After some research, I found out a solution by creating a errorModal service and inject it to resolve. Here is my solution.
$stateProvider
.state('customer', {
url: '/customer',
templateUrl: '/customer.html',
controller: 'CustomerCtrl',
resolve: {
data: function(CustomerService, SelectedCustomerService, errorModalService) {
var shared = SelectedCustomerService;
return CustomerService.getData(shared.customerid).then(
function (data) { return data; },
function(error) {
var modalOptions = {
closeButtonText: 'Close',
headerText: 'Customer Error',
bodyText: error
};
errorModalService.showModal({}, modalOptions);
}
);
}
}
});

UI-router won't resolve promise

I'm not quite sure what I'm doing wrong, but it seems that my profile doesn't resolve by the time we get to the MainCtrl. The user however does, resolve. Am I, perhaps not fetching the profile information properly in the Auth Service?
Router:
angular.module('app')
.config(function ($stateProvide) {
$stateProvider
.state('main', {
url: '/main',
templateUrl: 'app/main/main',
controller: 'MainCtrl',
resolve: {
user: function (Auth) {
return Auth.getUser();
},
profile: function (user) {
return Auth.getProfile();
}
}
});
});
Controller:
angular.module('app')
.controller('MainCtrl', function ($scope, user, profile) {
$scope.user = user;
$scope.profile = profile; <- DOESNT RESOLVE
});
Auth Service:
angular.module('app')
.factory('Auth', function ($firebaseSimpleLogin, $firebase, FBURL) {
var ref = new Firebase(FBURL);
var auth = $firebaseSimpleLogin(ref);
var Auth = {
user: {},
getUser: function () {
return auth.$getCurrentUser();
},
getProfile: function(uid) {
return $firebase(ref.child('users').child(uid)).$asObject();
}
};
return Auth;
});
Something like
auth.$getCurrentUser()
returns a promise so you need a
.then(function(user) {
event before your callback complete
In your case you may just resolve on the then, something like
Auth.getUser().then(function(user){ return user; });
Also $asObject() needs $loaded() for it's promise
var obj = $firebase(ref).$asObject();
obj.$loaded()
.then(function(data) {})
Try this structure for your promises:
var fetchSomething = function (action, params) {
var promise = $http({
method: 'POST',
url: 'someurl to the firebase',
data: params,
headers: {
'Access-Control-Allow-Origin': true,
'Content-Type': 'application/json'
}
}).success(function (data, status, headers, config) {
return data;
});
return promise;
};

How to redirect with $state.go

I'm trying to redirect my user to Dashboard after Third Party Login. But when success callback is fired, the application still on Login Page, nothing happened. If I refresh the browser my interceptor catch already login and change to Dashboard... My Login Controller looks like this:
ThirdParty.login(function(result){
callbackSUCCESS(result);
},function(){});
function callbackSUCCESS(result){
AuthenticationService.login(result).then(
callbackServerSUCCESS(), function(reject) {
callbackServerERROR(reject);
});
}
function callbackServerSUCCESS() {
$scope.$apply(function() {
$state.go('dashboard');
});
}
My route in app.js
$stateProvider
.state('dashboard', {
url: '/dashboard',
views: {
'': {
templateUrl: 'views/dashboard/dashboard.html',
controller: 'DashboardCtrl'
}
}
});
My Header Controller
.controller('HeaderCtrl', ['$scope', 'AuthenticationService', '$state',
function($scope, AuthenticationService, $state) {
$scope.logout = function() {
AuthenticationService.logout().then(callbackServer(), callbackServer());
};
function callbackServer() {
$state.go('login');
}
}
]);
Authentication Controller Angular Factory
var headersConfig = {
'Cache-Control': 'no-cache',
'Pragma': 'no-cache'
};
return {
login: function(credentials) {
var deferred = $q.defer();
$http.post('/api/users/sign_in', sanitizeCredentials(credentials), {
headers: headersConfig,
timeout: deferred.promise
}).then(function(result) {
if (result.status === 200) {
UserSessionService.cacheSession(result.data);
deferred.resolve();
} else {
deferred.reject();
}
}, function(reject) {
UserSessionService.clean();
deferred.reject(reject);
});
$timeout(function() {
deferred.resolve();
}, 15000);
return deferred.promise;
}
};
I can't remember the exact semantics of $state.go, but usually you need to use $scope.$apply in some manner when responding to events Angular isn't aware of, to ensure a digest cycle occurs. I.e. you could try:
ThirdParty.login(function(result) {
$scope.$apply(function() {
$state.go('dashboard');
});
}, function() {});
You need to get a reference to a scope from somewhere, but one shouldn't be hard to find in an Angular app.
Get rid of both () in
then(callbackServer(), callbackServer())

Resources