A "hello world" style iOS Credential Provider Extension demonstrating how third-party password managers can handle passkey (WebAuthn) creation and authentication on iOS.
This sample app shows how to build a credential provider extension that:
- Registers passkeys when websites request them
- Authenticates users with stored passkeys
- Lists available passkeys for a relying party
Similar in concept to the Windows PasskeyManager sample, but for iOS.
- iOS 18.0+
- Xcode 16+
- Physical iOS device (credential provider extensions don't work in the Simulator)
PasskeyProviderDemo/
├── PasskeyProviderDemo/ # Host app
│ ├── ContentView.swift # Lists stored passkeys
│ └── SetupInstructionsView.swift
├── PasskeyProviderExtension/ # Credential Provider Extension
│ ├── CredentialProviderViewController.swift
│ ├── Views/
│ │ ├── RegistrationConfirmView.swift
│ │ └── CredentialListView.swift
│ └── Info.plist
└── Shared/ # Swift Package (shared code)
└── Sources/Shared/
├── Models/
│ └── StoredPasskey.swift
├── Storage/
│ └── PasskeyStore.swift
└── WebAuthn/
├── AuthenticatorData.swift
├── AttestationBuilder.swift
├── AssertionBuilder.swift
├── COSEKey.swift
└── CBORHelpers.swift
-
Clone the repository
-
Open in Xcode
open PasskeyProviderDemo.xcodeproj -
Configure signing
- Select the project in the navigator
- For both targets (PasskeyProviderDemo and PasskeyProviderExtension):
- Set your Development Team
- Update the Bundle Identifier to use your own prefix
-
Configure App Group
- In Xcode, select each target → Signing & Capabilities
- Update the App Group identifier if needed
- Update
PasskeyStore.appGroupIdinShared/Sources/Shared/Storage/PasskeyStore.swiftto match
-
Build and run on a physical device
After installing, enable the credential provider in Settings:
Settings → General → AutoFill & Passwords → Enable "Passkey Provider Demo"
- Open Safari on your device
- Navigate to webauthn.io
- Enter a username and tap "Register"
- Select "Passkey Provider Demo" when prompted
- Confirm passkey creation in the extension UI
- Tap "Authenticate" to test sign-in with the passkey
- Website calls
navigator.credentials.create() - iOS invokes
prepareInterface(forPasskeyRegistration:) - Extension generates P-256 key pair using CryptoKit
- Builds authenticator data with attested credential data
- Creates attestation object (CBOR encoded, "none" format)
- Stores private key in Keychain, metadata in App Group UserDefaults
- Returns
ASPasskeyRegistrationCredentialto the system
- Website calls
navigator.credentials.get() - iOS invokes
prepareInterfaceToProvideCredential(for:) - Extension retrieves private key from Keychain
- Builds authenticator data and signs
authData || clientDataHash - Returns
ASPasskeyAssertionCredentialto the system
Based on W3C WebAuthn Level 2 specification:
- AuthenticatorData - RP ID hash, flags (UP, UV, BE, BS, AT), sign counter, attested credential data
- COSEKey - EC2 public key encoding per RFC 8152
- AttestationBuilder - Creates attestation objects with "none" format
- AssertionBuilder - Signs challenges for authentication
- Private keys: Keychain with
kSecAttrAccessibleAfterFirstUnlock - Passkey metadata: App Group UserDefaults (shared between app and extension)
- SwiftCBOR - CBOR encoding (required by WebAuthn spec)
MIT License