Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Social Native Token Exchange endpoint #281

Merged
merged 3 commits into from
Feb 6, 2020
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
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
import static com.auth0.android.authentication.ParameterBuilder.GRANT_TYPE_PASSWORD;
import static com.auth0.android.authentication.ParameterBuilder.GRANT_TYPE_PASSWORDLESS_OTP;
import static com.auth0.android.authentication.ParameterBuilder.GRANT_TYPE_PASSWORD_REALM;
import static com.auth0.android.authentication.ParameterBuilder.GRANT_TYPE_TOKEN_EXCHANGE;
import static com.auth0.android.authentication.ParameterBuilder.ID_TOKEN_KEY;
import static com.auth0.android.authentication.ParameterBuilder.SCOPE_OPENID;

Expand Down Expand Up @@ -87,6 +88,8 @@ public class AuthenticationAPIClient {
private static final String TOKEN_KEY = "token";
private static final String MFA_TOKEN_KEY = "mfa_token";
private static final String ONE_TIME_PASSWORD_KEY = "otp";
private static final String SUBJECT_TOKEN_KEY = "subject_token";
private static final String SUBJECT_TOKEN_TYPE_KEY = "subject_token_type";
private static final String DELEGATION_PATH = "delegation";
private static final String ACCESS_TOKEN_PATH = "access_token";
private static final String SIGN_UP_PATH = "signup";
Expand Down Expand Up @@ -318,6 +321,45 @@ public AuthenticationRequest loginWithOAuthAccessToken(@NonNull String token, @N
.addAuthenticationParameters(parameters);
}

/**
* Log in a user using a token obtained from a Native Social Identity Provider, such as Facebook, using <a href="https://auth0.com/docs/api/authentication#token-exchange-for-native-social">'\oauth\token' endpoint</a>
* The default scope used is 'openid'.
* Example usage:
* <pre>
* {@code
* client.loginWithNativeSocialToken("{subject token}", "{subject token type}")
* .start(new BaseCallback<Credentials>() {
* {@literal}Override
* public void onSuccess(Credentials payload) { }
*
* {@literal}Override
* public void onFailure(AuthenticationException error) { }
* });
* }
* </pre>
*
* @param token the subject token, typically obtained through the Identity Provider's SDK
* @param tokenType the subject token type that is associated with this Identity Provider. e.g. 'http:https://auth0.com/oauth/token-type/facebook-session-access-token'
* @return a request to configure and start that will yield {@link Credentials}
*/
@SuppressWarnings("WeakerAccess")
public AuthenticationRequest loginWithNativeSocialToken(@NonNull String token, @NonNull String tokenType) {
lbalmaceda marked this conversation as resolved.
Show resolved Hide resolved
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lbalmaceda Where can I find the list of the supported tokenTypes. Is there one for Google SignIn?
From the doc (https://auth0.com/docs/api/authentication#token-exchange-for-native-social) it looks like only apple-authz-code is supported:

Identifier that indicates the type of subject_token. 
Currently supported native social values are: 
http:https://auth0.com/oauth/token-type/apple-authz-code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Google Sign-In is not supported yet, unfortunately. There's an ongoing effort to add Facebook Sign-In. The server-side team is working on this case by case per request from our customers.
That link you posted will be updated when support for another provider is added. And of course, once it's available, we will update the SDKs.

HttpUrl url = HttpUrl.parse(auth0.getDomainUrl()).newBuilder()
.addPathSegment(OAUTH_PATH)
.addPathSegment(TOKEN_PATH)
.build();

Map<String, Object> parameters = ParameterBuilder.newAuthenticationBuilder()
.setGrantType(GRANT_TYPE_TOKEN_EXCHANGE)
.setClientId(getClientId())
.set(SUBJECT_TOKEN_KEY, token)
.set(SUBJECT_TOKEN_TYPE_KEY, tokenType)
.asDictionary();

return factory.authenticationPOST(url, client, gson)
.addAuthenticationParameters(parameters);
lbalmaceda marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Log in a user using a phone number and a verification code received via SMS (Part of passwordless login flow)
* The default scope used is 'openid'.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public class ParameterBuilder {
public static final String GRANT_TYPE_AUTHORIZATION_CODE = "authorization_code";
public static final String GRANT_TYPE_MFA_OTP = "http:https://auth0.com/oauth/grant-type/mfa-otp";
public static final String GRANT_TYPE_PASSWORDLESS_OTP = "http:https://auth0.com/oauth/grant-type/passwordless/otp";
public static final String GRANT_TYPE_TOKEN_EXCHANGE = "urn:ietf:params:oauth:grant-type:token-exchange";

public static final String SCOPE_OPENID = "openid";
public static final String SCOPE_OFFLINE_ACCESS = "openid offline_access";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -382,8 +382,7 @@ public void shouldFetchUserInfoSync() throws Exception {

@Test
public void shouldLoginWithOAuthAccessToken() throws Exception {
mockAPI
.willReturnSuccessfulLogin();
mockAPI.willReturnSuccessfulLogin();

final MockAuthenticationCallback<Credentials> callback = new MockAuthenticationCallback<>();
client.loginWithOAuthAccessToken("fbtoken", "facebook")
Expand Down Expand Up @@ -421,6 +420,50 @@ public void shouldLoginWithOAuthAccessTokenSync() throws Exception {
assertThat(credentials, is(notNullValue()));
}

@Test
public void shouldLoginWithNativeSocialToken() throws Exception {
mockAPI.willReturnSuccessfulLogin();

final MockAuthenticationCallback<Credentials> callback = new MockAuthenticationCallback<>();
client.loginWithNativeSocialToken("test-token-value", "test-token-type")
.start(callback);

final RecordedRequest request = mockAPI.takeRequest();
assertThat(request.getHeader("Accept-Language"), is(getDefaultLocale()));
assertThat(request.getPath(), equalTo("/oauth/token"));

Map<String, String> body = bodyFromRequest(request);
assertThat(body, hasEntry("client_id", CLIENT_ID));
assertThat(body, hasEntry("grant_type", ParameterBuilder.GRANT_TYPE_TOKEN_EXCHANGE));
assertThat(body, hasEntry("subject_token", "test-token-value"));
assertThat(body, hasEntry("subject_token_type", "test-token-type"));
assertThat(body, hasEntry("scope", OPENID));

assertThat(callback, hasPayloadOfType(Credentials.class));
}

@Test
public void shouldLoginWithNativeSocialTokenSync() throws Exception {
mockAPI.willReturnSuccessfulLogin();

final Credentials credentials = client
.loginWithNativeSocialToken("test-token-value", "test-token-type")
.execute();

final RecordedRequest request = mockAPI.takeRequest();
assertThat(request.getHeader("Accept-Language"), is(getDefaultLocale()));
assertThat(request.getPath(), equalTo("/oauth/token"));

Map<String, String> body = bodyFromRequest(request);
assertThat(body, hasEntry("client_id", CLIENT_ID));
assertThat(body, hasEntry("grant_type", ParameterBuilder.GRANT_TYPE_TOKEN_EXCHANGE));
assertThat(body, hasEntry("subject_token", "test-token-value"));
assertThat(body, hasEntry("subject_token_type", "test-token-type"));
assertThat(body, hasEntry("scope", OPENID));

assertThat(credentials, is(notNullValue()));
}

@Test
public void shouldLoginWithPhoneNumberWithCustomConnectionWithOTPGrantIfOIDCConformant() throws Exception {
mockAPI.willReturnSuccessfulLogin();
Expand Down Expand Up @@ -553,8 +596,7 @@ public void shouldLoginWithEmailOnlyWithOTPGrantIfOIDCConformant() throws Except

@Test
public void shouldLoginWithEmailOnlySyncWithOTPGrantIfOIDCConformant() throws Exception {
mockAPI
.willReturnSuccessfulLogin()
mockAPI.willReturnSuccessfulLogin()
.willReturnTokenInfo();

Auth0 auth0 = new Auth0(CLIENT_ID, mockAPI.getDomain(), mockAPI.getDomain());
Expand Down Expand Up @@ -691,8 +733,7 @@ public void shouldLoginWithEmailOnly() throws Exception {

@Test
public void shouldLoginWithEmailOnlySync() throws Exception {
mockAPI
.willReturnSuccessfulLogin()
mockAPI.willReturnSuccessfulLogin()
.willReturnTokenInfo();

final Credentials credentials = client
Expand Down Expand Up @@ -1262,8 +1303,7 @@ public void shouldGetCustomizedDelegationRequestWithIdToken() throws Exception {
public void shouldGetCustomizedDelegationRequestWithIdTokenSync() throws Exception {
mockAPI.willReturnNewIdToken();

client
.delegationWithIdToken(ID_TOKEN, "custom_api_type")
client.delegationWithIdToken(ID_TOKEN, "custom_api_type")
.setScope("custom_scope")
.setTarget("custom_target")
.execute();
Expand Down Expand Up @@ -1916,8 +1956,7 @@ public void shouldGetOAuthTokensUsingCodeVerifier() throws Exception {

@Test
public void shouldParseUnauthorizedPKCEError() throws Exception {
mockAPI
.willReturnPlainTextUnauthorized();
mockAPI.willReturnPlainTextUnauthorized();

final MockAuthenticationCallback<Credentials> callback = new MockAuthenticationCallback<>();
client.token("code", "http:https://redirect.uri")
Expand Down