Introduction
next generation authentication technology
Passwords
Difficult to use correctly
Security vs convenience tradeoffs
Phishable and reusable
Currently 2-factor authentication takes several steps:
add passkey button after being signed in
system sheet for creating a passkey
device generates a passkey
With passkey: just select the user name, Face ID, and you’re signed in!
Passkeys works on the web, too
With FIDO alliance the passkeys work cross-platform
When on a friends computer, just scan QR code with the phone, it recognizes that it’s for sign in with passkey
Looks simple, but does lots of steps in the background, including proximity check and agreements
Sharing passkey possible, e.g. via AirDrop
Designing for passkeys
Passkeys are replacements for passwords
Refer to them with the noun “passkey”, pluralizes to “passkeys”
Don’t design new interfaces for logins, just remove the password text field, but keep the user name field
Passkeys and AutoFill
Built on WebAuthn standard, use public key cryptopgraphy
Require WebAuthn backend adoption
Part of ASAuthorization API
Supports many different credential types, new flexible UI options
Setup associated domains first:
{
"webcredentials": {
"apps": [ "A1B2C3D4E5.com.example.Shiny" ]
}
}Make sure to use the
.usernametext content type on text fieldSign in logic via creating a request and starting the controller
func signIn() {
let challenge: Data = … // Fetched from server
let provider =
ASAuthorizationPlatformPublicKeyCredentialProvider(
relyingPartyIdentifier: "example.com")
let request =
provider.createCredentialAssertionRequest(
challenge: challenge)
let controller =
ASAuthorizationController(
authorizationRequests: [request])
controller.delegate = self
controller.presentationContextProvider = self
// Start the request
controller.performAutoFillAssistedRequests()
}System will offer autofill when this logic has been run before the text field is clicked by user
Nothing gets filled in text field, instead this callback will trigger:
func authorizationController(controller: ASAuthorizationController,
didCompleteWithAuthorization authorization: ASAuthorization) {
guard let passkeyAssertion = authorization.credential as?
ASAuthorizationPlatformPublicKeyCredentialAssertion
else { … }
let signature = passkeyAssertion.signature
let clientDataJSON = passkeyAssertion.rawClientDataJSON
// Pass these values to your server, and complete the sign in
…
}make sure to verify sign in on the backend using the signature & data
Same code also allows passkey sign in from nearby devices
Apple was not showing the password field, only when no passkey (maybe a new best practice?)
there’s also a modal request, just call
performRequestsinstead, it will present a modal sheet with all available keysOn web, annotate your username field with
WebAuthn
<input type="text" id="username-field" autocomplete="username webauthn" > AutoFill-assisted WebAuthn request (JavaScript)
function signIn() {
if (!PublicKeyCredential.isConditionalMediationAvailable || !PublicKeyCredential.isConditionalMediationAvailable()) {
// Browser doesn't support AutoFill-assisted requests.
return;
}
const options = {
"publicKey": {
challenge: … // Fetched from server
},
mediation: "conditional"
};
navigator.credentials.get(options)
.then(assertion => {
// Pass the assertion to your server.
});
}Switching to a modal request by removing the
mediationparameterStreamlining sign-in
Modal request with allow lists:
func signIn(userName: String) {
let challenge: Data = … // Fetched from server
let provider = ASAuthorizationPlatformPublicKeyCredentialProvider(
relyingPartyIdentifier:"example.com")
let request = provider.createCredentialAssertionRequest(
challenge: challenge)
let credentialIDs: [Data] = … // Fetched from server for provided userName
request.allowedCredentials = credentialIDs.map(
ASAuthorizationPlatformPublicKeyCredentialDescriptor.init(credentialID:))
let controller = ASAuthorizationController(authorizationRequests: [request])
controller.delegate = self
controller.presentationContextProvider = self
// Start the request
controller.performRequests()
}Apple platforms always require UV when biometrics are available, always use
userVerification: "preferred"Using passkeys on the web
Make AutoFill requests early
Modal requests require a user gesture
Passkeys replace Safari’s legacy platform authenticator
