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

fix: AutoRenewService should perform renew if token has expired before service start #1354

Merged
merged 8 commits into from
Dec 7, 2022
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
# Changelog

## 7.2.0

### Fixes

- [#1354](https://github.com/okta/okta-auth-js/pull/1354) Fixes token auto renew if token has expired before `AutoRenewService` start

## 7.1.1

### Fixes

- [#1355](https://github.com/okta/okta-auth-js/pull/1355) Adds missing type `currentAuthenticatorEnrollment` to `IdxContext`


## 7.1.0

### Features
Expand Down
7 changes: 7 additions & 0 deletions lib/oidc/TokenManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ const DEFAULT_OPTIONS = {
interface TokenManagerState {
expireTimeouts: Record<string, unknown>;
renewPromise: Promise<Token | undefined> | null;
started?: boolean;
}
function defaultState(): TokenManagerState {
return {
Expand Down Expand Up @@ -138,10 +139,16 @@ export class TokenManager implements TokenManagerInterface {
this.clearPendingRemoveTokens();
}
this.setExpireEventTimeoutAll();
this.state.started = true;
}

stop() {
this.clearExpireEventTimeoutAll();
this.state.started = false;
}

isStarted() {
return !!this.state.started;
}

getOptions(): TokenManagerOptions {
Expand Down
1 change: 1 addition & 0 deletions lib/oidc/types/TokenManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,5 @@ export interface TokenManagerInterface {

start();
stop();
isStarted(): boolean;
}
18 changes: 17 additions & 1 deletion lib/services/AutoRenewService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

import { AuthSdkError } from '../errors';
import { ServiceInterface, ServiceManagerOptions } from '../core/types';
import { EVENT_EXPIRED, TokenManagerInterface } from '../oidc/types';
import { EVENT_EXPIRED, TokenManagerInterface, isRefreshToken } from '../oidc/types';
import { isBrowser } from '../features';

export class AutoRenewService implements ServiceInterface {
Expand Down Expand Up @@ -46,6 +46,17 @@ export class AutoRenewService implements ServiceInterface {
return !!this.options.syncStorage && isBrowser();
}

private processExpiredTokens() {
const tokenStorage = this.tokenManager.getStorage();
const tokens = tokenStorage.getStorage();
Object.keys(tokens).forEach(key => {
const token = tokens[key];
if (!isRefreshToken(token) && this.tokenManager.hasExpired(token)) {
this.onTokenExpiredHandler(key);
}
});
}

private onTokenExpiredHandler(key: string) {
if (this.options.autoRenew) {
if (this.shouldThrottleRenew()) {
Expand All @@ -67,6 +78,11 @@ export class AutoRenewService implements ServiceInterface {
if (this.canStart()) {
await this.stop();
this.tokenManager.on(EVENT_EXPIRED, this.onTokenExpiredHandler);
if (this.tokenManager.isStarted()) {
// If token manager has been already started, we could miss token expire events,
// so need to process expired tokens manually.
this.processExpiredTokens();
}
this.started = true;
}
}
Expand Down
7 changes: 6 additions & 1 deletion test/spec/OktaAuth/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,16 @@ describe('OktaAuth (api)', function() {
});

describe('start', () => {
it('starts the token service', async () => {
it('starts the token manager', async () => {
jest.spyOn(auth.tokenManager, 'start');
await auth.start();
expect(auth.tokenManager.start).toHaveBeenCalled();
});
it('starts the service manager', async () => {
jest.spyOn(auth.serviceManager, 'start');
await auth.start();
expect(auth.serviceManager.start).toHaveBeenCalled();
});
it('updates auth state', async () => {
jest.spyOn(auth.authStateManager, 'updateAuthState');
await auth.start();
Expand Down
1 change: 1 addition & 0 deletions test/spec/ServiceManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ describe('ServiceManager', () => {
it('starts autoRenew service after becoming leader (for syncStorage == true)', async () => {
const options = { tokenManager: { syncStorage: true, autoRenew: true } };
const client = createAuth(options);
expect(client.serviceManager.isLeaderRequired()).toBeTruthy();
await client.serviceManager.start();
expect(client.serviceManager.isLeader()).toBeFalsy();
expect(client.serviceManager.getService('autoRenew')?.isStarted()).toBeFalsy();
Expand Down
Loading