Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular-app, authentication and order of resolvers in (ui-)router

This questions refers to the angular-app project and the way it authenticates users.

The original implementation guards access to some urls by using resolve clause on the router. This looks like:

$routeProvider.when('/projects', {
templateUrl:'projects/projects-list.tpl.html',
controller:'ProjectsViewCtrl',
resolve:{
  projects:['Projects', function (Projects) {
    //TODO: fetch only for the current user
    return Projects.all();
  }],
  authenticatedUser: securityAuthorizationProvider.requireAuthenticatedUser
}

});

The view is not rendered until a user is authenticated and projects are fetched (to prevent ui flickering). If a user is not authenticated then login popup pops up and after user submits it then the promise is resolved and a requested page is displayed. This works nice if auth is not required on Projects.all() call.

Here's the log of server calls:

127.0.0.1 - - [Mon, 28 Oct 2013 11:15:47 GMT] "GET /projects HTTP/1.1" 200 739 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:24.0) Gecko/20100101 Firefox/24.0"
Unauthenticated
127.0.0.1 - - [Mon, 28 Oct 2013 11:15:47 GMT] "GET /current-user HTTP/1.1" 200 24 "http://localhost:3000/projects" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:24.0) Gecko/20100101 Firefox/24.0"
Unauthenticated
Unauthenticated
127.0.0.1 - - [Mon, 28 Oct 2013 11:15:47 GMT] "GET /current-user HTTP/1.1" 200 24 "http://localhost:3000/projects" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:24.0) Gecko/20100101 Firefox/24.0"
127.0.0.1 - - [Mon, 28 Oct 2013 11:15:47 GMT] "GET /databases/angular_app/collections/projects?q=%7B%7D HTTP/1.1" 200 10 "http://localhost:3000/projects" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:24.0) Gecko/20100101 Firefox/24.0"
Unauthenticated
127.0.0.1 - - [Mon, 28 Oct 2013 11:15:59 GMT] "POST /login HTTP/1.1" 200 161 "http://localhost:3000/projects" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:24.0) Gecko/20100101 Firefox/24.0"

The call to angular_app/collections/projects is valid even for unauthenticated user.

In my case I have the following code:

$stateProvider
    .state('root.tickets', {
        url: '/tickets',
        views: {
            'container@': {
                templateUrl: 'tickets/tickets-list.tpl.html',
                controller:'TicketsViewCtrl',
                resolve:{
                  ticketsy: ['Restangular', function (Restangular) {
                    //Call to tickets must be authenticated
                    return Restangular.all('tickets').getList();
                  }],
                  authenticatedUser: securityAuthorizationProvider.requireAuthenticatedUser
                }
            }
        }

The difference is (except I use ui-router and Restangular) that the api call has to be authenticated. Server log looks like:

[28/Oct/2013 05:50:15] "GET /api/tickets/ HTTP/1.1" 403 59
[28/Oct/2013 05:50:15] "GET / HTTP/1.1" 200 963
[28/Oct/2013 05:50:16] "GET /api/current-user/ HTTP/1.1" 200 14
[28/Oct/2013 05:50:16] "GET /api/tickets HTTP/1.1" 301 0
[28/Oct/2013 05:50:16] "GET /api/tickets/ HTTP/1.1" 403 59
[28/Oct/2013 05:50:22] "POST /api/login/ HTTP/1.1" 200 120

Note 403 status code here. The effect is that user sees login popup, authenticates but then sees empty page. I suppose this is because of data fetching promise failure.

My question is - is it somehow possible to force order of promises? I'd like to first check if user is authenticated then issue a call to the backend etc. Or is there any other solution I can use here? I'm learning Angular so even if there's a simple solution it is not obvious to me.

like image 554
eXt Avatar asked Oct 28 '13 11:10

eXt


1 Answers

I love using the resolver pattern but find it very difficult to do these types of things in angular ui router.

one solution is to dependency inject the result of the authenticatedUser resolver into the api call resolver you want protected like:

$stateProvider
    .state('root.tickets', {
        url: '/tickets',
        views: {
            'container@': {
                templateUrl: 'tickets/tickets-list.tpl.html',
                controller:'TicketsViewCtrl',
                resolve:{
                  authenticatedUser: securityAuthorizationProvider.requireAuthenticatedUser,
                  ticketsy: function (Restangular, authenticatedUser) {
                    //Call to tickets must be authenticated
                    return Restangular.all('tickets').getList();
                  }
                }
            }
        }

This way the resolvers will run in a chain (authenticatedUser -> ticketsy) rather than async all at once.

I hope this helped.. wish there was a better way of doing it.. thats why im search through stack overflow.

like image 158
Corban Brook Avatar answered Nov 15 '22 21:11

Corban Brook