Skip to main content

Passkey Authentication

Passkeys provide a secure, passwordless authentication method that uses public key cryptography. This guide covers how to implement passkey authentication in your React Native application using react-native-credentials-manager.

Understanding Passkeys

Passkeys are a replacement for passwords that provide stronger security and a better user experience. They use public key cryptography to authenticate users without the need to remember or type passwords.

Key advantages of passkeys include:

  • Strong Security: Resistant to phishing, credential stuffing, and other common attacks
  • Passwordless: No passwords to forget, reset, or have stolen
  • Biometric Authentication: Uses device biometrics (fingerprint, face recognition) for verification
  • Cross-Device Compatibility: Can be synced across devices via platform account (Google/Apple)

Integration Steps

1. Prerequisites

Ensure you've completed the installation and setup process, including:

  • Digital Asset Links configuration for Android
  • Associated Domains setup for iOS
  • Domain verification

2. Passkey Registration (Sign Up)

To create a new passkey during user registration:

import { signUpWithPasskeys } from "react-native-credentials-manager";

// Create a registration request
const registrationRequest = {
challenge: "BASE64_ENCODED_CHALLENGE", // Random challenge from your server
rp: {
name: "Your App Name",
id: "yourdomain.com",
},
user: {
id: "BASE64_ENCODED_USER_ID", // User ID from your server
name: "username@example.com",
displayName: "User Name",
},
pubKeyCredParams: [
{
type: "public-key",
alg: -7, // ES256
},
{
type: "public-key",
alg: -257, // RS256
},
],
timeout: 60000, // 60 seconds
attestation: "none",
excludeCredentials: [], // Array of existing credentials to exclude
authenticatorSelection: {
authenticatorAttachment: "platform",
requireResidentKey: true,
residentKey: "required",
userVerification: "required",
},
};

try {
const response = await signUpWithPasskeys(
registrationRequest,
false // preferImmediatelyAvailableCredentials (Android only)
);

// Send the response to your server for verification and storage
await sendToServer("/register-passkey", response);

console.log("Passkey registration successful");
} catch (error) {
console.error("Passkey registration failed:", error);
}

3. Passkey Authentication (Sign In)

To authenticate a user with an existing passkey:

import { signIn } from "react-native-credentials-manager";

// Create an authentication request
const authRequest = {
challenge: "BASE64_ENCODED_CHALLENGE", // Random challenge from your server
timeout: 60000, // 60 seconds
userVerification: "required",
rpId: "yourdomain.com",
};

try {
const credential = await signIn(["passkeys"], {
passkeys: authRequest,
});

if (credential.type === "passkey") {
// Send the authentication response to your server for verification
const authResult = await sendToServer(
"/verify-passkey",
credential.authenticationResponseJson
);

console.log("Passkey authentication successful");
}
} catch (error) {
console.error("Passkey authentication failed:", error);
}

Request Parameters Explained

Registration Request Parameters

ParameterTypeDescriptionRequired
challengestringBase64-encoded random challenge from your serverYes
rp.namestringHuman-readable name of your app/websiteYes
rp.idstringDomain name of your website (e.g., "example.com")Yes
user.idstringBase64-encoded unique user identifierYes
user.namestringUsername (often email address)Yes
user.displayNamestringHuman-readable name for displayYes
pubKeyCredParamsarrayArray of supported public key algorithmsYes
timeoutnumberTimeout in millisecondsYes
attestationstringAttestation conveyance preference ("none", "indirect", "direct")Yes
excludeCredentialsarrayArray of existing credentials to excludeNo
authenticatorSelectionobjectAuthenticator selection criteriaNo
authenticatorSelection.authenticatorAttachmentstringType of authenticator ("platform" or "cross-platform")No
authenticatorSelection.requireResidentKeybooleanWhether to require resident key (discoverable credential)No
authenticatorSelection.residentKeystringResident key requirement ("discouraged", "preferred", "required")No
authenticatorSelection.userVerificationstringUser verification requirement ("discouraged", "preferred", "required")No

Authentication Request Parameters

ParameterTypeDescriptionRequired
challengestringBase64-encoded random challenge from your serverYes
timeoutnumberTimeout in millisecondsYes
rpIdstringDomain name of your website (e.g., "example.com")Yes
userVerificationstringUser verification requirement ("discouraged", "preferred", "required")Yes
allowCredentialsarrayArray of credential descriptors to allow (optional)No

Platform-Specific Considerations

Android

  • Android uses the Credential Manager API which supports passkeys on Android 14+ (API 34+)
  • Passkey data is stored in the Android Keystore and synced via Google Password Manager
  • The preferImmediatelyAvailableCredentials parameter is Android-specific

iOS

  • iOS uses Authentication Services which supports passkeys on iOS 16.0+
  • Passkey data is stored in the iOS Keychain and synced via iCloud Keychain
  • The preferImmediatelyAvailableCredentials parameter is ignored on iOS

Best Practices

  1. Generate Strong Challenges: Use a cryptographically secure random number generator to create your challenges
  2. Verify on the Server: Always verify the authentication response on your server
  3. Use User Verification: Set userVerification to "required" for enhanced security
  4. Resident Keys: Use residentKey: "required" for a better user experience
  5. Error Handling: Implement robust error handling for a smooth user experience
  6. User Education: Educate users about passkeys if they're unfamiliar with the concept

Example Helper Function

Here's a helper function to generate valid base64 challenges:

function generateChallenge(): string {
// Generate random bytes
const randomBytes = new Uint8Array(32);
crypto.getRandomValues(randomBytes);

// Convert to base64
return bytesToBase64(randomBytes);
}

function bytesToBase64(bytes: Uint8Array): string {
const binString = Array.from(bytes)
.map((byte) => String.fromCharCode(byte))
.join("");

return btoa(binString);
}

Server-Side Integration

Your server needs to:

  1. Generate and verify challenges
  2. Store public key credentials for each user
  3. Verify authentication responses

For server implementation examples and detailed guides, refer to:

Troubleshooting Passkey Issues

  1. Registration Failures:

    • Verify your Digital Asset Links/Associated Domains are correctly set up
    • Ensure the rp.id matches your verified domain
    • Check that the challenge is properly encoded in base64
  2. Authentication Failures:

    • Ensure the user has previously registered a passkey
    • Verify the rpId matches your verified domain
    • Check for proper base64 encoding of the challenge
  3. Cross-Device Issues:

    • Ensure the user's device is syncing credentials (Google/Apple account is set up)
    • Verify that your implementation works with both resident and non-resident keys