The scenario is this;
- I have a web application (JS / jQuery), a Spring-Boot backend and a custom domain hosting the sign-in widget.
- My web app has a landing page and several other pages that require authentication, one of which uses websockets for communicating with the server.
- When a user accesses one of the authentication-required pages, they are redirected to the sign-in and, once signed-in, the bounce back to the original page.
- The
okta.oauth2.scopes=profile, email, openid, offline_access
- Each time a page is accessed the JS client requests session information from my back-end via a REST request. If there is an
@AuthenticationPrincipal OidcUser
then the user details (username, email etc) is returned. If there is aOAuth2AuthorizedClient
available (taken from the REST method receiving a validOAuth2AuthenticationToken
), then theOAuth2AccessToken
andOAuth2RefreshToken
details are also returned to the client. - When the client-side user accesses the websocket communication page, the accessToken value is added to the stompClient headers (
"Bearer [accessToken value]"
), which the server validates and, if valid, returns the required websocket message.
This all works fine, until the accessToken expires after x amount of time and the server-side websocket validation rejects the token and stops sending messages to the client. It is important to note that the websocket connection needs to be maintained for 2-8 hours.
In this scenario, the user is still known to the client as the oauth scopes includes offline_access, but the token has expired.
My question is; How do I silently refresh the accessToken?
Problems:
I have tried to work around this for two days, but am still having no joy.
On the client-side (using https://global.oktacdn.com/okta-auth-js/4.0.2/okta-auth-js.min.js) I have tried creating
authClient = new OktaAuth({
issuer: ‘[my custom domain]/oauth2/default’,
clientId: ‘[my client id]’,
redirectUri: ‘[my web domain]/authorization-code/callback’,
tokenManager: {
storage: ‘sessionStorage’,
autoRenew: true
},
cookies: { secure: true }
});
and then using the following call when the new information is returned from the server
authClient.tokenManager.add(‘accessToken’, at );
as the documentation suggests that this will enable autoRenew, but it doesn’t. More-over, if I use
authClient.tokenManager.hasExpired(at);
this always returns true, even if the token has expired!
I tried using localStorage and sessionStorage in the client configuration, but it makes no difference. If I try to load the accessToken with
await authClient.tokenManager.get(‘accessToken’).then( token => {});
it appears that client tries to attempt a refresh, but it errors with ‘401 Not authorized’.
Looking at the API guide, it appears that auto-renew relies on third-party cookies, which is possibly why this doesn’t work.
I have tried requesting a refresh of the token on the server-side using a POST to the
[my custom login domain]/oauth2/default/v1/token
endpoint with the settings
client_id = [my client id]
refresh_token = [refresh token]
grant_type = “refresh_token”
authorization = Base64( [clientId] : [clientSecret] )
scopes = [oauth scopes]
redirect_uri = [redirectUrl]
but this returns ‘Client not authorised’.
Many other applications must refresh their access tokens, so what am I missing?!
Currently I have simply increased my accessToken lifetime to 1 day (the maximum), but this doesn’t feel like the right solution…