-
Notifications
You must be signed in to change notification settings - Fork 267
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
Token manager auto renew not working #1164
Comments
Thanks for the post! Someone from our team will review this soon. |
@jameslessen Please update to the latest version of AuthJS. From the network response you posted, it is clear that the client is falling back to using an iframe/cookie method to refresh tokens (getWithoutPrompt) rather than using the refresh tokens. This was a known issue which was fixed in version 5.2.1. We recommend updating to the latest version to take advantage of all current fixes. |
Same here ok 7.0.1 ! |
@nitrique can you provide a code snippet? |
export const oktaAuth = new OktaAuth({
redirectUri: `${ window.location.origin }/login/callback`,
clientId: window.config.OKTA_CLIENT_ID,
pkce: true,
issuer: `${ issuer?.endpoint ?? authIssuers[0].endpoint }/oauth2/default`,
// Enable this to get auth event messages in console
devMode: false,
});
oktaAuth.start();
export async function initAuthentication(): Promise<UserClaims | null> {
const tokenParams: TokenParams = {
scopes: [ "groups", "openid", "profile", "email" ],
};
const accessToken: AccessToken = (await oktaAuth.tokenManager.get(
"accessToken",
)) as AccessToken;
if (!accessToken) {
await oktaAuth.token.getWithRedirect(tokenParams);
}
return accessToken?.claims;
}
initAuthentication(); Some code has been omitted. The okta service never trigger auto renew, we have to do the following: const interval = setInterval(async () => {
// Renew 60 seconds before expiry
if (claims?.exp && getUnixTime(new Date()) > (claims.exp - 60)) {
oktaAuth.tokenManager.renew("accessToken");
}
}, 15e3); |
@nitrique How are you handling the redirect back to your app? |
@jaredperreault-okta can you be more specific ? Redirect only occur on login. On token renew we don't have redirect (classic OIDC flow). If you are talking about first redirect: if (oktaAuth.isLoginRedirect()) {
try {
oktaAuth
.handleLoginRedirect()
.then(() => location.href = location.origin);
} catch (e) {
// log or display error details
}
} else {
location.href = location.origin;
} |
@nitrique can you try oktaAuth.tokenManager.on('expired', console.log); I'm curious if the renew requests are failing or whether the renew process is never triggered |
it write "accessToken" on console. |
Can you check the network tab to see if any network requests are being made (when renew triggers) and add the following log line oktaAuth.tokenManager.on('error', console.log); to see if token renewal is throwing any errors |
Nothing. I don't have the time to debug the lib sorry, I've implemented my own timer which work ;) |
Not sure if this is the same issue but I'm seeing the token renewal working in most of the cases but then quite often not (let's say 60-80% success rate). Here's my config:
I'm using
When it works correctly, I'm seeing logs like this:
A minute later there's another similar logging sequence. When I see that the renewal process works, I refresh the browser page and wait. If it turns out that the renewal process doesn't work, the logs look like this:
In the latter case there are no calls to the I'm using these versions meaning I have the latest ones:
I have also tried this version but the result is the same:
|
@nitrique @karhatsu did you notice your respective issues prior to upgrading to I've added a ticket to our next sprint to investigate this Internal Ref: OKTA-549676 |
I have built the app on top of 7.0.1 so I don't have experience from the prior versions. |
@karhatsu It works for me with the following on React: export const oktaAuth = new OktaAuth({
redirectUri: `${ window.location.origin }/login/callback`,
clientId: window.config.OKTA_CLIENT_ID,
pkce: true,
issuer: `${ issuer?.endpoint ?? authIssuers[0].endpoint }/oauth2/default`,
// Enable this to get auth event messages in console
devMode: false,
cookies: {
secure: true,
},
storageManager: {
token: {
storageTypes: [
"cookie",
],
},
cache: {
storageTypes: [
"cookie",
],
},
transaction: {
storageTypes: [
"cookie",
],
},
},
tokenManager: {
expireEarlySeconds: 15,
autoRemove: true,
autoRenew: true,
secure: true,
},
}); export const tokenRenewListener = (key: string) => {
oktaAuth.tokenManager.renew(key);
}; (I agree this function is a pass-through) In auth context: useEffect(() => {
oktaAuth.start();
// .... truncated code (handle auth state check and redirect) .....
oktaAuth.tokenManager.on("expired", tokenRenewListener);
return () => {
stopOktaAuth();
oktaAuth.tokenManager.off("expired", tokenRenewListener);
};
}, []); HINT : oktaAuth.start() is needed to monitor token state -> Take care of token storage, this lib stores by default on localStorage, which is a big CVE as any browser's plugin can access it (see https://auth0.com/docs/secure/security-guidance/data-security/token-storage), prefer secure cookies. -> My advice is to DON'T use Okta React lib, which has to much dependencies like react-navigation (old version) and can differ from your implementation -> Second advice, find an OpenIDConnect generic lib, it's better than auth providers libs which lead to vendor lock-in and most of the time has a better maintainance, if you look at this lib in exemple, it still uses "var" keyword, in 2022... |
@nitrique Thank you for the hints! I've been trying it out with as default Okta settings as possible, so been using local storage for now. Not sure if it has any impact for this case. Probably the biggest difference to your code is the usage of 59 minutes in |
@karhatsu tons of articles explain what you shouldn't use local storage like this one https://dev.to/rdegges/please-stop-using-local-storage-1i04 , but it's up to you :) The token should last the biggest time frame that you can accept if access token is stolen (I'm not talking of refresh token), for critical applications 1 minute can be good, but generally 30 minutes is a good start. Take care of not emitting too long access token, this is the purpose of refresh token. In my client's company, I requested to set the token expiration to 5 minutes which I think is good, even if it's not a critical application. Token renewal is free :) To answer your question, OpenIDConnect doesn't mention a limit of renewal as I know, but Okta devs can have implemented it differently |
I appreciate your advice @nitrique. My point here are not the security details but providing the Okta team information how to figure out the issue in the code. I would be quite surprised if it's related to the storage type. Also the 59 minutes early expiration is not something I'd use normally (and wouldn't be even possible according to Okta docs), it's just for easier debugging the problem. |
@karhatsu (We use broadcast-channel for leader election) Could you please answer following question to help me investigate your issue further?
|
Sorry for taking so long to respond. I'm using Chrome, haven't tested with other browsers. When the renewal doesn't work, it logs these values:
When it does work, then it logs like this:
I have never tried this before with having two tabs open. Now when I tested it and when it worked, the first tab logged the latter output and the second the former. Both were still able to update the token. I tried your test page several times and all the time it logged |
Version 7.2.0 will include the fix for your issue. |
I tried with 7.2.0 but unfortunately I'm still seeing similar behaviour. |
Could you please also debug these values with auth-js 7.2.0 when token is expired: {
syncStorageCanStart: oktaAuth.serviceManager.services.get('syncStorage').canStart(),
syncStorageStarted: oktaAuth.serviceManager.services.get('syncStorage').isStarted(),
autoRenewCanStart: oktaAuth.serviceManager.services.get('autoRenew').canStart(),
autoRenewStarted: oktaAuth.serviceManager.services.get('autoRenew').isStarted(),
isLeader: oktaAuth.serviceManager.services.get('leaderElection').elector.isLeader,
hasLeader: oktaAuth.serviceManager.services.get('leaderElection').elector.hasLeader,
type: oktaAuth.serviceManager.services.get('leaderElection').elector.broadcastChannel.method.type
} From your logs above
it seems like there are 2 tabs opened and current one is not a leader, but from your comment I understand that there is only 1 tab opened, so it can be a bug with leader election. Could you please also contact support@okta.com to help us gathering more information to investigarte? |
I've looked at the issue in Currently this seems to be a reliable workaround oktaAuth
.start()
.then(() => {
const autoRenew = oktaAuth.serviceManager.getService('autoRenew')
if (autoRenew && !autoRenew.isStarted()) {
return autoRenew.start()
}
}) |
@alexfedosov When
Internal ref: OKTA-597615 |
Basically we have this import { useOktaAuth } from '@okta/okta-react'
export const useAccessTokenRenewal = () => {
const { oktaAuth } = useOktaAuth()
useEffect(() => {
oktaAuth.start()
return () => {
oktaAuth.stop()
}
}, [oktaAuth])
} and then there are several places where we again use |
Right, should not create extra instances. But:
You can debug what's going on with start and stop calls with this code example const origStart = OktaAuth.prototype.start;
const origStop = OktaAuth.prototype.stop;
OktaAuth.prototype.start = function() {
console.log('>>> OktaAuth start');
return origStart.apply(this);
};
OktaAuth.prototype.stop = function() {
console.log('>>> OktaAuth stop');
return origStop.apply(this);
}; |
We certainly have it called twice due to the Regarding the |
@alexfedosov |
@denysoblohin-okta removing |
I am having this issue with the latest okta-react the on "renewed" event is never called but the "added" event works
|
This leadership election bug is present on mobile ios safari v 17.2.1 I get isLeader = false Using the testing tool https://pubkey.github.io/broadcast-channel/ I do not have this bug |
@RyAndrew Do you mind filing a new issue? |
Describe the bug?
The access token expires without renewal despite having refresh token lifetime set to unlimited. The refresh token and access token expire and the user is redirected to login again. This happens even when refresh token rotation is disabled
What is expected to happen?
The access token should be periodically refreshed until the expiration of the refresh token
What is the actual behavior?
Access Token Lifetime: 5 minutes
Refresh Token Lifetime: unlimited
Refresh token rotation is enabled
Login
09:27:04.420
tokenManageradded
event emitted for accessToken and refreshToken, both with the sameexpiresAt
value09:31:33.998
tokenManagerexpired
event emitted for refreshToken09:31:34.011
Request made to/oauth2/default/v1/token
to fetch new tokens usingrefresh_token_a
09:31:35.005
tokenManagerexpired
event emitted for accessToken09:31:35.791
tokenManageradded
event emitted for refreshToken09:31:35.797
Request made to/oauth2/default/v1/token
to fetch new tokens usingrefresh_token_a
(Returns the same refreshToken as the request in step 4, but a different accessToken)09:31:35.005
tokenManagerexpired
event emitted for accessToken09:31:36.729
OKTA-AUTH-JS:updateAuthState: Event:added Status:canceled09:36:05.001
tokenManagerexpired
event emitted for refreshToken09:36:05.565
Okta Error Event:09:36:05.998
tokenManagerexpired
event emitted for accessToken09:36:06.340
Okta Error Event (Same as above)09:36:06.536
Redirect to login pageReproduction Steps?
Set up an application with the following properties
Access Token Lifetime: 5 minutes
Refresh Token Lifetime: unlimited
Sign in and wait 10 minutes
I added some event listeners to the token manager for better visibility
Okta Config:
SDK Versions
"@okta/okta-react": "5.1.2"
"@okta/okta-auth-js": "4.9.0"
Execution Environment
Firefox 98.0.2 (64-bit)
Additional Information?
Posting this here since the behavior seems to be related to the token manager and our application does not have any custom token refresh behavior
The text was updated successfully, but these errors were encountered: