Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,10 @@ const { data: tokens } = await auth.oauth.clientCredentialsGrant({
});
```

### Exchange a Refresh Token for an Access Token for a Connection
### Exchange a Refresh or Access Token for an Access Token for a Connection

```js
import { AuthenticationClient } from 'auth0';
import { AuthenticationClient, SUBJECT_TOKEN_TYPES } from 'auth0';

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

const { data: token } = await auth.oauth.tokenForConnection({
subject_token: '{refresh_token}',
subject_token_type: SUBJECT_TOKEN_TYPES.REFRESH_TOKEN, // Optional: defaults to refresh token type
connection: 'google-oauth2', // The target social provider connection
login_hint: 'user@example.com', // Optional: to target a specific account
});
Expand Down
34 changes: 30 additions & 4 deletions src/auth/oauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,13 +278,17 @@ export interface TokenExchangeGrantRequest {
*/
export interface TokenForConnectionRequest {
/**
* The subject token(refresh token in this case) to exchange for an access token for a connection.
* The subject token to exchange for an access token for a connection.
*/
subject_token: string;
/**
* The target social provider connection (e.g., "google-oauth2").
*/
connection: string;
/**
* 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`.
*/
subject_token_type?: SUBJECT_TOKEN_TYPES;
/**
* Optional login hint
*/
Expand All @@ -299,9 +303,30 @@ export interface TokenForConnectionResponse {
[key: string]: unknown;
}

export enum SUBJECT_TOKEN_TYPES {
/**
* Constant representing the subject type for a refresh token.
* This is used in OAuth 2.0 token exchange to specify that the token being exchanged is a refresh token.
*
* @see {@link https://tools.ietf.org/html/rfc8693#section-3.1 RFC 8693 Section 3.1}
*/
REFRESH_TOKEN = 'urn:ietf:params:oauth:token-type:refresh_token',

/**
* Constant representing the subject type for a access token.
* This is used in OAuth 2.0 token exchange to specify that the token being exchanged is an access token.
*
* @see {@link https://tools.ietf.org/html/rfc8693#section-3.1 RFC 8693 Section 3.1}
*/
ACCESS_TOKEN = 'urn:ietf:params:oauth:token-type:access_token',
}

export const TOKEN_FOR_CONNECTION_GRANT_TYPE =
'urn:auth0:params:oauth:grant-type:token-exchange:federated-connection-access-token';

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

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

const body: Record<string, string> = {
subject_token_type: SUBJECT_TOKEN_TYPES.REFRESH_TOKEN,
...bodyParameters,
grant_type: TOKEN_FOR_CONNECTION_GRANT_TYPE,
subject_token_type: TOKEN_FOR_CONNECTION_TOKEN_TYPE,
requested_token_type: TOKEN_FOR_CONNECTION_REQUESTED_TOKEN_TYPE,
};

Expand Down
16 changes: 14 additions & 2 deletions test/auth/fixtures/oauth.json
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@
"scope": "https://test-domain.auth0.com",
"method": "POST",
"path": "/oauth/token",
"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",
"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",
"status": 200,
"response": {
"access_token": "connection-access-token",
Expand All @@ -206,7 +206,19 @@
"scope": "https://test-domain.auth0.com",
"method": "POST",
"path": "/oauth/token",
"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",
"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",
"status": 200,
"response": {
"access_token": "connection-access-token",
"expires_in": 86400,
"token_type": "Bearer"
}
},
{
"scope": "https://test-domain.auth0.com",
"method": "POST",
"path": "/oauth/token",
"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",
"status": 200,
"response": {
"access_token": "connection-access-token",
Expand Down
18 changes: 18 additions & 0 deletions test/auth/oauth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
RevokeRefreshTokenRequest,
PushedAuthorizationRequest,
TokenForConnectionRequest,
SUBJECT_TOKEN_TYPES,
} from '../../src/index.js';
import { withIdToken } from '../utils/index.js';

Expand Down Expand Up @@ -418,6 +419,23 @@ describe('OAuth', () => {
},
});
});

it('should use subject_token_type when provided', async () => {
const oauth = new OAuth(opts);
await expect(
oauth.tokenForConnection({
connection: 'google-oauth2',
subject_token: 'test-id-token',
subject_token_type: SUBJECT_TOKEN_TYPES.ACCESS_TOKEN,
})
).resolves.toMatchObject({
data: {
access_token: 'connection-access-token',
expires_in: 86400,
token_type: 'Bearer',
},
});
});
});
});

Expand Down
Loading