Skip to content

Commit fbe4019

Browse files
authored
feat(oauth): add support for access token subject type to Token Vault (#1169)
1 parent 125c178 commit fbe4019

File tree

4 files changed

+65
-8
lines changed

4 files changed

+65
-8
lines changed

EXAMPLES.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,10 @@ const { data: tokens } = await auth.oauth.clientCredentialsGrant({
152152
});
153153
```
154154

155-
### Exchange a Refresh Token for an Access Token for a Connection
155+
### Exchange a Refresh or Access Token for an Access Token for a Connection
156156

157157
```js
158-
import { AuthenticationClient } from 'auth0';
158+
import { AuthenticationClient, SUBJECT_TOKEN_TYPES } from 'auth0';
159159

160160
const auth = new AuthenticationClient({
161161
domain: '{YOUR_TENANT_AND REGION}.auth0.com',
@@ -165,6 +165,7 @@ const auth = new AuthenticationClient({
165165

166166
const { data: token } = await auth.oauth.tokenForConnection({
167167
subject_token: '{refresh_token}',
168+
subject_token_type: SUBJECT_TOKEN_TYPES.REFRESH_TOKEN, // Optional: defaults to refresh token type
168169
connection: 'google-oauth2', // The target social provider connection
169170
login_hint: 'user@example.com', // Optional: to target a specific account
170171
});

src/auth/oauth.ts

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -278,13 +278,17 @@ export interface TokenExchangeGrantRequest {
278278
*/
279279
export interface TokenForConnectionRequest {
280280
/**
281-
* The subject token(refresh token in this case) to exchange for an access token for a connection.
281+
* The subject token to exchange for an access token for a connection.
282282
*/
283283
subject_token: string;
284284
/**
285285
* The target social provider connection (e.g., "google-oauth2").
286286
*/
287287
connection: string;
288+
/**
289+
* An optional subject token type parameter to pass to the authorization server. If not provided, it defaults to `urn:ietf:params:oauth:token-type:refresh_token`.
290+
*/
291+
subject_token_type?: SUBJECT_TOKEN_TYPES;
288292
/**
289293
* Optional login hint
290294
*/
@@ -299,9 +303,30 @@ export interface TokenForConnectionResponse {
299303
[key: string]: unknown;
300304
}
301305

306+
export enum SUBJECT_TOKEN_TYPES {
307+
/**
308+
* Constant representing the subject type for a refresh token.
309+
* This is used in OAuth 2.0 token exchange to specify that the token being exchanged is a refresh token.
310+
*
311+
* @see {@link https://tools.ietf.org/html/rfc8693#section-3.1 RFC 8693 Section 3.1}
312+
*/
313+
REFRESH_TOKEN = 'urn:ietf:params:oauth:token-type:refresh_token',
314+
315+
/**
316+
* Constant representing the subject type for a access token.
317+
* This is used in OAuth 2.0 token exchange to specify that the token being exchanged is an access token.
318+
*
319+
* @see {@link https://tools.ietf.org/html/rfc8693#section-3.1 RFC 8693 Section 3.1}
320+
*/
321+
ACCESS_TOKEN = 'urn:ietf:params:oauth:token-type:access_token',
322+
}
323+
302324
export const TOKEN_FOR_CONNECTION_GRANT_TYPE =
303325
'urn:auth0:params:oauth:grant-type:token-exchange:federated-connection-access-token';
304326

327+
/**
328+
* @deprecated Use {@link SUBJECT_TOKEN_TYPES.REFRESH_TOKEN} instead.
329+
*/
305330
export const TOKEN_FOR_CONNECTION_TOKEN_TYPE = 'urn:ietf:params:oauth:token-type:refresh_token';
306331
export const TOKEN_FOR_CONNECTION_REQUESTED_TOKEN_TYPE =
307332
'http://auth0.com/oauth/token-type/federated-connection-access-token';
@@ -591,12 +616,13 @@ export class OAuth extends BaseAuthAPI {
591616
}
592617

593618
/**
594-
* Exchanges a subject token (refresh token in this case) for an access token for the connection.
619+
* Exchanges a subject token for an access token for the connection.
595620
*
596621
* The request body includes:
597622
* - client_id (and client_secret/client_assertion via addClientAuthentication)
598623
* - grant_type set to `urn:auth0:params:oauth:grant-type:token-exchange:federated-connection-access-token`
599-
* - subject_token (refresh token) and fixed subject_token_type for refresh tokens (`urn:ietf:params:oauth:token-type:refresh_token`)
624+
* - subject_token: the token to exchange
625+
* - subject_token_type: the type of token being exchanged. Defaults to refresh tokens (`urn:ietf:params:oauth:token-type:refresh_token`).
600626
* - requested_token_type (`http://auth0.com/oauth/token-type/federated-connection-access-token`) indicating that a federated connection access token is desired
601627
* - connection name and an optional `login_hint` if provided
602628
*
@@ -611,9 +637,9 @@ export class OAuth extends BaseAuthAPI {
611637
validateRequiredRequestParams(bodyParameters, ['connection', 'subject_token']);
612638

613639
const body: Record<string, string> = {
640+
subject_token_type: SUBJECT_TOKEN_TYPES.REFRESH_TOKEN,
614641
...bodyParameters,
615642
grant_type: TOKEN_FOR_CONNECTION_GRANT_TYPE,
616-
subject_token_type: TOKEN_FOR_CONNECTION_TOKEN_TYPE,
617643
requested_token_type: TOKEN_FOR_CONNECTION_REQUESTED_TOKEN_TYPE,
618644
};
619645

test/auth/fixtures/oauth.json

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@
194194
"scope": "https://test-domain.auth0.com",
195195
"method": "POST",
196196
"path": "/oauth/token",
197-
"body": "connection=google-oauth2&subject_token=test-refresh-token&grant_type=urn%3Aauth0%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange%3Afederated-connection-access-token&subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Arefresh_token&requested_token_type=http%3A%2F%2Fauth0.com%2Foauth%2Ftoken-type%2Ffederated-connection-access-token&client_secret=test-client-secret",
197+
"body": "subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Arefresh_token&connection=google-oauth2&subject_token=test-refresh-token&grant_type=urn%3Aauth0%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange%3Afederated-connection-access-token&requested_token_type=http%3A%2F%2Fauth0.com%2Foauth%2Ftoken-type%2Ffederated-connection-access-token&client_secret=test-client-secret",
198198
"status": 200,
199199
"response": {
200200
"access_token": "connection-access-token",
@@ -206,7 +206,19 @@
206206
"scope": "https://test-domain.auth0.com",
207207
"method": "POST",
208208
"path": "/oauth/token",
209-
"body": "connection=google-oauth2&subject_token=test-refresh-token&login_hint=user%40example.com&grant_type=urn%3Aauth0%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange%3Afederated-connection-access-token&subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Arefresh_token&requested_token_type=http%3A%2F%2Fauth0.com%2Foauth%2Ftoken-type%2Ffederated-connection-access-token&client_secret=test-client-secret",
209+
"body": "subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Arefresh_token&connection=google-oauth2&subject_token=test-refresh-token&login_hint=user%40example.com&grant_type=urn%3Aauth0%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange%3Afederated-connection-access-token&requested_token_type=http%3A%2F%2Fauth0.com%2Foauth%2Ftoken-type%2Ffederated-connection-access-token&client_secret=test-client-secret",
210+
"status": 200,
211+
"response": {
212+
"access_token": "connection-access-token",
213+
"expires_in": 86400,
214+
"token_type": "Bearer"
215+
}
216+
},
217+
{
218+
"scope": "https://test-domain.auth0.com",
219+
"method": "POST",
220+
"path": "/oauth/token",
221+
"body": "subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token&connection=google-oauth2&subject_token=test-id-token&grant_type=urn%3Aauth0%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange%3Afederated-connection-access-token&requested_token_type=http%3A%2F%2Fauth0.com%2Foauth%2Ftoken-type%2Ffederated-connection-access-token&client_secret=test-client-secret",
210222
"status": 200,
211223
"response": {
212224
"access_token": "connection-access-token",

test/auth/oauth.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
RevokeRefreshTokenRequest,
1010
PushedAuthorizationRequest,
1111
TokenForConnectionRequest,
12+
SUBJECT_TOKEN_TYPES,
1213
} from '../../src/index.js';
1314
import { withIdToken } from '../utils/index.js';
1415

@@ -418,6 +419,23 @@ describe('OAuth', () => {
418419
},
419420
});
420421
});
422+
423+
it('should use subject_token_type when provided', async () => {
424+
const oauth = new OAuth(opts);
425+
await expect(
426+
oauth.tokenForConnection({
427+
connection: 'google-oauth2',
428+
subject_token: 'test-id-token',
429+
subject_token_type: SUBJECT_TOKEN_TYPES.ACCESS_TOKEN,
430+
})
431+
).resolves.toMatchObject({
432+
data: {
433+
access_token: 'connection-access-token',
434+
expires_in: 86400,
435+
token_type: 'Bearer',
436+
},
437+
});
438+
});
421439
});
422440
});
423441

0 commit comments

Comments
 (0)