diff --git a/EXAMPLES.md b/EXAMPLES.md
index 1fc8ca1ee..52ea9eae9 100644
--- a/EXAMPLES.md
+++ b/EXAMPLES.md
@@ -2800,8 +2800,19 @@ To sign up a user with passkey
```kotlin
// Using Coroutines
try {
+ val userData = UserData(
+ email = "user@example.com",
+ phoneNumber = "+11234567890",
+ name = "John Doe",
+ givenName = "John",
+ familyName = "Doe",
+ nickName = "johnny",
+ picture = "https://example.com/photo.png",
+ userMetadata = mapOf("signup_source" to "android_app")
+ )
+
val challenge = authenticationApiClient.signupWithPasskey(
- "{user-data}",
+ userData,
"{realm}",
"{organization-id}"
).await()
@@ -2831,7 +2842,19 @@ try {
Using Java
```java
- authenticationAPIClient.signupWithPasskey("{user-data}", "{realm}","{organization-id}")
+ UserData userData = new UserData(
+ "user@example.com", // email
+ "+11234567890", // phoneNumber
+ null, // userName
+ "John Doe", // name
+ "John", // givenName
+ "Doe", // familyName
+ "johnny", // nickName
+ "https://example.com/photo.png", // picture
+ Map.of("signup_source", "android_app") // userMetadata
+ );
+
+ authenticationAPIClient.signupWithPasskey(userData, "{realm}","{organization-id}")
.start(new Callback() {
@Override
public void onSuccess(PasskeyRegistrationChallenge 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 f33aee9ca..1f986e917 100755
--- a/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt
+++ b/auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt
@@ -313,17 +313,24 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
*
* Example usage:
*
- *
* ```
- * client.signupWithPasskey("{userData}","{realm}","{organization}")
- * .addParameter("scope","scope")
- * .start(object: Callback {
- * override fun onSuccess(result: PasskeyRegistration) { }
+ * val userData = UserData(
+ * email = "user@example.com",
+ * name = "John Doe",
+ * givenName = "John",
+ * familyName = "Doe",
+ * nickName = "johnny",
+ * picture = "https://example.com/photo.png",
+ * userMetadata = mapOf("signup_source" to "android_app")
+ * )
+ * client.signupWithPasskey(userData, "{realm}", "{organization}")
+ * .start(object: Callback {
+ * override fun onSuccess(result: PasskeyRegistrationChallenge) { }
* override fun onFailure(error: AuthenticationException) { }
* })
* ```
*
- * @param userData user information of the client
+ * @param userData user information for registration.
* @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]
@@ -333,7 +340,6 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
realm: String? = null,
organization: String? = null
): Request {
- val user = gson.toJsonTree(userData)
val url = auth0.getDomainUrl().toHttpUrl().newBuilder()
.addPathSegment(PASSKEY_PATH)
.addPathSegment(REGISTER_PATH)
@@ -351,7 +357,8 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
)
val post = factory.post(url.toString(), passkeyRegistrationChallengeAdapter)
.addParameters(params) as BaseRequest
- post.addParameter(USER_PROFILE_KEY, user)
+ post.addParameter(USER_PROFILE_KEY, gson.toJsonTree(userData.toUserProfile()))
+ userData.userMetadata?.let { post.addParameter(USER_METADATA_KEY, it) }
return post
}
diff --git a/auth0/src/main/java/com/auth0/android/request/UserData.kt b/auth0/src/main/java/com/auth0/android/request/UserData.kt
index 693b64368..f1bc73f6d 100644
--- a/auth0/src/main/java/com/auth0/android/request/UserData.kt
+++ b/auth0/src/main/java/com/auth0/android/request/UserData.kt
@@ -8,10 +8,22 @@ import com.google.gson.annotations.SerializedName
* @param phoneNumber the phone number of the user. phone number can be optional, required, or forbidden depending on the attribute configuration for the database
* @param userName the username of the user. username can be optional, required, or forbidden depending on the attribute configuration for the database
* @param name optional display name
+ * @param givenName the first name of the user
+ * @param familyName the last name of the user
+ * @param nickName the preferred nickname of the user
+ * @param picture URL pointing to the user's profile picture
+ * @param userMetadata additional user metadata as key-value pairs
*/
public data class UserData(
@field:SerializedName("email") val email: String? = null,
@field:SerializedName("phone_number") val phoneNumber: String? = null,
@field:SerializedName("username") val userName: String? = null,
@field:SerializedName("name") val name: String? = null,
-)
\ No newline at end of file
+ @field:SerializedName("given_name") val givenName: String? = null,
+ @field:SerializedName("family_name") val familyName: String? = null,
+ @field:SerializedName("nickname") val nickName: String? = null,
+ @field:SerializedName("picture") val picture: String? = null,
+ @field:SerializedName("user_metadata") val userMetadata: Map? = null,
+) {
+ internal fun toUserProfile(): UserData = copy(userMetadata = null)
+}
\ No newline at end of file
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 1eb962e33..acc23a85a 100755
--- a/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt
+++ b/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt
@@ -13,6 +13,7 @@ import com.auth0.android.provider.JwtTestUtils
import com.auth0.android.request.HttpMethod
import com.auth0.android.request.NetworkingClient
import com.auth0.android.request.PublicKeyCredentials
+import com.auth0.android.request.UserData
import com.auth0.android.request.RequestOptions
import com.auth0.android.request.ServerResponse
import com.auth0.android.request.internal.RequestFactory
@@ -243,7 +244,7 @@ public class AuthenticationAPIClientTest {
val auth0 = auth0
val client = AuthenticationAPIClient(auth0)
val registrationResponse = client.signupWithPasskey(
- mock(),
+ UserData(email = "test@example.com"),
MY_CONNECTION,
"testOrganization"
)
@@ -264,6 +265,49 @@ public class AuthenticationAPIClientTest {
assertThat(registrationResponse.authSession, Matchers.comparesEqualTo(SESSION_ID))
}
+ @Test
+ public fun shouldSignupWithPasskeyWithAllUserDataFields() {
+ mockAPI.willReturnSuccessfulPasskeyRegistration()
+ val auth0 = auth0
+ val client = AuthenticationAPIClient(auth0)
+ val userData = UserData(
+ email = "test@example.com",
+ phoneNumber = "+1234567890",
+ userName = "testuser",
+ name = "Test User",
+ givenName = "Test",
+ familyName = "User",
+ nickName = "testy",
+ picture = "https://example.com/photo.png",
+ userMetadata = mapOf("key1" to "value1")
+ )
+ val registrationResponse = client.signupWithPasskey(
+ userData,
+ MY_CONNECTION,
+ "testOrganization"
+ ).execute()
+ val request = mockAPI.takeRequest()
+ val body = bodyFromRequest(request)
+ assertThat(request.path, Matchers.equalTo("/passkey/register"))
+ assertThat(body, Matchers.hasKey("user_profile"))
+ @Suppress("UNCHECKED_CAST")
+ val userProfile = body["user_profile"] as Map
+ assertThat(userProfile, Matchers.hasEntry("email", "test@example.com"))
+ assertThat(userProfile, Matchers.hasEntry("phone_number", "+1234567890"))
+ assertThat(userProfile, Matchers.hasEntry("username", "testuser"))
+ assertThat(userProfile, Matchers.hasEntry("name", "Test User"))
+ assertThat(userProfile, Matchers.hasEntry("given_name", "Test"))
+ assertThat(userProfile, Matchers.hasEntry("family_name", "User"))
+ assertThat(userProfile, Matchers.hasEntry("nickname", "testy"))
+ assertThat(userProfile, Matchers.hasEntry("picture", "https://example.com/photo.png"))
+ assertThat(userProfile, Matchers.not(Matchers.hasKey("user_metadata")))
+ assertThat(body, Matchers.hasKey("user_metadata"))
+ @Suppress("UNCHECKED_CAST")
+ val metadata = body["user_metadata"] as Map
+ assertThat(metadata, Matchers.hasEntry("key1", "value1"))
+ assertThat(registrationResponse, Matchers.`is`(Matchers.notNullValue()))
+ }
+
@Test
public fun shouldGetPasskeyChallenge() {
mockAPI.willReturnSuccessfulPasskeyChallenge()