Skip to main content

Overview

Passkeys in the Dynamic Swift SDK provide secure, passwordless authentication using biometrics or device credentials. Users can use passkeys as their primary login method (1FA) or as a second factor to enhance account security (2FA). To use passkeys in your iOS app, your website domain must grant credential access to the mobile app so passkeys can be created associated with the website domain. This allows users to seamlessly use the same passkeys across both your app and website.

Prerequisites

Before setting up passkeys, ensure you have:
  • Dynamic SDK initialized in your iOS project (see Installation Guide)
  • A domain/website that you control (for hosting the required .well-known endpoints)
  • Access to your app’s bundle identifier and signing certificates
  • Apple Developer account with Xcode configured

App Configuration

There are a few steps to configure passkey support. If you have already set up an associated domain for your application, you can skip to Managing Passkeys.

Set Up an Associated Domain

Follow the Apple documentation to associate a domain with your application. On your webserver, set up this route:
GET https://{{yourdomain}}/.well-known/apple-app-site-association
This route should serve a static JSON object containing your team ID and bundle identifier. Replace XXXXXXXXXX with your team identifier and YYY.YYYYY.YYYYYYYYYYYYYY with your bundle ID (e.g., H123456789.com.example.mobileApp):
{
  "applinks": {
    "details": []
  },
  "webcredentials": {
    "apps": ["XXXXXXXXXX.YYY.YYYYY.YYYYYYYYYYYYYY"]
  }
}

Add Associated Domains Capability in Xcode

  1. Open your project in Xcode
  2. Select your app target
  3. Go to Signing & Capabilities
  4. Click + Capability and add Associated Domains
  5. Add your webcredentials domain (replace yourdomain.com with your actual domain):
webcredentials:yourdomain.com
You can validate your Apple App Site Association (AASA) configuration using the Branch AASA Validator. This tool tests your domain against Apple’s requirements and helps ensure your Universal Links and passkey setup is properly configured.

SDK Configuration

Configure the appOrigin in your SDK initialization to associate passkeys with your domain:
import DynamicSDKSwift

