Passkey Provider Assertion Help

Hi all, I'm trying to create a passkey provider application and I can consistently register a passkey through 'prepareInterface(forPasskeyRegistration registrationRequest: ASCredentialRequest)' everywhere that accepts passkeys.

However, when I attempt to then use that passkey to sign in with 'provideCredentialWithoutUserInteraction(for credentialRequest: ASCredentialRequest)'

My code will work on some sites.. and not others. For instance: https://passkey.org/ and https://webauthn.io/ both work flawlessly.

But if I then try to do the same with Github, Uber, or Coinbase (really any application "in the wild") the assertion portion fails with a generic error like "This passkey can't be used anymore."

I've debugged every request and response object line by line and can't find a single difference between a site that works, and one that doesn't.. Does anyone know why this could be the case?

Here's my assertion code:

guard let request: ASPasskeyCredentialRequest = credentialRequest as? ASPasskeyCredentialRequest else { return }

// Following specs from here: https://www.w3.org/TR/webauthn/#sctn-signature-attestation-types
let hashedRp: [UInt8] = Array(SHA256.hash(data: Data(request.credentialIdentity.serviceIdentifier.identifier.data(using: .utf8) ?? Data([]))))
                
let flags:[UInt8] = [29] // 00011101 - only one that seems to work
let counter:[UInt8] = [0, 0, 0, 1] // just for testing, always the first for now until this works

let authData = hashedRp + flags + counter
                
let signature = try privateKey.signature( for: Data(authData + request.clientDataHash) )
                
let response: ASPasskeyAssertionCredential = ASPasskeyAssertionCredential(
         userHandle: Data(request.credentialIdentity.user.utf8),
         relyingParty: request.credentialIdentity.serviceIdentifier.identifier,
         signature: signature.derRepresentation,
         clientDataHash: request.clientDataHash,
         authenticatorData: Data(authData),
         credentialID: Data(credentialId.utf8)
)
                
                
extensionContext.completeAssertionRequest(using: response)

Any help would be very appreciated as I've been stuck on this for a while now.

Answered by Apple Designer in 778341022
userHandle: Data(request.credentialIdentity.user.utf8),

It looks like you're passing the user (the user name) here instead of the userHandle. There are several similarly named fields in WebAuthn which can be confusing:

  • userHandle: The "user handle" or "user id" is an opaque blob that serves as a record identifier to the website to identify this specific account. You can think of this like an account number. All passkeys for the same account will have the same userHandle.
  • userName: This is the user-visible string to identify that specific credential, which will be shown in the system UI. This is generally an email address. Note that ASPasskeyCredentialIdentity.user and ASPasskeyCredentialIdentity.userName are interchangeable.
  • credentialID: This is a unique identifier for that specific passkey. Different passkeys for the same account will have different credentialIDs.
  • WebAuthn also has a "display name" concept, which is supposed to represent an additional user-friendly string to identify the account holder, such as "Firstname Lastname". This is not currently supported on any of our platforms.
Accepted Answer
userHandle: Data(request.credentialIdentity.user.utf8),

It looks like you're passing the user (the user name) here instead of the userHandle. There are several similarly named fields in WebAuthn which can be confusing:

  • userHandle: The "user handle" or "user id" is an opaque blob that serves as a record identifier to the website to identify this specific account. You can think of this like an account number. All passkeys for the same account will have the same userHandle.
  • userName: This is the user-visible string to identify that specific credential, which will be shown in the system UI. This is generally an email address. Note that ASPasskeyCredentialIdentity.user and ASPasskeyCredentialIdentity.userName are interchangeable.
  • credentialID: This is a unique identifier for that specific passkey. Different passkeys for the same account will have different credentialIDs.
  • WebAuthn also has a "display name" concept, which is supposed to represent an additional user-friendly string to identify the account holder, such as "Firstname Lastname". This is not currently supported on any of our platforms.

Hello, Can you please provide me code of method : 'prepareInterface(forPasskeyRegistration registrationRequest: ASCredentialRequest)' @DavidPettey

not working for me and trying since last few days so it will be very helpful for me.

Thanks in advanced, Jaydip.

Passkey Provider Assertion Help
 
 
Q