Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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.SUBJECT_TYPE_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
40 changes: 36 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}
*/
SUBJECT_TYPE_REFRESH_TOKEN = 'urn:ietf:params:oauth:token-type:refresh_token',
Comment thread
guabu marked this conversation as resolved.
Outdated

/**
* 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}
*/
SUBJECT_TYPE_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.SUBJECT_TYPE_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 @@ -610,10 +636,16 @@ export class OAuth extends BaseAuthAPI {
): Promise<JSONApiResponse<TokenSet>> {
validateRequiredRequestParams(bodyParameters, ['connection', 'subject_token']);

let subjectTokenType = SUBJECT_TOKEN_TYPES.SUBJECT_TYPE_REFRESH_TOKEN;

if (bodyParameters.subject_token_type) {
subjectTokenType = bodyParameters.subject_token_type;
}

const body: Record<string, string> = {
...bodyParameters,
grant_type: TOKEN_FOR_CONNECTION_GRANT_TYPE,
subject_token_type: TOKEN_FOR_CONNECTION_TOKEN_TYPE,
subject_token_type: subjectTokenType,
Comment thread
guabu marked this conversation as resolved.
Outdated
requested_token_type: TOKEN_FOR_CONNECTION_REQUESTED_TOKEN_TYPE,
};

Expand Down
12 changes: 12 additions & 0 deletions test/auth/fixtures/oauth.json
Original file line number Diff line number Diff line change
Expand Up @@ -213,5 +213,17 @@
"expires_in": 86400,
"token_type": "Bearer"
}
},
{
"scope": "https://test-domain.auth0.com",
"method": "POST",
"path": "/oauth/token",
"body": "connection=google-oauth2&subject_token=test-id-token&subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_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",
"expires_in": 86400,
"token_type": "Bearer"
}
}
]
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.SUBJECT_TYPE_ACCESS_TOKEN,
})
).resolves.toMatchObject({
data: {
access_token: 'connection-access-token',
expires_in: 86400,
token_type: 'Bearer',
},
});
});
});
});

Expand Down