Bestpractice Authentication in AngularJS 1x with ui router - angularjs

I got a working angular code to successfully get/store/retrieve a jwt token for authentication.
Also I got the following route controller:
// Router configuration
App.config(['$stateProvider', '$urlRouterProvider',
function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/dashboard');
$stateProvider
.state('dashboard', {
url: '/dashboard',
templateUrl: 'assets/views/dashboard.html'
})
.state('customers', {enter code here
url: '/customers',
templateUrl: 'assets/views/customers.html'
})
.state('customersAdd', {
url: '/customers/add',
templateUrl: 'assets/views/customers_add.html'
})
.state('account', {
url: '/account',
templateUrl: 'assets/views/account.html'
})
.state('login', {
url: '/login',
templateUrl: 'assets/views/login.html'
});
}
]);
I went through different tutorials on the net and I think, for my understanding the best approach would be using resolve on my routes. Yet I don't know how to bring the pieces togehter. I got a working auth service with a function auth.isAuth() that returns true or false.
But how can I bind this into my routes? And also I would like to explicit mark routes that doesn't need auth.isAuth() to be true, because there are only like two states that doesn't require an authenticated user.
Greetings
eXe

Just in case someone is looking for the same, this is my final solution:
// Router configuration
App.config(['$stateProvider', '$urlRouterProvider',
function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/dashboard');
$stateProvider
.state('auth', {
url: '',
abstract: true,
resolve: {
loginRequired: loginRequired
},
})
.state('auth.dashboard', {
url: '/dashboard',
templateUrl: 'assets/views/dashboard.html'
})
.state('auth.customers', {
url: '/customers',
templateUrl: 'assets/views/customers.html'
})
.state('auth.customersAdd', {
url: '/customers/add',
templateUrl: 'assets/views/customers_add.html'
})
.state('auth.account', {
url: '/account',
templateUrl: 'assets/views/account.html'
})
.state('login', {
url: '/login',
templateUrl: 'assets/views/login.html'
});
}
]);
function loginRequired($q, $location, auth) {
var deferred = $q.defer();
if (auth.isAuthed()) {
deferred.resolve();
} else {
$location.path('/login');
}
return deferred.promise;
}
By using nested states my 'auth' state acts like a middleware. Now I can easily manage my routes.

Here is how I do it. Borrowed from the Satellizer library. Works great!
//note the resolve block.
$stateProvider
.state('home', {
url: '/home',
controller: 'HomeCtrl',
templateUrl: 'views/home.html',
data: {
pageTitle: 'Home'
},
resolve: {
loginRequired: loginRequired
}
})
.state('splash', {
url: '/',
templateUrl: 'views/auth/splash.html',
controller: 'SplashCtrl',
data: {
pageTitle: 'Welcome'
},
resolve: {
skipIfLoggedIn: skipIfLoggedIn
}
})
//add to your apps config block, below your route definitions
function skipIfLoggedIn($q, auth) {
var deferred = $q.defer();
if (auth.isAuth()) {
deferred.reject();
} else {
deferred.resolve();
}
return deferred.promise;
}
function loginRequired($q, $location, auth) {
var deferred = $q.defer();
if (auth.isAuth()) {
deferred.resolve();
} else {
$location.path('/');
}
return deferred.promise;
}

Related

Ionic 1 - Show login page if user not logged and skip if user is already logged

Working on an Ionic version 1.3.3 application where need following functionalities for user login. I had go through all stackoverflow answer but nothing found a workable solution for me.
App will check on start if user already logged in (check through Ionic $localstorage) then redirect to Home page
If the user is not logged redirect to login page on app start
On login page after login success redirect to home page and clear login page history.
angular.module('starter', ['ionic', 'starter.controllers', 'starter.directives', 'starter.services', 'ngStorage','ab-base64',])
.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('app', {
url: '/app',
abstract: true,
cache: false,
templateUrl: 'templates/menu.html',
controller: 'AppCtrl',
onEnter: function ($state) {
console.log($state);
}
})
.state('app.home', {
cache: false,
url: '/home',
views: {
'menuContent': {
templateUrl: 'templates/home.html'
}
}
})
.state('app.login', {
cache: false,
url: '/login/:username/:password',
views: {
'menuContent': {
templateUrl: 'templates/login.html',
controller: 'LoginController'
}
}
})
.state('app.profile', {
cache: false,
url: '/profile',
views: {
'menuContent': {
templateUrl: 'templates/profile.html',
controller: 'ProfileController'
}
}
})
$urlRouterProvider.otherwise('/app/home');
})
This is how I accomplished this in Ionic v1:
For the redirect if user is logged in:
.state("app.dash", {
url: "/dashboard",
abstract: true,
views: {
mainContent: {
templateUrl: "templates/dashboard.html",
controller: "DashboardCtrl",
controllerAs: "vm",
resolve: {
auth: [
"authService",
function(authService) {
return authService.isAuthenticated();
}
],
permissions: [
"authService",
function(authService) {
return authService.getPermissions();
}
]
}
}
}
})
For the redirect when user logs in or is already logged in.
.state("app.login", {
url: "/login?accountCreated",
views: {
mainContent: {
templateUrl: "templates/login.html",
controller: "LoginCtrl",
controllerAs: "vm",
resolve: {
isLoggedIn: [
"$q",
"$state",
"authService",
function($q, $state, authService) {
authService.isAuthenticated().then(function(res) {
$state.go("app.dash.home");
});
return $q.defer().resolve();
}
]
}
}
}
})
Auth service isAuthenticated()
function isAuthenticated() {
var deferred = $q.defer();
getToken().then(function(token) {
isExpired().then(function(isExpired) {
if (!token || isExpired) {
deferred.reject("Not Authenticated");
} else {
decodeToken().then(function(decodedToken) {
deferred.resolve(decodedToken);
});
}
});
});
return deferred.promise;
}

