Skip to content

Commit 93c507b

Browse files
committed
Incorporated review comments and added Sample in EXAMPLE.md
1 parent 6ae1fe1 commit 93c507b

File tree

5 files changed

+129
-101
lines changed

5 files changed

+129
-101
lines changed

EXAMPLES.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,17 @@
2525
- [DPoP [EA]](#dpop-ea-1)
2626
- [My Account API](#my-account-api)
2727
- [Enroll a new passkey](#enroll-a-new-passkey)
28+
- [Get Available Factors](#get-available-factors)
29+
- [Get All Enrolled Authentication Methods](#get-all-enrolled-authentication-methods)
30+
- [Get a Single Authentication Method by ID](#get-a-single-authentication-method-by-id)
31+
- [Enroll a Phone Method](#enroll-a-phone-method)
32+
- [Enroll an Email Method](#enroll-an-email-method)
33+
- [Enroll a TOTP (Authenticator App) Method](#enroll-a-totp-authenticator-app-method)
34+
- [Enroll a Push Notification Method](#enroll-a-push-notification-method)
35+
- [Enroll a Recovery Code](#enroll-a-recovery-code)
36+
- [Verify an Enrollment](#verify-an-enrollment)
37+
- [Update an Authentication Method](#update-an-authentication-method)
38+
- [Delete an Authentication Method](#delete-an-authentication-method)
2839
- [Credentials Manager](#credentials-manager)
2940
- [Secure Credentials Manager](#secure-credentials-manager)
3041
- [Usage](#usage)

auth0/src/main/java/com/auth0/android/myaccount/MyAccountAPIClient.kt

Lines changed: 109 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -155,15 +155,21 @@ public class MyAccountAPIClient @VisibleForTesting(otherwise = VisibleForTesting
155155
override fun fromJson(
156156
reader: Reader, metadata: Map<String, Any>
157157
): PasskeyEnrollmentChallenge {
158-
val location = (metadata[LOCATION_KEY] as? List<*>)?.filterIsInstance<String>()?.firstOrNull()
159-
val authId = location?.split("/")?.lastOrNull()?.let { URLDecoder.decode(it, "UTF-8") }
160-
?: throw MyAccountException("Authentication method ID not found in Location header.")
158+
val location = (metadata[LOCATION_KEY] as? List<*>)?.filterIsInstance<String>()
159+
?.firstOrNull()
160+
val authId =
161+
location?.split("/")?.lastOrNull()?.let { URLDecoder.decode(it, "UTF-8") }
162+
?: throw MyAccountException("Authentication method ID not found in Location header.")
161163
val challenge = gson.fromJson(reader, PasskeyRegistrationChallenge::class.java)
162-
return PasskeyEnrollmentChallenge(authId, challenge.authSession, challenge.authParamsPublicKey)
164+
return PasskeyEnrollmentChallenge(
165+
authId,
166+
challenge.authSession,
167+
challenge.authParamsPublicKey
168+
)
163169
}
164170
}
165171
return factory.post(url.toString(), passkeyEnrollmentAdapter)
166-
.addParameters(params.mapValues { it.value.toString() }) // FIX: Safely convert map values to String
172+
.addParameters(params)
167173
.addHeader(AUTHORIZATION_KEY, "Bearer $accessToken")
168174
}
169175

@@ -228,8 +234,11 @@ public class MyAccountAPIClient @VisibleForTesting(otherwise = VisibleForTesting
228234
.set(AUTH_SESSION_KEY, challenge.authSession)
229235
.asDictionary()
230236

231-
return factory.post(url.toString(), GsonAdapter(PasskeyAuthenticationMethod::class.java, gson))
232-
.addParameters(params.mapValues { it.value.toString() }) // FIX: Safely convert map values to String
237+
return factory.post(
238+
url.toString(),
239+
GsonAdapter(PasskeyAuthenticationMethod::class.java, gson)
240+
)
241+
.addParameters(params)
233242
.addParameter(AUTHN_RESPONSE_KEY, authnResponse)
234243
.addHeader(AUTHORIZATION_KEY, "Bearer $accessToken")
235244
}
@@ -360,7 +369,12 @@ public class MyAccountAPIClient @VisibleForTesting(otherwise = VisibleForTesting
360369

361370
val params = ParameterBuilder.newBuilder().apply {
362371
name?.let { set(AUTHENTICATION_METHOD_NAME, it) }
363-
preferredAuthenticationMethod?.let { set(PREFERRED_AUTHENTICATION_METHOD, it.value) } // Now correctly uses .value
372+
preferredAuthenticationMethod?.let {
373+
set(
374+
PREFERRED_AUTHENTICATION_METHOD,
375+
it.value
376+
)
377+
} // Now correctly uses .value
364378
}.asDictionary()
365379

366380
return factory.patch(url.toString(), GsonAdapter(AuthenticationMethod::class.java, gson))
@@ -388,7 +402,7 @@ public class MyAccountAPIClient @VisibleForTesting(otherwise = VisibleForTesting
388402
* val apiClient = MyAccountAPIClient(auth0, accessToken)
389403
*
390404
*
391-
* apiClient.deleteAuthenticationMethod(authenticationMethodId, )
405+
* apiClient.deleteAuthenticationMethod(authenticationMethodId)
392406
* .start(object : Callback<Void, MyAccountException> {
393407
* override fun onSuccess(result: Void) {
394408
* Log.d("MyApp", "Authentication method deleted")
@@ -428,14 +442,14 @@ public class MyAccountAPIClient @VisibleForTesting(otherwise = VisibleForTesting
428442
* val apiClient = MyAccountAPIClient(auth0, accessToken)
429443
*
430444
* apiClient.getFactors()
431-
* .start(object : Callback<List<Factor>, MyAccountException> {
432-
* override fun onSuccess(result: List<Factor>) {
433-
* Log.d("MyApp", "Available factors: $result")
434-
* }
435-
* override fun onFailure(error: MyAccountException) {
436-
* Log.e("MyApp", "Error getting factors: $error")
437-
* }
438-
* })
445+
* .start(object : Callback<List<Factor>, MyAccountException> {
446+
* override fun onSuccess(result: List<Factor>) {
447+
* Log.d("MyApp", "Available factors: $result")
448+
* }
449+
* override fun onFailure(error: MyAccountException) {
450+
* Log.e("MyApp", "Error getting factors: $error")
451+
* }
452+
* })
439453
* ```
440454
* @return A request to get the list of available factors.
441455
*/
@@ -458,27 +472,30 @@ public class MyAccountAPIClient @VisibleForTesting(otherwise = VisibleForTesting
458472
* val apiClient = MyAccountAPIClient(auth0, accessToken)
459473
*
460474
* apiClient.enrollPhone("+11234567890", "sms")
461-
* .start(object : Callback<EnrollmentChallenge, MyAccountException> {
462-
* override fun onSuccess(result: EnrollmentChallenge) {
463-
* // The enrollment has started. 'result.id' contains the ID for verification.
464-
* Log.d("MyApp", "Enrollment started. ID: ${result.id}")
465-
* }
466-
* override fun onFailure(error: MyAccountException) {
467-
* Log.e("MyApp", "Failed with: ${error.message}")
468-
* }
469-
* })
475+
* .start(object : Callback<EnrollmentChallenge, MyAccountException> {
476+
* override fun onSuccess(result: EnrollmentChallenge) {
477+
* // The enrollment has started. 'result.id' contains the ID for verification.
478+
* Log.d("MyApp", "Enrollment started. ID: ${result.id}")
479+
* }
480+
* override fun onFailure(error: MyAccountException) {
481+
* Log.e("MyApp", "Failed with: ${error.message}")
482+
* }
483+
* })
470484
* ```
471485
* @param phoneNumber The phone number to enroll in E.164 format.
472486
* @param preferredMethod The preferred method for this factor ("sms" or "voice").
473487
* @return A request that will yield an enrollment challenge.
474488
*/
475-
public fun enrollPhone(phoneNumber: String, preferredMethod: PhoneAuthenticationMethodType): Request<EnrollmentChallenge, MyAccountException> {
489+
public fun enrollPhone(
490+
phoneNumber: String,
491+
preferredMethod: PhoneAuthenticationMethodType
492+
): Request<EnrollmentChallenge, MyAccountException> {
476493
val params = ParameterBuilder.newBuilder()
477494
.set(TYPE_KEY, "phone")
478495
.set(PHONE_NUMBER_KEY, phoneNumber)
479496
.set(PREFERRED_AUTHENTICATION_METHOD, preferredMethod.value)
480497
.asDictionary()
481-
return createEnrollmentRequest(params)
498+
return buildEnrollmentRequest(params)
482499
}
483500

484501
/**
@@ -494,15 +511,15 @@ public class MyAccountAPIClient @VisibleForTesting(otherwise = VisibleForTesting
494511
* val apiClient = MyAccountAPIClient(auth0, accessToken)
495512
*
496513
* apiClient.enrollEmail("[email protected]")
497-
* .start(object : Callback<EnrollmentChallenge, MyAccountException> {
498-
* override fun onSuccess(result: EnrollmentChallenge) {
514+
* .start(object : Callback<EnrollmentChallenge, MyAccountException> {
515+
* override fun onSuccess(result: EnrollmentChallenge) {
499516
* // The enrollment has started. 'result.id' contains the ID for verification.
500-
* Log.d("MyApp", "Enrollment started. ID: ${result.id}")
517+
* Log.d("MyApp", "Enrollment started. ID: ${result.id}")
501518
* }
502-
* override fun onFailure(error: MyAccountException) {
503-
* Log.e("MyApp", "Failed with: ${error.message}")
504-
* }
505-
* })
519+
* override fun onFailure(error: MyAccountException) {
520+
* Log.e("MyApp", "Failed with: ${error.message}")
521+
* }
522+
* })
506523
* ```
507524
* @param email the email address to enroll.
508525
* @return a request that will yield an enrollment challenge.
@@ -512,7 +529,7 @@ public class MyAccountAPIClient @VisibleForTesting(otherwise = VisibleForTesting
512529
.set(TYPE_KEY, "email")
513530
.set(EMAIL_KEY, email)
514531
.asDictionary()
515-
return createEnrollmentRequest(params)
532+
return buildEnrollmentRequest(params)
516533
}
517534

518535
/**
@@ -528,19 +545,19 @@ public class MyAccountAPIClient @VisibleForTesting(otherwise = VisibleForTesting
528545
* val apiClient = MyAccountAPIClient(auth0, accessToken)
529546
*
530547
* apiClient.enrollTotp()
531-
* .start(object : Callback<EnrollmentChallenge, MyAccountException> {
532-
* override fun onSuccess(result: EnrollmentChallenge) {
548+
* .start(object : Callback<EnrollmentChallenge, MyAccountException> {
549+
* override fun onSuccess(result: EnrollmentChallenge) {
533550
* // The result will be a TotpEnrollmentChallenge with a barcode_uri
534-
* Log.d("MyApp", "Enrollment started for TOTP.")
535-
* }
536-
* override fun onFailure(error: MyAccountException) { //... }
537-
* })
551+
* Log.d("MyApp", "Enrollment started for TOTP.")
552+
* }
553+
* override fun onFailure(error: MyAccountException) { //... }
554+
* })
538555
* ```
539556
* @return a request that will yield an enrollment challenge.
540557
*/
541558
public fun enrollTotp(): Request<EnrollmentChallenge, MyAccountException> {
542559
val params = ParameterBuilder.newBuilder().set(TYPE_KEY, "totp").asDictionary()
543-
return createEnrollmentRequest(params)
560+
return buildEnrollmentRequest(params)
544561
}
545562

546563
/**
@@ -556,19 +573,19 @@ public class MyAccountAPIClient @VisibleForTesting(otherwise = VisibleForTesting
556573
* val apiClient = MyAccountAPIClient(auth0, accessToken)
557574
*
558575
* apiClient.enrollPushNotification()
559-
* .start(object : Callback<EnrollmentChallenge, MyAccountException> {
560-
* override fun onSuccess(result: EnrollmentChallenge) {
576+
* .start(object : Callback<EnrollmentChallenge, MyAccountException> {
577+
* override fun onSuccess(result: EnrollmentChallenge) {
561578
* // The result will be a TotpEnrollmentChallenge containing a barcode_uri
562579
* Log.d("MyApp", "Enrollment started for Push Notification.")
563-
* }
564-
* override fun onFailure(error: MyAccountException) { //... }
565-
* })
580+
* }
581+
* override fun onFailure(error: MyAccountException) { //... }
582+
* })
566583
* ```
567584
* @return a request that will yield an enrollment challenge.
568585
*/
569586
public fun enrollPushNotification(): Request<EnrollmentChallenge, MyAccountException> {
570587
val params = ParameterBuilder.newBuilder().set(TYPE_KEY, "push-notification").asDictionary()
571-
return createEnrollmentRequest(params)
588+
return buildEnrollmentRequest(params)
572589
}
573590

574591
/**
@@ -584,19 +601,19 @@ public class MyAccountAPIClient @VisibleForTesting(otherwise = VisibleForTesting
584601
* val apiClient = MyAccountAPIClient(auth0, accessToken)
585602
*
586603
* apiClient.enrollRecoveryCode()
587-
* .start(object : Callback<EnrollmentChallenge, MyAccountException> {
588-
* override fun onSuccess(result: EnrollmentChallenge) {
589-
* // The result will be a RecoveryCodeEnrollmentChallenge containing the code
590-
* Log.d("MyApp", "Recovery Code enrollment started.")
591-
* }
592-
* override fun onFailure(error: MyAccountException) { //... }
593-
* })
604+
* .start(object : Callback<EnrollmentChallenge, MyAccountException> {
605+
* override fun onSuccess(result: EnrollmentChallenge) {
606+
* // The result will be a RecoveryCodeEnrollmentChallenge containing the code
607+
* Log.d("MyApp", "Recovery Code enrollment started.")
608+
* }
609+
* override fun onFailure(error: MyAccountException) { //... }
610+
* })
594611
* ```
595612
* @return a request that will yield an enrollment challenge containing the recovery code.
596613
*/
597614
public fun enrollRecoveryCode(): Request<EnrollmentChallenge, MyAccountException> {
598615
val params = ParameterBuilder.newBuilder().set(TYPE_KEY, "recovery-code").asDictionary()
599-
return createEnrollmentRequest(params)
616+
return buildEnrollmentRequest(params)
600617
}
601618

602619
/**
@@ -616,17 +633,21 @@ public class MyAccountAPIClient @VisibleForTesting(otherwise = VisibleForTesting
616633
* val otp = "123456"
617634
*
618635
* apiClient.verifyOtp(authMethodId, otp, authSession)
619-
* .start(object : Callback<AuthenticationMethod, MyAccountException> {
636+
* .start(object : Callback<AuthenticationMethod, MyAccountException> {
620637
* override fun onSuccess(result: AuthenticationMethod) { //... }
621638
* override fun onFailure(error: MyAccountException) { //... }
622-
* })
639+
* })
623640
* ```
624641
* @param authenticationMethodId The ID of the method being verified (from the enrollment challenge).
625642
* @param otpCode The OTP code sent to the user's phone or email, or from their authenticator app.
626643
* @param authSession The auth session from the enrollment challenge.
627644
* @return a request that will yield the newly verified authentication method.
628645
*/
629-
public fun verifyOtp(authenticationMethodId: String, otpCode: String, authSession: String): Request<AuthenticationMethod, MyAccountException> {
646+
public fun verifyOtp(
647+
authenticationMethodId: String,
648+
otpCode: String,
649+
authSession: String
650+
): Request<AuthenticationMethod, MyAccountException> {
630651
val url = getDomainUrlBuilder()
631652
.addPathSegment(AUTHENTICATION_METHODS)
632653
.addPathSegment(authenticationMethodId)
@@ -654,16 +675,19 @@ public class MyAccountAPIClient @VisibleForTesting(otherwise = VisibleForTesting
654675
* val authSession = "from_enrollment_challenge"
655676
*
656677
* apiClient.verify(authMethodId, authSession)
657-
* .start(object : Callback<AuthenticationMethod, MyAccountException> {
678+
* .start(object : Callback<AuthenticationMethod, MyAccountException> {
658679
* override fun onSuccess(result: AuthenticationMethod) { //... }
659-
* override fun onFailure(error: MyAccountException) { //... }
660-
* })
680+
* override fun onFailure(error: MyAccountException) { //... }
681+
* })
661682
* ```
662683
* @param authenticationMethodId The ID of the method being verified (from the enrollment challenge).
663684
* @param authSession The auth session from the enrollment challenge.
664685
* @return a request that will yield the newly verified authentication method.
665686
*/
666-
public fun verify(authenticationMethodId: String, authSession: String): Request<AuthenticationMethod, MyAccountException> {
687+
public fun verify(
688+
authenticationMethodId: String,
689+
authSession: String
690+
): Request<AuthenticationMethod, MyAccountException> {
667691
val url = getDomainUrlBuilder()
668692
.addPathSegment(AUTHENTICATION_METHODS)
669693
.addPathSegment(authenticationMethodId)
@@ -689,19 +713,18 @@ public class MyAccountAPIClient @VisibleForTesting(otherwise = VisibleForTesting
689713
* val apiClient = MyAccountAPIClient(auth0, accessToken)
690714
*
691715
* apiClient.enrollWebAuthnPlatform()
692-
* .start(object : Callback<EnrollmentChallenge, MyAccountException> {
693-
* override fun onSuccess(result: EnrollmentChallenge) {
694-
* // The result will be a PasskeyEnrollmentChallenge for WebAuthn
695-
* Log.d("MyApp", "Enrollment started for WebAuthn Platform.")
696-
* }
697-
* override fun onFailure(error: MyAccountException) { //... }
698-
* })
716+
* .start(object : Callback<EnrollmentChallenge, MyAccountException> {
717+
* override fun onSuccess(result: EnrollmentChallenge) {
718+
* Log.d("MyApp", "Enrollment started for WebAuthn Platform.")
719+
* }
720+
* override fun onFailure(error: MyAccountException) { //... }
721+
* })
699722
* ```
700723
* @return a request that will yield an enrollment challenge.
701724
*/
702725
private fun enrollWebAuthnPlatform(): Request<EnrollmentChallenge, MyAccountException> {
703726
val params = ParameterBuilder.newBuilder().set(TYPE_KEY, "webauthn-platform").asDictionary()
704-
return createEnrollmentRequest(params)
727+
return buildEnrollmentRequest(params)
705728
}
706729

707730
/**
@@ -717,27 +740,29 @@ public class MyAccountAPIClient @VisibleForTesting(otherwise = VisibleForTesting
717740
* val apiClient = MyAccountAPIClient(auth0, accessToken)
718741
*
719742
* apiClient.enrollWebAuthnRoaming()
720-
* .start(object : Callback<EnrollmentChallenge, MyAccountException> {
721-
* override fun onSuccess(result: EnrollmentChallenge) {
722-
* // The result will be a PasskeyEnrollmentChallenge for WebAuthn
723-
* Log.d("MyApp", "Enrollment started for WebAuthn Roaming.")
724-
* }
743+
* .start(object : Callback<EnrollmentChallenge, MyAccountException> {
744+
* override fun onSuccess(result: EnrollmentChallenge) {
745+
* // The result will be a PasskeyEnrollmentChallenge for WebAuthn
746+
* Log.d("MyApp", "Enrollment started for WebAuthn Roaming.")
747+
* }
725748
* override fun onFailure(error: MyAccountException) { //... }
726-
* })
749+
* })
727750
* ```
728751
* @return a request that will yield an enrollment challenge.
729752
*/
730753
private fun enrollWebAuthnRoaming(): Request<EnrollmentChallenge, MyAccountException> {
731754
val params = ParameterBuilder.newBuilder().set(TYPE_KEY, "webauthn-roaming").asDictionary()
732-
return createEnrollmentRequest(params)
755+
return buildEnrollmentRequest(params)
733756
}
734757

735-
736-
private fun createEnrollmentRequest(params: Map<String, Any>): Request<EnrollmentChallenge, MyAccountException> {
758+
private fun buildEnrollmentRequest(params: Map<String, Any>): Request<EnrollmentChallenge, MyAccountException> {
737759
val url = getDomainUrlBuilder().addPathSegment(AUTHENTICATION_METHODS).build()
738-
return factory.post(url.toString(), GsonAdapter(EnrollmentChallenge::class.java, gson))
739-
.addParameters(params.mapValues { it.value.toString() }) // FIX: Safely convert map values to String
740-
.addHeader(AUTHORIZATION_KEY, "Bearer $accessToken")
760+
val request =
761+
factory.post(url.toString(), GsonAdapter(EnrollmentChallenge::class.java, gson))
762+
for ((key, value) in params) {
763+
request.addParameter(key, value)
764+
}
765+
return request.addHeader(AUTHORIZATION_KEY, "Bearer $accessToken")
741766
}
742767

743768
private fun getDomainUrlBuilder(): HttpUrl.Builder {

0 commit comments

Comments
 (0)