### Install Capacitor Passkey Plugin Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/README.md Install the plugin using npm and sync with Capacitor. Ensure Node.js version 18.0.0 or higher for development. ```bash npm install capacitor-passkey-plugin npx cap sync ``` -------------------------------- ### Serve Web App for Local Testing Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/integration-guide.md Start a local development server to test your web application. Access it via `http://localhost:8100`. ```bash npm run serve ``` -------------------------------- ### Install Capacitor Passkey Plugin Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/integration-guide.md Install the plugin using npm. This command adds the plugin to your project's dependencies. ```bash npm install capacitor-passkey-plugin ``` -------------------------------- ### Common Issue: Plugin Not Found Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/integration-guide.md If you encounter a 'Plugin not found' error, ensure you have run `npx cap sync` after installing the plugin. ```text Run `npx cap sync` after installation ``` -------------------------------- ### Web Passkey Creation Source: https://context7.com/argo-navis-dev/capacitor-passkey-plugin/llms.txt Create a passkey on the web using the PasskeyPlugin. Ensure the rpId matches the origin exactly. This example works on localhost without HTTPS. ```typescript // Web: rpId must match the origin exactly; works on localhost without HTTPS const webCredential = await PasskeyPlugin.createPasskey({ publicKey: { challenge: 'c2VydmVyQ2hhbGxlbmdl', rp: { id: window.location.hostname, // 'example.com' or 'localhost' name: 'Example App', }, user: { id: 'dXNlcjEyMw', name: 'user@example.com', displayName: 'Jane Smith', }, pubKeyCredParams: [{ alg: -7, type: 'public-key' }], timeout: 60000, }, }); ``` -------------------------------- ### Using Passkey Service in App Component Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/integration-guide.md Integrate the PasskeyService into your application component to handle user registration and login flows. This example shows how to call the service's register and authenticate methods. ```typescript import { PasskeyService } from './services/passkey.service'; export class AuthComponent { private passkeyService = new PasskeyService(); async onRegister() { try { await this.passkeyService.register('user123', 'john@example.com'); console.log('Passkey registered successfully'); } catch (error) { console.error('Registration failed:', error); } } async onLogin() { try { await this.passkeyService.authenticate(); console.log('Authentication successful'); } catch (error) { console.error('Authentication failed:', error); } } } ``` -------------------------------- ### Sync Android Project with Capacitor Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/integration-guide.md Sync your Android project with Capacitor after installing the plugin. This command ensures native dependencies are updated. ```bash npx cap sync android ``` -------------------------------- ### Create Passkey with External Key Attachment (iOS) Source: https://context7.com/argo-navis-dev/capacitor-passkey-plugin/llms.txt Example of creating a passkey using the `PasskeyPlugin.createPasskey` method, specifically configured for external authenticators like YubiKeys via `authenticatorAttachment: 'cross-platform'`. Requires server-side challenge and user information. ```typescript // iOS: use authenticatorAttachment 'cross-platform' for YubiKey (USB/NFC) const yubikeyCreate = await PasskeyPlugin.createPasskey({ publicKey: { challenge: 'serverChallenge', rp: { id: 'example.com', name: 'Example App' }, user: { id: 'dXNlcjEyMw', name: 'user@example.com', displayName: 'Jane' }, pubKeyCredParams: [{ alg: -7, type: 'public-key' }], authenticatorSelection: { authenticatorAttachment: 'cross-platform', // forces external key userVerification: 'required', }, timeout: 90000, }, }); ``` -------------------------------- ### Sync iOS Project with Capacitor Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/integration-guide.md Sync your iOS project with Capacitor after installing the plugin. This command updates native iOS dependencies. ```bash npx cap sync ios ``` -------------------------------- ### Build Optimized Web App for Production Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/integration-guide.md Build your web application with production optimizations. Use the `--prod` flag for this purpose. ```bash npm run build --prod ``` -------------------------------- ### Create Passkey (Web) Source: https://context7.com/argo-navis-dev/capacitor-passkey-plugin/llms.txt Demonstrates how to create a new passkey for a user on the web using the `PasskeyPlugin.createPasskey` method. It includes essential parameters like challenge, relying party information, user details, and credential parameters. ```APIDOC ## Create Passkey ### Description Initiates the creation of a new passkey for a user via the web interface. This method requires detailed information about the relying party and the user, along with cryptographic parameters for the public key. ### Method `PasskeyPlugin.createPasskey(options)` ### Parameters - **options** (object) - Required - Configuration object for creating the passkey. - **publicKey** (object) - Required - Object containing public key credential information. - **challenge** (string) - Required - A unique challenge generated by the server. - **rp** (object) - Required - Relying Party information. - **id** (string) - Required - The origin of the relying party (e.g., `window.location.hostname`). Must match the origin exactly. - **name** (string) - Required - The display name of the relying party. - **user** (object) - Required - User information. - **id** (string) - Required - A unique identifier for the user. - **name** (string) - Optional - The user's name. - **displayName** (string) - Optional - The user's display name. - **pubKeyCredParams** (array) - Required - An array of objects specifying the public key credential algorithms and types. - **alg** (number) - Required - The algorithm identifier (e.g., -7 for ES256). - **type** (string) - Required - The type of public key credential (e.g., 'public-key'). - **timeout** (number) - Optional - The maximum time in milliseconds to wait for the user to complete the operation. ### Request Example ```typescript const webCredential = await PasskeyPlugin.createPasskey({ publicKey: { challenge: 'c2VydmVyQ2hhbGxlbmdl', rp: { id: window.location.hostname, // 'example.com' or 'localhost' name: 'Example App', }, user: { id: 'dXNlcjEyMw', name: 'user@example.com', displayName: 'Jane Smith', }, pubKeyCredParams: [{ alg: -7, type: 'public-key' }], timeout: 60000, }, }); ``` ### Response - **webCredential** (object) - The created credential object. (Specific fields depend on the WebAuthn API response.) ### Notes - The `rp.id` must precisely match the origin where the code is running. It can work on `localhost` without HTTPS. - Ensure the challenge is securely generated and unique for each authentication attempt. ``` -------------------------------- ### Build Web App Assets Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/integration-guide.md Build your web application's assets before copying them to the native platforms. This command is typically `npm run build`. ```bash npm run build ``` -------------------------------- ### Get Android SHA256 Certificate Fingerprint Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/integration-guide.md Use the `keytool` command to retrieve the SHA256 fingerprint for your Android debug or release keystore. This is required for the `assetlinks.json` file. ```bash # Debug keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android # Release keytool -list -v -keystore your-release.keystore -alias your-alias ``` -------------------------------- ### Sync Plugin with Native Platforms Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/integration-guide.md Sync the plugin with your native Android and iOS projects. This command copies the plugin files to the respective native directories. ```bash npx cap sync ``` -------------------------------- ### Increase Operation Timeout Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/android.md Adjust the `timeout` option within `publicKey` to extend the duration for passkey operations. The default is 60 seconds; this example sets it to 2 minutes (120000 milliseconds). ```typescript publicKey: { timeout: 120000, // 2 minutes in milliseconds // ... other options } ``` -------------------------------- ### Copy Web Assets to Native Platforms Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/integration-guide.md Copy the built web assets into the appropriate directories for Android and iOS Capacitor projects. ```bash npx cap copy ``` -------------------------------- ### Open iOS Project in Xcode Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/integration-guide.md Open your iOS project in Xcode to configure capabilities and Info.plist settings. This is typically done after syncing. ```bash npx cap open ios ``` -------------------------------- ### Get Android Debug Keystore SHA256 Fingerprint Source: https://context7.com/argo-navis-dev/capacitor-passkey-plugin/llms.txt Command to retrieve the SHA256 certificate fingerprint for Android debug builds using the `keytool` utility. This is required for the Digital Asset Links configuration. ```bash # Get SHA256 fingerprint for debug builds keytool -list -v -keystore ~/.android/debug.keystore \ -alias androiddebugkey -storepass android -keypass android | grep SHA256 ``` -------------------------------- ### PasskeyService for Registration and Authentication Source: https://context7.com/argo-navis-dev/capacitor-passkey-plugin/llms.txt This service class encapsulates the complete passkey workflow, including server communication for challenges and verification. Ensure your server endpoints for 'auth/register/begin', 'auth/register/finish', 'auth/login/begin', and 'auth/login/finish' are correctly implemented. ```typescript import { PasskeyPlugin, PasskeyCreateResult, PasskeyAuthResult } from 'capacitor-passkey-plugin'; import { Capacitor } from '@capacitor/core'; export class PasskeyService { private readonly rpId: string; private readonly apiBase: string; constructor(rpId: string, apiBase: string) { this.rpId = rpId; this.apiBase = apiBase; } /** Register a new passkey; returns the server-confirmed credential ID */ async register(userId: string, userName: string): Promise { // Step 1: get challenge + creation options from server const beginResp = await fetch(`${this.apiBase}/auth/register/begin`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ userId }), }); if (!beginResp.ok) throw new Error('Failed to begin registration'); const { challenge, excludeCredentials } = await beginResp.json(); // Step 2: create the passkey on-device const credential: PasskeyCreateResult = await PasskeyPlugin.createPasskey({ publicKey: { challenge, rp: { id: this.rpId, name: 'My App' }, user: { id: btoa(userId).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ''), name: userName, displayName: userName, }, pubKeyCredParams: [ { alg: -7, type: 'public-key' }, { alg: -257, type: 'public-key' }, ], authenticatorSelection: { authenticatorAttachment: 'platform', userVerification: 'required', }, timeout: 60000, attestation: 'none', excludeCredentials, }, }); // Step 3: verify attestation on server const finishResp = await fetch(`${this.apiBase}/auth/register/finish`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(credential), }); if (!finishResp.ok) throw new Error('Server rejected credential'); const { credentialId } = await finishResp.json(); return credentialId; } /** Authenticate; returns a session token on success */ async login(): Promise { // Step 1: get challenge from server const beginResp = await fetch(`${this.apiBase}/auth/login/begin`, { method: 'POST' }); if (!beginResp.ok) throw new Error('Failed to begin authentication'); const { challenge, allowCredentials } = await beginResp.json(); // Step 2: sign the challenge on-device const assertion: PasskeyAuthResult = await PasskeyPlugin.authenticate({ publicKey: { challenge, rpId: this.rpId, timeout: 60000, userVerification: 'required', allowCredentials: allowCredentials?.map((c: any) => ({ id: c.id, type: 'public-key' as const, transports: c.transports ?? [], })), }, }); // Step 3: verify assertion on server and receive session token const finishResp = await fetch(`${this.apiBase}/auth/login/finish`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(assertion), }); if (!finishResp.ok) throw new Error('Authentication rejected by server'); const { sessionToken } = await finishResp.json(); return sessionToken; } } // --- Usage in a component --- const svc = new PasskeyService('example.com', 'https://api.example.com'); // Register try { const credId = await svc.register('user-uuid-123', 'jane@example.com'); console.log('Registered credential:', credId); } catch (err: any) { if (err.code === 'CANCELLED') console.log('User cancelled'); else console.error(err.message); } // Login try { const token = await svc.login(); localStorage.setItem('session', token); } catch (err: any) { if (err.code === 'NO_CREDENTIAL') { console.log('No passkey found — redirect to registration'); } else if (err.code === 'UNSUPPORTED_ERROR') { console.log('Falling back to password auth'); } } ``` -------------------------------- ### Run Development Build on iOS Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/integration-guide.md Run the Capacitor application on an iOS device or simulator for development testing. ```bash npx cap run ios ``` -------------------------------- ### Test App Links Verification with ADB Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/android.md This ADB command helps verify if your app's links are correctly configured and recognized by the system. It's a key step in troubleshooting 'No credentials available' errors. ```bash adb shell pm get-app-links com.yourcompany.yourapp ``` -------------------------------- ### PasskeyCreateOptions for createPasskey() Source: https://context7.com/argo-navis-dev/capacitor-passkey-plugin/llms.txt Defines the structure for creating a new passkey, mirroring WebAuthn `PublicKeyCredentialCreationOptions`. Ensure the Relying Party ID matches domain configurations. ```typescript import type { PasskeyCreateOptions, PasskeyAuthenticationOptions, PasskeyCreateResult, PasskeyAuthResult, } from 'capacitor-passkey-plugin'; // Full creation options type const createOptions: PasskeyCreateOptions = { publicKey: { // Server-issued random challenge (base64url, no padding) challenge: 'dGhpcyBpcyBhIHRlc3Q', // Relying Party – must match domain in Associated Domains (iOS) / assetlinks.json (Android) rp: { id: 'example.com', name: 'Example App' }, // User account user: { id: 'dXNlcjEyMw', // base64url encoded user ID name: 'user@example.com', displayName: 'Jane Smith', }, // Accepted algorithms: ES256 (-7) and RS256 (-257) pubKeyCredParams: [ { alg: -7, type: 'public-key' }, // ES256 { alg: -257, type: 'public-key' }, // RS256 ], authenticatorSelection: { // 'platform' = biometrics, 'cross-platform' = YubiKey etc. authenticatorAttachment: 'platform', userVerification: 'required', }, timeout: 60000, attestation: 'none', // Prevent duplicate passkeys for already-registered credentials excludeCredentials: [ { id: 'existingCredId', type: 'public-key', transports: ['internal'], }, ], }, }; ``` -------------------------------- ### Create and Authenticate Passkeys with Capacitor Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/README.md Use the PasskeyPlugin to create new passkeys or authenticate existing ones. Requires base64url-encoded challenges and RP information. Ensure your platform meets minimum version requirements. ```typescript import { PasskeyPlugin } from 'capacitor-passkey-plugin'; // Create a new passkey const credential = await PasskeyPlugin.createPasskey({ publicKey: { challenge: 'base64url-encoded-challenge', rp: { id: 'example.com', name: 'Example App' }, user: { id: 'base64url-encoded-user-id', name: 'user@example.com', displayName: 'User Name' }, pubKeyCredParams: [ { alg: -7, type: 'public-key' }, { alg: -257, type: 'public-key' } ], timeout: 60000 } }); // Authenticate with an existing passkey const authResult = await PasskeyPlugin.authenticate({ publicKey: { challenge: 'base64url-encoded-challenge', rpId: 'example.com', timeout: 60000 } }); ``` -------------------------------- ### Run Development Build on Android Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/integration-guide.md Run the Capacitor application on an Android device or emulator for development testing. ```bash npx cap run android ``` -------------------------------- ### Register a New Passkey with Capacitor Source: https://context7.com/argo-navis-dev/capacitor-passkey-plugin/llms.txt Use this function to initiate the passkey registration process. It requires fetching a challenge from your server and then sending the generated credential data back to the server for verification. Ensure the `userId` is correctly formatted for the `user.id` field. ```typescript import { PasskeyPlugin } from 'capacitor-passkey-plugin'; async function registerPasskey(userId: string, userName: string): Promise { // 1. Fetch a fresh challenge from your server const { challenge } = await fetch('https://api.example.com/auth/register/begin', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ userId }), }).then(r => r.json()); try { const result: PasskeyCreateResult = await PasskeyPlugin.createPasskey({ publicKey: { challenge, rp: { id: 'example.com', name: 'Example App' }, user: { id: btoa(userId).replace(/\+/g,'-').replace(/\//g,'_').replace(/=/g,''), name: userName, displayName: userName, }, pubKeyCredParams: [ { alg: -7, type: 'public-key' }, { alg: -257, type: 'public-key' }, ], authenticatorSelection: { authenticatorAttachment: 'platform', userVerification: 'required', }, timeout: 60000, attestation: 'none', }, }); // 2. Send credential to server for storage // result.id — base64url credential ID // result.rawId — base64url raw ID // result.response.attestationObject — base64url attestation // result.response.clientDataJSON — base64url client data await fetch('https://api.example.com/auth/register/finish', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(result), }); console.log('Passkey registered:', result.id); } catch (error: any) { // error.code is always a standardized string switch (error.code) { case 'CANCELLED': console.log('User cancelled registration'); break; case 'UNSUPPORTED_ERROR': console.log('Device does not support passkeys'); break; case 'INVALID_INPUT': console.log('Bad challenge or user.id format'); break; case 'TIMEOUT': console.log('Operation timed out'); break; default: console.error('Unexpected error:', error.message); } } } ``` -------------------------------- ### Testing Error Scenarios with PasskeyPlugin Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/error-handling.md Demonstrates how to trigger and test specific error conditions like timeouts, invalid input formats, and incorrect domain configurations. ```typescript await PasskeyPlugin.createPasskey({ publicKey: { ...options, timeout: 1 } }); ``` ```typescript await PasskeyPlugin.createPasskey({ publicKey: { challenge: 'invalid-base64!' } }); ``` ```typescript await PasskeyPlugin.authenticate({ publicKey: { rpId: 'wrong-domain.com' } }); ``` -------------------------------- ### Open Android Project in IDE Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/integration-guide.md Open your Android project in Android Studio to make native modifications. This is typically done after syncing. ```bash npx cap open android ``` -------------------------------- ### Local Development Server Configuration Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/web.md For local development, WebAuthn works on `localhost` without requiring HTTPS. Configure the `rpId` to 'localhost' and provide a suitable app name. ```typescript // Works in development const credential = await PasskeyPlugin.createPasskey({ publicKey: { rp: { id: 'localhost', name: 'Dev App' }, // ... } }); ``` -------------------------------- ### iOS Async Task for Create Passkey Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/architecture.md Handles the `createPasskey` call using Swift's async/await pattern. It calls the core implementation and resolves or rejects the plugin call. ```swift @objc func createPasskey(_ call: CAPPluginCall) { Task { do { let result = try await implementation.createPasskey(publicKeyData) call.resolve(result) } catch { // Error handling } } } ``` -------------------------------- ### Check WebAuthn Support Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/web.md Before using passkey features, verify if the browser supports WebAuthn by checking for `window.PublicKeyCredential`. If not supported, implement a fallback authentication method. ```typescript if (window.PublicKeyCredential) { // WebAuthn is supported const credential = await PasskeyPlugin.createPasskey(options); } else { // Fall back to password authentication } ``` -------------------------------- ### Configure Relying Party ID for Passkey Creation Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/web.md Set the `rpId` to match your domain when creating a passkey. This is crucial for security and proper functioning. Ensure your server is accessible via HTTPS. ```typescript const credential = await PasskeyPlugin.createPasskey({ publicKey: { rp: { id: 'yourdomain.com', // Must match your actual domain name: 'Your App Name' }, // ... other options } }); ``` -------------------------------- ### PasskeyPlugin.createPasskey Source: https://context7.com/argo-navis-dev/capacitor-passkey-plugin/llms.txt Initiates platform-native credential creation for registering a new passkey. It requires specific options related to the public key, relying party, user information, and authenticator settings. The method returns attestation data that should be sent to the server for verification and storage. ```APIDOC ## PasskeyPlugin.createPasskey(options) ### Description Initiates platform-native credential creation (biometric prompt on mobile, WebAuthn dialog on web). Returns attestation data to be forwarded to the server for storage and verification. ### Method ```typescript PasskeyPlugin.createPasskey(options: { publicKey: { challenge: string; // base64url from server rp: { id: string; name: string }; user: { id: string; // base64url encoded user ID name: string; displayName: string; }; pubKeyCredParams: Array<{ alg: number; type: 'public-key' }>; authenticatorSelection: { authenticatorAttachment: 'platform'; userVerification: 'required'; }; timeout?: number; attestation: 'none' | 'direct' | 'indirect' | 'enterprise'; }; }): Promise; ``` ### Parameters #### Request Body (options) - **publicKey** (object) - Required - Configuration for the public key credential. - **challenge** (string) - Required - Base64url encoded challenge fetched from the server. - **rp** (object) - Required - Relying party information. - **id** (string) - Required - The origin (e.g., `example.com`) of the relying party. - **name** (string) - Required - The display name of the relying party. - **user** (object) - Required - Information about the user. - **id** (string) - Required - Base64url encoded unique identifier for the user. - **name** (string) - Required - The user's login name or handle. - **displayName** (string) - Required - The user's display name. - **pubKeyCredParams** (array) - Required - Parameters for the public key credential. - **alg** (number) - Required - The algorithm identifier (e.g., -7 for Ed25519, -257 for RSASSA-PKCS1-v1_5). - **type** (string) - Required - Must be 'public-key'. - **authenticatorSelection** (object) - Required - Specifies authenticator requirements. - **authenticatorAttachment** (string) - Required - Must be 'platform' for platform authenticators (e.g., Touch ID, Face ID). - **userVerification** (string) - Required - Must be 'required' to enforce user verification. - **timeout** (number) - Optional - The maximum time in milliseconds to wait for the operation. - **attestation** (string) - Required - Type of attestation to request ('none', 'direct', 'indirect', 'enterprise'). ### Response #### Success Response (`PasskeyCreateResult`) - **id** (string) - Base64url credential identifier. - **rawId** (string) - Base64url raw credential identifier. - **response** (object) - Object containing attestation details. - **attestationObject** (string) - Base64url attestation blob. - **clientDataJSON** (string) - Base64url client data JSON. ### Response Example ```json { "id": "some_base64url_credential_id", "rawId": "some_base64url_raw_id", "response": { "attestationObject": "some_base64url_attestation_object", "clientDataJSON": "some_base64url_client_data_json" } } ``` ### Error Handling - **CANCELLED**: User cancelled the operation. - **UNSUPPORTED_ERROR**: The device does not support passkeys. - **INVALID_INPUT**: The provided challenge or user ID format is invalid. - **TIMEOUT**: The operation timed out. ``` -------------------------------- ### Build Android Release APK/AAB Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/integration-guide.md Generate a release-ready APK or AAB for your Android application using Gradle. ```bash cd android ./gradlew assembleRelease # or ./gradlew bundleRelease # for AAB ``` -------------------------------- ### Run Android Build on Specific Device Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/integration-guide.md Deploy and run your Capacitor Android application on a connected device, specifying the device ID. ```bash # Connect device via USB adb devices # Verify device connected npx cap run android --target [device-id] ``` -------------------------------- ### Passkey Service TypeScript Implementation Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/integration-guide.md Implement passkey registration and authentication logic using the PasskeyService class. This service handles interactions with the PasskeyPlugin, including fetching challenges from a server and verifying credentials. ```typescript import { PasskeyPlugin } from 'capacitor-passkey-plugin'; export class PasskeyService { private rpId = 'your-domain.com'; // Your domain private rpName = 'Your App Name'; async register(userId: string, userName: string): Promise { try { // Get challenge from your backend const challenge = await this.getChallengeFromServer(); const result = await PasskeyPlugin.createPasskey({ publicKey: { challenge: challenge, rp: { id: this.rpId, name: this.rpName }, user: { id: this.base64urlEncode(userId), name: userName, displayName: userName }, pubKeyCredParams: [ { alg: -7, type: 'public-key' }, // ES256 { alg: -257, type: 'public-key' } // RS256 ], authenticatorSelection: { authenticatorAttachment: 'platform', userVerification: 'required' }, timeout: 60000, attestation: 'none' } }); // Send to backend for verification and storage await this.verifyWithServer('register', result); return result; } catch (error) { this.handleError(error); throw error; } } async authenticate(): Promise { try { const challenge = await this.getChallengeFromServer(); const result = await PasskeyPlugin.authenticate({ publicKey: { challenge: challenge, rpId: this.rpId, timeout: 60000, userVerification: 'required' } }); // Verify with backend await this.verifyWithServer('authenticate', result); return result; } catch (error) { this.handleError(error); throw error; } } private handleError(error: any) { switch (error.code) { case 'USER_CANCELLED': console.log('User cancelled'); break; case 'NOT_SUPPORTED': console.log('Device does not support passkeys'); break; case 'NO_CREDENTIAL': console.log('No passkey found'); break; default: console.error('Passkey error:', error); } } private base64urlEncode(str: string): string { return btoa(str).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ''); } private async getChallengeFromServer(): Promise { // Implement your server API call const response = await fetch('https://your-api.com/auth/challenge'); const data = await response.json(); return data.challenge; } private async verifyWithServer(type: string, credential: any): Promise { // Implement your server verification await fetch(`https://your-api.com/auth/${type}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(credential) }); } } ``` -------------------------------- ### Host assetlinks.json File Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/android.md Host the `assetlinks.json` file at the specified URL (`https://yourdomain.com/.well-known/assetlinks.json`). This file establishes the trust relationship between your app and your website. This is Step 3 of configuring Digital Asset Links. ```json [{ "relation": ["delegate_permission/common.get_login_creds", "delegate_permission/common.handle_all_urls"], "target": { "namespace": "android_app", "package_name": "com.yourcompany.yourapp", "sha256_cert_fingerprints": [ "YOUR_APP_SIGNING_CERTIFICATE_SHA256_FINGERPRINT" ] } }] ``` -------------------------------- ### Create Passkey with Security Key Attachment Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/ios.md Use this TypeScript code to create a passkey, specifically requiring an external security key like a YubiKey. Ensure your server is configured with the correct `rpId` and associated domains. ```typescript const credential = await PasskeyPlugin.createPasskey({ publicKey: { // ... other options authenticatorSelection: { authenticatorAttachment: 'cross-platform', userVerification: 'required' } } }); ``` -------------------------------- ### iOS Domain Association File Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/integration-guide.md Deploy this JSON file to your web server at `/.well-known/apple-app-site-association` for iOS app association. Ensure your TEAMID and app bundle identifier are correct. ```json { "webcredentials": { "apps": ["TEAMID.com.yourcompany.yourapp"] } } ``` -------------------------------- ### Common Issue: Not Supported Error Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/integration-guide.md A 'Not supported' error may indicate that the device does not meet minimum requirements or that screen lock/biometrics are not enabled. ```text Verify device meets minimum requirements Check if screen lock/biometrics enabled ``` -------------------------------- ### PasskeyPlugin.authenticate Source: https://context7.com/argo-navis-dev/capacitor-passkey-plugin/llms.txt Triggers the platform authenticator to sign a server challenge with the stored passkey. Returns an assertion to be verified server-side. ```APIDOC ## PasskeyPlugin.authenticate(options) ### Description Triggers the platform authenticator to sign a server challenge with the stored passkey. Returns an assertion to be verified server-side. ### Method `PasskeyPlugin.authenticate` ### Parameters #### Request Body - **options** (object) - Required - Options for authentication. - **publicKey** (object) - Required - Public key credential options. - **challenge** (string) - Required - The challenge from the server. - **rpId** (string) - Required - The relying party identifier. - **timeout** (number) - Optional - The timeout in milliseconds. - **userVerification** (string) - Optional - Specifies whether user verification is required ('required', 'preferred', 'discouraged'). Defaults to 'required'. - **allowCredentials** (array) - Optional - An array of credentials to allow. - **id** (string) - Required - The credential ID. - **type** (string) - Required - The credential type, should be 'public-key'. - **transports** (array) - Optional - The transports for the credential. ### Response #### Success Response Returns `PasskeyAuthResult` object containing the assertion details. - **id** (string) - Base64url credential ID used. - **rawId** (string) - Base64url raw credential ID. - **type** (string) - Always `'public-key'`. - **response.clientDataJSON** (string) - Base64url client data. - **response.authenticatorData** (string) - Base64url authenticator data. - **response.signature** (string) - Base64url ECDSA/RSA signature. - **response.userHandle** (string | undefined) - Base64url user handle (optional). ### Error Handling - **CANCELLED**: The operation was cancelled by the user. - **NO_CREDENTIAL**: No passkey registered for the user. - **TIMEOUT**: The authentication timed out. - **DOM_ERROR**: A security policy error occurred (e.g., check HTTPS and rpId). ``` -------------------------------- ### Run iOS Build on Specific Device Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/integration-guide.md Deploy and run your Capacitor iOS application on a connected iPhone or iPad, specifying the device ID. ```bash # Connect iPhone/iPad npx cap run ios --target [device-id] ``` -------------------------------- ### Graceful Degradation with Passkey Fallbacks Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/error-handling.md Implement graceful degradation by attempting passkey authentication first and falling back to password authentication or other methods based on specific errors like 'NOT_SUPPORTED' or 'NO_CREDENTIAL'. ```typescript class AuthenticationManager { async authenticate(): Promise { try { // Try passkey first return await PasskeyPlugin.authenticate(options); } catch (error: any) { // Fallback based on error switch (error.code) { case 'NOT_SUPPORTED': return this.usePasswordAuth(); case 'NO_CREDENTIAL': return this.offerRegistration(); case 'USER_CANCELLED': return this.showAuthOptions(); default: return this.usePasswordAuth(); } } } } ``` -------------------------------- ### Common Issue: Domain Association Failed Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/integration-guide.md If domain association fails, verify that your domain association files are accessible via HTTPS and that the correct Team ID/SHA256 fingerprint is used. ```text Verify files are accessible via HTTPS Check correct Team ID/SHA256 fingerprint ``` -------------------------------- ### View Logs with adb logcat Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/android.md Use this command to view detailed logs from the PasskeyPlugin on Android. Set the log level to Debug for the plugin and filter out other logs. ```bash adb logcat PasskeyPlugin:D *:S ``` -------------------------------- ### Configure Digital Asset Links - strings.xml Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/android.md Define the asset statements resource in `strings.xml` to link your website domain to your Android app. This is Step 1 of configuring Digital Asset Links. ```xml [{ "include": "https://yourdomain.com/.well-known/assetlinks.json" }] ``` -------------------------------- ### Configure Digital Asset Links - AndroidManifest.xml Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/android.md Add the `asset_statements` meta-data tag within the `` tag in your `AndroidManifest.xml`. This is Step 2 of configuring Digital Asset Links. ```xml ``` -------------------------------- ### PasskeyAuthenticationOptions for authenticate() Source: https://context7.com/argo-navis-dev/capacitor-passkey-plugin/llms.txt Specifies options for authenticating an existing passkey, aligning with WebAuthn `PublicKeyCredentialRequestOptions`. Can optionally restrict authentication to specific credentials. ```typescript const authOptions: PasskeyAuthenticationOptions = { publicKey: { challenge: 'cmFuZG9tQ2hhbGxlbmdl', rpId: 'example.com', timeout: 60000, userVerification: 'required', // Optional: restrict to specific credentials allowCredentials: [ { id: 'credentialIdBase64url', type: 'public-key', transports: ['internal', 'hybrid'], }, ], }, }; ``` -------------------------------- ### Configure Associated Domains for Passkey Operations Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/ios.md This JSON configuration is required on your server to enable passkey operations. It associates your app with your domain for credential verification. ```json { "webcredentials": { "apps": ["TEAM_ID.com.yourcompany.yourapp"] } } ``` -------------------------------- ### Android Domain Association File Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/integration-guide.md Deploy this JSON file to your web server at `/.well-known/assetlinks.json` for Android app association. Replace placeholders with your app's package name and SHA256 fingerprint. ```json [ { "relation": ["delegate_permission/common.handle_all_urls", "delegate_permission/common.get_login_creds"], "target": { "namespace": "android_app", "package_name": "com.yourcompany.yourapp", "sha256_cert_fingerprints": ["YOUR_SHA256_FINGERPRINT"] } } ] ``` -------------------------------- ### Authenticate with Existing Passkey Source: https://context7.com/argo-navis-dev/capacitor-passkey-plugin/llms.txt Triggers the platform authenticator to sign a server challenge with the stored passkey. Returns an assertion to be verified server-side. Handles various error codes for robust error management. ```typescript import { PasskeyPlugin } from 'capacitor-passkey-plugin'; async function loginWithPasskey(): Promise { // 1. Request a challenge from the server const { challenge, allowCredentials } = await fetch( 'https://api.example.com/auth/login/begin', { method: 'POST' } ).then(r => r.json()); try { const result: PasskeyAuthResult = await PasskeyPlugin.authenticate({ publicKey: { challenge, rpId: 'example.com', timeout: 60000, userVerification: 'required', allowCredentials: allowCredentials?.map((c: any) => ({ id: c.id, type: 'public-key' as const, transports: c.transports, })), }, }); // 2. Verify the assertion on the server // result.id — credential ID used // result.rawId — raw credential ID // result.type — 'public-key' // result.response.clientDataJSON — base64url // result.response.authenticatorData — base64url RP ID hash + flags + counter // result.response.signature — base64url cryptographic signature // result.response.userHandle — base64url user handle (optional) const { success } = await fetch('https://api.example.com/auth/login/finish', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(result), }).then(r => r.json()); return success; } catch (error: any) { switch (error.code) { case 'CANCELLED': return false; case 'NO_CREDENTIAL': throw new Error('No passkey registered — please sign up first'); case 'TIMEOUT': throw new Error('Authentication timed out'); case 'DOM_ERROR': throw new Error('Security policy error — check HTTPS and rpId'); default: throw error; } } } ``` -------------------------------- ### Add Dependency - Gradle Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/android.md Include the Credential Manager API library in your Android project's build.gradle file. The plugin manages this automatically. ```gradle implementation "androidx.credentials:credentials:1.5.0" ``` -------------------------------- ### Feature Detection and Conditional UI Source: https://context7.com/argo-navis-dev/capacitor-passkey-plugin/llms.txt Provides functions to check if passkey functionality is available in the browser and if conditional UI for passkey mediation is supported. ```APIDOC ## Feature Detection ### Description Checks if the browser supports passkey operations by verifying the existence of `window.PublicKeyCredential` and its `isUserVerifyingPlatformAuthenticatorAvailable` method. ### Function Signature `async function isPasskeyAvailable(): Promise` ### Usage ```typescript async function isPasskeyAvailable(): Promise { if (!window.PublicKeyCredential) return false; try { return await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(); } catch { return false; } } ``` ## Conditional UI Check ### Description Determines if the browser supports conditional mediation for passkeys, which enables an autofill-integrated passkey UI (Chrome 108+). ### Function Signature `async function checkConditionalUI(): Promise` ### Usage ```typescript async function checkConditionalUI(): Promise { if (!window.PublicKeyCredential?.isConditionalMediationAvailable) return false; return PublicKeyCredential.isConditionalMediationAvailable(); } ``` ``` -------------------------------- ### Android Build Gradle Dependencies Source: https://context7.com/argo-navis-dev/capacitor-passkey-plugin/llms.txt Include necessary dependencies for Android credentials and Play Services authentication in your app's build.gradle file. ```gradle android { compileSdkVersion 35 defaultConfig { minSdkVersion 28 targetSdkVersion 35 } } dependencies { implementation 'androidx.credentials:credentials:1.5.0' implementation 'androidx.credentials:credentials-play-services-auth:1.5.0' } ``` -------------------------------- ### Check Conditional UI Availability Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/web.md For a more seamless user experience on Chrome 108+, determine if conditional UI is available using `window.PublicKeyCredential.isConditionalMediationAvailable`. This allows for smoother UX when creating or retrieving passkeys. ```typescript if (window.PublicKeyCredential?.isConditionalMediationAvailable) { const available = await PublicKeyCredential.isConditionalMediationAvailable(); if (available) { // Can use conditional UI for smoother UX } } ``` -------------------------------- ### Handle Passkey Plugin Errors Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/README.md Implement try-catch blocks to handle potential errors during passkey operations. Standardized error codes include CANCELLED, UNSUPPORTED_ERROR, TIMEOUT, NO_CREDENTIAL, INVALID_INPUT, and RPID_VALIDATION_ERROR. ```typescript try { const result = await PasskeyPlugin.createPasskey(options); } catch (error: any) { switch (error.code) { case 'CANCELLED': // User cancelled break; case 'UNSUPPORTED_ERROR': // Device doesn't support passkeys break; default: // Handle other errors } } ``` -------------------------------- ### Update Android build.gradle Dependencies Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/integration-guide.md Add necessary dependencies for Android credentials and Play Services authentication in your `android/app/build.gradle` file. ```gradle android { compileSdkVersion 35 defaultConfig { minSdkVersion 28 targetSdkVersion 35 } } dependencies { implementation 'androidx.credentials:credentials:1.5.0' implementation 'androidx.credentials:credentials-play-services-auth:1.5.0' } ``` -------------------------------- ### Basic Passkey Error Handling with Switch Statement Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/error-handling.md Use a switch statement to handle different passkey error codes. This pattern is useful for providing specific user feedback or alternative actions based on the error. ```typescript async function handlePasskeyOperation() { try { const result = await PasskeyPlugin.createPasskey(options); // Handle success return result; } catch (error: any) { switch (error.code) { case 'USER_CANCELLED': // User cancelled - offer retry break; case 'NOT_SUPPORTED': // Use fallback authentication break; case 'NO_CREDENTIAL': // Offer to register new passkey break; default: // Generic error handling console.error('Passkey error:', error); } } } ``` -------------------------------- ### Register Capacitor Plugin Source: https://github.com/argo-navis-dev/capacitor-passkey-plugin/blob/main/docs/architecture.md Registers the PasskeyPlugin with Capacitor, enabling lazy loading for the web implementation. Capacitor automatically handles platform routing. ```typescript import { registerPlugin } from '@capacitor/core'; const PasskeyPlugin = registerPlugin('PasskeyPlugin', { web: () => import('./web').then(m => new m.WebPasskeyPlugin()), }); ``` -------------------------------- ### Android Digital Asset Links Configuration Source: https://context7.com/argo-navis-dev/capacitor-passkey-plugin/llms.txt Configuration for Android to enable passkey functionality using Digital Asset Links. This involves defining the asset statements in `strings.xml` and hosting a `assetlinks.json` file on your domain. ```xml [{ "include": "https://yourdomain.com/.well-known/assetlinks.json" }] ``` ```json [{ "relation": [ "delegate_permission/common.get_login_creds", "delegate_permission/common.handle_all_urls" ], "target": { "namespace": "android_app", "package_name": "com.yourcompany.yourapp", "sha256_cert_fingerprints": ["AB:CD:EF:..."] } }] ```