DEV Community

Discussion on: The Ultimate Guide to JWT client side auth (Stop using local storage!!!)

Collapse
 
cyberhck profile image
Nishchal Gautam

Why does your refresh token look like a jwt?

Having api calls on separate domain doesn't work in case user blocks 3rd party cookies.

Your spa shouldn't do a set timeout, on every request, if the jwt has expired, it should refresh and set new jwt, this belongs in client side middleware.

Setting in cookie is good only if you know your api is being served under exactly same domain.

We do need to save jwt in cookie on client so that we can do server side rendering (else you don't have access to jwt in server side)

Collapse
 
bahdcoder profile image
Kati Frantz • Edited

Hello, thanks for sharing.

First, the refresh token is a JWT, but it cannot be used to gain access to the API. It can only be used to get a new access token.

Secondly, you're right about 3rd party cookies. The ideal situation in this case would be when both sides of the application are running on the same domain.

Thirdly, there's really no disadvantage in my opinion to use a set time out at this level, its just some tiny background job that makes sure the backend never even needs to return a 401. Another approach like you mentioned is handling this in client side HTTP middleware. Some people prefer it this way, but I prefer not having to do a 401 check, requesting an access token, and making the request again when the user is waiting for an API call to resolve.

Also, This sample project works neatly across different domains, and I make a mention about that at the end of my article. Cookies can be exchanged cross domain, you just need to configure them.

And, for SSR, using something like Next.js for example, you can always either forward the cookie to the browser, or still handle it on Next.js own server without too much problems. Maybe I'll make a tutorial on this in future.

Thanks for sharing your ideas, I hope this makes sense to you.

Collapse
 
cyberhck profile image
Nishchal Gautam

A few things, I don't agree with:

  • When you say refresh token is a JWT but can't access, are you storing that in db? What is the expiry time? Because it must be enough long lived, say you did 3 months, and you're relying on JWT properties (signing key) to validate refresh token, then when I get access to your refresh token, how will you log me out? Unless you keep a list of blocklist of refresh token, (then how would you block those token anyway? Because you don't know, as you didn't save them), this means once I get a refresh token, you are duped. I suggest falling back to opaque tokens which reside in db, and you can show all the sessions in user's security page and you can simply revoke those refresh token, which means no more new JWT at least. (and it's more cheap to keep a blocklist of JWT with lower expiry time if you need instant signout)

  • I'm not saying middleware does a 401 check, if you write your middleware correctly it shouldn't return 401 because of expired JWT, here's a kinda rough middleware for tinka:

export class RefreshTokenMiddleware implements IMiddleware<IFetchRequest, Promise<IFetchResponse<any>>> {
    public async process(options: IFetchRequest, next: (nextOptions: IFetchRequest) => Promise<IFetchResponse<any>>): Promise<IFetchResponse<any>> {
        if (this.hasJwtExpired(options)) {
             const jwt = await refreshJwt(...)
             // save this jwt so we can use later
             options.headers["authorization"] = jwt;
        }
        return await next(options);
    }
    private hasJwtExpired(options: IFetchRequest): boolean {
        // if jwt doesn't even exist, I guess you can think of it like a guest mode, and you should just return false,
        // compare the expiry timestamp with current timestamp and return true or false,
    }
}
Enter fullscreen mode Exit fullscreen mode

This way just before you make an API call, it ensures there's a valid JWT on request. (There's nothing wrong with having setTimeout but think about opening a tab in new browser, how many JWT do you want to actually store in db, a lot of people use multiple tabs and that'll invoke refresh token in each tab if we do setTimeout, but with this since it's done just before making a request, opening a new tab is okay.

"You just need to configure them." I don't think you understand what we meant. A lot of people are now blocking 3rd party cookies, I block 3rd party myself, which means, no matter what you do on your end, since I'm blocking 3rd party cookies, it simply won't work.

No, I mean I was agreeing with keeping tokens in cookie, but JWT must be accessible on client side, because if not, then how can I add authorization header for my API calls? If you want to say "don't use authorization header, just let API read those cookies", then I'm gonna stop even trying :)

Thread Thread
 
cyberhck profile image
Nishchal Gautam

also this:

Google will join Safari and Firefox in blocking third-party cookies in its Chrome web browser. However, unlike those browsers (which have already started blocking them by default),

Thread Thread
 
daniguardiola profile image
Dani Guardiola_

This post is misinformed. The only thing that had to change about the situation is to stop being vulnerable to XSS. As long as there's a token, it can be stolen, no matter how many steps it takes from having the token to using it to access the account. Making those tokens short-lived is a good practice but doesn't change the facts.

I'm with you brother.