Skip to content

Commit

Permalink
BREAKING CHANGE: set signOut option clearTokensAfterRedirect default …
Browse files Browse the repository at this point in the history
…to true

OKTA-459787
<<<Jenkins Check-In of Tested SHA: 3bb06dc for eng_productivity_ci_bot_okta@okta.com>>>
Artifact: okta-auth-js
Files changed count: 7
PR Link: "#1059"
  • Loading branch information
shuowu authored and aarongranick-okta committed Jan 14, 2022
1 parent 5f06051 commit d209c7c
Show file tree
Hide file tree
Showing 7 changed files with 25 additions and 21 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
- [#1050](https://github.com/okta/okta-auth-js/pull/1050) Removes `userAgent` field from oktaAuth instance
- [#1014](https://github.com/okta/okta-auth-js/pull/1014) Shared transaction storage is automatically cleared on success and error states. Storage is not cleared for "terminal" state which is neither success nor error.
- [#1051](https://github.com/okta/okta-auth-js/pull/1051) Removes `useMultipleCookies` from CookieStorage options
- [#1059](https://github.com/okta/okta-auth-js/pull/1059)
- Removes signOut option `clearTokensAfterRedirect`
- Adds signOut option `clearTokensBeforeRedirect` (default: `false`) to remove local tokens before logout redirect happen

### Features

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -928,7 +928,7 @@ Signs the user out of their current [Okta session](https://developer.okta.com/do
* `postLogoutRedirectUri` - Setting a value will override the `postLogoutRedirectUri` configured on the SDK.
* `state` - An optional value, used along with `postLogoutRedirectUri`. If set, this value will be returned as a query parameter during the redirect to the `postLogoutRedirectUri`
* `idToken` - Specifies the ID token object. By default, `signOut` will look for a token object named `idToken` within the `TokenManager`. If you have stored the id token object in a different location, you should retrieve it first and then pass it here.
* `clearTokensAfterRedirect` - If `true` (default: `false`) a flag (`pendingRemove`) will be added to local tokens instead of clearing them immediately. Calling `oktaAuth.start()` after logout redirect will clear local tokens if flags are found. This option can be used when work with `SecureRoute` component from Okta's downstream client SDKs to guarantee the local tokens can only be cleared after the Okta SSO session is fully killed.
* `clearTokensBeforeRedirect` - If `true` (default: `false`) local tokens will be removed before the logout redirect happen. Otherwise a flag (`pendingRemove`) will be added to each local token instead of clearing them immediately. Calling `oktaAuth.start()` after logout redirect will clear local tokens if flags are found. Use this option with care: removing local tokens before fully kill the Okta SSO session can result in logging back in again when `SecureRoute` component from Okta's downstream client SDKs is used in the application.
* `revokeAccessToken` - If `false` (default: `true`) the access token will not be revoked. Use this option with care: not revoking tokens may pose a security risk if tokens have been leaked outside the application.
* `revokeRefreshToken` - If `false` (default: `true`) the refresh token will not be revoked. Use this option with care: not revoking tokens may pose a security risk if tokens have been leaked outside the application. Revoking a refresh token will revoke any access tokens minted by it, even if `revokeAccessToken` is `false`.
* `accessToken` - Specifies the access token object. By default, `signOut` will look for a token object named `accessToken` within the `TokenManager`. If you have stored the access token object in a different location, you should retrieve it first and then pass it here. This options is ignored if the `revokeAccessToken` option is `false`.
Expand Down
6 changes: 3 additions & 3 deletions lib/OktaAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -510,11 +510,11 @@ class OktaAuth implements SDKInterface, SigninAPI, SignoutAPI {
}
});
} else {
if (options.clearTokensAfterRedirect) {
this.tokenManager.addPendingRemoveFlags();
} else {
if (options.clearTokensBeforeRedirect) {
// Clear all local tokens
this.tokenManager.clear();
} else {
this.tokenManager.addPendingRemoveFlags();
}
// Flow ends with logout redirect
window.location.assign(logoutUri);
Expand Down
2 changes: 1 addition & 1 deletion lib/types/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ export interface SignoutOptions extends SignoutRedirectUrlOptions {
revokeRefreshToken?: boolean;
accessToken?: AccessToken;
refreshToken?: RefreshToken;
clearTokensAfterRedirect?: boolean;
clearTokensBeforeRedirect?: boolean;
}

export interface SignoutAPI {
Expand Down
4 changes: 3 additions & 1 deletion test/apps/app/public/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,9 @@

function logout(e) {
e.preventDefault();
authClient.signOut()
// authClient.start() is not triggered by default
// clear local tokens before redirect to end up with correct authState
authClient.signOut({ clearTokensBeforeRedirect: true })
}

main();
Expand Down
4 changes: 2 additions & 2 deletions test/apps/app/src/testApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,10 @@ function logoutLink(app: TestApp): string {
<div class="actions signout pure-menu">
<ul class="pure-menu-list">
<li class="pure-menu-item">
<a id="logout-redirect" href="${app.originalUrl}" onclick="logoutRedirect(event)" class="pure-menu-link">Logout (and redirect here)</a>
<a id="logout-redirect" href="${app.originalUrl}" onclick="logoutRedirect(event, { clearTokensBeforeRedirect: true })" class="pure-menu-link">Logout (and redirect here)</a>
</li>
<li class="pure-menu-item">
<a id="logout-redirect-clear-tokens-after-redirect" href="${app.originalUrl}" onclick="logoutRedirect(event, { clearTokensAfterRedirect: true })" class="pure-menu-link">Logout (and redirect here, tokens will be cleared afterward)</a>
<a id="logout-redirect-clear-tokens-after-redirect" href="${app.originalUrl}" onclick="logoutRedirect(event)" class="pure-menu-link">Logout (and redirect here, tokens will be cleared afterward)</a>
</li>
<li class="pure-menu-item">
<a id="logout-xhr" href="${app.originalUrl}" onclick="logoutXHR(event)" class="pure-menu-link">Logout (XHR + reload)</a>
Expand Down
25 changes: 12 additions & 13 deletions test/spec/OktaAuth/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,8 @@ describe('OktaAuth (browser)', function() {
it('Default options when no refreshToken: will revokeAccessToken and use window.location.origin for postLogoutRedirectUri', function() {
return auth.signOut()
.then(function() {
expect(auth.tokenManager.getTokensSync).toHaveBeenCalledTimes(3);
expect(auth.revokeRefreshToken).not.toHaveBeenCalled();
expect(auth.revokeAccessToken).toHaveBeenCalledWith(accessToken);
expect(auth.tokenManager.clear).toHaveBeenCalled();
expect(auth.closeSession).not.toHaveBeenCalled();
expect(window.location.assign).toHaveBeenCalledWith(`${issuer}/oauth2/v1/logout?id_token_hint=${idToken.idToken}&post_logout_redirect_uri=${encodedOrigin}`);
});
Expand All @@ -231,10 +229,8 @@ describe('OktaAuth (browser)', function() {

return auth.signOut()
.then(function() {
expect(auth.tokenManager.getTokensSync).toHaveBeenCalledTimes(3);
expect(auth.revokeAccessToken).toHaveBeenCalledWith(accessToken);
expect(auth.revokeRefreshToken).toHaveBeenCalledWith(refreshToken);
expect(auth.tokenManager.clear).toHaveBeenCalled();
expect(auth.closeSession).not.toHaveBeenCalled();
expect(window.location.assign).toHaveBeenCalledWith(`${issuer}/oauth2/v1/logout?id_token_hint=${idToken.idToken}&post_logout_redirect_uri=${encodedOrigin}`);
});
Expand All @@ -257,7 +253,6 @@ describe('OktaAuth (browser)', function() {
var customToken = { idToken: 'fake-custom' };
return auth.signOut({ idToken: customToken })
.then(function() {
expect(auth.tokenManager.getTokensSync).toHaveBeenCalledTimes(2);
expect(window.location.assign).toHaveBeenCalledWith(`${issuer}/oauth2/v1/logout?id_token_hint=${customToken.idToken}&post_logout_redirect_uri=${encodedOrigin}`);
});
});
Expand Down Expand Up @@ -302,10 +297,8 @@ describe('OktaAuth (browser)', function() {

return auth.signOut({ revokeAccessToken: false })
.then(function() {
expect(auth.tokenManager.getTokensSync).toHaveBeenCalledTimes(2);
expect(auth.revokeAccessToken).not.toHaveBeenCalled();
expect(auth.revokeRefreshToken).toHaveBeenCalled();
expect(auth.tokenManager.clear).toHaveBeenCalled();
expect(window.location.assign).toHaveBeenCalledWith(`${issuer}/oauth2/v1/logout?id_token_hint=${idToken.idToken}&post_logout_redirect_uri=${encodedOrigin}`);
});
});
Expand All @@ -316,33 +309,39 @@ describe('OktaAuth (browser)', function() {

return auth.signOut({ revokeRefreshToken: false })
.then(function() {
expect(auth.tokenManager.getTokensSync).toHaveBeenCalledTimes(2);
expect(auth.revokeAccessToken).toHaveBeenCalled();
expect(auth.revokeRefreshToken).not.toHaveBeenCalled();
expect(auth.tokenManager.clear).toHaveBeenCalled();
expect(window.location.assign).toHaveBeenCalledWith(`${issuer}/oauth2/v1/logout?id_token_hint=${idToken.idToken}&post_logout_redirect_uri=${encodedOrigin}`);
});
});

it('Can pass a "accessToken=false" to skip accessToken logic', function() {
return auth.signOut({ accessToken: false })
.then(function() {
expect(auth.tokenManager.getTokensSync).toHaveBeenCalledTimes(2);
expect(auth.revokeAccessToken).not.toHaveBeenCalled();
expect(auth.tokenManager.clear).toHaveBeenCalled();
expect(window.location.assign).toHaveBeenCalledWith(`${issuer}/oauth2/v1/logout?id_token_hint=${idToken.idToken}&post_logout_redirect_uri=${encodedOrigin}`);
});
});

it('Can pass a "clearTokensAfterRedirect=true" to skip clear tokens logic', function() {
it('skips token clear logic by default', () => {
auth.tokenManager.addPendingRemoveFlags = jest.fn();
return auth.signOut({ clearTokensAfterRedirect: true })
return auth.signOut()
.then(function() {
expect(auth.tokenManager.clear).not.toHaveBeenCalled();
expect(auth.tokenManager.addPendingRemoveFlags).toHaveBeenCalled();
expect(window.location.assign).toHaveBeenCalledWith(`${issuer}/oauth2/v1/logout?id_token_hint=${idToken.idToken}&post_logout_redirect_uri=${encodedOrigin}`);
});
});

it('if "clearTokensBeforeRedirect" is true, then tokens will be cleared and pending remove flag will not be set', function() {
auth.tokenManager.addPendingRemoveFlags = jest.fn();
return auth.signOut({ clearTokensBeforeRedirect: true })
.then(function() {
expect(auth.tokenManager.clear).toHaveBeenCalled();
expect(auth.tokenManager.addPendingRemoveFlags).not.toHaveBeenCalled();
expect(window.location.assign).toHaveBeenCalledWith(`${issuer}/oauth2/v1/logout?id_token_hint=${idToken.idToken}&post_logout_redirect_uri=${encodedOrigin}`);
});
});
});

describe('without idToken', () => {
Expand Down

0 comments on commit d209c7c

Please sign in to comment.