Search code examples
iosswiftwebauthnpasskey

How do I show passkeys in the autofill prompt when the user taps the "key icon"?


I'm busy implementing passkeys but I'm encountering an issue with the autofill feature.

According to the WWDC video (https://developer.apple.com/videos/play/wwdc2022/10092/ at around 14:30), when the user taps the "key icon" in the quick type bar, the user is presented with a prompt to choose a passkey or password to login, and (very important!) also the ability to "Use Passkey from a Nearby Device...".

Correct modal that I want to show

However, my app (and most others) only gives the user the ability to choose a password.

Incorrect modal that currently gets shown

How can I get my app to show the first modal (that includes passkeys)?

  • Associated domains is correctly setup
  • The correct textContentType is set on both the username and password fields
  • ASAuthorisation is correctly implemented as per the WWDC video
  • I've tried performRequests() as well as performAutoFillAssistedRequests(), didn't make a difference
  • I've tried removing the password field, and only showing the username field - no difference
  • Tested on iOS 16.3 and 16.4 - no difference

Solution

  • Only passkey requests are allowed on the ASAuthorizationController when using performAutoFillAssistedRequests()

    As per the shiny example:

    // AutoFill-assisted requests only support ASAuthorizationPlatformPublicKeyCredentialAssertionRequest.
    

    PS: Remember to cancel this request afterwards! You can do this by storing a reference to the ASAuthorizationController (ex. self.authController = authController) and when you want to cancel the request then simply call authController.cancel()

    So the complete solution should look something like this:

    var authController: ASAuthorizationController?
    
    func beginAutoFillAssistedPasskeySignIn(anchor: ASPresentationAnchor) {
        self.authenticationAnchor = anchor
        
        let publicKeyCredentialProvider = ASAuthorizationPlatformPublicKeyCredentialProvider(relyingPartyIdentifier: domain)
        
        // Fetch the challenge from the server. The challenge needs to be unique for each request.
        let challenge = Data()
        let assertionRequest = publicKeyCredentialProvider.createCredentialAssertionRequest(challenge: challenge)
        
        // AutoFill-assisted requests only support ASAuthorizationPlatformPublicKeyCredentialAssertionRequest.
        let authController = ASAuthorizationController(authorizationRequests: [ assertionRequest ] )
        authController.delegate = self
        authController.presentationContextProvider = self
        authController.performAutoFillAssistedRequests()
        self.authController = authController
    }
    
    func cancelAutoFillAssistedPasskeySignIn() {
        if let authController {
            authController.cancel()
            self.authController = nil
        }
    }