Passkeys replace phishable passwords with cryptographic key pairs. Learn how to implement the WebAuthn standard on iOS—and avoid the CDN caching trap that can brick your auth flow.
🔓The Problem with Passwords
Passwords are fundamentally broken. Users reuse them across sites, they get phished, they end up in data breaches, and even strong passwords can be intercepted. The authentication secret travels over the network every time you log in—that's the core vulnerability.
Passkeys flip the model: Instead of proving you know a secret, you prove you possess a private key. That key never leaves your device. What travels over the network is cryptographic proof—unforgeable, unreplayable, and tied to the specific domain.
🔐Public Key Cryptography to the Rescue
Passkeys are built on WebAuthn, which uses asymmetric cryptography. When you register a passkey:
- Your device generates a unique key pair for that website
- The public key goes to the server
- The private key stays in the Secure Enclave
When you authenticate, the server sends a challenge. Your device signs it with the private key. The server verifies with the public key. No shared secret, no phishing opportunity, no credential to steal.
💃The Two-Step Dance: Registration and Assertion
Registration Flow
The first step is creating the passkey. Your server generates a challenge and sends it to the client along with user info and your relying party (RP) identifier—typically your domain name.
Use ASAuthorizationPlatformPublicKeyCredentialProvider to create a registration request with createCredentialRegistrationRequest(challenge:name:userID:), then present it via ASAuthorizationController.
Assertion Flow
Subsequent logins use assertion—proving you still have the private key. The server sends a new challenge, you sign it, and the server verifies against the stored public key.
Call createCredentialAssertionRequest(challenge:) on the same provider, then present via ASAuthorizationController.
Handling the Response
Both flows complete through the ASAuthorizationControllerDelegate. Switch on the credential type:
ASAuthorizationPlatformPublicKeyCredentialRegistration— extractrawAttestationObjectandrawClientDataJSONASAuthorizationPlatformPublicKeyCredentialAssertion— extractsignatureandrawAuthenticatorData
Send these to your server for WebAuthn verification.
📚Official Implementation References
Apple Developer Documentation
- Supporting Passkeys → Complete guide with sample code for registration and assertion
- ASAuthorizationPlatformPublicKeyCredentialProvider → API reference for creating passkey requests
- ASAuthorizationController → Presenting and handling authorization requests
- Supporting Associated Domains → Setting up webcredentials for passkeys
WebAuthn / FIDO2 Specifications
- W3C WebAuthn Level 2 Specification → The underlying standard that passkeys implement
- FIDO Alliance Specifications → FIDO2 protocols and conformance requirements
🔧 Apple Sample Projects
- Connecting to a Service with Passkeys → Full working sample project from Apple
⚠️The Associated Domains CDN Trap
⚠️ WARNING: This is the silent killer of passkey implementations
Passkeys require Associated Domains to verify your app is allowed to create credentials for your domain. iOS fetches your apple-app-site-association (AASA) file from Apple's CDN, not directly from your server.
The nightmare scenario: You deploy your AASA file, it gets cached by Apple's CDN, then you discover a typo. The fix you deploy won't take effect for 24 hours or more. Your passkey flow is bricked until the cache expires.
Setting Up Associated Domains
Add the Associated Domains entitlement to your app with the webcredentials: prefix for your domain. You might also have applinks: for Universal Links.
See Apple's Supporting Associated Domains documentation for the complete setup guide and AASA file format.
Safety Checklist
- ✓Validate your JSON thoroughly before deploying. Use
jqor an online validator. One misplaced comma breaks everything. - ✓Use Developer Mode during development. Add
?mode=developerto your entitlement to bypass the CDN cache. - ✓Deploy AASA early in your development cycle, not right before launch. Give yourself time to catch mistakes.
- ✓Test with a fresh device or clear the Associated Domains cache to verify your changes took effect.
🎯Key Takeaways
- 1.Passkeys use public key cryptography—the private key never leaves your device, eliminating phishing and credential theft.
- 2.Two-step dance: Registration creates the key pair, Assertion proves possession. Both use challenge-response patterns.
- 3.ASAuthorizationController is your gateway to passkeys on iOS. Master the delegate pattern and credential types.
- 4.Respect the CDN—Apple caches your AASA file. Deploy early, validate thoroughly, and use Developer Mode during testing.
📱About Sandboxed
Sandboxed is a deep-dive podcast series on iOS security—built for developers who ship apps to millions of users and need to understand what's actually happening under the hood.
Each episode gives you a mental model you can apply this week—concrete enough to act on, without the 50-page spec.
If that sounds useful, I'd love to have you along.
Ready to dive deeper?
Stay tuned for more episodes exploring iOS security topics.