Skip to content

Commit 3dfa352

Browse files
authored
feat : support to pass organisation while signing-up and signing-in with passkeys (#843)
1 parent 6740faa commit 3dfa352

File tree

4 files changed

+73
-31
lines changed

4 files changed

+73
-31
lines changed

EXAMPLES.md

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,7 +1032,8 @@ To sign up a user with passkey
10321032
try {
10331033
val challenge = authenticationApiClient.signupWithPasskey(
10341034
"{user-data}",
1035-
"{realm}"
1035+
"{realm}",
1036+
"{organization-id}"
10361037
).await()
10371038

10381039
//Use CredentialManager to create public key credentials
@@ -1048,7 +1049,7 @@ try {
10481049
)
10491050

10501051
val userCredential = authenticationApiClient.signinWithPasskey(
1051-
challenge.authSession, authRequest, "{realm}"
1052+
challenge.authSession, authRequest, "{realm}" , "{organization-id}"
10521053
)
10531054
.validateClaims()
10541055
.await()
@@ -1060,7 +1061,7 @@ try {
10601061
<summary>Using Java</summary>
10611062

10621063
```java
1063-
authenticationAPIClient.signupWithPasskey("{user-data}", "{realm}")
1064+
authenticationAPIClient.signupWithPasskey("{user-data}", "{realm}","{organization-id}")
10641065
.start(new Callback<PasskeyRegistrationChallenge, AuthenticationException>() {
10651066
@Override
10661067
public void onSuccess(PasskeyRegistrationChallenge result) {
@@ -1078,7 +1079,7 @@ try {
10781079
PublicKeyCredentials.class);
10791080

10801081
authenticationAPIClient.signinWithPasskey(result.getAuthSession(),
1081-
credentials, "{realm}")
1082+
credentials, "{realm}","{organization-id}")
10821083
.start(new Callback<Credentials, AuthenticationException>() {
10831084
@Override
10841085
public void onSuccess(Credentials result) {}
@@ -1104,7 +1105,7 @@ To sign in a user with passkey
11041105
try {
11051106

11061107
val challenge =
1107-
authenticationApiClient.passkeyChallenge("{realm}")
1108+
authenticationApiClient.passkeyChallenge("{realm}","{organization-id}")
11081109
.await()
11091110

11101111
//Use CredentialManager to create public key credentials
@@ -1122,7 +1123,8 @@ try {
11221123
val userCredential = authenticationApiClient.signinWithPasskey(
11231124
challenge.authSession,
11241125
authRequest,
1125-
"{realm}"
1126+
"{realm}",
1127+
"{organization-id}"
11261128
)
11271129
.validateClaims()
11281130
.await()
@@ -1138,7 +1140,7 @@ try {
11381140
<summary>Using Java</summary>
11391141

11401142
```java
1141-
authenticationAPIClient.passkeyChallenge("realm")
1143+
authenticationAPIClient.passkeyChallenge("realm","{organization-id}")
11421144
.start(new Callback<PasskeyChallenge, AuthenticationException>() {
11431145
@Override
11441146
public void onSuccess(PasskeyChallenge result) {
@@ -1158,7 +1160,7 @@ authenticationAPIClient.passkeyChallenge("realm")
11581160
responseJson,
11591161
PublicKeyCredentials.class
11601162
);
1161-
authenticationAPIClient.signinWithPasskey(result.getAuthSession(), publicKeyCredentials,"{realm}")
1163+
authenticationAPIClient.signinWithPasskey(result.getAuthSession(), publicKeyCredentials,"{realm}","{organization-id}")
11621164
.start(new Callback<Credentials, AuthenticationException>() {
11631165
@Override
11641166
public void onSuccess(Credentials result) {}

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

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
166166
* Example usage:
167167
*
168168
* ```
169-
* client.signinWithPasskey("{authSession}", "{authResponse}","{realm}")
169+
* client.signinWithPasskey("{authSession}", "{authResponse}","{realm}","${organization}")
170170
* .validateClaims() //mandatory
171171
* .setScope("{scope}")
172172
* .start(object: Callback<Credentials, AuthenticationException> {
@@ -178,17 +178,20 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
178178
* @param authSession the auth session received from the server as part of the public key challenge request.
179179
* @param authResponse the [PublicKeyCredentials] authentication response
180180
* @param realm the connection to use. If excluded, the application will use the default connection configured in the tenant
181+
* @param organization id of the organization to be associated with the user while signing in
181182
* @return a request to configure and start that will yield [Credentials]
182183
*/
183184
public fun signinWithPasskey(
184185
authSession: String,
185186
authResponse: PublicKeyCredentials,
186-
realm: String? = null
187+
realm: String? = null,
188+
organization: String? = null,
187189
): AuthenticationRequest {
188190
val params = ParameterBuilder.newBuilder().apply {
189191
setGrantType(ParameterBuilder.GRANT_TYPE_PASSKEY)
190192
set(AUTH_SESSION_KEY, authSession)
191193
realm?.let { setRealm(it) }
194+
organization?.let { set(ORGANIZATION_KEY, organization) }
192195
}.asDictionary()
193196

194197
return loginWithToken(params)
@@ -210,7 +213,7 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
210213
* Example usage:
211214
*
212215
* ```
213-
* client.signinWithPasskey("{authSession}", "{authResponse}","{realm}")
216+
* client.signinWithPasskey("{authSession}", "{authResponse}","{realm}","{organization}")
214217
* .validateClaims() //mandatory
215218
* .setScope("{scope}")
216219
* .start(object: Callback<Credentials, AuthenticationException> {
@@ -222,18 +225,20 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
222225
* @param authSession the auth session received from the server as part of the public key challenge request.
223226
* @param authResponse the public key credential authentication response in JSON string format that follows the standard webauthn json format
224227
* @param realm the connection to use. If excluded, the application will use the default connection configured in the tenant
228+
* @param organization id of the organization to be associated with the user while signing in
225229
* @return a request to configure and start that will yield [Credentials]
226230
*/
227231
public fun signinWithPasskey(
228232
authSession: String,
229233
authResponse: String,
230-
realm: String? = null
234+
realm: String? = null,
235+
organization: String? = null,
231236
): AuthenticationRequest {
232237
val publicKeyCredentials = gson.fromJson(
233238
authResponse,
234239
PublicKeyCredentials::class.java
235240
)
236-
return signinWithPasskey(authSession, publicKeyCredentials, realm)
241+
return signinWithPasskey(authSession, publicKeyCredentials, realm, organization)
237242
}
238243

239244

@@ -247,7 +252,7 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
247252
*
248253
*
249254
* ```
250-
* client.signupWithPasskey("{userData}","{realm}")
255+
* client.signupWithPasskey("{userData}","{realm}","{organization}")
251256
* .addParameter("scope","scope")
252257
* .start(object: Callback<PasskeyRegistration, AuthenticationException> {
253258
* override fun onSuccess(result: PasskeyRegistration) { }
@@ -257,11 +262,13 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
257262
*
258263
* @param userData user information of the client
259264
* @param realm the connection to use. If excluded, the application will use the default connection configured in the tenant
265+
* @param organization id of the organization to be associated with the user while signing up
260266
* @return a request to configure and start that will yield [PasskeyRegistrationChallenge]
261267
*/
262268
public fun signupWithPasskey(
263269
userData: UserData,
264-
realm: String? = null
270+
realm: String? = null,
271+
organization: String? = null
265272
): Request<PasskeyRegistrationChallenge, AuthenticationException> {
266273
val user = gson.toJsonTree(userData)
267274
val url = auth0.getDomainUrl().toHttpUrl().newBuilder()
@@ -272,6 +279,7 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
272279
val params = ParameterBuilder.newBuilder().apply {
273280
setClientId(clientId)
274281
realm?.let { setRealm(it) }
282+
organization?.let { set(ORGANIZATION_KEY, it) }
275283
}.asDictionary()
276284

277285
val passkeyRegistrationChallengeAdapter: JsonAdapter<PasskeyRegistrationChallenge> =
@@ -293,18 +301,20 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
293301
* Example usage:
294302
*
295303
* ```
296-
* client.passkeyChallenge("{realm}")
304+
* client.passkeyChallenge("{realm}", "{organization}")
297305
* .start(object: Callback<PasskeyChallenge, AuthenticationException> {
298306
* override fun onSuccess(result: PasskeyChallenge) { }
299307
* override fun onFailure(error: AuthenticationException) { }
300308
* })
301309
* ```
302310
*
303311
* @param realm the connection to use. If excluded, the application will use the default connection configured in the tenant
312+
* @param organization id of the organization to be associated with the user while signing in
304313
* @return a request to configure and start that will yield [PasskeyChallenge]
305314
*/
306315
public fun passkeyChallenge(
307-
realm: String? = null
316+
realm: String? = null,
317+
organization: String? = null
308318
): Request<PasskeyChallenge, AuthenticationException> {
309319
val url = auth0.getDomainUrl().toHttpUrl().newBuilder()
310320
.addPathSegment(PASSKEY_PATH)
@@ -314,6 +324,7 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
314324
val parameters = ParameterBuilder.newBuilder().apply {
315325
setClientId(clientId)
316326
realm?.let { setRealm(it) }
327+
organization?.let { set(ORGANIZATION_KEY, organization) }
317328
}.asDictionary()
318329

319330
val passkeyChallengeAdapter: JsonAdapter<PasskeyChallenge> = GsonAdapter(
@@ -1054,7 +1065,7 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
10541065
private const val RECOVERY_CODE_KEY = "recovery_code"
10551066
private const val SUBJECT_TOKEN_KEY = "subject_token"
10561067
private const val SUBJECT_TOKEN_TYPE_KEY = "subject_token_type"
1057-
private const val REQUESTED_TOKEN_TYPE_KEY = "requested_token_type"
1068+
private const val ORGANIZATION_KEY = "organization"
10581069
private const val USER_METADATA_KEY = "user_metadata"
10591070
private const val AUTH_SESSION_KEY = "auth_session"
10601071
private const val AUTH_RESPONSE_KEY = "authn_response"

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,8 @@ public class AuthenticationAPIClientTest {
193193
val callback = MockAuthenticationCallback<Credentials>()
194194
val auth0 = auth0
195195
val client = AuthenticationAPIClient(auth0)
196-
client.signinWithPasskey("auth-session", mock<PublicKeyCredentials>(), MY_CONNECTION)
196+
client.signinWithPasskey("auth-session", mock<PublicKeyCredentials>(), MY_CONNECTION,
197+
"testOrganisation")
197198
.start(callback)
198199
ShadowLooper.idleMainLooper()
199200
assertThat(
@@ -216,6 +217,7 @@ public class AuthenticationAPIClientTest {
216217
)
217218
assertThat(body, Matchers.hasKey("authn_response"))
218219
assertThat(body, Matchers.hasEntry("auth_session", "auth-session"))
220+
assertThat(body, Matchers.hasEntry("organization", "testOrganisation"))
219221
}
220222

221223
@Test
@@ -225,7 +227,8 @@ public class AuthenticationAPIClientTest {
225227
val client = AuthenticationAPIClient(auth0)
226228
val registrationResponse = client.signupWithPasskey(
227229
mock(),
228-
MY_CONNECTION
230+
MY_CONNECTION,
231+
"testOrganization"
229232
)
230233
.execute()
231234
val request = mockAPI.takeRequest()
@@ -238,6 +241,7 @@ public class AuthenticationAPIClientTest {
238241
assertThat(request.path, Matchers.equalTo("/passkey/register"))
239242
assertThat(body, Matchers.hasEntry("client_id", CLIENT_ID))
240243
assertThat(body, Matchers.hasEntry("realm", MY_CONNECTION))
244+
assertThat(body, Matchers.hasEntry("organization", "testOrganization"))
241245
assertThat(body, Matchers.hasKey("user_profile"))
242246
assertThat(registrationResponse, Matchers.`is`(Matchers.notNullValue()))
243247
assertThat(registrationResponse.authSession, Matchers.comparesEqualTo(SESSION_ID))
@@ -248,7 +252,7 @@ public class AuthenticationAPIClientTest {
248252
mockAPI.willReturnSuccessfulPasskeyChallenge()
249253
val auth0 = auth0
250254
val client = AuthenticationAPIClient(auth0)
251-
val challengeResponse = client.passkeyChallenge(MY_CONNECTION)
255+
val challengeResponse = client.passkeyChallenge(MY_CONNECTION, "testOrganization")
252256
.execute()
253257
val request = mockAPI.takeRequest()
254258
assertThat(
@@ -260,6 +264,7 @@ public class AuthenticationAPIClientTest {
260264
assertThat(request.path, Matchers.equalTo("/passkey/challenge"))
261265
assertThat(body, Matchers.hasEntry("client_id", CLIENT_ID))
262266
assertThat(body, Matchers.hasEntry("realm", MY_CONNECTION))
267+
assertThat(body, Matchers.hasEntry("organization", "testOrganization"))
263268
assertThat(challengeResponse, Matchers.`is`(Matchers.notNullValue()))
264269
assertThat(challengeResponse.authSession, Matchers.comparesEqualTo(SESSION_ID))
265270

@@ -2749,7 +2754,6 @@ public class AuthenticationAPIClientTest {
27492754
private const val FIRST_NAME = "John"
27502755
private const val LAST_NAME = "Doe"
27512756
private const val COMPANY = "Auth0"
2752-
private const val OPENID = "openid"
27532757
private const val DEFAULT_LOCALE_IF_MISSING = "en_US"
27542758
}
27552759
}

auth0/src/test/java/com/auth0/android/provider/PasskeyManagerTest.kt

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,8 @@ public class PasskeyManagerTest {
140140
authenticationAPIClient.signinWithPasskey(
141141
any(),
142142
any<PublicKeyCredentials>(),
143-
any()
143+
any(),
144+
eq(null)
144145
)
145146
).thenReturn(
146147
AuthenticationRequestMock(
@@ -185,7 +186,10 @@ public class PasskeyManagerTest {
185186

186187
verify(authenticationAPIClient).signupWithPasskey(userMetadata, "testRealm")
187188
verify(credentialManager).createCredentialAsync(eq(context), any(), any(), any(), any())
188-
verify(authenticationAPIClient).signinWithPasskey(any(), any<PublicKeyCredentials>(), any())
189+
verify(authenticationAPIClient).signinWithPasskey(
190+
any(), any<PublicKeyCredentials>(), any(),
191+
eq(null)
192+
)
189193
verify(callback).onSuccess(credentialsCaptor.capture())
190194
Assert.assertEquals("codeAccess", credentialsCaptor.firstValue.accessToken)
191195
Assert.assertEquals("codeScope", credentialsCaptor.firstValue.scope)
@@ -215,7 +219,8 @@ public class PasskeyManagerTest {
215219
verify(authenticationAPIClient, never()).signinWithPasskey(
216220
any(),
217221
any<PublicKeyCredentials>(),
218-
any()
222+
any(),
223+
eq(null)
219224
)
220225
verify(credentialManager, never()).createCredentialAsync(
221226
any(),
@@ -265,7 +270,7 @@ public class PasskeyManagerTest {
265270
verify(authenticationAPIClient, never()).signinWithPasskey(
266271
any(),
267272
any<PublicKeyCredentials>(),
268-
any()
273+
any(), eq(null)
269274
)
270275
verify(callback).onFailure(exceptionCaptor.capture())
271276
Assert.assertEquals(
@@ -292,7 +297,14 @@ public class PasskeyManagerTest {
292297
PublicKeyCredential(registrationResponseJSON)
293298
)
294299

295-
`when`(authenticationAPIClient.signinWithPasskey(any(), any<PublicKeyCredentials>(), any())).thenReturn(
300+
`when`(
301+
authenticationAPIClient.signinWithPasskey(
302+
any(),
303+
any<PublicKeyCredentials>(),
304+
any(),
305+
eq(null)
306+
)
307+
).thenReturn(
296308
AuthenticationRequestMock(
297309
Credentials(
298310
"expectedIdToken",
@@ -324,7 +336,10 @@ public class PasskeyManagerTest {
324336
any(),
325337
any()
326338
)
327-
verify(authenticationAPIClient).signinWithPasskey(any(), any<PublicKeyCredentials>(), any())
339+
verify(authenticationAPIClient).signinWithPasskey(
340+
any(), any<PublicKeyCredentials>(), any(),
341+
eq(null)
342+
)
328343
verify(callback).onSuccess(credentialsCaptor.capture())
329344
Assert.assertEquals("codeAccess", credentialsCaptor.firstValue.accessToken)
330345
Assert.assertEquals("codeScope", credentialsCaptor.firstValue.scope)
@@ -342,15 +357,20 @@ public class PasskeyManagerTest {
342357

343358
passkeyManager.signin(context, "testRealm", parameters, callback, serialExecutor)
344359

345-
verify(authenticationAPIClient).passkeyChallenge(any())
360+
verify(authenticationAPIClient).passkeyChallenge(any(), eq(null))
346361
verify(credentialManager, never()).getCredentialAsync(
347362
any(),
348363
any<GetCredentialRequest>(),
349364
any(),
350365
any(),
351366
any()
352367
)
353-
verify(authenticationAPIClient, never()).signinWithPasskey(any(), any<PublicKeyCredentials>(), any())
368+
verify(authenticationAPIClient, never()).signinWithPasskey(
369+
any(),
370+
any<PublicKeyCredentials>(),
371+
any(),
372+
eq(null)
373+
)
354374
verify(callback).onFailure(error)
355375
}
356376

@@ -384,7 +404,12 @@ public class PasskeyManagerTest {
384404
any(),
385405
any()
386406
)
387-
verify(authenticationAPIClient, never()).signinWithPasskey(any(), any<PublicKeyCredentials>(), any())
407+
verify(authenticationAPIClient, never()).signinWithPasskey(
408+
any(),
409+
any<PublicKeyCredentials>(),
410+
any(),
411+
eq(null)
412+
)
388413
verify(callback).onFailure(exceptionCaptor.capture())
389414
Assert.assertEquals(
390415
AuthenticationException::class.java,

0 commit comments

Comments
 (0)