Skip to content

Commit 60d0e42

Browse files
authored
feat(auth0-server-js): add revokeRefreshToken and logout revocation support (#222)
* feat(auth0-server-js): add revokeRefreshToken and logout revocation support (SDK-8850) Adds ServerClient.revokeRefreshToken() which reads the refresh token from the session and delegates to AuthClient.revokeToken(). Logout now always attempts revocation best-effort before clearing the session. In resolver mode, both revocation and session deletion are gated by a domain-match guard. Exports TokenRevocationError from the public surface. * fix(auth0-server-js): add domain-match guard to revokeRefreshToken in resolver mode * fix(auth0-server-js): remove console.warn from logout catch blocks, strengthen revoke test assertion * fix(auth0-server-js): treat session with no stored domain as mismatch in resolver mode * fix(auth0-server-js): address review comments - bump auth-js dep, empty token guard, docs - Bump @auth0/auth0-auth-js dependency from ^1.11.0 to ^1.12.0 - Add explicit empty-string token guard in revokeRefreshToken (throws MissingRequiredArgumentError) - Add offline_access scope to example-express-web authorizationParams - Expand JSDoc and EXAMPLES.md to document resolver-mode behavior for explicit tokens - Assert revoke-before-delete ordering in logout test using ops array
1 parent bd562d1 commit 60d0e42

8 files changed

Lines changed: 597 additions & 6 deletions

File tree

examples/example-express-web/src/auth0.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export function auth0(options: Auth0ExpressOptions) {
3232
clientSecret: options.clientSecret,
3333
authorizationParams: {
3434
redirect_uri: redirectUri.toString(),
35+
scope: 'openid profile email offline_access',
3536
},
3637
transactionStore: new CookieTransactionStore(
3738
{
@@ -84,5 +85,10 @@ export function auth0(options: Auth0ExpressOptions) {
8485
response.redirect(logoutUrl.href);
8586
});
8687

88+
router.post('/auth/revoke', async (request: Request, response: Response) => {
89+
await request.auth0Client.revokeRefreshToken({}, { request, response });
90+
response.redirect('/private');
91+
});
92+
8793
return router;
8894
}
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
1-
This is a private page.
1+
This is a private page.
2+
3+
<form method="POST" action="/auth/revoke" class="mt-3">
4+
<button type="submit" class="btn btn-warning">Revoke Refresh Token</button>
5+
</form>

packages/auth0-server-js/EXAMPLES.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@
5858
- [Passing `StoreOptions`](#passing-storeoptions-9)
5959
- [Retrieving an Access Token for a Connection](#retrieving-an-access-token-for-a-connection)
6060
- [Passing `StoreOptions`](#passing-storeoptions-10)
61+
- [Revoking a Refresh Token](#revoking-a-refresh-token)
62+
- [Revoking the session token](#revoking-the-session-token)
63+
- [Revoking an explicit token](#revoking-an-explicit-token)
64+
- [Revoking on logout](#revoking-on-logout)
6165
- [Logout](#logout)
6266
- [Passing the `returnTo` parameter](#passing-the-returnto-parameter)
6367
- [Passing `StoreOptions`](#passing-storeoptions-11)
@@ -1473,6 +1477,47 @@ Once an enterprise connection has the option enabled, `getSession()` / `getUser(
14731477
- The connection must be an `okta` or `oidc` enterprise connection with `id_token_session_expiry_supported: true` (Dashboard toggle "Use ID Token for Session Expiry", Management API, or Terraform).
14741478
- Authorization Code flow.
14751479
1480+
## Revoking a Refresh Token
1481+
1482+
Revoking a refresh token invalidates it at Auth0 so it can no longer be used to obtain new access tokens.
1483+
This is useful when implementing secure logout flows or when a user's session needs to be forcibly terminated.
1484+
1485+
Revocation requires the application to have been granted `offline_access` scope so Auth0 issues a refresh token, and the target API must have **Allow Offline Access** enabled.
1486+
1487+
> **Note:** Revocation does not affect access tokens that have already been issued. They remain valid until their expiry. For immediate session termination, combine revocation with `logout()`.
1488+
1489+
### Revoking the session token
1490+
1491+
When called without arguments, `revokeRefreshToken()` reads the refresh token directly from the current session:
1492+
1493+
```ts
1494+
await serverClient.revokeRefreshToken();
1495+
```
1496+
1497+
If no session exists or the session has no refresh token, a `MissingSessionError` is thrown.
1498+
1499+
### Revoking an explicit token
1500+
1501+
A specific token can be passed via `options.token`, bypassing the session lookup:
1502+
1503+
```ts
1504+
await serverClient.revokeRefreshToken({ token: '<refresh_token>' });
1505+
```
1506+
1507+
In resolver mode, the domain-match guard still applies even when a token is supplied explicitly. If the session domain does not match the domain resolved for the current request (or if the session has no stored domain), the call returns without revoking. Pass an empty string to `options.token` to get a `MissingRequiredArgumentError` rather than a silent no-op.
1508+
1509+
### Revoking on logout
1510+
1511+
`logout()` automatically revokes the session's refresh token before clearing the local session.
1512+
Revocation is best-effort: if it fails for any reason (network error, token already revoked, misconfiguration), logout still proceeds. In resolver mode, both revocation and local session deletion only occur when the stored session domain matches the resolved domain — if they differ, the session belongs to a different tenant and is left untouched.
1513+
1514+
```ts
1515+
const logoutUrl = await serverClient.logout({
1516+
returnTo: 'http://localhost:3000',
1517+
});
1518+
// Redirect user to logoutUrl
1519+
```
1520+
14761521
## Logout
14771522
14781523
Logging out ensures the stored tokens and user information are removed, and that the user is no longer considered logged-in by the SDK.

packages/auth0-server-js/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
}
2626
},
2727
"dependencies": {
28-
"@auth0/auth0-auth-js": "^1.11.0",
28+
"@auth0/auth0-auth-js": "^1.12.0",
2929
"jose": "^6.0.8"
3030
},
3131
"devDependencies": {

packages/auth0-server-js/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ export { ServerClient } from './server-client.js';
22
export { AbstractStateStore } from './store/abstract-state-store.js';
33
export { AbstractTransactionStore } from './store/abstract-transaction-store.js';
44
export type { TokenResponse, ActClaim } from '@auth0/auth0-auth-js';
5-
export { TokenExchangeError, MissingClientAuthError, OrganizationValidationError } from '@auth0/auth0-auth-js';
5+
export { TokenExchangeError, TokenRevocationError, MissingClientAuthError, OrganizationValidationError } from '@auth0/auth0-auth-js';
66

77
export type { CookieHandler, CookieSerializeOptions } from './store/cookie-handler.js';
88
export { CookieTransactionStore } from './store/cookie-transaction-store.js';

0 commit comments

Comments
 (0)