Platform-specific guide for configuring WebAuthn passkey authentication in iOS applications using the KMP Stellar SDK Smart Account Kit.
- iOS 16.0+ (passkeys require AuthenticationServices with platform public key credential support)
- Xcode 14+
- An Apple Developer account with associated domains capability
- A domain you control for apple-app-site-association
The KMP Stellar SDK supports iOS 14.0+. Passkey features require iOS 16.0+.
The SDK requires libsodium for cryptographic operations. Add Clibsodium via Swift Package Manager in Xcode:
- File > Add Package Dependencies
- Enter URL:
https://github.com/jedisct1/swift-sodium - Select "Up to Next Major Version" and add the
Clibsodiumproduct to your app target
No additional dependencies are needed for WebAuthn -- it uses the built-in AuthenticationServices framework.
Passkeys on iOS require the Associated Domains entitlement to link your app with a web domain. The rpId you pass to AppleWebAuthnProvider must match an associated domain.
In Xcode:
- Select your app target
- Go to "Signing & Capabilities"
- Click "+ Capability" and add "Associated Domains"
- Add an entry:
webcredentials:your-domain.com
Create a file at https://your-domain.com/.well-known/apple-app-site-association:
{
"webcredentials": {
"apps": [
"TEAM_ID.com.example.yourapp"
]
}
}Replace TEAM_ID with your Apple Developer Team ID (found in the Apple Developer portal under Membership). Replace com.example.yourapp with your app's bundle identifier.
Serve this file:
- Over HTTPS (no redirects)
- With
Content-Type: application/json - Without a
.jsonfile extension in the URL
Apple validates associated domains when the app is installed. To test during development, add the ?mode=developer query parameter to the domain entry: webcredentials:your-domain.com?mode=developer. This bypasses Apple's CDN cache and fetches the file directly.
AppleWebAuthnProvider uses the AuthenticationServices framework (ASAuthorizationPlatformPublicKeyCredentialProvider) for passkey creation and assertion.
Constructor parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
rpId |
String |
Yes | - | Relying party domain (must match associated domain) |
rpName |
String |
Yes | - | Display name shown during passkey prompts |
timeout |
Long |
No | 60000 | Timeout in milliseconds |
import com.soneso.stellar.sdk.smartaccount.AppleWebAuthnProvider
val webauthnProvider = AppleWebAuthnProvider(
rpId = "your-domain.com",
rpName = "My Stellar Wallet"
)The provider can also be created via the companion factory method:
val webauthnProvider = AppleWebAuthnProvider.create(
rpId = "your-domain.com",
rpName = "My Stellar Wallet"
)iOS offers two storage options:
Stores data in an isolated NSUserDefaults suite. Adequate for most use cases since stored credentials contain public keys, not secret keys.
Constructor parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
suiteName |
String |
No | "com.soneso.stellar.smartaccount" |
NSUserDefaults suite name |
import com.soneso.stellar.sdk.smartaccount.UserDefaultsStorageAdapter
val storage = UserDefaultsStorageAdapter()
// Or with a custom suite name
val storage = UserDefaultsStorageAdapter(suiteName = "com.yourapp.smartaccount")Stores data in the iOS Keychain using the Security framework. Provides encryption at rest and kSecAttrAccessibleAfterFirstUnlock access control. Use this when stronger data protection is required.
Constructor parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
serviceName |
String |
No | "com.soneso.stellar.smartaccount" |
Keychain service name |
import com.soneso.stellar.sdk.smartaccount.KeychainStorageAdapter
val storage = KeychainStorageAdapter()
// Or with a custom service name
val storage = KeychainStorageAdapter(serviceName = "com.yourapp.smartaccount")import com.soneso.stellar.sdk.smartaccount.AppleWebAuthnProvider
import com.soneso.stellar.sdk.smartaccount.KeychainStorageAdapter
import com.soneso.stellar.sdk.smartaccount.oz.OZSmartAccountConfig
import com.soneso.stellar.sdk.smartaccount.oz.OZSmartAccountKit
val webauthnProvider = AppleWebAuthnProvider(
rpId = "your-domain.com",
rpName = "My Stellar Wallet"
)
val storage = KeychainStorageAdapter()
val config = OZSmartAccountConfig(
rpcUrl = "https://soroban-testnet.stellar.org",
networkPassphrase = "Test SDF Network ; September 2015",
accountWasmHash = "your-wasm-hash-hex",
webauthnVerifierAddress = "<C-address of the WebAuthn verifier>",
webauthnProvider = webauthnProvider,
storage = storage
)
val kit = OZSmartAccountKit.create(config)The iOS Simulator supports passkeys starting with the iOS 16 Simulator (Xcode 14). Simulator passkeys are stored locally and do not sync via iCloud Keychain, so credentials created on a device are not available on the simulator and vice versa. The associated-domain validation still applies — use ?mode=developer on the domain entry during development.
The user dismissed the passkey prompt. The SDK maps this to WebAuthnException.Cancelled. Handle this gracefully in your UI.
The authenticator operation failed. Common causes:
- The
rpIddoes not match any associated domain in the app's entitlements - The
apple-app-site-associationfile is not accessible or has incorrect content - The provisioning profile does not include the Associated Domains capability
Associated Domains requires a provisioning profile that includes the capability. Automatic signing in Xcode handles this, but manual signing requires regenerating profiles after adding the entitlement in the Apple Developer portal.
If authenticate() throws WebAuthnException.AuthenticationFailed, the user may not have a passkey for this rpId on the device. Ensure the passkey was created with the same rpId and that iCloud Keychain sync is enabled if testing across devices.