Crypto Wallets on iOS with Passkeys? Yeah, It's a Thing.
How to create a crypto wallet on iOS using passkeys, because passwords are so last season.
Remember when Face ID felt like magic? Well, now we can use that same magic to create and secure crypto wallets. The idea of using Passkeys for this is both brilliant and slightly terrifying. Instead of making users write down a 24-word seed phrase they're guaranteed to lose, you can tie a wallet directly to the secure hardware on their iPhone. It's smoother, it's safer (probably), and it feels like you're living in a cyberpunk future.
So, how do you actually build this? You'll be diving into Apple's AuthenticationServices
framework. The star of the show is ASAuthorizationPlatformPublicKeyCredentialProvider
. It's a mouthful, but this is what lets you generate a public-key credential that's stored securely in the user's iCloud Keychain and synced across their devices. This credential is your wallet. The private key is managed by the Secure Enclave, meaning it never leaves the device. You, the developer, only ever see the public key. This is great for security, but a bit of a headache for... well, for doing anything useful with it.
Here's a rough sketch of the code. First, you have to create a request to generate the key pair. You'll specify a "relying party identifier," which is basically your app's domain, and a user identifier. This isn't the user's real name, just a random data blob to uniquely identify their account.
import AuthenticationServices
func createNewWallet() {
let rpID = "your-relying-party-id.com"
let userID = UUID().uuidString.data(using: .utf8)!
let provider = ASAuthorizationPlatformPublicKeyCredentialProvider(relyingPartyIdentifier: rpID)
let request = provider.createCredentialRegistrationRequest(challenge: Data(), name: "user-wallet", userID: userID)
// You need to present this request to the user
// This will trigger the Face ID / Touch ID prompt
}
Once the user authenticates, the system gives you back a credential containing the public key. From this public key, you can derive a public address for whatever blockchain you're targeting. For Ethereum, you'd typically hash the public key with Keccak-256. Now you have an address, and the user has a "wallet" that's as secure as their iPhone. They can receive crypto, but sending it? That's the fun part. To sign a transaction, you have to create another ASAuthorization
request, this time asking the Secure Enclave to sign a hash of the transaction data with the private key it's been hiding from you.
func signTransaction(transactionData: Data) {
let rpID = "your-relying-party-id.com"
let provider = ASAuthorizationPlatformPublicKeyCredentialProvider(relyingPartyIdentifier: rpID)
// You need the key credential you saved earlier
let assertionRequest = provider.createCredentialAssertionRequest(challenge: transactionData)
// Present the request, get the signature, and broadcast it
}
And there you have it. A wallet without a seed phrase. Is it a good idea? Maybe! It's an amazing user experience, but it also ties the user's assets to the Apple ecosystem. If they lose their Apple account, they might lose their crypto. But let's be real, they were probably going to lose that piece of paper with their seed phrase on it anyway.
© Jacob Mizraji.