Skip to content

Android: Registration

Register a Passkey with the Service and attest the knowledge of a KeyPair. This is done by creating a new Passkey with the liquid extension enabled. The extension is used to sign the challenge with an additional KeyPair as well as authenticate a remote peer.

Get started by initializing the AttestationApi with an OkHttpClient and CookieJar.

val httpClient = OkHttpClient.Builder()
.cookieJar(cookieJar) // Use Cookie jar to share cookies between requests
.build()
val attestationApi = AttestationApi(httpClient)

👤 User Agent

The User-Agent string is used to authenticate the device.

// UA used to authenticate the device
val userAgent = "${BuildConfig.APPLICATION_ID}/${BuildConfig.VERSION_NAME} " +
"(Android ${Build.VERSION.RELEASE}; ${Build.MODEL}; ${Build.BRAND})"

🧮 Options

Fetch attestation options from the service. The remote client should present a Deep Link which contains the Origin.

// Create Options for FIDO2 Server
val options = JSONObject()
options.put("username", account.address.toString())
options.put("displayName", "Liquid Auth User")
options.put("authenticatorSelection", JSONObject().put("userVerification", "required"))
// Enable Liquid Extension
val extensions = JSONObject()
extensions.put("liquid", true)
options.put("extensions", extensions)
val response = attestationApi.postAttestationOptions(
origin, // Origin Server
userAgent, // Required for checking the authenticator fingerprint
options // Additional Request Options
)

✨ Creating

There are several ways to handle the creation of a new Passkey. We recommend using the FIDO2ApiClient or CredentialManager

MainActivity.kt
val fido2Client = Fido2ApiClient(context)
val origin = "https://my-liquid-service.com"
// Fetch Attestation Options
val response = attestationApi.postAttestationOptions(origin, userAgent, options).await()
// Parse the response to PublicKeyCredentialCreationOptions
val pubKeyCredentialCreationOptions = response.body!!.toPublicKeyCredentialCreationOptions()
// Sign the challenge with the additional keypair you have custody of
val signature = KeyPairs.rawSignBytes(
pubKeyCredentialCreationOptions.challenge,
keyPair.private
)
// Activity Result Handler
fun handleAuthenticatorAttestationResult(activityResult: ActivityResult){
val bytes = activityResult.data?.getByteArrayExtra(Fido.FIDO2_KEY_CREDENTIAL_EXTRA)
when {
activityResult.resultCode != Activity.RESULT_OK ->
Log.d(TAG, "Not OK!")
bytes == null ->
Log.e(TAG, "Error!")
else -> {
/**
* Handle the PublicKeyCredential
*
* Now you can combine the PublicKeyCredential with the Liquid Extension JSON
* and submit it to the service for verification
*/
val credential = PublicKeyCredential.deserializeFromBytes(bytes)
}
}
}
// Register/Attestation Intent Launcher
val attestationIntentLauncher = registerForActivityResult(
ActivityResultContracts.StartIntentSenderForResult(),
::handleAuthenticatorAttestationResult
)
// Launch the FIDO2 Intent
val pendingIntent = fido2Client.getRegisterPendingIntent(pubKeyCredentialCreationOptions).await()
attestationIntentLauncher.launch(
IntentSenderRequest.Builder(pendingIntent).build()
)

🔐 Liquid Extension

Sign the challenge with an additional KeyPair and optionally authenticate a remote peer.

// Create the Liquid Extension JSON
val liquidExtJSON = JSONObject()
// The type of signature and public key, this is also used
// to determine the type of encoding for the user.id
liquidExtJSON.put("type", "algorand")
// The address of the account
liquidExtJSON.put("address", account.address.toString())
// The signature of the challenge, signed by the account
liquidExtJSON.put("signature", Base64.encodeBase64URLSafeString(signature))
// Optionally authenticate a remote peer
liquidExtJSON.put("requestId", "<UUID_FROM_QR_CODE>")
// Optional device name
liquidExtJSON.put("device", Build.MODEL)

🚚 Response

val response = attestationApi.postAttestationResponse(
origin, // Origin Server
"User-Agent-String", // Required for checking the authenticator fingerprint
credential, // PublicKeyCredential
liquidExtJSON // Liquid Extension JSON
)