Yeoman Angular Fullstack - how to secure routes? - angularjs

I have created a new route 'Rooms' with the generator and have modelled my $stateProvider on the admin route
.state('admin', {
url: '/admin',
templateUrl: 'app/admin/admin.html',
controller: 'AdminController',
controllerAs: 'admin',
authenticate: 'admin'
});
Vs
$stateProvider
.state('rooms', {
url: '/rooms',
templateUrl: 'app/rooms/rooms.html',
controller: 'RoomsCtrl',
controllerAs: 'rooms',
authenticate: 'admin'
});
But my route still appears without authentication!
I guess I am missing a few things to make it secure, though I am unable to understand what!
Can anyone help?
Thanks

your controller should be like:
angular.module('auth8App').controller('RoomsCtrl', function ($scope,Auth,$location) {
//check if the user is logged-in
Auth.isLoggedInAsync(function(loggedIn) {
if (!loggedIn) {
//if the user is not logged Redirect to login
event.preventDefault();
$location.path('/login');
}
});
$scope.message = 'Hello';
});

check if you have an interceptor service factorie defined and called in your app.js lik this:
.config(function ($stateProvider, $urlRouterProvider, $locationProvider, $httpProvider) {
$urlRouterProvider
.otherwise('/');
$locationProvider.html5Mode(true);
$httpProvider.interceptors.push('authInterceptor');
})
.factory('authInterceptor', function ($rootScope, $q, $cookieStore, $location) {
return {
// Add authorization token to headers
request: function (config) {
config.headers = config.headers || {};
if ($cookieStore.get('token')) {
config.headers.Authorization = 'Bearer ' + $cookieStore.get('token');
}
return config;
},
// Intercept 401s and redirect you to login
responseError: function(response) {
if(response.status === 401) {
$location.path('/login');
// remove any stale tokens
$cookieStore.remove('token');
return $q.reject(response);
}
else {
return $q.reject(response);
}
}
};
})
.run(function ($rootScope, $location, Auth) {
// Redirect to login if route requires auth and you're not logged in
$rootScope.$on('$stateChangeStart', function (event, next) {
Auth.isLoggedInAsync(function(loggedIn) {
if (next.authenticate && !loggedIn) {
event.preventDefault();
$location.path('/login');
}
});
});
});

Related

Redirect to UI-Route after Login Successfully

