Skip to content

Commit 95c8861

Browse files
jd3vi1claude
andcommitted
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 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent faee2c4 commit 95c8861

5 files changed

Lines changed: 21 additions & 6 deletions

File tree

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

Lines changed: 1 addition & 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
{

packages/auth0-server-js/EXAMPLES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1504,6 +1504,8 @@ A specific token can be passed via `options.token`, bypassing the session lookup
15041504
await serverClient.revokeRefreshToken({ token: '<refresh_token>' });
15051505
```
15061506
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+
15071509
### Revoking on logout
15081510
15091511
`logout()` automatically revokes the session's refresh token before clearing the local session.

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/server-client.spec.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7051,16 +7051,19 @@ describe('logout revocation', () => {
70517051
};
70527052

70537053
test('should revoke refresh token before clearing session on logout', async () => {
7054-
let revocationCalled = false;
7054+
const ops: string[] = [];
70557055
setupRevocation(() => {
7056-
revocationCalled = true;
7056+
ops.push('revoke');
70577057
return new HttpResponse(null, { status: 200 });
70587058
});
70597059

70607060
const mockStateStore = {
70617061
get: vi.fn(),
70627062
set: vi.fn(),
7063-
delete: vi.fn(),
7063+
delete: vi.fn().mockImplementation(() => {
7064+
ops.push('delete');
7065+
return Promise.resolve();
7066+
}),
70647067
deleteByLogoutToken: vi.fn(),
70657068
};
70667069

@@ -7085,8 +7088,7 @@ describe('logout revocation', () => {
70857088

70867089
await serverClient.logout({ returnTo: '/after-logout' });
70877090

7088-
expect(revocationCalled).toBe(true);
7089-
expect(mockStateStore.delete).toHaveBeenCalled();
7091+
expect(ops).toEqual(['revoke', 'delete']);
70907092
});
70917093

70927094
test('should continue with logout even if revocation fails', async () => {

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,16 +1081,26 @@ export class ServerClient<TStoreOptions = unknown> {
10811081
/**
10821082
* Revokes the refresh token stored in the current session, or an explicitly supplied token.
10831083
*
1084+
* In resolver mode, revocation only occurs when the session domain matches the domain resolved
1085+
* for the current request. If the domains differ (or the session has no stored domain), the call
1086+
* returns without revoking to avoid sending a token to the wrong tenant. This guard applies even
1087+
* when a token is passed explicitly via `options.token`.
1088+
*
10841089
* @param options Optionally supply a token to revoke instead of reading from the session.
10851090
* @param storeOptions Optional options passed to the StateStore.
10861091
*
1092+
* @throws {MissingRequiredArgumentError} If `options.token` is an empty string.
10871093
* @throws {MissingSessionError} If no refresh token is found in the session and none was provided.
10881094
* @throws {TokenRevocationError} If the revocation request fails.
10891095
*/
10901096
public async revokeRefreshToken(
10911097
options: RevokeRefreshTokenOptions = {},
10921098
storeOptions?: TStoreOptions
10931099
): Promise<void> {
1100+
if (options.token !== undefined && options.token.length === 0) {
1101+
throw new MissingRequiredArgumentError('options.token must not be an empty string.');
1102+
}
1103+
10941104
let refreshToken = options.token;
10951105

10961106
// Skip the store read when a token is supplied and we are in static mode:

0 commit comments

Comments
 (0)