Skip to content

Commit d426fd8

Browse files
committed
Merge pull request #27 from auth0/feature-update-token-endpoint
Allow /oauth/token for private clients
2 parents ccb52ef + 458e613 commit d426fd8

5 files changed

Lines changed: 134 additions & 30 deletions

File tree

auth0/src/main/java/com/auth0/authentication/AuthenticationAPIClient.java

Lines changed: 50 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@
4646
/**
4747
* API client for Auth0 Authentication API.
4848
*
49+
* <pre><code>
50+
* Auth0 auth0 = new Auth0("your_client_id", "your_domain");
51+
* AuthenticationAPIClient client = new AuthenticationAPIClient(auth0);
52+
* </code></pre>
4953
* @see <a href="https://auth0.com/docs/auth-api">Auth API docs</a>
5054
*/
5155
public class AuthenticationAPIClient {
@@ -72,7 +76,6 @@ public class AuthenticationAPIClient {
7276
private static final String RESOURCE_OWNER_PATH = "ro";
7377
private static final String TOKEN_INFO_PATH = "tokeninfo";
7478
private static final String OAUTH_CODE_KEY = "code";
75-
private static final String OAUTH_CODE_VERIFIER_KEY = "code_verifier";
7679
private static final String REDIRECT_URI_KEY = "redirect_uri";
7780

7881
private final Auth0 auth0;
@@ -680,6 +683,52 @@ public ProfileRequest getProfileAfter(AuthenticationRequest authenticationReques
680683
return new ProfileRequest(authenticationRequest, profileRequest);
681684
}
682685

686+
/**
687+
* Fetch the token information from Auth0, using the authorization_code grant type
688+
*
689+
* For Public Client, e.g. Android apps ,you need to provide the code_verifier
690+
* used to generate the challenge sent to Auth0 {@literal /authorize} method like:
691+
*
692+
* <pre>{@code
693+
* AuthenticationAPIClient client = new AuthenticationAPIClient(new Auth0("clientId", "domain"));
694+
* client
695+
* .token("code", "redirect_uri")
696+
* .setCodeVerifier("code_verifier")
697+
* .start(new Callback<Credentials> {...});
698+
* }</pre>
699+
*
700+
* For the rest of clients, clients who can safely keep a {@literal client_secret}, you need to provide it instead like:
701+
*
702+
* <pre>{@code
703+
* AuthenticationAPIClient client = new AuthenticationAPIClient(new Auth0("clientId", "domain"));
704+
* client
705+
* .token("code", "redirect_uri")
706+
* .setClientSecret("client_secret")
707+
* .start(new Callback<Credentials> {...});
708+
* }</pre>
709+
*
710+
* @param authorizationCode the authorization code received from the /authorize call.
711+
* @param redirectUri the uri sent to /authorize as the 'redirect_uri'.
712+
* @return a request to obtain access_token by exchanging a authorization code.
713+
*/
714+
@SuppressWarnings("WeakerAccess")
715+
public TokenRequest token(String authorizationCode, String redirectUri) {
716+
Map<String, Object> parameters = ParameterBuilder.newBuilder()
717+
.setClientId(getClientId())
718+
.setGrantType(GRANT_TYPE_AUTHORIZATION_CODE)
719+
.set(OAUTH_CODE_KEY, authorizationCode)
720+
.set(REDIRECT_URI_KEY, redirectUri)
721+
.asDictionary();
722+
723+
HttpUrl url = HttpUrl.parse(auth0.getDomainUrl()).newBuilder()
724+
.addPathSegment(OAUTH_PATH)
725+
.addPathSegment(TOKEN_PATH)
726+
.build();
727+
728+
ParameterizableRequest<Credentials> request = factory.POST(url, client, gson, Credentials.class).addParameters(parameters);
729+
return new TokenRequest(request);
730+
}
731+
683732
private AuthenticationRequest loginWithResourceOwner(Map<String, Object> parameters) {
684733
HttpUrl url = HttpUrl.parse(auth0.getDomainUrl()).newBuilder()
685734
.addPathSegment(OAUTH_PATH)
@@ -703,29 +752,4 @@ private ParameterizableRequest<UserProfile> profileRequest() {
703752
return factory.POST(url, client, gson, UserProfile.class);
704753
}
705754

706-
/**
707-
* Fetch the token information from Auth0, using the authorization_code grant type
708-
*
709-
* @param authorizationCode the authorization code received from the /authorize call.
710-
* @param codeVerifier the code verifier used when requesting a code to /authorize.
711-
* @param redirectUri the uri to redirect after a successful request.
712-
* @return a request to configure and start
713-
*/
714-
public AuthenticationRequest token(String authorizationCode, String codeVerifier, String redirectUri) {
715-
Map<String, Object> parameters = ParameterBuilder.newBuilder()
716-
.setClientId(getClientId())
717-
.setGrantType(GRANT_TYPE_AUTHORIZATION_CODE)
718-
.set(OAUTH_CODE_KEY, authorizationCode)
719-
.set(OAUTH_CODE_VERIFIER_KEY, codeVerifier)
720-
.set(REDIRECT_URI_KEY, redirectUri)
721-
.asDictionary();
722-
723-
HttpUrl url = HttpUrl.parse(auth0.getDomainUrl()).newBuilder()
724-
.addPathSegment(OAUTH_PATH)
725-
.addPathSegment(TOKEN_PATH)
726-
.build();
727-
728-
return factory.authenticationPOST(url, client, gson)
729-
.addAuthenticationParameters(parameters);
730-
}
731755
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package com.auth0.authentication;
2+
3+
import com.auth0.Auth0Exception;
4+
import com.auth0.authentication.result.Credentials;
5+
import com.auth0.callback.BaseCallback;
6+
import com.auth0.request.ParameterizableRequest;
7+
import com.auth0.request.Request;
8+
9+
/**
10+
* Auth Request to obtain tokens using OAuth2 {@literal /oauth/token} method
11+
*/
12+
@SuppressWarnings("WeakerAccess")
13+
public class TokenRequest implements Request<Credentials> {
14+
15+
private static final String OAUTH_CODE_VERIFIER_KEY = "code_verifier";
16+
private static final String OAUTH_CLIENT_SECRET_KEY = "client_secret";
17+
18+
private final ParameterizableRequest<Credentials> request;
19+
20+
TokenRequest(ParameterizableRequest<Credentials> request) {
21+
this.request = request;
22+
}
23+
24+
/**
25+
* Adds the code verifier to the request (Public Clients)
26+
* @param codeVerifier the code verifier used to generate the challenge sent to /authorize.
27+
* @return itself
28+
*/
29+
@SuppressWarnings("WeakerAccess")
30+
public TokenRequest setCodeVerifier(String codeVerifier) {
31+
this.request.addParameter(OAUTH_CODE_VERIFIER_KEY, codeVerifier);
32+
return this;
33+
}
34+
35+
/**
36+
* Adds the client secret to the request (Private Clients)
37+
* @param clientSecret the secret of the client used when making a request to /authorize
38+
* @return iself
39+
*/
40+
@SuppressWarnings("WeakerAccess")
41+
public TokenRequest setClientSecret(String clientSecret) {
42+
this.request.addParameter(OAUTH_CLIENT_SECRET_KEY, clientSecret);
43+
return this;
44+
}
45+
46+
@Override
47+
public void start(BaseCallback<Credentials> callback) {
48+
request.start(callback);
49+
}
50+
51+
@Override
52+
public Credentials execute() throws Auth0Exception {
53+
return request.execute();
54+
}
55+
}

auth0/src/main/java/com/auth0/authentication/UserProfileDeserializer.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import com.auth0.authentication.result.UserProfile;
55
import com.google.gson.*;
66
import com.google.gson.reflect.TypeToken;
7-
import com.sun.org.apache.xpath.internal.operations.Bool;
87

98
import java.lang.reflect.Type;
109
import java.util.Date;

auth0/src/test/java/com/auth0/authentication/AuthenticationAPIClientTest.java

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import com.auth0.authentication.result.DatabaseUser;
3232
import com.auth0.authentication.result.Delegation;
3333
import com.auth0.authentication.result.UserProfile;
34+
import com.auth0.request.ParameterizableRequest;
3435
import com.auth0.util.AuthenticationAPI;
3536
import com.auth0.util.MockBaseCallback;
3637
import com.google.gson.Gson;
@@ -1214,13 +1215,14 @@ public void shouldFetchProfileAfterLoginRequest() throws Exception {
12141215
}
12151216

12161217
@Test
1217-
public void shouldGetOAuthTokens() throws Exception {
1218+
public void shouldGetOAuthTokensUsingCodeVerifier() throws Exception {
12181219
mockAPI
12191220
.willReturnTokens()
12201221
.willReturnTokenInfo();
12211222

12221223
final MockBaseCallback<Credentials> callback = new MockBaseCallback<>();
1223-
client.token("code", "codeVerifier", "http://redirect.uri")
1224+
client.token("code", "http://redirect.uri")
1225+
.setCodeVerifier("codeVerifier")
12241226
.start(callback);
12251227

12261228
final RecordedRequest request = mockAPI.takeRequest();
@@ -1236,6 +1238,30 @@ public void shouldGetOAuthTokens() throws Exception {
12361238
assertThat(callback, hasPayloadOfType(Credentials.class));
12371239
}
12381240

1241+
@Test
1242+
public void shouldGetOAuthTokensUsingClientSecret() throws Exception {
1243+
mockAPI
1244+
.willReturnTokens()
1245+
.willReturnTokenInfo();
1246+
1247+
final MockBaseCallback<Credentials> callback = new MockBaseCallback<>();
1248+
client.token("code", "http://redirect.uri")
1249+
.setClientSecret("clientSecret")
1250+
.start(callback);
1251+
1252+
final RecordedRequest request = mockAPI.takeRequest();
1253+
assertThat(request.getPath(), equalTo("/oauth/token"));
1254+
1255+
Map<String, String> body = bodyFromRequest(request);
1256+
assertThat(body, hasEntry("grant_type", ParameterBuilder.GRANT_TYPE_AUTHORIZATION_CODE));
1257+
assertThat(body, hasEntry("client_id", CLIENT_ID));
1258+
assertThat(body, hasEntry("code", "code"));
1259+
assertThat(body, hasEntry("client_secret", "clientSecret"));
1260+
assertThat(body, hasEntry("redirect_uri", "http://redirect.uri"));
1261+
1262+
assertThat(callback, hasPayloadOfType(Credentials.class));
1263+
}
1264+
12391265
private Map<String, String> bodyFromRequest(RecordedRequest request) throws java.io.IOException {
12401266
final Type mapType = new TypeToken<Map<String, String>>() {
12411267
}.getType();

auth0/src/test/java/com/auth0/util/UserProfileMatcher.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,6 @@ public void describeTo(Description description) {
3838
}
3939

4040
public static Matcher<UserProfile> isNormalizedProfile(String id, String name, String nickname) {
41-
return new UserProfileMatcher(equalTo(id), equalTo(name), equalTo(nickname), not(isEmptyOrNullString()));
41+
return new UserProfileMatcher(equalTo(id), equalTo(name), equalTo(nickname), not(emptyOrNullString()));
4242
}
4343
}

0 commit comments

Comments
 (0)