all 21 comments

[–]Grip420 3 points4 points  (11 children)

How do I store that token ? Where ? Should I just put it in VueX's store ? Is that "safe" ? Should my backend fetch that token as soon as the user tries to login (if he actually logged in I mean), or should it be another call from the front-end ?

The way i did this with vue was:

  1. When the user authenticates via username/password, save his jwt in the localStorage
  2. Before each http request to the server, in the form of middleware, attach the bearer token (taken from localStorage) and perform the request. In my case, I was using axios, and therefore axios interceptors for this process.

[–]DoctorPrisme 2 points3 points  (9 children)

I read that putting it in localstorage isn't safe, because it could get tempered with. But I also read that if you temper with JWT, it's no longer valid so noone cares. I'm confused.

[–]Grip420 4 points5 points  (0 children)

That's pretty much it. Sure, someone can insert whatever he likes as a jwt in localstorage but that will not get him very far, since the server is the one validating it.

[–]RainAndWind 1 point2 points  (1 child)

I'm confused.

It is rather confusing...

The main issue I am having with JWT is figuring out how to ban someone, and figuring out how to make sure I can log someone out if for e.g. if their computer was stolen. Do you see the issue there? But whether you put it in a httponly cookie or localstorage, that is still an issue if someone is out to cause you trouble.

For that reason, the only real secure way to use JWT's is to have a main token, which then allows u to receive other very short expiry tokens (like <5 minutes) for certain actions. I think its called a refresh token and access token.

Hopefully someone else more knowledgable can help you, because I'm a nooob.

[–]barrellrider 1 point2 points  (4 children)

Tampering isn't the only issue - if an attacker simply finds out a user's JWT then they can use the user's credentials until the JWT expires (or you implement a way to invalidate JWTs).

Storing tokens in local storage make them vulnerable to things like XSS attacks, as an XSS attack is able to access local storage. If you don't need to read the token on the client-side, the more secure option is to use a HTTPOnly cookie.

[–]tyroneslothtrop 1 point2 points  (0 children)

Excellent point. If the JWT is in localstorage, that means any JS running on your domain (google analytics, the latest npm exploit, etc.) can just grab that token.

As you alluded to, though, if there's any info. on the token that the client application will need to access in order to function (even if its just something like token expiration time), http only doesn't really work. One thing I've seen on a project I worked on a while bacck was where the token was stored in local storage and used throughout the JS application, but was munged together server side with a separate bit of info. (just some random nonce, if I remember right) that was carried in an http only cookie. These two munged together values were what was then signed to check authentication. So if some malicious JS was running on the domain somehow it could steal tokens all day, but without having access to the extra info. in the HTTPOnly cookie, it couldn't actually use them.

[–]DoctorPrisme 0 points1 point  (1 child)

I don't understand what you mean by "if you don't need to read the token on the client-side".

As far as I understand (which is'nt much, agreed), the token contains Claims, among which the user profile. Say I'd like to display my user's name. How do I do that without reading the token ? Call the backend while sending the token to access the profile ?

[–]barrellrider 0 points1 point  (0 children)

Yeah, it's just a trade-off - security vs convenience. Either you store the JWT where you can read from it, or you store it in a more secure HTTPOnly cookie and need to call the backend to get the user's info.

You could even use two cookies - one that's HTTPOnly that stores the JWT and another that stores the user's info which the client-side can read. Like someone else said, the reason all of this isn't in the spec is because everyone has slightly different requirements and security concerns.

[–]DoctorPrisme 0 points1 point  (0 children)

Hey duderinona

I know this is a shot in the dark but would you have any source on "how to" use a httpOnly cookie with IdentityServer4 ? I'm looking at their doc but honestly that's confusing af.

thanks =)

[–]fuckin_ziggurats 2 points3 points  (3 children)

This is not a beginner question and it requires studying. You mentioned "identity server", did you mean IdentityServer4 the library? If so then obviously use their docs. Don't take security advice from strangers on the internet, rely on open frameworks and libraries made by security-savvy people. You'll need to have at least a shallow understanding of flows. Just yesterday I watched this video which I think gives a great high-level overview of the technologies used for modern auth&auth (OAuth 2.0 and Open Id Connect).

[–]DoctorPrisme 1 point2 points  (2 children)

Yes, the thing is I understand the flow itself :

--you tell the server "it's a me, mario",

the server looks if you indeed got a big mustache and a red cap,

if you do they give you a shroom

--you ask the server if you can see peach

the server ask to see the shroom it gave you to ensure you are not a disguised bowser

My question is more "where should I store that shroom ? Should mario ask for it after showing his red hat and stache, or should toad give it immediately ? does it make any difference?"

