Skip to content

Commit 4fff6a9

Browse files
authored
feat: add access token exchange and generic subject token type to Tok… (#904)
1 parent 2e51e05 commit 4fff6a9

3 files changed

Lines changed: 355 additions & 15 deletions

File tree

EXAMPLES.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- [Management API usage](#management-api-usage)
66
- [Verifying an ID token](#verifying-an-id-token)
77
- [Organizations](#organizations)
8+
- [Token Vault](#token-vault)
89
- [Logging](#logging)
910
- [Asynchronous operations](#asynchronous-operations)
1011

@@ -325,6 +326,57 @@ String url = auth.authorizeUrl("https://me.auth0.com/callback")
325326
.build();
326327
```
327328

329+
## Token Vault
330+
331+
[Token Vault](https://auth0.com/docs/secure/tokens/token-vault) lets you exchange an Auth0 token for a federated identity provider's access token, so your application can call the provider's APIs on the user's behalf. The connection must have Token Vault enabled, and because this exchange requires a private client, the `AuthAPI` instance must be configured with a client secret or client assertion.
332+
333+
### Exchange a refresh token
334+
335+
Use `getTokenForConnectionWithRefreshToken()` to exchange an Auth0 refresh token for the federated provider's access token:
336+
337+
```java
338+
AuthAPI auth = AuthAPI.newBuilder("{YOUR_DOMAIN}", "{YOUR_CLIENT_ID}", "{YOUR_CLIENT_SECRET}").build();
339+
TokenHolder result = auth.getTokenForConnectionWithRefreshToken("google-oauth2", "{REFRESH_TOKEN}", null)
340+
.execute()
341+
.getBody();
342+
String federatedAccessToken = result.getAccessToken();
343+
```
344+
345+
### Exchange an access token
346+
347+
Use `getTokenForConnectionWithAccessToken()` to exchange an Auth0 access token instead:
348+
349+
```java
350+
TokenHolder result = auth.getTokenForConnectionWithAccessToken("google-oauth2", "{ACCESS_TOKEN}", null)
351+
.execute()
352+
.getBody();
353+
String federatedAccessToken = result.getAccessToken();
354+
```
355+
356+
### Specifying a login hint
357+
358+
When a user has multiple accounts on the same connection, pass their ID within the identity provider as the `loginHint` (the final argument). Pass `null` to omit it:
359+
360+
```java
361+
TokenHolder result = auth.getTokenForConnectionWithRefreshToken("google-oauth2", "{REFRESH_TOKEN}", "{GOOGLE_USER_ID}")
362+
.execute()
363+
.getBody();
364+
```
365+
366+
### Exchanging other token types
367+
368+
For full control over the subject token type, use the generic `getTokenForConnection()` and supply the `subject_token_type` URN yourself:
369+
370+
```java
371+
TokenHolder result = auth.getTokenForConnection(
372+
"google-oauth2",
373+
"{SUBJECT_TOKEN}",
374+
"urn:ietf:params:oauth:token-type:refresh_token",
375+
"{GOOGLE_USER_ID}")
376+
.execute()
377+
.getBody();
378+
```
379+
328380
## Logging
329381
330382
The SDK is silent by default. You can enable logging to see HTTP requests and responses, which is useful for debugging.

src/main/java/com/auth0/client/auth/AuthAPI.java

Lines changed: 108 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -843,15 +843,20 @@ public TokenRequest exchangeToken(String subjectToken, String subjectTokenType)
843843
}
844844

845845
/**
846-
* Creates a request to exchange an Auth0 refresh token for a federated identity provider's access token
846+
* Creates a request to exchange a subject token for a federated identity provider's access token
847847
* using the Token Vault grant
848848
* {@code urn:auth0:params:oauth:grant-type:token-exchange:federated-connection-access-token}.
849+
* This is the generic entry point: the caller supplies both the {@code subjectToken} and its
850+
* {@code subjectTokenType}, so no assumption is made about which kind of token is being exchanged.
851+
* For the common cases, prefer {@link #getTokenForConnectionWithRefreshToken(String, String, String)}
852+
* or {@link #getTokenForConnectionWithAccessToken(String, String, String)}.
849853
* The connection must have the token vault enabled, and client authentication
850854
* (client secret or client assertion) is required as this must be a private client.
851855
* <pre>
852856
* {@code
853857
* try {
854-
* TokenHolder result = authAPI.getTokenForConnection("google-oauth2", refreshToken, "google-user-id")
858+
* TokenHolder result = authAPI.getTokenForConnection(
859+
* "google-oauth2", subjectToken, "urn:ietf:params:oauth:token-type:refresh_token", "google-user-id")
855860
* .execute()
856861
* .getBody();
857862
* String federatedAccessToken = result.getAccessToken();
@@ -864,22 +869,27 @@ public TokenRequest exchangeToken(String subjectToken, String subjectTokenType)
864869
* @see <a href="https://auth0.com/docs/secure/tokens/token-vault">Token Vault documentation</a>
865870
* @param connection the name of the federated connection to obtain an access token for
866871
* (for example {@code google-oauth2}). Must not be null.
867-
* @param refreshToken a valid Auth0 refresh token to exchange. Must not be null.
872+
* @param subjectToken the Auth0 token to exchange. Must not be null.
873+
* @param subjectTokenType the identifier for the type of {@code subjectToken}, for example
874+
* {@code urn:ietf:params:oauth:token-type:refresh_token} or
875+
* {@code urn:ietf:params:oauth:token-type:access_token}. Must not be null.
868876
* @param loginHint the user's ID within the identity provider specified by the connection
869877
* (for example, the Google user ID when the connection is {@code google-oauth2}).
870878
* May be null, in which case no {@code login_hint} is sent.
871879
* @return a Request to configure and execute.
872880
*/
873-
public TokenRequest getTokenForConnection(String connection, String refreshToken, String loginHint) {
881+
public TokenRequest getTokenForConnection(
882+
String connection, String subjectToken, String subjectTokenType, String loginHint) {
874883
Asserts.assertNotNull(connection, "connection");
875-
Asserts.assertNotNull(refreshToken, "refresh token");
884+
Asserts.assertNotNull(subjectToken, "subject token");
885+
Asserts.assertNotNull(subjectTokenType, "subject token type");
876886

877887
TokenRequest request = new TokenRequest(client, getTokenUrl());
878888
request.addParameter(KEY_CLIENT_ID, clientId);
879889
request.addParameter(
880890
KEY_GRANT_TYPE, "urn:auth0:params:oauth:grant-type:token-exchange:federated-connection-access-token");
881-
request.addParameter(KEY_SUBJECT_TOKEN, refreshToken);
882-
request.addParameter(KEY_SUBJECT_TOKEN_TYPE, "urn:ietf:params:oauth:token-type:refresh_token");
891+
request.addParameter(KEY_SUBJECT_TOKEN, subjectToken);
892+
request.addParameter(KEY_SUBJECT_TOKEN_TYPE, subjectTokenType);
883893
request.addParameter(
884894
KEY_REQUESTED_TOKEN_TYPE, "http://auth0.com/oauth/token-type/federated-connection-access-token");
885895
request.addParameter(KEY_CONNECTION, connection);
@@ -890,6 +900,97 @@ public TokenRequest getTokenForConnection(String connection, String refreshToken
890900
return request;
891901
}
892902

903+
/**
904+
* Convenience method to exchange an Auth0 refresh token for a federated identity provider's access token
905+
* using the Token Vault grant. Delegates to
906+
* {@link #getTokenForConnection(String, String, String, String)} with the subject token type
907+
* {@code urn:ietf:params:oauth:token-type:refresh_token}.
908+
* The connection must have the token vault enabled, and client authentication
909+
* (client secret or client assertion) is required as this must be a private client.
910+
* <pre>
911+
* {@code
912+
* try {
913+
* TokenHolder result = authAPI.getTokenForConnectionWithRefreshToken("google-oauth2", refreshToken, "google-user-id")
914+
* .execute()
915+
* .getBody();
916+
* String federatedAccessToken = result.getAccessToken();
917+
* } catch (Auth0Exception e) {
918+
* //Something happened
919+
* }
920+
* }
921+
* </pre>
922+
*
923+
* @see <a href="https://auth0.com/docs/secure/tokens/token-vault">Token Vault documentation</a>
924+
* @param connection the name of the federated connection to obtain an access token for
925+
* (for example {@code google-oauth2}). Must not be null.
926+
* @param refreshToken a valid Auth0 refresh token to exchange. Must not be null.
927+
* @param loginHint the user's ID within the identity provider specified by the connection
928+
* (for example, the Google user ID when the connection is {@code google-oauth2}).
929+
* May be null, in which case no {@code login_hint} is sent.
930+
* @return a Request to configure and execute.
931+
*/
932+
public TokenRequest getTokenForConnectionWithRefreshToken(
933+
String connection, String refreshToken, String loginHint) {
934+
Asserts.assertNotNull(refreshToken, "refresh token");
935+
return getTokenForConnection(
936+
connection, refreshToken, "urn:ietf:params:oauth:token-type:refresh_token", loginHint);
937+
}
938+
939+
/**
940+
* Convenience method to exchange an Auth0 access token for a federated identity provider's access token
941+
* using the Token Vault grant. Delegates to
942+
* {@link #getTokenForConnection(String, String, String, String)} with the subject token type
943+
* {@code urn:ietf:params:oauth:token-type:access_token}.
944+
* The connection must have the token vault enabled, and client authentication
945+
* (client secret or client assertion) is required as this must be a private client.
946+
* <pre>
947+
* {@code
948+
* try {
949+
* TokenHolder result = authAPI.getTokenForConnectionWithAccessToken("google-oauth2", accessToken, "google-user-id")
950+
* .execute()
951+
* .getBody();
952+
* String federatedAccessToken = result.getAccessToken();
953+
* } catch (Auth0Exception e) {
954+
* //Something happened
955+
* }
956+
* }
957+
* </pre>
958+
*
959+
* @see <a href="https://auth0.com/docs/secure/tokens/token-vault">Token Vault documentation</a>
960+
* @param connection the name of the federated connection to obtain an access token for
961+
* (for example {@code google-oauth2}). Must not be null.
962+
* @param accessToken a valid Auth0 access token to exchange. Must not be null.
963+
* @param loginHint the user's ID within the identity provider specified by the connection
964+
* (for example, the Google user ID when the connection is {@code google-oauth2}).
965+
* May be null, in which case no {@code login_hint} is sent.
966+
* @return a Request to configure and execute.
967+
*/
968+
public TokenRequest getTokenForConnectionWithAccessToken(String connection, String accessToken, String loginHint) {
969+
Asserts.assertNotNull(accessToken, "access token");
970+
return getTokenForConnection(
971+
connection, accessToken, "urn:ietf:params:oauth:token-type:access_token", loginHint);
972+
}
973+
974+
/**
975+
* Creates a request to exchange an Auth0 refresh token for a federated identity provider's access token
976+
* using the Token Vault grant.
977+
*
978+
* @deprecated Use {@link #getTokenForConnectionWithRefreshToken(String, String, String)} to exchange a
979+
* refresh token, or {@link #getTokenForConnectionWithAccessToken(String, String, String)} to
980+
* exchange an access token. For full control over the subject token type, use
981+
* {@link #getTokenForConnection(String, String, String, String)}.
982+
* @param connection the name of the federated connection to obtain an access token for
983+
* (for example {@code google-oauth2}). Must not be null.
984+
* @param refreshToken a valid Auth0 refresh token to exchange. Must not be null.
985+
* @param loginHint the user's ID within the identity provider specified by the connection.
986+
* May be null, in which case no {@code login_hint} is sent.
987+
* @return a Request to configure and execute.
988+
*/
989+
@Deprecated
990+
public TokenRequest getTokenForConnection(String connection, String refreshToken, String loginHint) {
991+
return getTokenForConnectionWithRefreshToken(connection, refreshToken, loginHint);
992+
}
993+
893994
/**
894995
* Creates a request to revoke an existing Refresh Token.
895996
* Confidential clients (Regular Web Apps) <strong>must</strong> have a client secret configured on this {@code AuthAPI} instance.

0 commit comments

Comments
 (0)