How to redirect to Home Page after Login successful,
I am using UI-router and below is my ui-router code.
var myrouting=angular.module('routingDemoApp', ['ui.router'])
myrouting.config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("", "/index")
$stateProvider
.state('index', {
url: "/index",
templateUrl: "../Views/home.html"
})
.state('contactus', {
url: "/contactus",
templateUrl: "../Views/contactus.html",
})
.state('home', {
url: "/home",
templateUrl: "../Views/home.html",
})
.state('myModal', {
url: "/myModal",
templateUrl: "../Views/SignInPage.html",
})
}]);
I have a button element calling ng-click='login function' for validation Username and Password. once credentials are valid i want to redirect a page to "Home.html"
How to call ui-route after successfully login in below function?
var mymodule2 = angular.module('module2', []);
mymodule2.controller('controller2', function ($scope) {
$scope.loginFunction = function () {
alert("Login");
if ($scope.username == 'abcd' && $scope.password == 'hello123') {
console.log('Login sucessfull');
***// Redirect to Home Page.***
}
}
});
You can just use $state.go("home"); like this, where the "home" is the state name where you would like to go:
var mymodule2 = angular.module('module2', []);
mymodule2.controller('controller2', function ($scope, $state) {
$scope.loginFunction = function () {
alert("Login");
if ($scope.username == 'abcd' && $scope.password == 'hello123') {
console.log('Login sucessfull');
$state.go("home");
}
}
});
Use `$state.go("home");
var mymodule2 = angular.module('module2', []);
mymodule2.controller('controller2', function ($scope) {
$scope.loginFunction = function () {
alert("Login");
if ($scope.username == 'abcd' && $scope.password == 'hello123') {
console.log('Login sucessfull');
// add $state.go here
$state.go("home);
}
}
});

AngularJS authentication service using Satellizer and Laravel

I'm experimenting with Angular, trying to setup a Angular frontend powered by a laravel backend (REST API). For authentication i want to use json web tokens. For Angular i'm using Satellizer (https://github.com/sahat/satellizer) and for Laravel JWT (https://github.com/tymondesigns/jwt-auth).
At the moment, i already can login with AngularJS using correct credentials stored in Laravel. The user info and token is stored in localStorage.
I would like to implement some kind of Angular service that can check if the user is authenticated in order to protect states form ui-router. I already tried a few tuts but i'm unable te get it working. Can someone point me in the right direction? Would be awesome!
loginController (Angular)
.controller('loginCtrl', [
'$scope',
'$rootScope',
'utils',
'$auth',
'$location',
'SweetAlert',
function ($scope,$rootScope,utils, $auth, $location, SweetAlert) {
$scope.login = function() {
var credentials = {
email: $scope.email,
password: $scope.password
};
$auth.login(credentials)
.then(function (response) {
var user = JSON.stringify(response.data.user);
localStorage.setItem('currentUser', user);
$location.path('/restricted.dashboard');
})
.catch(function (response) {
SweetAlert.swal("Inloggen mislukt.", "Controleer je email adres en wachtwood en probeer opnieuw.", "warning");
console.log("LOGIN NOT OK" + response);
});
};
app.states.js (ui-router, Angular)
.config([
'$stateProvider',
'$urlRouterProvider',
function ($stateProvider, $urlRouterProvider) {
// Use $urlRouterProvider to configure any redirects (when) and invalid urls (otherwise).
$urlRouterProvider
.when('/dashboard', '/')
.otherwise('/');
$stateProvider
.state("error.404", {
url: "/404",
templateUrl: 'app/componentsOld/pages/error_404View.html'
})
.state("error.500", {
url: "/500",
templateUrl: 'app/componentsOld/pages/error_500View.html'
})
// -- LOGIN PAGE --
.state("login", {
url: "/login",
templateUrl: 'app/components/auth/loginView.html',
controller: 'loginCtrl',
resolve: {
deps: ['$ocLazyLoad', function ($ocLazyLoad) {
return $ocLazyLoad.load([
'lazy_uikit',
'lazy_iCheck',
'app/components/auth/loginController.js',
'sweetAlert'
]);
}]
}
})
// -- RESTRICTED --
.state("restricted", {
abstract: true,
url: "",
views: {
'main_header': {
templateUrl: 'app/shared/header/headerView.html',
controller: 'main_headerCtrl'
},
'main_sidebar': {
templateUrl: 'app/shared/main_sidebar/main_sidebarView.html',
controller: 'main_sidebarCtrl'
},
'': {
templateUrl: 'app/views/restricted.html'
}
},
resolve: {
deps: ['$ocLazyLoad', function ($ocLazyLoad) {
return $ocLazyLoad.load([
'lazy_uikit',
'lazy_selectizeJS',
'lazy_switchery',
'lazy_prismJS',
'lazy_autosize',
'lazy_iCheck',
'lazy_themes',
'lazy_style_switcher',
'sweetAlert'
]);
}]
}
})
app.js (Angular)
....
// Satellizer configuration that specifies which API
// route the JWT should be retrieved from
$authProvider.loginUrl = 'zz/zz/laravel/public/api/authenticate';
// Redirect to the auth state if any other states
// are requested other than users
$urlRouterProvider.otherwise('/auth');
....
Laravel authenticateController (Laravel 5.2)
public function authenticate(Request $request)
{
$credentials = $request->only('email', 'password');
try {
// verify the credentials and create a token for the user
if (!$token = JWTAuth::attempt($credentials)) {
return response()->json(['error' => 'invalid_credentials'], 401);
}
} catch (JWTException $e) {
// something went wrong
return response()->json(['error' => 'could_not_create_token'], 500);
}
$user = Auth::user();
//$user->company;
//$street = $user->company->street;
// if no errors are encountered we can return a JWT
return response()->json([
"token" => $token,
"user" => $user
]);
}
What i would like to accomplish is creating a laravel Middleware for Angular so that we can check if a user is authenticated and in the future he or she has the right user role to load a Angular state.
Thanks for taking time for my issue. I'm looking forward to see how you would handle this :)
I found this solution somewhere on SO a while ago and it suits my needs(cant provide original answer yet, credits to the original answer). Event hough I use $routeProvider, I think you can apply similar logic with $stateProvider
.config(function ($routeProvider) {
$routeProvider
.when('/awesomeRoute', {
templateUrl: 'views/view.tpl.html',
controller: 'someAwesomeCtrl',
controllerAs: 'someCtrl',
resolve : {
//This function is injected with the AuthService where you'll put your authentication logic
'auth' : function(AuthService){
return AuthService.authenticate();
}
}
})
})
The Auth service:
.factory('AuthService', function($q, Service, $window, $location){
return {
authenticate : function(){
//Authentication logic here, some service to check
against backend if token you provided is valid and/or you are authorized to enter those sections of application,
or in your case with satellizer you can use `$auth.isAuthenticated()` method.
Service.isTokenValid(_yourToken).then( (res) => {
if(res.data === true){
return true;
} else {
return $q.reject('Not Authenticated');
}
}, (err) => {
$location.path('/');
return $q.reject('Not Authenticated');
})
} else {
return $q.reject('Not Authenticated');
}
}
};
})
and finally the "route error catcher", when there is something wrong, return home or to /auth route
.run(function($rootScope, $location){
//If the route change failed due to authentication error, redirect them out
$rootScope.$on('$routeChangeError', function(event, current, previous, rejection){
if(rejection === 'Not Authenticated'){
$location.path('/');
}
});
})

How to set my initial page to a Login html page

In my AngularJS SPA, I would like to force a sort of splash page with a login form. Once the user is authenticated, then I would like to load the full website.
Here is my app.config, which currently triggers a modal logon. However, this is not the right solution for my application. I do not want to load any nav bars UNTIL the user is fully logged in.
angular
.module('rage')
.config(config);
function config($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/dashboard");
$stateProvider
.state('corp', {
url: "/corp",
templateUrl: "app/views/corp.html",
data: { pageTitle: 'RAGE' }
})
.state('maint', {
url: "/maint",
templateUrl: "app/views/maint.html",
data: { pageTitle: 'RAGE' }
})
.state('main', {
url: "/dashboard",
templateUrl: "app/views/dashboard.html",
controller: 'MainCtrl',
data: { pageTitle: 'RAGE', requireLogin: true },
resolve: {
authUser: ['$rootScope', 'loginService', 'userService', function ($rootScope, loginService, userService, authUser) {
return loginService.loginModal().then(function (user) {
$rootScope.userID = user.userId;
initSession(user, $rootScope, loginService, userService);
return user;
})
}]
}
})
.state('login', {
url: "/login",
templateUrl: "app/components/login/login.html",
controller: 'LoginCtrl'
})
}
function initSession(user, $rootScope, loginService, userService) {
userService.getInitParams().then(function (envJson) {
$rootScope.rageSessionVars = envJson;
userService.initRazor(envJson).then(function (data) {
var response = data.status;
if (response.match(/SUCCESS/g)) {
userService.openUserSession(razorEnvJson).then(function (data) {
// ...
});
}
});
});
}
Should I handle this in my app.config ?
Any advice is appreciated.
thanks,
Bob
You can do one thing.Make a state like below.
state("context", {
url:"/context",
template: '',
controller : "contextCtrl"
}).
And in 'contextCtrl' you should check if the user is authenticated or not like below
app.controller("contextCtrl",function($scope, $rootScope, userLoginService,$state, SessionService){
if(SessionService.getSessionUser()==null){
$state.go("login");
}else{
if(SessionService.getSessionUser().authorities[0].authority=="ROLE_ADMIN"){
$state.go("dashboard");
}
}
});
SessionService in above code is your localStorageService which stores the logged in user. Now in dashBoardCtrl of dashboard state (MainCtrl in your case) you should check on every request whether the user is authenticated or not. Following service checks the response of every request.If the response's status is 403,page will be redirected to login page.
service.factory('APIInterceptor', function($q) {
return {
'response': function(response) {
// do something on error
if(response.status == 302 ){
window.location.href = contextPath;
return $q.reject(response);
}
return response;
},
'responseError': function (rejection) {
if(rejection.status === 0) {
location.reload();
}
if(rejection.status == 403 ){
window.location.href = contextPath+'/#/login';
return $q.reject(response);
}
return $q.reject(rejection);
}
};
})
With this,if the user is not authenticated he will be redirected to login page.Hope it helps you.

How to restrict routes with $routeProvider

In a MEAN app, I have an authService module with an Auth factory which contains an authFactory.isLoggedIn function:
// check if a user is logged in
// checks if there is a local token
authFactory.isLoggedIn = function() {
if (AuthToken.getToken())
return true;
else
return false;
};
So I thought I could use this with the resolve property of $routeProvider like this:
var MyModule = angular.module('app.routes', ['ngRoute']);
MyModule.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
$routeProvider
// route for the home page
.when('/', {
templateUrl : 'app/views/pages/home.html'
})
// login page
.when('/login', {
templateUrl : 'app/views/pages/login.html',
controller : 'mainController',
controllerAs: 'login'
})
// register page
.when('/register', {
templateUrl: 'app/views/pages/register.html',
controller: 'userCreateController',
controllerAs: 'register'
})
// upload page
.when('/upload', {
templateUrl : 'app/views/pages/upload.html',
controller: 'uploadController',
controllerAs: 'userupload',
resolve: function($q, $location) {
var deferred = $q.defer();
deferred.resolve();
if (!Auth.isLoggedIn) {
$location.path('/login');
}
return deferred.promise;
}
})
//logout
.otherwise({redirectTo: '/'});
$locationProvider.html5Mode(true);
}]);
Unfortunately this doesn't work to stop unauthenticated users accessing the upload page and I don't see any errors being reported.
I have seen instances of simpler ways to do this eg:
.when('/upload', {
templateUrl : 'app/views/pages/upload.html',
controller: 'uploadController',
controllerAs: 'userupload',
isLoggedIn: true
})
But that doesn't work either, which is a shame as it's far simpler.
In the end I was determined to use the resolve property of $routeProvider so after experimenting with the solution on http://midgetontoes.com/blog/2014/08/31/angularjs-check-user-login
I came up with:
var MyModule = angular.module('app.routes', ['ngRoute']);
MyModule.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
var onlyLoggedIn = function($location, $q, Auth) {
var deferred = $q.defer();
if (Auth.isLoggedIn()) {
deferred.resolve();
} else {
deferred.reject();
$location.url('/login');
}
return deferred.promise;
};
$routeProvider
// route for the home page
.when('/', {
templateUrl : 'app/views/pages/home.html'
})
// login page
.when('/login', {
templateUrl : 'app/views/pages/login.html',
controller : 'mainController',
controllerAs: 'login'
})
// register page
.when('/register', {
templateUrl: 'app/views/pages/register.html',
controller: 'userCreateController',
controllerAs: 'register'
})
// upload page
.when('/upload', {
templateUrl : 'app/views/pages/upload.html',
controller: 'uploadController',
controllerAs: 'userupload',
resolve:{loggedIn:onlyLoggedIn}
})
//logout
.otherwise({redirectTo: '/'});
$locationProvider.html5Mode(true);
}]);
I am sure this isn't as good as the custom http interceptor as posited by #Dimitiri Algazin or as simple as the solution from #Pasan Ratnayake but it does fulfil my quest to use resolve. Thanks to #Dimitri and #Pasan anyway.
Add custom http interceptor. This is not exact code, just algorithm, some syntax might missing:
.factory('myHttpInterceptor', function($q, $location, AuthToken) {
function isLoggedIn() {
return !!AuthToken.getToken();
}
function canRecover(response) {
var status = response.status;
var config = response.config;
var method = config.method;
var url = config.url;
console.log("--->>> ", method, status, url);
if (status == 401) {
alert("401");
} else if ( status == 403) {
alert("403");
} else if (status == 404) {
alert("404");
} else if (status == 405) {
alert("405");
} else if (status == 500) {
alert("500");
} else {
}
return response;
}
return {
// optional method
'request': function(config) {
if (isLoggedIn()) {
return config;
} else {
$location.path('login');
}
},
// optional method
'response': function(response) {
// do something on success
return response;
},
// optional method
'requestError': function(rejection) {
return $q.reject(canRecover(rejection));
},
// optional method
'responseError': function(rejection) {
return $q.reject(canRecover(rejection));
}
};
})
.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('myHttpInterceptor');
}])
There are multiple ways you could achieve this functionality.
Easiest would be to add a check similar to below code to each controller that you don't want your users to access.
// You could re-direct the user to a '401' state as well
if (!authFactory.isLoggedIn())
$state.go('login');

Divert to alternate homepage if user is not logged in using UI-Router & AngularJS

I would like to have two home pages, the first would be for users who have not logged in and the second for users that are logged in.
This is my current set up:
.config(function ($stateProvider, $urlRouterProvider, $locationProvider, $httpProvider) {
$urlRouterProvider
.otherwise('/');
$locationProvider.html5Mode(true);
$httpProvider.interceptors.push('authInterceptor');
})
.factory('authInterceptor', function ($rootScope, $q, $cookieStore, $location) {
return {
// Add authorization token to headers
request: function (config) {
config.headers = config.headers || {};
if ($cookieStore.get('token')) {
config.headers.Authorization = 'Bearer ' + $cookieStore.get('token');
}
return config;
},
// Intercept 401s and redirect you to login
responseError: function(response) {
if(response.status === 401) {
$location.path('/login');
// remove any stale tokens
$cookieStore.remove('token');
return $q.reject(response);
}
else {
return $q.reject(response);
}
}
};
})
.run(function ($rootScope, $location, Auth) {
// Redirect to login if route requires auth and you're not logged in
$rootScope.$on('$stateChangeStart', function (event, next) {
if (next.authenticate && !Auth.isLoggedIn()) {
$location.path('/login');
}
});
});
.config(function ($stateProvider) {
$stateProvider
.state('main', {
url: '/',
templateUrl: 'app/main/main.html',
controller: 'MainCtrl',
title: 'Home',
mainClass: 'home',
headerSearch: true
});
});
How could I reconfigure this so I could do something like the following:
.config(function ($stateProvider) {
$stateProvider
.state('welcome', {
url: '/',
templateUrl: 'app/welcome/welcome.html',
controller: 'WelcomeCtrl',
title: 'Welcome',
mainClass: 'welcome',
isLoggedIn: false
});
$stateProvider
.state('main', {
url: '/',
templateUrl: 'app/main/main.html',
controller: 'MainCtrl',
title: 'Home',
mainClass: 'home',
isLoggedIn: true
});
});
Just wanted to show, how we can manage authentication driven access to states. Based on this answer and its plunker, we can enrich each state (which should be accessible only for authenticated users) with a data setting, explained here: Attach Custom Data to State Objects (cite:)
You can attach custom data to the state object (we recommend using a data property to avoid conflicts)...
So let's have some states with public access:
// SEE no explicit data defined
.state('public',{
url : '/public',
template : '<div>public</div>',
})
// the log-on screen
.state('login',{
url : '/login',
templateUrl : 'tpl.login.html',
controller : 'UserCtrl',
})
...
And some with private access:
// DATA is used - saying I need authentication
.state('some',{
url : '/some',
template : '<div>some</div>',
data : {requiresLogin : true }, // HERE
})
.state('other',{
url : '/other',
template : '<div>other</div>',
data : {requiresLogin : true }, // HERE
})
And this could be hooked on on the state change:
.run(['$rootScope', '$state', 'User', function($rootScope, $state, User)
{
$rootScope.$on('$stateChangeStart'
, function(event, toState, toParams, fromState, fromParams) {
var isAuthenticationRequired = toState.data
&& toState.data.requiresLogin
&& !User.isLoggedIn
;
if(isAuthenticationRequired)
{
event.preventDefault();
$state.go('login');
}
});
}])
See all that in action here
There is similar Q & A were I try to show the concept of redirecting Authenticated and Not Authenticated user:
Angular UI Router: nested states for home to differentiate logged in and logged out
maybe that could help to get some idea, how we can use ui-router, and its event '$stateChangeStart' to hook on our decision manager - and its forced redirecting...
the code should be something like this
$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState) { //, fromParams
console.log(toState.name + "," + fromState.name);
if(fromState.name === "") {
if (Auth.isLoggedIn()) {
$state.go('welcome');
event.preventDefault();
} else {
$state.go('home');
event.preventDefault();
} else {
if (toState.authenticate && !Auth.isLoggedIn()) {
$toState.go('login');
event.preventDefault();
}
}
}
so if user entering the application, then if he is logged in take him to welcome else take him to home.
once he is inside, then if he hits some route which needs auth.. then redirect him to login page..
sorry if i did not understood you requirement fully...
.config(function ($stateProvider,$rootScope) {
$stateProvider
.state('welcome', {
url: '/',
templateUrl: 'app/welcome/welcome.html',
controller: 'WelcomeCtrl',
onEnter: function() {
if (userIsLoggedIn()) {
$stateProvider.go('home');
}
});
});
I had problem like this and I solved it like this
.run(function ($rootScope, $location, AuthService) {
// start showing PRELOADER because we doing async call and we dont know when it will be resolved/rej
AuthService.checkLoginStatus().then(
(resp) => {
// fire logged in user event
$rootScope.$broadcast('userLogged',resp);
$location.path(YourHomePageUrl);
},
(err)=> {
// Check that the user is not authorized redirect to login page
$location.path(loginURL);
}
}).finally(
()=> {
// finnaly Set a watch on the $routeChangeStart
/// Hide PRELOADER
$rootScope.$on('$routeChangeStart',
function (evt, next, curr) {
if (!AuthService.isLoggedIn()) {
$location.path(art7.API.loginURL);
}
});
}
)
});
and im not using interceptor for handling 401 not authorized errors, thats my solution

Resources