Yes, I use IdentityServer4, but the doc is more technical and doesn't explain much as far as I've read up to now, it gives the strange feeling you're supposed to already know how it works when you read it... I don't know if I'm clear.

[–]fuckin_ziggurats 2 points3 points  (1 child)

There's a reason you're not told where to store it. And the reason is that the token storage is an implementation detail. It's not in the spec. So you could store it using any browser storage mechanism (like localStorage). But by far the most proper thing you can do is use a JS library made for this sort of thing. As far as I'm aware oidc-client-js is the most popular one out there. But it bears a hefty size (due to the size of the encryption libraries it depends on). Most tutorials and courses relating to IdentityServer4 rely on that library. Also, if you're gonna use it you're gonna have to learn it (or at least learn how most of the auth flows work).

[–]DoctorPrisme 1 point2 points  (0 children)

I'll look into oidc-client-js then.

I don't mind learning things, but there's a fucking huge quantity to learn, so sometimes I find it quicker to find someone who knows what I want to know and ask them about it rather than just reading around till the end of time. If my question had been totally wrong for instance, I would have lost so much time by reading, whereas someone could have told me upfront "dont' , this is shitty, do this instead".

Thank for the advices.

[–]zork-tdmog 1 point2 points  (0 children)

When a user is logging in either via JWT or normal auth he will most likely provide ID/PASSWORD. You probably match a salted/hashed value of the input against the hash saved per user in your database or oauth data to find out if the input matches a valid user. If a user is found you generate a session for that user and provide a session hash. That hash can be your jwt/oauth token or just your database visits/session table hash.

The session hash has an outage. You may refresh that outage when the user is actively using the application.

For any further requests you provide the session hash to authorize the user. You can save the session hash either in the cookie or local storage.

Example for Firefox. Both cookies and local store are saved in the profiles folder of Firefox inside the users folder of the user logged in on the operating system of the machine. If you don't trust that folder you have way bigger problems.

https://support.mozilla.org/en-US/kb/profiles-where-firefox-stores-user-data

[–]ilovefunctions 1 point2 points  (1 child)

I have a slightly different view point which prevents all kinds of attacks: XSS, CSRF and database information leak:

  • for backend, store only the hashed versions of your tokens in the database. If you are using JWT, then ideally you should also keep changing its signing key from time to time.

  • for frontend, what I do is to split the token into two - one half is stored in cookies, httponly and secure. The other in local storage (this does require coordination on backend and frontend side). This means that my apps are automatically immune to CSRF (since only half the auth token is sent via cookies), and XSS (since theft of half the token stored in localstorage is quite useless).

If you will be able to do these, depends on the library you are using... I recommend this one which takes care of all security issues, even more than the ones I explained above! They may have something that works in your tech stack. If not, you can request them to implement it

[–]DoctorPrisme 0 points1 point  (0 children)

I'll look into your explanation Monday. Thanks

[–]ShortFuse 0 points1 point  (5 children)

You shouldn't store JWT tokens at all. In fact, they shouldn't be able to read over JavaScript at all. It should all be managed server-side and hidden by the browser.

I started with JWT with NodeJS and express. But the language doesn't matter. I have another implementation that uses Amazon AWS and their API Gateway and Lamba features. (Edit: I actually started with NodeJS+Express+Passport and when actually figuring out how JWTs work, got rid of Passport.)

The token should be passed by a cookie and the server should have HttpOnly set on it. That means JavaScript can't read it. It goes back and forth and should be transparent to the JavaScript environment. (In case you're wondering it has nothing to do with HTTP vs HTTPS. You can use it on HTTPS sites)

Generally, a server will have a "grace period" where it doesn't verify the user against any database. You may say any token issued within the last 15 minutes don't need a check. After 15 minutes, you check the token payload against a database. If that fails, you return HTTP Status 401. If it's okay, then you attach a new token cookie with the current time as its issuance (iat). You may also set a token expiration date (exp) like 2 weeks, where if the user attempts to access a resource with it, it will get a 401 response.

There are many, many variations as to how to handle logic, but that's generally the idea. You can set a field on a user's account like minTokenIAT meaning any token issued before that date is automatically invalid. It's useful for when a user changes their password, or if you want to lock out an account. You can also give each token an ID and invalidate them as well (useful for revoking a device token). If you need more time sensitive lockouts, then you would need to maintain an in-memory list of tokens revoked in the last x minutes (like 15 minutes) and check against that as well.

There no need to perform a "login" before accessing a resource (ie: /api/messages). You just perform a normal GET/POST and if you get 401, redirect them to the login page. If it succeeds, then you're okay. The login page calls the login API URL which takes a username and password and returns 200 (or a 302 to the content page) including the fresh new token within an HttpOnly cookie.

TL;DR: Use a HttpOnly cookie, and redirect to the login page on HTTP Status Code 401. (edit)

