1

I'm building a SPA using Angular.js and ASP.NET and I would like to know what is the best way to secure it.

Here is what I need :

I would like to use MVC framework to hide my application only to logged users. So the first thing that users will do before launching the SPA will be to log into the website using a simple login form.

When the Angular app will be launched, it will communicate with my ApiController using REST requests.

I also want my user to be logged out automatically after 20 minutes of inactivity.

I know that REST is supposed to be stateless... but I can't figure how to implement all I need without sessions...

But on the other side, I want to be able to use my WebAPI with a future mobile application. I will have to use Tokens for the authentication on this application.

What is the best way for me to achieve that kind of authentication?

Thanks for your time!

2 Answers2

3

I developed an entire security layer with the same conditions as yours following those very well explained in this post here.

BTW, the token will expire automatically after 20 minutes because when you create it you will set it's expiration date immediately; every time you're going to make a request, the system will check the token exp date with the current date, refusing your token if the time passed. For example this a tipical oauth server configuration with token and refresh token settings:

internal static OAuthAuthorizationServerOptions GetAuthorizationServerOptions(IComponentContext scope)
    {
        OAuthAuthorizationServerOptions oAuthServerOptions = new OAuthAuthorizationServerOptions
        {                
            AllowInsecureHttp = true,
            ApplicationCanDisplayErrors = true,
            TokenEndpointPath = new PathString(Constants.PublicAuth.OAUTH_TOKEN_PATH),
            AuthorizeEndpointPath = new PathString(Constants.ExternalAuth.AUTH_ENDPOINT),
            AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(Constants.PublicAuth.TOKEN_EXPIRATION_IN_MINUTES),
            Provider = scope.Resolve<AuthorizationServerProvider>(),
            AccessTokenFormat = new CustomJwtFormat(),
            RefreshTokenProvider = scope.Resolve<SimpleRefreshTokenProvider>()
        };
        return oAuthServerOptions;
    }

The refresh token is also very useful, but you have to manage the token replacement by yourself; for example in our application we pass every API call through a single service that, if the server responds 401 (unauthorized), it will try to request a new token using the refresh token and then it will try the same call again. Only after the second failure you'll be redirected to the login page.

For example:

function executeCallWithAuth(method, url, payload, params) {
    var defer = $q.defer();
    debug.logf('{0}: {1}', method.toUpperCase(), url);
    $http({ method: method, url: url, data: payload, headers: createHeaders(), params: params }).then(
        function(results) { defer.resolve(results); }, 
        function(error) {
            if (error.status !== 401) defer.reject(error);
            else {
                debug.warn(`Call to: ${method}:${url} result in 401, try token refresh...`);
                auth.refreshToken().then(
                    function() {
                        debug.warn('Token refresh succesfully, retry api call...');
                        $http({ method: method, url: url, data: payload, headers: createHeaders() }).then(
                            function(results) { defer.resolve(results); },
                            function(errors) { defer.reject(errors); });
                    },
                    function(tokenError) {
                        debug.warn('Token refresh rejected, redirect to login.');
                        $state.go('login');
                        defer.reject(tokenError);
                    });
            }
        });

    return defer.promise;
}

and

    function createHeaders() {
    var headers = {
    };

    var authData = storage.get('authorizationData');
    if (authData) {
        headers.Authorization = 'Bearer ' + authData.token;
    }

    return headers;
}

Using Angular the best way to secure a route is "do not create a route". Basically, you need to load the user profile, and only after that you will create the routes only to the pages he can navigate to. If you don't create the route for a page you don't need to secure that page: Angular will automatically send the user to a 404.

Luca Ghersi
  • 3,261
  • 18
  • 32
  • Thanks for your time Luca! Please take this example : User logs in the application at 12:00... token is valid for 20 minutes... so it expires at 12:20. User don't use the application for ten minutes... at 12:10, he uses it... token is still only got for ten other minutes... How do I prolongate the expiration for another 20 minutes? As ASP.NET sessions would do? I have a little idea... do I have to extend refresh token by 20 minutes everytime one of my WebAPI get a call? Or is there another better way to acheive that? Thanks! – Jean-Philippe B. Mar 16 '16 at 18:21
  • This is a kind of "sliding-expiration" token, and yes, you can do it if you want. But the problem with this kind of approach is that you have to generate a new token for EVERY call, no matter when or how many. It's definitively expensive. One other option is to set the token expiration at, for example, 30 minutes, and set a timeout func in JS to refresh the token every 25 minutes, for example, using the refresh token as explained; you can use the api service with 401 management as last resort. – Luca Ghersi Mar 16 '16 at 18:30
  • Instead of generating a new token... I think it would be better to use a simple SQL query to update my RefreshToken expiration date... no? Your timeout func would work... but in our case here, we don't want an inactive user session (token) to be extended... it's a security risk we can't afford. – Jean-Philippe B. Mar 16 '16 at 18:45
  • Another way would maybe to create a new class of AuthorizeAttribute, and override IsAuthorized method to go in database to look at the last time an API was called... a kind of custom session management using tokens... But in that case... I'm asking myself... why not simply use Session authentication instead of Sliding Tokens... – Jean-Philippe B. Mar 16 '16 at 18:48
  • I suggest you to read the article I linked at the beginning of my answer. The refresh token validity can be extremely long, even days. The point here is to have a valid bearer token. The refresh token is just a "trick" to avoid calculating again the user claims. – Luca Ghersi Mar 16 '16 at 18:49
0

I would secure your WebAPI calls with OAuth2 (you can even use the built in Identity 2.0 provider that comes baked in with it). Keep your WebAPI stateless, use SSL (consider a filter to force it), and use the [Authorize] tags to secure you services. On the MVC side, this will have to maintain state and you will want to have the login form get an OAuth2 token from your WebAPI layer and pass that down into Angular. Set the expiration on this to 20 minutes. You can also use the cookies authentication model here since it will need to be stateful on the MVC side, but all ajax calls made to the WebAPI layer by Angular will need to pass the OAuth2 token as a bearer token in the Authorization request header.

Daniel Graham
  • 399
  • 3
  • 13
  • Thanks Daniel for your quick answer.How do I make the Bearer token expire after 20 minutes of inactivity? I every documentation I read, it's not normal to prolongate the expiration time of a Bearer token... Maybe a refresh token? But how can I manage the fact that after 20 minutes of inactivity, I want the token to be canceled? – Jean-Philippe B. Mar 16 '16 at 01:00
  • See this post: http://stackoverflow.com/questions/33701398/oauth2-webapi-token-expiration Realize that since bearer tokens are going to be stateless, I don't believe you're going to get a sliding expiration out of them. The best thing to do here is to setup in Angular to catch any http unauthorized errors and have it refresh its bearer token by getting a new one. – Daniel Graham Mar 23 '16 at 18:55