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)
Platform Requirements for Passkeys
- Android: Requires Android 9 (API 28) or higher for full passkey support
- iOS: Requires iOS 16.0 or higher
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
| Parameter | Type | Description | Required | 
|---|---|---|---|
| challenge | string | Base64-encoded random challenge from your server | Yes | 
| rp.name | string | Human-readable name of your app/website | Yes | 
| rp.id | string | Domain name of your website (e.g., "example.com") | Yes | 
| user.id | string | Base64-encoded unique user identifier | Yes | 
| user.name | string | Username (often email address) | Yes | 
| user.displayName | string | Human-readable name for display | Yes | 
| pubKeyCredParams | array | Array of supported public key algorithms | Yes | 
| timeout | number | Timeout in milliseconds | Yes | 
| attestation | string | Attestation conveyance preference ("none", "indirect", "direct") | Yes | 
| excludeCredentials | array | Array of existing credentials to exclude | No | 
| authenticatorSelection | object | Authenticator selection criteria | No | 
| authenticatorSelection.authenticatorAttachment | string | Type of authenticator ("platform" or "cross-platform") | No | 
| authenticatorSelection.requireResidentKey | boolean | Whether to require resident key (discoverable credential) | No | 
| authenticatorSelection.residentKey | string | Resident key requirement ("discouraged", "preferred", "required") | No | 
| authenticatorSelection.userVerification | string | User verification requirement ("discouraged", "preferred", "required") | No | 
Authentication Request Parameters
| Parameter | Type | Description | Required | 
|---|---|---|---|
| challenge | string | Base64-encoded random challenge from your server | Yes | 
| timeout | number | Timeout in milliseconds | Yes | 
| rpId | string | Domain name of your website (e.g., "example.com") | Yes | 
| userVerification | string | User verification requirement ("discouraged", "preferred", "required") | Yes | 
| allowCredentials | array | Array of credential descriptors to allow (optional) | No | 
Platform-Specific Considerations
Android
- Android uses the Credential Manager API which supports passkeys on Android 9+ (API 28+)
- Passkey data is stored in the Android Keystore and synced via Google Password Manager
- Third-party provider integration (e.g., 1Password, Samsung Wallet) requires Android 14+ (API 34+)
- The preferImmediatelyAvailableCredentialsparameter 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 preferImmediatelyAvailableCredentialsparameter is ignored on iOS
Best Practices
- Generate Strong Challenges: Use a cryptographically secure random number generator to create your challenges
- Verify on the Server: Always verify the authentication response on your server
- Use User Verification: Set userVerificationto"required"for enhanced security
- Resident Keys: Use residentKey: "required"for a better user experience
- Error Handling: Implement robust error handling for a smooth user experience
- 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:
- Generate and verify challenges
- Store public key credentials for each user
- Verify authentication responses
For server implementation examples and detailed guides, refer to:
- WebAuthn Guide
- SimpleWebAuthn Server Library (Node.js)
- Passkeys.dev (Cross-platform implementation examples)
Troubleshooting Passkey Issues
- 
Registration Failures: - Verify your Digital Asset Links/Associated Domains are correctly set up
- Ensure the rp.idmatches your verified domain
- Check that the challenge is properly encoded in base64
 
- 
Authentication Failures: - Ensure the user has previously registered a passkey
- Verify the rpIdmatches your verified domain
- Check for proper base64 encoding of the challenge
 
- 
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