Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to do token renewal #44

Closed
philippendrulat opened this issue Jul 1, 2019 · 14 comments
Closed

How to do token renewal #44

philippendrulat opened this issue Jul 1, 2019 · 14 comments

Comments

@philippendrulat
Copy link

On the old auth0-js tutorial, you could renew the token until the user session expires. How would one implement it in the auth0-spa-js?

@luisrudge
Copy link
Contributor

@philippendrulat There's no need to do token renewal. We do it automatically for you. Every time you call getTokenSilently, you'll get a valid token.

@jackmercy
Copy link

jackmercy commented Jul 2, 2019

Hey @luisrudge , I'm building a SPA and the session that I set in the Auth0 configuration page is 5 mins. So in 5 mins after successful login, auth0-spa-js will automatically refresh token every time I call getTokenSilently.
1 - Can I call this function to get a new token after 5 mins (session in auth0 is timeout)?
2- In 5 mins after login, if the expired time of token is 1 min, so I have to call getTokenSilently every 1 min? Do we have the function to schedule it or I have to implement it?

@philippendrulat
Copy link
Author

As I understood, you are not able to use token refresh in a SPA. You are only able to renew the token until the session expires.
I would also like to know the answer to your second question.

@luisrudge
Copy link
Contributor

@jackmercy the Auth0 session has nothing to do with your token. Your token can expire before the session (that's pretty common). If you have an active session at Auth0, when you call getTokenSilently, you'll always get a non-expired token, either from cache or by fetching a new token from Auth0.

@luisrudge
Copy link
Contributor

@philippendrulat correct. we're not using refresh tokens. We're just asking for fresh tokens from the server.

@luisrudge
Copy link
Contributor

2- In 5 mins after login, if the expired time of token is 1 min, so I have to call getTokenSilently every 1 min? Do we have the function to schedule it or I have to implement it?

That's precisely what we do. Once we put a token in the cache, we schedule its removal once it expires:

https://github.com/auth0/auth0-spa-js/blob/master/src/cache.ts#L35-L41

So, next time you ask for a token, if it's not in the cache, it will just fetch remotely (if you have an active session)

@ifflanb
Copy link

ifflanb commented Nov 28, 2019

Just to be clear here, when your referring to the session expiring, are you meaning the access token?

@freeman-g
Copy link

Hello, I'm also interested in a little more guidance on this.

I have implemented a recursive async function to scheduleRenewal like this:

  const scheduleRenewal = async (token, delay) => {
    let jwt = parseJwt(token)
    let expiresAt = jwt.exp * 1000
    if (!delay) {
      delay = expiresAt - Date.now()
      delay -= 60000
    }

    await new Promise(f => setTimeout(f, delay))
    try {
      const newToken = await auth0.getTokenSilently()
      window.localStorage.setItem('access_token', newToken)
      // schedule the next renewal
      scheduleRenewal(newToken)
    } catch (e) {
      console.log('error renewing token silently')
      console.log(e)
      // retry
      scheduleRenewal(token, 3000)
    }
  }

Is this the best practice? Should we build in a delay like I have done or wait until exactly when the JWT expires? It seems like we would want to renew the token a little bit earlier than the actual expiration.

@stevehobbsdev
Copy link
Contributor

@freeman-g Yes there are a few things going on here that we deal with on your behalf:

  • We already take a small leeway of 60 seconds before token expiry into account, so you don't need to do that yourself
  • We already store access tokens in local storage for you if you use cacheLocation: 'localstorage' when setting up the SDK

Also, you are decoding and inspecting the JWT access token on the client, which it has no business doing (only the resource server consuming the access token should do that). The main reason is that there is effectively a contract between the resource server (to which the access token is issued) and the IdP - that contract could change without the client's knowledge, which may break the application. Just something to be aware of. It's not best practise, to answer your question.

Considering those things, given that we manage a small leeway for you, you should be able to get away with not parsing that JWT, removing the leeway logic and potentially stop storing the token. Does that help?

@freeman-g
Copy link

@stevehobbsdev

Yes, this is very helpful. If I don't parse the JWT on the client, how do I determine the expiration time (exp property) to set the timeout function?

Thank you

@stevehobbsdev
Copy link
Contributor

stevehobbsdev commented Oct 28, 2020

@freeman-g what's the use case for scheduling that renewal? Are you trying to sync up some operation with the renewal of the access token?

Otherwise, we handle the renewal of the access token for you inside the SDK, there's no need to do that manually.

@freeman-g
Copy link

freeman-g commented Oct 28, 2020

I'm just trying to make sure that I have a current token for API calls. I'm using Axios, so I'm doing this:

// configured-axios.js
import axios from 'axios'

const jwt = () => {
  return window.localStorage.getItem('access_token')
}

axios.interceptors.request.use((config) => {
    config.headers['Authorization'] = `Bearer ${jwt()}`
    return config
})

export default axios

Then I'll use Axios like this:

import axios from 'configured-axios'
axios.get('https://mysecuredapi')

Therefore, I want to schedule background renewal so that the token is current when I need it.

Are you suggesting that I should just call await auth0.getTokenSilently() any time I need to make an API call? I think this might have two challenges:

  1. getTokenSilently is async, so I'll have to figure out how to wrap that in an async function and await it

  2. The user may have been idle and the token may be completely expired. In this case, my API call would incur a delay while waiting for a network based renewal via Auth0. I think I would rather "pre-renew" the token so that it's already done when I need to make an API call.

Thanks again

@frederikprijck
Copy link
Member

frederikprijck commented Oct 28, 2020

getTokenSilently is async, so I'll have to figure out how to wrap that in an async function and await it

Keep in mind that interceptors in axios can be async: https://github.com/axios/axios/blob/master/index.d.ts#L126

So you should be able to just use

// configured-axios.js
import axios from 'axios'

axios.interceptors.request.use(async (config) => {
    const token = await getTokenSilently();
    config.headers['Authorization'] = `Bearer ${token}`
    return config
})

export default axios

The user may have been idle and the token may be completely expired. In this case, my API call would incur a delay while waiting for a network based renewal via Auth0. I think I would rather "pre-renew" the token so that it's already done when I need to make an API call.

You can still implement some kind of renewal elsewhere, e.g. call our SDK's checkSession() on a specific interval. This does the same as getTokenSilently , with the only difference that it doesnt return a token:

  • See if a token is in the cache
  • If there is and its valid, do nothing.
  • If there isnt or it is expired, get a new one and store it in the cache

Calling checkSession() with { ignoreCache: true } will ensure it will ignore the cache and always get a new token, even if not expired. It will also update the cache. But I think you might not need that and will be able to get away with relying on the cache.

@freeman-g
Copy link

@frederikprijck

Very cool! I will give it a try the way you described with an async interceptor and just rely on the SDK and not worry about "pre-renewing".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants