Is there a way to know when client JWT token has expired?

We have page on app which will be typically kept open for long duration if time. We are currently extracting token from dom so at times token is getting expired and 401 reponse is returned from our backend. I did try using AP.context.getToken but using this call for every request seems to be slowing down the requests itself. Can someone suggest what’s the best way to handle this scenario where page could have longer duration of inactivity and ensuring token stays updated with user having to refresh the page.

Hi @VishwajeetSingh,

The token retrieved by AP.context.getToken is cached in the client, but if the app has very low activity, then you may observe a high rate of cache misses due to the token being stale and needing to be re-created in the server. We don’t proactively keep the cache primed with fresh tokens, but if there is a particular sequence of operations in your app, then it may be possible to fetch the token ahead of when it is needed - e.g whilst a form is being filled out.

I noticed you are getting 401 from your backend - are you calling AP.context.getToken just prior to sending it to your backend? If not, try changing your code this way:

AP.context.getToken((jwt) => {
  call backend with token here
})

Regards,
Dugald

In case if you are using react… below is live example to append fresh jwt token on each request…

const getToken = () => {
  return new Promise(resolve =>
    window.AP.context.getToken(token => {
      return resolve(token);
    })
  );
};

axiosInstance.interceptors.request.use(
  async function(config) {
    let token = await getToken();
    if (token) {
      config.headers.Authorization = `jwt ${token}`;
    }
    return config;
  },
  function(err) {
    return Promise.reject(err);
  }
);
2 Likes

This does help, what it essentialy means that I would need to use AP.context.getToken everytime I am making a call to backend to keep token from getting expired.
As you mentioned I can try to fetch token ahead of time rather than just in time when it’s needed I am sure that will be faster. Thank you.

1 Like

Thanks for this I am doing exactly the same just wanted to know if there’s a better way to know when token is expired rather than calling AP.context.getToken everytime .

Hi @umang.savaliya,

FYI, as with many other JavaScript APIs provided by AP, AP.context.getToken() returns a promise so you don’t need your first method. For example, the following two lines do the same thing:

AP.context.getToken(token => {console.log('Token:', token)})
AP.context.getToken().then(token => {console.log('Token:', token)})

Regards,
Dugald

2 Likes

I may be wrong but this won’t work in axios interceptor that’s the reason we will need use the function which @umang.savaliya has used as I had to do it in the same way.

What we really need is a user-logout webhook for Jira Cloud; without it, our apps cannot synchronize the lifetime of the user sessions our apps create with the lifetimes of Jira user sessions. In other words, it’s possible for a cunning user to re-use an app-generated session token to interact with an app even after that user has logged out of Jira.

See also Ever been fired?

Yes, except this is not a proper promise - it is never rejected in case of error. If getToken fails to get a new token from the server, it logs:
ACJS: content resolver failed to get context jwt token
but doesn’t reject the promise, so it’s kind of useless…
See the code for getToken:

getToken: function(a) {
            return new window.ES6Promise.Promise(function(b, e) {
                var d = f(a._context.extension);
                g(a._context.extension);
                c[d].promise ? c[d].promise.done(function(a) {
                    k(a.options.contextJwt);
                    try {
                        b(a.options.contextJwt)
                    } catch (q) {}
                }
                .bind(this)).fail(function() {
                    console.error("ACJS: content resolver failed to get context jwt token")
                }).always(function() {
                    delete c[d].promise
                }
                .bind(this)) : (k(c[d].context.contextJwt),
                b(c[d].context.contextJwt))
            }
            )
        },
1 Like

Hi @david2,

Thanks for pointing this out. I’ve created https://ecosystem.atlassian.net/browse/ACJS-1146 to address this.

Regards,
Dugald

1 Like