diff --git a/EXAMPLES.md b/EXAMPLES.md index 763a4188..71c21b61 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -1032,7 +1032,8 @@ To sign up a user with passkey try { val challenge = authenticationApiClient.signupWithPasskey( "{user-data}", - "{realm}" + "{realm}", + "{organization-id}" ).await() //Use CredentialManager to create public key credentials @@ -1048,7 +1049,7 @@ try { ) val userCredential = authenticationApiClient.signinWithPasskey( - challenge.authSession, authRequest, "{realm}" + challenge.authSession, authRequest, "{realm}" , "{organization-id}" ) .validateClaims() .await() @@ -1060,7 +1061,7 @@ try { Using Java ```java - authenticationAPIClient.signupWithPasskey("{user-data}", "{realm}") + authenticationAPIClient.signupWithPasskey("{user-data}", "{realm}","{organization-id}") .start(new Callback() { @Override public void onSuccess(PasskeyRegistrationChallenge result) { @@ -1078,7 +1079,7 @@ try { PublicKeyCredentials.class); authenticationAPIClient.signinWithPasskey(result.getAuthSession(), - credentials, "{realm}") + credentials, "{realm}","{organization-id}") .start(new Callback() { @Override public void onSuccess(Credentials result) {} @@ -1104,7 +1105,7 @@ To sign in a user with passkey try { val challenge = - authenticationApiClient.passkeyChallenge("{realm}") + authenticationApiClient.passkeyChallenge("{realm}","{organization-id}") .await() //Use CredentialManager to create public key credentials @@ -1122,7 +1123,8 @@ try { val userCredential = authenticationApiClient.signinWithPasskey( challenge.authSession, authRequest, - "{realm}" + "{realm}", + "{organization-id}" ) .validateClaims() .await() @@ -1138,7 +1140,7 @@ try { Using Java ```java -authenticationAPIClient.passkeyChallenge("realm") +authenticationAPIClient.passkeyChallenge("realm","{organization-id}") .start(new Callback() { @Override public void onSuccess(PasskeyChallenge result) { @@ -1158,7 +1160,7 @@ authenticationAPIClient.passkeyChallenge("realm") responseJson, PublicKeyCredentials.class ); - authenticationAPIClient.signinWithPasskey(result.getAuthSession(), publicKeyCredentials,"{realm}") + authenticationAPIClient.signinWithPasskey(result.getAuthSession(), publicKeyCredentials,"{realm}","{organization-id}") .start(new Callback() { @Override public void onSuccess(Credentials result) {} diff --git a/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt b/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt index 560e649f..a026084c 100755 --- a/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt +++ b/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt @@ -166,7 +166,7 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe * Example usage: * * ``` - * client.signinWithPasskey("{authSession}", "{authResponse}","{realm}") + * client.signinWithPasskey("{authSession}", "{authResponse}","{realm}","${organization}") * .validateClaims() //mandatory * .setScope("{scope}") * .start(object: Callback { @@ -178,17 +178,20 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe * @param authSession the auth session received from the server as part of the public key challenge request. * @param authResponse the [PublicKeyCredentials] authentication response * @param realm the connection to use. If excluded, the application will use the default connection configured in the tenant + * @param organization id of the organization to be associated with the user while signing in * @return a request to configure and start that will yield [Credentials] */ public fun signinWithPasskey( authSession: String, authResponse: PublicKeyCredentials, - realm: String? = null + realm: String? = null, + organization: String? = null, ): AuthenticationRequest { val params = ParameterBuilder.newBuilder().apply { setGrantType(ParameterBuilder.GRANT_TYPE_PASSKEY) set(AUTH_SESSION_KEY, authSession) realm?.let { setRealm(it) } + organization?.let { set(ORGANIZATION_KEY, organization) } }.asDictionary() return loginWithToken(params) @@ -210,7 +213,7 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe * Example usage: * * ``` - * client.signinWithPasskey("{authSession}", "{authResponse}","{realm}") + * client.signinWithPasskey("{authSession}", "{authResponse}","{realm}","{organization}") * .validateClaims() //mandatory * .setScope("{scope}") * .start(object: Callback { @@ -222,18 +225,20 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe * @param authSession the auth session received from the server as part of the public key challenge request. * @param authResponse the public key credential authentication response in JSON string format that follows the standard webauthn json format * @param realm the connection to use. If excluded, the application will use the default connection configured in the tenant + * @param organization id of the organization to be associated with the user while signing in * @return a request to configure and start that will yield [Credentials] */ public fun signinWithPasskey( authSession: String, authResponse: String, - realm: String? = null + realm: String? = null, + organization: String? = null, ): AuthenticationRequest { val publicKeyCredentials = gson.fromJson( authResponse, PublicKeyCredentials::class.java ) - return signinWithPasskey(authSession, publicKeyCredentials, realm) + return signinWithPasskey(authSession, publicKeyCredentials, realm, organization) } @@ -247,7 +252,7 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe * * * ``` - * client.signupWithPasskey("{userData}","{realm}") + * client.signupWithPasskey("{userData}","{realm}","{organization}") * .addParameter("scope","scope") * .start(object: Callback { * override fun onSuccess(result: PasskeyRegistration) { } @@ -257,11 +262,13 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe * * @param userData user information of the client * @param realm the connection to use. If excluded, the application will use the default connection configured in the tenant + * @param organization id of the organization to be associated with the user while signing up * @return a request to configure and start that will yield [PasskeyRegistrationChallenge] */ public fun signupWithPasskey( userData: UserData, - realm: String? = null + realm: String? = null, + organization: String? = null ): Request { val user = gson.toJsonTree(userData) val url = auth0.getDomainUrl().toHttpUrl().newBuilder() @@ -272,6 +279,7 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe val params = ParameterBuilder.newBuilder().apply { setClientId(clientId) realm?.let { setRealm(it) } + organization?.let { set(ORGANIZATION_KEY, it) } }.asDictionary() val passkeyRegistrationChallengeAdapter: JsonAdapter = @@ -293,7 +301,7 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe * Example usage: * * ``` - * client.passkeyChallenge("{realm}") + * client.passkeyChallenge("{realm}", "{organization}") * .start(object: Callback { * override fun onSuccess(result: PasskeyChallenge) { } * override fun onFailure(error: AuthenticationException) { } @@ -301,10 +309,12 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe * ``` * * @param realm the connection to use. If excluded, the application will use the default connection configured in the tenant + * @param organization id of the organization to be associated with the user while signing in * @return a request to configure and start that will yield [PasskeyChallenge] */ public fun passkeyChallenge( - realm: String? = null + realm: String? = null, + organization: String? = null ): Request { val url = auth0.getDomainUrl().toHttpUrl().newBuilder() .addPathSegment(PASSKEY_PATH) @@ -314,6 +324,7 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe val parameters = ParameterBuilder.newBuilder().apply { setClientId(clientId) realm?.let { setRealm(it) } + organization?.let { set(ORGANIZATION_KEY, organization) } }.asDictionary() val passkeyChallengeAdapter: JsonAdapter = GsonAdapter( @@ -1054,7 +1065,7 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe private const val RECOVERY_CODE_KEY = "recovery_code" private const val SUBJECT_TOKEN_KEY = "subject_token" private const val SUBJECT_TOKEN_TYPE_KEY = "subject_token_type" - private const val REQUESTED_TOKEN_TYPE_KEY = "requested_token_type" + private const val ORGANIZATION_KEY = "organization" private const val USER_METADATA_KEY = "user_metadata" private const val AUTH_SESSION_KEY = "auth_session" private const val AUTH_RESPONSE_KEY = "authn_response" diff --git a/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt b/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt index fc37fcfa..3c42da6f 100755 --- a/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt +++ b/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt @@ -193,7 +193,8 @@ public class AuthenticationAPIClientTest { val callback = MockAuthenticationCallback() val auth0 = auth0 val client = AuthenticationAPIClient(auth0) - client.signinWithPasskey("auth-session", mock(), MY_CONNECTION) + client.signinWithPasskey("auth-session", mock(), MY_CONNECTION, + "testOrganisation") .start(callback) ShadowLooper.idleMainLooper() assertThat( @@ -216,6 +217,7 @@ public class AuthenticationAPIClientTest { ) assertThat(body, Matchers.hasKey("authn_response")) assertThat(body, Matchers.hasEntry("auth_session", "auth-session")) + assertThat(body, Matchers.hasEntry("organization", "testOrganisation")) } @Test @@ -225,7 +227,8 @@ public class AuthenticationAPIClientTest { val client = AuthenticationAPIClient(auth0) val registrationResponse = client.signupWithPasskey( mock(), - MY_CONNECTION + MY_CONNECTION, + "testOrganization" ) .execute() val request = mockAPI.takeRequest() @@ -238,6 +241,7 @@ public class AuthenticationAPIClientTest { assertThat(request.path, Matchers.equalTo("/passkey/register")) assertThat(body, Matchers.hasEntry("client_id", CLIENT_ID)) assertThat(body, Matchers.hasEntry("realm", MY_CONNECTION)) + assertThat(body, Matchers.hasEntry("organization", "testOrganization")) assertThat(body, Matchers.hasKey("user_profile")) assertThat(registrationResponse, Matchers.`is`(Matchers.notNullValue())) assertThat(registrationResponse.authSession, Matchers.comparesEqualTo(SESSION_ID)) @@ -248,7 +252,7 @@ public class AuthenticationAPIClientTest { mockAPI.willReturnSuccessfulPasskeyChallenge() val auth0 = auth0 val client = AuthenticationAPIClient(auth0) - val challengeResponse = client.passkeyChallenge(MY_CONNECTION) + val challengeResponse = client.passkeyChallenge(MY_CONNECTION, "testOrganization") .execute() val request = mockAPI.takeRequest() assertThat( @@ -260,6 +264,7 @@ public class AuthenticationAPIClientTest { assertThat(request.path, Matchers.equalTo("/passkey/challenge")) assertThat(body, Matchers.hasEntry("client_id", CLIENT_ID)) assertThat(body, Matchers.hasEntry("realm", MY_CONNECTION)) + assertThat(body, Matchers.hasEntry("organization", "testOrganization")) assertThat(challengeResponse, Matchers.`is`(Matchers.notNullValue())) assertThat(challengeResponse.authSession, Matchers.comparesEqualTo(SESSION_ID)) @@ -2749,7 +2754,6 @@ public class AuthenticationAPIClientTest { private const val FIRST_NAME = "John" private const val LAST_NAME = "Doe" private const val COMPANY = "Auth0" - private const val OPENID = "openid" private const val DEFAULT_LOCALE_IF_MISSING = "en_US" } } \ No newline at end of file diff --git a/auth0/src/test/java/com/auth0/android/provider/PasskeyManagerTest.kt b/auth0/src/test/java/com/auth0/android/provider/PasskeyManagerTest.kt index 4c39dea3..3f99b78d 100644 --- a/auth0/src/test/java/com/auth0/android/provider/PasskeyManagerTest.kt +++ b/auth0/src/test/java/com/auth0/android/provider/PasskeyManagerTest.kt @@ -140,7 +140,8 @@ public class PasskeyManagerTest { authenticationAPIClient.signinWithPasskey( any(), any(), - any() + any(), + eq(null) ) ).thenReturn( AuthenticationRequestMock( @@ -185,7 +186,10 @@ public class PasskeyManagerTest { verify(authenticationAPIClient).signupWithPasskey(userMetadata, "testRealm") verify(credentialManager).createCredentialAsync(eq(context), any(), any(), any(), any()) - verify(authenticationAPIClient).signinWithPasskey(any(), any(), any()) + verify(authenticationAPIClient).signinWithPasskey( + any(), any(), any(), + eq(null) + ) verify(callback).onSuccess(credentialsCaptor.capture()) Assert.assertEquals("codeAccess", credentialsCaptor.firstValue.accessToken) Assert.assertEquals("codeScope", credentialsCaptor.firstValue.scope) @@ -215,7 +219,8 @@ public class PasskeyManagerTest { verify(authenticationAPIClient, never()).signinWithPasskey( any(), any(), - any() + any(), + eq(null) ) verify(credentialManager, never()).createCredentialAsync( any(), @@ -265,7 +270,7 @@ public class PasskeyManagerTest { verify(authenticationAPIClient, never()).signinWithPasskey( any(), any(), - any() + any(), eq(null) ) verify(callback).onFailure(exceptionCaptor.capture()) Assert.assertEquals( @@ -292,7 +297,14 @@ public class PasskeyManagerTest { PublicKeyCredential(registrationResponseJSON) ) - `when`(authenticationAPIClient.signinWithPasskey(any(), any(), any())).thenReturn( + `when`( + authenticationAPIClient.signinWithPasskey( + any(), + any(), + any(), + eq(null) + ) + ).thenReturn( AuthenticationRequestMock( Credentials( "expectedIdToken", @@ -324,7 +336,10 @@ public class PasskeyManagerTest { any(), any() ) - verify(authenticationAPIClient).signinWithPasskey(any(), any(), any()) + verify(authenticationAPIClient).signinWithPasskey( + any(), any(), any(), + eq(null) + ) verify(callback).onSuccess(credentialsCaptor.capture()) Assert.assertEquals("codeAccess", credentialsCaptor.firstValue.accessToken) Assert.assertEquals("codeScope", credentialsCaptor.firstValue.scope) @@ -342,7 +357,7 @@ public class PasskeyManagerTest { passkeyManager.signin(context, "testRealm", parameters, callback, serialExecutor) - verify(authenticationAPIClient).passkeyChallenge(any()) + verify(authenticationAPIClient).passkeyChallenge(any(), eq(null)) verify(credentialManager, never()).getCredentialAsync( any(), any(), @@ -350,7 +365,12 @@ public class PasskeyManagerTest { any(), any() ) - verify(authenticationAPIClient, never()).signinWithPasskey(any(), any(), any()) + verify(authenticationAPIClient, never()).signinWithPasskey( + any(), + any(), + any(), + eq(null) + ) verify(callback).onFailure(error) } @@ -384,7 +404,12 @@ public class PasskeyManagerTest { any(), any() ) - verify(authenticationAPIClient, never()).signinWithPasskey(any(), any(), any()) + verify(authenticationAPIClient, never()).signinWithPasskey( + any(), + any(), + any(), + eq(null) + ) verify(callback).onFailure(exceptionCaptor.capture()) Assert.assertEquals( AuthenticationException::class.java,