⚠️ This lesson is retired and might contain outdated information.

Auth Interceptor in Angular for JWT

Kent C. Dodds
InstructorKent C. Dodds
Share this video with your friends

Social Share Links

Send Tweet
Published 10 years ago
Updated 2 years ago

Adding JWT support to the client. Storing the token in local storage, then creating a http interceptor to add the token to requests made to the server. Also implementing logout.

Kent C. Dodds: [00:01] OK, so here we have our application. We can login, and get users. We don't have to be logged in to get users, and that's what we want to try and protect. Also, we don't actually know if we're logged in from the UI, so we're just going to add this really quickly. We'll say H2, Welcome, and it'll be VMuser.username.

[00:26] I'm not going to hide this. Normally you'd want to hide this if there is no VM.user, because this really doesn't make sense, but I'm going to keep it here because we can still get users, and I want to show that until that resource is protected. Anyway, if we login here, now we can see, we do actually login, that comes from the server. Here we have our application.

[00:46] There are a couple of things that we want to do to this application. If you recall from previous videos, when we login, the server is sending us back a token, and we want to save that token, and send it on every request to a protected resource, and so that's what we're going to do in this video, is save this token in local storage, and then set up sending that token on every protected resource request, and then we'll also implement logout.

[01:14] Let's go ahead and set up saving this to local storage. We're going to create a factory that will manage our token for us. We'll just say app.factory, and it'll be an auth token factory, and this will be a simple factory, it'll have a get token, and a set token, which will take a token. We're going to inject window in here so that we can get our store, which will be local storage. Then we'll have a key, and that will be auth token, and then we'll have our get token, and set token.

[01:52] Then here, get token will just simply return store.getitemkey, and our set token will say if a token is provided then we'll say store.setitemkeytoken else store.removeitem. Now we have our auth token factory. When we login, we want to save this token, so we'll add a then here. Here we'll take auth token factory and set token to the response.data.token, and then we'll return the response for future items in the chain.

[02:31] We'll need to inject the auth token factory. Now let's go ahead and see this in action. If I refresh the page, get that new client code, we'll say kentcdodds P login, and let's look at our local storage here, and our resources. There we have our auth token. Now we're storing our token in our local storage, and we want to us it on request to protected resources, and so now we're going to use what in Angular is called an interceptor, and we're going to create that as a factory.

[03:04] We'll say app.factory, it will be our auth interceptor, and interceptors have a couple of cool things on them. You have the request, the request error response, and response error. Each one of them is doing different things to the HTTP config. The one that we care about is the request, so we'll have request and it will be add token, and then here we'll implement that. It'll take the config, and it'll return the config.

[03:35] Here we'll have a token, and let's inject our auth token factory, and will get the token. Now if there is a token, so if the user is authenticated, then we're going to add this to a header on this config object, so we'll say config.headers is equal to config.headers or an empty object, just to kind of protect ourselves there. Then we'll say authorization equals bearer plus the token, and this is just part of the spec. It's the authorization header, image prefixed with bearer space, and then the token.

[04:16] Now we're sending the token on every HTTP request, but not quite actually. We do need to do one more thing, and that is in our app config, we need to inject HTTP provider. On that HTTP provider, it has an array called interceptors. We're going to push on that array just a string that is the name of our interceptor. Angular will look up this interceptor, and every single request that HTTP makes will pass through our function that we defined down here, this add token function.

[04:51] We can see that in action here. Let's refresh our client code, we'll say kentcdodds P, and let's go ahead and look at our network tab. We login, and we can see the token is given to us. We can see in here in our resources we have that token there. Now if we say get user, we'll click here, look at our headers, and it's right there, our auth authorization header with the bearer and the token.

[05:21] Now we need to alter the server to take this token into account when get user is invoked so that that resource can be protected. That's what we'll do in the next video. One more thing that we want to do in here that I want to show you is how easy it is to logout. We have this auth token in here, we're logged in, and it's very simple to add a logout, so we'll just do that now.

[05:45] We'll create a button called logout, I'll just add a break here, and we'll say ngclick is equal to vm.logout, and then up here we'll say vm.logout is equal to logout, and we'll create logout here, and we'll say userfactory.logout. Then in our user factory we have it log in, we'll have it logout. Then well logout, and we say that auth token factory set token to nothing, and that will remove the token from local storage, because that's how we implemented it down here.

[06:29] Then for the view side of things, will say vm.user equals null. If we refresh the page, you can see the auth token is still there, and we don't have any logic to automatically log the user in to the client, and we'll build that in the future. But here we have the user logged in, now we have this logout button. If we click logout, you can see that the auth token is removed from local storage, and no longer is the user name appearing here.

[06:55] If we refresh, that key is still gone, and then we can login and logout. Just in review, we simply added a auth token factory to store the auth token in local storage, and then we get that token from local storage on every single request using our auth interceptor, and attaching it to the authorization header, with bearer and token. Then we take that auth interceptor and push it onto the interceptors of the HTTP provider.

[07:27] We also implemented a logout function that simply set the user to null, and removed the token from local storage. In the next video, we'll talk about what we need to do to make it so that when a request is made to get a random user, the server will take that token into account, and protect that resource to only those who have a signed, verified token. That's what we'll see in the next video.

[07:52] [cuts off]

Marek
Marek
~ 9 years ago

may be little bit easier is use https://github.com/auth0/angular-jwt

Toby
Toby
~ 9 years ago

This is a little off-topic, but how are you getting all of the ng support within WebStorm? I noticed when you make you factories, it auto-completes much of the code for you. Is that something you did within WS? I'm using the Live Templates, but they behave the same way I'm seeing your stuff work.

Kent C. Dodds
Kent C. Doddsinstructor
~ 9 years ago

