This lesson is for PRO members.

Unlock this lesson NOW!
Already subscribed? sign in

Finalizing JWT Authentication with AngularJS

8:13 Angular 1.x lesson by

Adding middleware to the node server to decode and verify the token sent from the client. And tidying up the rest of the application. Finally, a walkthrough of the whole process.

Get the Code Now
click to level up

egghead.io comment guidelines

Avatar
egghead.io

Adding middleware to the node server to decode and verify the token sent from the client. And tidying up the rest of the application. Finally, a walkthrough of the whole process.

Avatar
Dinesh Dharmawardena

How would you go about implement a sliding expiration concept, where the expiration time of the token is reset based on user activity. I'm not sure if this is by way of refresh tokens, regardless there is very little information regarding refresh token with express-jwt.

In reply to egghead.io
Avatar
Kent C.

This is something several people have asked about and it's definitely on my radar of things that I'd like to do as an addendum to the series. Thanks!

In reply to Dinesh Dharmawardena
Avatar
Danny

Tnx for putting together this series! I haven't used JWTs in production before so maybe this is an obvious question...

From the vantage of the client, its plain old token-based security, right? Client does a form-based auth challenge (TLS still the only thing protecting username/password in transit) and gets a server-generated key to authenticate/authorize subsequent requests. The only client-side impact I see from the JWT spec deals with formatting the Authorization HTTP header (as opposed to some other means of passing along the key eg; different header scheme, cookies, query string params, or somewhere in the request body).

So same old song and dance on the front-end. Correct?

In reply to egghead.io
Avatar
Kent C.

You got it exactly right. Same old song once you have that interceptor in place.

In reply to Danny
Avatar
Ben

Question! How does the app.use(expressJwt({ secret: jwtSecret }).unless({ path: [ '/login' ]})); in conjunction with the /me route work? I'm writing my own version (http://stackoverflow.com/questions/27330953/jwt-angular-authentication-on-refresh) but somehow all I get back when I refresh and init my controller is a blank user object, so while I have the token on refresh it doesn't log in my user.

In reply to egghead.io
Avatar
Kent C.

I added a comment on the stack overflow post.

In reply to Ben
Avatar
Luigi

Hi Kent, i've read your comment on Stack Overflow, but i'm still in troubles about how to fix the issue after browser refresh!

In reply to Kent C.
Avatar
Kent C.

Hi Luigi,
I'm not sure I quite understand what issue you're having. Could you provide an example perhaps in a Stack Overflow question?

In reply to Luigi
Avatar
Luigi

Hi Kent, i mean that if i refresh the page, after login successfully, the Get User button and the vm.user.username variable don't work anymore. The strange thing is that i don't receive any error in the browser console or in the terminal..

In reply to Kent C.
Avatar
Kent C.

Hmmm, my best guess is that you're missing something from the lesson. Hard for me to know what the real problem is.

In reply to Luigi
Avatar
Luigi

You can check my code here:
https://github.com/LuigiFrattolillo/expressangular-jwt
I've downloaded egghead.io code from github as well and there is the same issue actually... :O

In reply to Kent C.
Avatar
Kent C.

Hmmm, interesting. When I cloned your repo and ran it, everything worked fine. My steps were:

$ git clone https://github.com/LuigiFrattolillo/expressangular-jwt.git

$ cd expressangular-jwt/

$ npm i && bower i

$ node server.js

Then in another terminal

$ http-server public/

Then I opened localhost:8080

Then logged in with username: luigi and password: p and everything seemed to work as expected. I tried logging out and back in and clicking Get User and everything worked fine. I'm not sure what trouble you're having.

Maybe try flushing your bower and node dependencies? (delete the directories and reinstall)

In reply to Luigi
Avatar
Luigi

Hi Kent, i just tried with firefox and it works! But it still breaks in Chrome...i'm using a macbook pro. Any idea?

In reply to Kent C.
Avatar
Luigi

I' ve just flushed the cache of Chrome... it seems working now, but there is a bit of delay during the which i can see hidden elements... in Firefox works perfectly instead!

In reply to Kent C.
Avatar
Kent C.