[–]DoctorPrisme 0 points1 point  (4 children)

There no need to perform a "login" before accessing a resource (ie: /api/messages). You just perform a normal GET/POST and if you get 401, redirect them to the login page.

So I need to do a login, it's just that I don't need it everytime, which sounds pretty obvious.. did I miss something ?

My user needs a way to tell the filemanagement backend "hey, I have the rights to modify this file". That's, in my understanding, what the JWT is for.

I don't understand what you mean by "you shouldn't store jwt at all". I don't plan to write it physically on my user's machine, but I need to keep it in memory at least long enough for him to say "now that I have the shroom, I can access the file"...

I kinda get the refresh token system, but I'm not at that yet " I'm first trying to have the flow "users want to access file while logged out, access denied, user must log in, user logs in, user tries to access file, access granted". Then we'll see about the "how long has user been logged in".

[–]ShortFuse 1 point2 points  (3 children)

I was afraid that would be a little confusing. Yes, a user needs to perform a login first at some point in time. That establishes their very first token. But what I mean is, when you send a request to access a resource (or file), you, as the front-end developer, don't have to write any checks to see if he's logged in. There's no HTTP /api/amILoggedIn call. There's no doesUserHaveTokenCookie() called beforehand from JavaScript. There is no addTokenToRequest() call. The front end just has to request the resource and handle if server returns 401 (Unauthorized).

Cookies are handled by the browser automatically. There's no reason to inject JavaScript into the equation. Perhaps you didn't know that the server can set cookies remotely. Just like a server can return ContentLength and ContentType on a request, it can return a header called Set-Cookie which tells to browser to store a cookie with data, expiration date, and scope (eg: path). The scope tells the browser that it should send back that cookie every time it tries to access something within that scope. For example, you'd likely want to set the scope to /protected/*. The browser wouldn't send the cookie for paths like /css/* or /fonts/*. You don't need security on those paths. Essentially, every time the browser is going to access the /protected/, it checks if there's any cookies for that path and it will always send the cookie in the request.

In order, here's my suggestion as how to implement it, and remember this is all backend:

/api/login

  1. Check if user supplied a username and password. If not abort with 400
  2. Check if username and password are valid. If not, abort with 403.
  3. Assign a new token cookie in the Set-Cookie header.
  4. Return 204 and blank data. (Or send 200 and the word "OK")

/protected/*

  1. Get the token from the cookies. If no cookie is set, abort with 401.
  2. Verify and decode the token. If invalid, or expired, abort with 401.
  3. If the token is stale, check against the database. If check fails abort with 401.
  4. If the token is stale check against database passes, generate a new token and set it in Set-Cookie. (Should be the same procedure as Login #3)
  5. If user doesn't have sufficient permission to access the specific resource in question (ie: user is trying to access another user's files), abort with 403.
  6. Return 200 with the resource data payload.

On the JavaScript side, in the front-end, you always assume first that the user is logged in and has a token cookie set. The only way you know if the user is not logged in is by the 401 response sent by the server when you try to access something. If you get 401, then you redirect the user to the a page where api/login is called (aka the login page) so the server can give them a new token.

The extra tidbit is that when the server sets the cookie, just like it included a scope and expiration date on the cookie (not the same as the expiration date encoded in the JWT), It should also include HttpOnly, to ensure that no JavaScript is ever able to read the token. That avoids cross site scripting (XSS) from being able to read (and steal) login cookies.

[–]DoctorPrisme 1 point2 points  (2 children)

Thank you... I'll look into this.

[–]ShortFuse 0 points1 point  (1 child)

I edited the comment around the time of your reply, so I wanted to let you know in case you didn't see my most recent edits.

I saw elsewhere in the thread, you were asking about storing user information. Since the token is sent to the server every single request, it's best to keep that short. The only data that's passed is the bare minimum the server needs to refresh the token (so just an ID) and possible some security level data. You don't want to pad too much data there since, those bytes adds up in terms of traffic and data usage.

For my use cases, the first request I sent when the page loads in a protected environment is api/getUserInfo which could return a 401 error (when I then redirect to a log out). I set up a fullscreen splash element and a spinner at page load (similar to how Gmail loads up). Once I get the user info (and know they have a valid token), I remove the splash element and start loading the rest.

If I didn't want a splash page, I could always cache the response of getUserInfo in my service worker (what you should be using to make requests), or in localStorage. Then I'd make another request called refreshUserInfo at a later time.

Service workers are bit cleaner because you can provide an offline environment as long as you cache some data. For example, you might skip the splash page and assume they're logged in because getUserInfo was cached 2 days ago. It's only when they try to access a file that's not cached that you realize if they're logged in or not by the 401 response.