Hi Toby, I actually don't believe I've done anything to get the auto-complete. All I can say is make sure that you're on the latest version of WebStorm. If that's not it, I'd just google around...

Kent C. Dodds
Kent C. Doddsinstructor
~ 9 years ago

Seems like an awesome library :-) I don't think it was around at the time I made this lesson.

Martin
Martin
~ 9 years ago

From what I've read saving the token to local storage isn't the best solution, and instead it should be saved to a cookie (with SSL for production). Why is there a discrepancy in where different developers suggest storing the token?

Kent C. Dodds
Kent C. Doddsinstructor
~ 9 years ago

From a security standpoint, it doesn't make a difference. The reason I chose not to store it as a cookie is because cookies are commonly used as a means to share data between the client and the server during requests. However, with the "new" architecture of having the client app and the api being served on different domains, that data sharing doesn't happen (clients don't send cookies to servers under a different domain including sub domains). Therefore I believe it's misleading to use cookies and choose local or session storage instead.

Martin
Martin
~ 9 years ago

Thanks Kent, that makes a lot of sense. Cheers

Martin
Martin
~ 9 years ago

Hi Kent, sorry one more question. I've also read about using a refresh token (or single request token) that produces a single use token to reduce the window of attack if a token is compromised, as it's only valid for the single request. Is this something you consider in your applications? and from the current video how would this change the implementation. Seems like you could do it by adding a response to the interceptor that used the AuthTokenFactory. If knowing something about the application I'm working on helps I'm building an Ionic application with a Laravel 5.1 API.

Kent C. Dodds
Kent C. Doddsinstructor
~ 9 years ago

I don't think there's anything wrong with that. The interceptor hides the complexity away from the rest of your app so you don't need to think about it at all.

I've never done this before, but I'd be concerned about timing issues. Like, what if I send several requests at once and the server sends response A before response B, so response B's token is the one that needs to be used next, but then response B gets to the client first and response A's token overrides B's token. I'm not sure how likely that is to occur, and like I said I've never tried this kind of an architecture, but I'd be concerned about that.

Just as a tip. As far as I'm aware, most web applications are using cookies or localStorage with a token that lasts the duration of the logged in session. This is no less secure than cookies. In either case, if someone gets ahold of the machine, they can look right at the cookie/token and use it to make requests. What you've suggested would help alleviate that concern only if that individual weren't able to get the next valid token from your server, which is unlikely. So I don't really see what this approach buys you...

Matheus
Matheus
~ 9 years ago

Hi Kent, thanks for the video it was awesome. About this topic I'm actually reading a lot to further understand the security discussion and from what I read I think that it still scares me a little bit using the localStorage because of possible XSS.

A malicious script (not necessary unsanitized from the end-user; it could be from a CDN compromised script) could get the JWT of a bunch of clients using this technique. Using an HTTP only SSL cookie would have the advantage of not allowing this kind of hijacking, however, now you have to worry about CSRF, and that sucks as well.

OWASP Top 10 list puts XSS before CSRF so I'm guessing this problem is harder to spot and mitigate the risks.

What is your opinion on that? I'm being completely honest with you in that I don't know what's the next step to take as I already have an Mobile App running on Windows Azure Mobile Services and now that we're implementing also a Web Client our team is having these kind of concerns.

Thanks a lot once again.

Kent C. Dodds
Kent C. Doddsinstructor
~ 9 years ago

I think, like with all technology, you have to make certain trade-offs. If you're concerned about a compromised script from a CDN hijacking your users and stealing tokens, then don't use a CDN you don't trust. You could potentially store the token in a cookie if you want to. But I'm not certain that you'd be able to access it.

Nate Gibbons
Nate Gibbons
~ 8 years ago

Here's a great article with a run-down on the security trade-offs for either storage solution:

https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage/

Juan
Juan
~ 8 years ago

Any samples on how to create the cookie alternative?

Juan
Juan
~ 8 years ago

A video series explaining the two alternatives would be great (tokens in local storage VS cookies for storage)

Kent C. Dodds
Kent C. Doddsinstructor
~ 8 years ago

It's pretty much just a matter of changing the AuthTokenFactory to utilize the Cookie API rather than the LocalStorage API.

Tony
Tony
~ 8 years ago

Kent,

My project's code is split out such that the controllers are within different files as is the factory. This method has been working well for me... until I try to add the Auth Interceptor. I have a module('report') to which I am trying to add the interceptor. I have tried the below, but while I get no error, the function also doesn't get called.

My question is, how to I inject the code defined in the jwt.js file (below) into the module after the module has been defined in my app.js rather than lumping all the code into a huge jumbled js file.

angular.module('report') .requires.push.apply(angular.module('report'), ['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('AuthInterceptor'); }]);

The jwt.js file:

(function () {

angular
    .module('report')
    .factory('AuthInterceptor', function (API, auth) {
        return {
            // automatically attach Authorization header
            request: addToken
        };
        function addToken (config) {
            var token = jwt.getToken();
            
            if (token) {
                config.headers = config.headers || {};
                config.headers.Authorization = 'Bearer ' + token;
            }
            return config;
        }
    })
    .factory('jwt', function ($window, $q) {
        var store = $window.localStorage,
            key = 'auth-token',
            dev = $q.defer();
    
        return {
            setToken: setToken,
            getToken: getToken
        };
    
        function setToken (token) {
            if (token) {
                store.setItem(key, token);
            } else {
                store.removeItem(key);
            }
        }
    
        function getToken () {
            return store.getItem(key);
        }
    });

})();

Cary
Cary
~ 7 years ago

In the video you say we usually hide the secret key in some where backend to keep it private,can you show me what to do to achieve that?

Markdown supported.
Become a member to join the discussionEnroll Today