Might be a result of the recent AngularJS Batarang Chrome Extension release. It's been a bit rocky. They're working on fixes. You may want to disable the extension for now...

In reply to Luigi
Avatar
Luigi

True... Batarang is active... thanks for the suggestion and your patience!

In reply to Kent C.
Avatar
James

Hi Ken,

I have this working. Problem is if I refresh the page I am no longer logged in even though the localStorage still has my jwt token. How do you persist the authentication? It sucks having to re-authenticate every time. I've tried a bunch of different things and all of them have failed me.

Avatar
Kent C.

Make sure you have this initialization step in your controller: https://github.com/eggheadio/egghead-angularjs-aunthentication-with-jwt/blob/master/public/app.js#L16

UserFactory.getUser().then(function success(response) {
  vm.user = response.data;
});
In reply to James
Avatar
ken

Is it possible to use JWT for anonymous authentication (e.g., allow a user to only add/remove items from his own shopping cart, but not require the user to create a a username/password)?

Avatar
ken

Thanks. So would I just skip the login part and handle everything else the same?

In reply to Kent C.
Avatar
Kent C.

That sounds like it would work well to me! Good luck!

In reply to ken
Avatar
Tommy

How would I go about authorizing routes with ui-router and this approach? say, a user gets redirected to a dashboard after they log in.

Avatar
Kent C.

Good question. That would make a good lesson. What I have done is have a loading state that gets the user from the token and forwards them to the dashboard or the login. That's my default state.

In reply to Tommy
Avatar
milad