how do i create the remember me on login with firebase me on the routes.js

this is an ionic app with firebase google authentication in it. i wanna know the correct way of doing this coz my login logic works but the routing crashes the app. unfortunately there are no errors on console the app just goes blank.
angular.module('app.routes', ['app.controllers','firebase'])
.config(function($stateProvider, $urlRouterProvider)
{
$stateProvider
.state('tabsController.allTasks', {
url: '/alltasks',
views: {
'tab1': {
templateUrl: 'templates/allTasks.html',
controller: 'allTasksCtrl',
resolve : {
}
}
}
})
.state('tabsController', {
url: '/page1',
templateUrl: 'templates/tabsController.html',
abstract:true
})
.state('signup', {
url: '/signup',
templateUrl: 'templates/signup.html',
controller: 'signupCtrl'
})
.state('onboarding', {
url: '/onboarding',
templateUrl: 'templates/onboarding.html',
controller: 'onBoardingCtrl'
})
.state('logOut', {
url: '/logout',
templateUrl: 'templates/logOut.html',
controller: 'logOutCtrl'
})
.state('taskdetail', {
url: '/taskdetail',
templateUrl: 'templates/taskdetail.html',
controller: 'allTasksCtrl'
})
firebase.auth().onAuthStateChanged(function(user) {
if (user)
{
$urlRouterProvider.otherwise('tabsController.allTasks')
}
else
{
$urlRouterProvider.otherwise('login')
}
});
Remove that firebase block of code in your .config and instead insert this in a .run block.
.run(function($state) {
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com");
var authData = ref.getAuth();
if (authData) {
$state.go('tabsController.allTasks');
} else {
$state.go('login');
}
})

Querystring on Routes in angular

I would like to configure my routes to be able to accept any integer after its' URL.
For example,
/product/id/1242
This integer will be accessed by the API factory as a value to be queried by the API.
I have read doc's on setting up routes, but haven't been able to do to it. If i enter a URL with an integer, it just redirects to the login page.
angular.module('myapp123.routes', [])
.config(['$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/login");
//$location.path('/product').search('queryStringKey', value).search( ...);
$stateProvider
.state('login', {
url: '/login',
views: {
'menuContent': {
templateUrl: 'app/core/login/login.html'
}
},
controller: function ($ionicHistory, $scope) {
console.log('Clearing history!');
$ionicHistory.nextViewOptions({
historyRoot: true,
disableBack: true
});
}
})
.state('product', {
url: '/product',
when:('/product/id/product_id')
views: {
'menuContent': {
templateUrl: 'app/components/product/product.html'
}
}
})
According to the docs This should do the trick. [0-9] limits the product Id to numbers.
.state('product', {
url: '/product/id/{productId:[0-9]}',
views: {
'menuContent': {
templateUrl: 'app/components/product/product.html'
}
}
Thanks- wasn't able to get your version working for some reason.
A work-around I've found, which works:
.state('product', {
url: '/product/id/:product_id',
templateUrl: 'app/components/product/product.html',
controller: function($scope, $stateParams) {
$scope.product_id = $stateParams.product_id;
}
})

Angular app initialization with asynchronous data

I need to initialize my Angular with some User data that the whole app depends on. Therefore I need the initialization to be resolved before the router kicks in and controllers are initialized.
Currently, I wrote the initialization code in a run() block of the angular module. The initialization involves an asynchronous http request to get user data and the rest of the application relies upon the user data.
How can I ensure that the http request is resolved before the router kicks-in initializing the controllers?
I am using the ui-router.
The initialization consists in the following:
1) get cookie 'userId'
2) get User from server (asynchronous http request, the whole app depends upon the User)
3) set authService.currentUser
this is a sample of the code
.run(['$cookies', 'userApiService', 'authService',
function($cookies, userApiService, authService){
var userId = $cookies.get('userId');
userId = parseCookieValue(userId);
userApiService.getOne(userId).then(function(user){
authService.currentUser = user;
});
}])
.config(['$stateProvider', '$urlRouterProvider', '$locationProvider',
function($stateProvider, $urlRouterProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$urlRouterProvider.when('/', '/main');
$stateProvider
.state('login', {
url: '/login',
views: {
'header': {
templateUrl: 'views/header.html',
controller: 'HeaderCtrl'
},
'content': {
templateUrl: 'views/login.html',
controller: 'LoginCtrl'
},
'footer': {
templateUrl: 'views/footer.html',
}
}
})
.state('main', {
url: '/main',
views: {
'content#': {
template: '',
controller: function($state, authService) {
if(!authService.isAuthenticated()) {
$state.go('login');
}
if(authService.isStudent()) {
$state.go('student');
}
if(authService.isAdmin()) {
$state.go('admin');
}
}
}
}
})
.state('student', {
url: '/student',
views: {
'header#': {
templateUrl: 'views/header.html',
controller: 'HeaderCtrl'
},
'content#': {
templateUrl: 'views/student.html',
controller: 'StudentCtrl'
},
'footer#': {
templateUrl: 'views/footer.html',
}
}
})
.state('admin', {
url: '/admin',
views: {
'header#': {
templateUrl: 'views/header.html',
controller: 'HeaderCtrl'
},
'content#': {
templateUrl: 'views/admin.html',
controller: 'AdminCtrl'
},
'footer#': {
templateUrl: 'views/footer.html',
}
}
})
}])
Expanding on someone's comment, you can create a root state that is a parent to all of your other app's states (children to the root). The root state resolves all the user data and then you can inject the user data to any controller or store it in a service.
$stateProvider
.state('root', {
url: '',
abstract: true,
template: '', // some template with header, content, footer ui-views
resolve: {
// fetch user data
}
})
.state('root.login', {
url: '/login',
views: {
'header': {
templateUrl: 'views/header.html',
controller: 'HeaderCtrl'
},
'content': {
templateUrl: 'views/login.html',
controller: 'LoginCtrl'
},
'footer': {
templateUrl: 'views/footer.html',
}
}
})
.state('root.main', {
url: '/main',
views: {
'content#': {
template: '',
controller: function($state, authService) {
if(!authService.isAuthenticated()) {
$state.go('login');
}
if(authService.isStudent()) {
$state.go('student');
}
if(authService.isAdmin()) {
$state.go('admin');
}
}
}
}
})
... // your other states
The key is that all of your app states must be a child of your root state i.e. root.<name> in your state declaration. This will ensure that no other controller starts until your user data is available. For more information on resolve and how to use it read here. Also, parent and child states.

How to configure state in ionic blank app?

I cretaed a new ionic blank app intuit there are two html pages: 1 index.html and a category.html. I configured the states and then added controller in app.js file but it is not working.
This is my state configurations:
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('camera',{
url:"/camera",
cache: false,
controller:"CameraCtrl",
templateUrl:'index.html'
})
.state('category', {
url: "/category",
templateUrl: "category.html",
controller: 'CategoryCtrl'
})
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/camera');
})
And this is my controller:
.controller('CameraCtrl',function($scope,$state){
$scope.menu = function() {
console.log('yesytest');
$state.go('category');
// window.location = "category.html";
};
})
This is my app.js. Is anything wrong here?
Unfortunately I only had 15 minutes to mock this together, Let me know if this works for you and if you need any further assistance drop me a response.
Controller
var example = angular.module('starter', ['ionic', 'starter.controllers',]);
example.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('tab', {
url: "/tab",
abstract: true,
templateUrl: "templates/tabs.html"
})
.state('tab.home', {
url: '/home',
views: {
'home': {
templateUrl: 'templates/home.html'
}
}
})
.state('tab.camera', {
url: '/camera',
views: {
'camera': {
templateUrl: 'templates/camera.html'
}
}
})
.state('tab.category', {
url: '/category',
views: {
'category': {
templateUrl: 'templates/category.html'
}
}
});
$urlRouterProvider.otherwise('/tab/home');
});
>>> Working Example <<<

Resources