let sdk = DynamicSDK.instance(
    with: DynamicSDKConfiguration(
        environmentId: "YOUR-ENVIRONMENT-ID",
        appOrigin: "https://yourdomain.com"  // Replace with your actual domain
    )
)
The appOrigin must match your website domain exactly (e.g., https://example.com). This domain will be used to create and associate passkeys, so ensure it’s the same domain where you host the .well-known/apple-app-site-association endpoint.

Managing Passkeys

Now that your application has correctly associated your domain, you can register and authenticate using passkeys.

Sign In with Passkey

Use passkeys as the primary authentication method:
import DynamicSDKSwift

let sdk = DynamicSDK.instance()

func signInWithPasskey() async {
    do {
        let user = try await sdk.auth.passkey.signIn()
        print("Signed in with passkey! User: \(user.userId)")
    } catch {
        print("Passkey sign-in failed: \(error)")
    }
}
When called, this triggers the system passkey prompt where users authenticate with Face ID, Touch ID, or their device passcode.

Register a New Passkey

After authentication, users can register passkeys for future sign-ins:
func registerPasskey() async {
    do {
        try await sdk.passkeys.registerPasskey()
        print("Passkey registered successfully!")
    } catch {
        print("Failed to register passkey: \(error)")
    }
}

List User Passkeys

Retrieve all passkeys registered for the authenticated user:
func getPasskeys() async {
    do {
        let passkeys = try await sdk.passkeys.getPasskeys()
        print("User has \(passkeys.count) passkeys")

        for passkey in passkeys {
            print("Passkey ID: \(passkey.id)")
            print("Created: \(passkey.createdAt)")
        }
    } catch {
        print("Failed to get passkeys: \(error)")
    }
}

Delete a Passkey

Remove a passkey from the user’s account:
func deletePasskey(passkeyId: String) async {
    do {
        try await sdk.passkeys.deletePasskey(
            DeletePasskeyRequest(passkeyId: passkeyId)
        )
        print("Passkey deleted successfully")
    } catch {
        print("Failed to delete passkey: \(error)")
    }
}

Complete Example

Here’s a complete SwiftUI example showing passkey authentication and management:
import SwiftUI
import DynamicSDKSwift

struct PasskeyView: View {
    @StateObject private var viewModel = PasskeyViewModel()

    var body: some View {
        VStack(spacing: 20) {
            if viewModel.isAuthenticated {
                // Passkey Management
                Text("Manage Passkeys")
                    .font(.headline)

                Button("Register New Passkey") {
                    viewModel.registerPasskey()
                }

                List(viewModel.passkeys, id: \.id) { passkey in
                    HStack {
                        VStack(alignment: .leading) {
                            Text("Passkey")
                                .font(.headline)
                            Text("Created: \(passkey.createdAt, style: .date)")
                                .font(.caption)
                        }

                        Spacer()

                        Button(role: .destructive) {
                            viewModel.deletePasskey(id: passkey.id)
                        } label: {
                            Image(systemName: "trash")
                        }
                    }
                }
                .refreshable {
                    await viewModel.loadPasskeys()
                }
            } else {
                // Sign In
                Button("Sign in with Passkey") {
                    viewModel.signInWithPasskey()
                }
                .buttonStyle(.borderedProminent)
            }

            if let error = viewModel.errorMessage {
                Text(error)
                    .foregroundColor(.red)
                    .font(.caption)
            }
        }
        .padding()
        .onAppear {
            viewModel.checkAuthStatus()
        }
    }
}

@MainActor
class PasskeyViewModel: ObservableObject {
    @Published var isAuthenticated = false
    @Published var passkeys: [UserPasskey] = []
    @Published var errorMessage: String?

    private let sdk = DynamicSDK.instance()

    func checkAuthStatus() {
        isAuthenticated = sdk.auth.authenticatedUser != nil
        if isAuthenticated {
            Task {
                await loadPasskeys()
            }
        }
    }

    func signInWithPasskey() {
        errorMessage = nil
        Task {
            do {
                _ = try await sdk.auth.passkey.signIn()
                isAuthenticated = true
                await loadPasskeys()
            } catch {
                errorMessage = "Sign-in failed: \(error.localizedDescription)"
            }
        }
    }

    func registerPasskey() {
        errorMessage = nil
        Task {
            do {
                try await sdk.passkeys.registerPasskey()
                await loadPasskeys()
            } catch {
                errorMessage = "Registration failed: \(error.localizedDescription)"
            }
        }
    }

    func loadPasskeys() async {
        do {
            passkeys = try await sdk.passkeys.getPasskeys()
        } catch {
            errorMessage = "Failed to load passkeys: \(error.localizedDescription)"
        }
    }

    func deletePasskey(id: String) {
        errorMessage = nil
        Task {
            do {
                try await sdk.passkeys.deletePasskey(
                    DeletePasskeyRequest(passkeyId: id)
                )
                await loadPasskeys()
            } catch {
                errorMessage = "Failed to delete: \(error.localizedDescription)"
            }
        }
    }
}

Troubleshooting

Common Issues

Passkey registration fails
  • Verify your associated domain is correctly configured
  • Check that the apple-app-site-association file is accessible at https://yourdomain.com/.well-known/apple-app-site-association
  • Ensure the JSON contains your correct team ID and bundle identifier
  • Validate your AASA file using the Branch AASA Validator
“No passkeys available” error
  • The user may not have any passkeys registered yet
  • Ensure the user is authenticated before attempting passkey sign-in
Domain mismatch errors
  • Verify appOrigin in SDK configuration matches your webcredentials domain exactly
  • Check that both use the same protocol (https://)

Using Passkeys for MFA

Passkeys can also be used as a second factor for multi-factor authentication. Once you’ve completed the configuration above, see the MFA Guide for details on using passkeys as a 2FA method.

What’s Next

Now that you have passkeys configured:
  1. MFA Guide - Use passkeys as a second factor for multi-factor authentication
  2. Authentication - Learn about other authentication methods
  3. API Reference - Complete passkey API documentation