One of the worst thing about this website ( which I've subscribed for Premium btw ) is that none of the teachers actually explain anything , they just do a fast-forward coding , like there is someone chasing them !!
For the love of god ( and the money that I've given to you) slow down !

Avatar
JACOB

Thanks for the tutorial!

I had a question about handling expired tokens. How would you handle, on the client, a token that has expired?

Avatar
Kent C.

You can have another interceptor that checks a failed response for some message (or status code) that indicates the token has expired. In that case, remove the token from localStorage and send the user to the login screen.

In reply to JACOB
Avatar
Tony

Kent,

Excellent tutorial. I do have a question on the .unless() usage. It seems very straightforward but for GET queries which pass parameters as part of the URL I've been having an issue where the .unless doesn't seem to think that it matches my values.

What I mean is this:

router.get('/:type/:email/:date?/', function ...

That is my route, off of "/v1/report"

I added the "/v1/report" as a test so my query could still work from the browser, however, it does not.

app.use(expressJwt({ secret: config.SECRET })
    .unless({ path: [ '/v1/auth', '/v1/is-alive', '/v1/report' ] }));

When I add the full parametrized URL it works correctly.
Given that this is a common issue, how is one to work with unless and parametrized URLs?

Avatar
inlightmedia

Hi I'm getting problems using the express-jwt module. As soon as I enter router.use(expressJwt({ secret: 'jwtSecret' }).unless({ path: [ '/login' ]})); I get an Error: UnauthorizedError: No authorization token was found<br> &nbsp; &nbsp;at middleware. Without the expressJwt line above, everything works and the Authorization header is present and the token is created fine and present in the auth header. It seems to me there is a problem with the unless() because when I put the expressJwt middleware on just the protected resources rather than using "app.use" (or router.use in my case) it works great. For some reason the '/login' path is not being excused from the authorization I think. Any ideas?

Avatar
Kent C.

It's likely the APIs have changed since this video was recorded. I recommend looking at the docs and reaching out for support from the community.

In reply to inlightmedia

...OK, so now here we have our application. Let's just make sure it's all working here. We can login, we can logout, we can get users whether or not we are logged in. This is what we are trying to protect. If we look in our local storage here, because I logged out there's nothing in here, but if I login again it is in here. I logout, it goes away. If I'm logged in and I say get user, then I'm sending the authorization header with the token that the server gave to me in this login request.

Now what we need to do is set up the server so that it will take this authorization header into account when this get user endpoint is hit. This is actually very simple with a module that we have called express jot. We'll go ahead and install that npm install Express Jot, and then up here we'll simply require express jot, and then it's as simple as an app.use Express Jot. This will take a secret, and we'll use the same jot secret.

Because this is going to be verifying our tokens, we need to verify it with the same secret. That's actually everything that we need to do. However, when a user is logging in, they obviously won't have a token, and so we need to add unless, on this middleware, and we'll say the path is in this array/login. Now Express Jot, under the covers, is going to intercept all of the requests that come in.

It will take this authorization header, with the bearer and the token, it will decode that token using the jot secret. If it does decode properly, and the signature is verified, then it will add user to the request object, and the user property will simply be the decoded json object. We're going to see what that looks like here in a moment, but let's go ahead and see what breaks here.

I'm pointing to the server directly, to the random user endpoint, and if I refresh here, now I'm getting an unauthorized error, "No authorization header was found." If I refresh here, I'm not logged in, I say get...Oh, except I think that I am logged in. Refresh here, OK, "No authorization header was found," and the air is coming from this Express Jot.

With just these two lines, we were able to take our application and add Express Jot. Obviously we do have this jot signing, so that's a couple of extra lines, but it's actually very simple to add this authentication scheme to a node application. We do have a couple of more things that we want to do on the front end to make things a little more sensible. If I go ahead and login here, I login. N

Now I have, in our resources, I have that auth token. We'll clear our network here, say get user, and I can get the user. We can see on here I have the authorization header, so that's why I was able to get through. If I refresh the page, you'll see it's not showing that I'm logged in, but when I get the user everything works out fine, because I do have that token locally in the client.

What we want to implement now is to be automatically logged in when we refresh the page if we have the token locally. Let's go to our app, and we'll have this initialization step in our controller, and we'll say, "user factory.getuser" then we'll say vmuser is equal to the data that comes back, and so let's go to our user factory, and will add a getuser function here, and we'll define that down here. We'll simply say if auth token factory get token, so if there is a token in existence, then we can return $HTTP.getapiurl/me.

Otherwise we'll return $Q.reject, with the data client has no auth token. We'll need to inject Q here. Now we need to implement this /me endpoint. We'll go back to our server and just right here we'll say app.get/me. Because we have this Express Jot here, and the only path that is being excluded is the /login, we can know that this /me is being protected by this Express Jot, so no request will come through to /me unless they pass through this Express Jot first.

We can rely on the fact that the user is authenticated at this point, and Express Jot will put the decoded token onto the user object, and so I can simply just say "response send rec.user." This is very simple, so all of this should work, assuming I didn't break anything. Let's just double check in our resources. We have the auth token, now if I refresh...There we go, Kent C Dodds, we're automatically logged in, and we can get the users right from the get go.

If we logout, there's no token in here. Oops. We'll refresh, and now we're not able to get any users, and so that's the basic gist of what we're trying to accomplish in the series. Actually, there's one other thing that we want to do, and that's just for the fun of it. Well say ng show, or actually we'll say a hide when there's a vm user we'll hide this, and ng show when there is a vm user. Now let's refresh.

Now it makes more sense. Get random user. We'll login, and we'll login with the wrong password, password/username incorrect. We login without a password...OK, so all of our error handling is working. Now we login, here we have...and now we can get the user, and we can logout, and go back and forth that way. If we just hit the endpoint directly, "No authorization header found," and so this application is a working application, and that is what we've created using Jots.

Let's just take this full circle and explain what's happening here. We'll refresh the page here, we'll clear out here. We login with kentcdodds, and P as our password. We send that with this login request. Let's make this big here. The payload is the password and username. Then the server returns us a signed token with a secret that only the server knows about, and the user.

Then behind the scenes on the client we add an auth token to local storage, and that we have an auth interceptor that will intercept every request that we make with HTTP, and add this auth token to our request. If we look at our network tab here again, say "get user," we get the user, and here in our headers we have that authorization header with the bearer and the token. Then when we want to logout, let's take a look at our resources here.

We say logout, and that auth token is removed from local storage, and the user on the view model is set to null, and so our view updates as well. That's the application, that's Jots with Angular.

HEY, QUICK QUESTION!
Joel's Head
Why are we asking?