Passkey Integration Question RE: iOS Generated authenticationData

I am integrating Apple's iOS implementation of WebAuthN using the ASAuthorization APIS provided by Apple. I am integrating with the Django py_webauthn host backend. I have the entire registration & account creations process working. And I almost have the account authentication process working. But there seems to be an issue between the version of authenticationData generated by iOS subsequent to the client (iPhone) authentication process and what py_webauthn is expecting. It results in a consistent

"Leftover bytes detected while parsing authentication data" error

on the host. According to the documentation, this error is generated when all the authenticatorData has been parsed but there are leftover bytes in the data.

I believe if I get past this issue everything will be working.

I could use some help here and provide code/logs below. But in the hopes that someone from the Authentication development team at Apple is watching for these posts, I have a related question.

Can we assume that the generated private key AND sign_count data is shared across an iOS user's devices through KeyChain cloud? Do we have to advise our users that they should enable this??

Here is code (OBJ C) and logs, in the hopes that someone can advise... and hoping that the code can help others trying to implement this capability.

Thanks.....

iPhone Client

-	(void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization

//Check to make sure that we are authenticating user after receiving authorization challenge from host


NSLog(@"%s  retrieve public/private key",__FUNCTION__);
NSDictionary *pubKeyDict    =   [NSDictionary dictionary];

ASAuthorizationPlatformPublicKeyCredentialAssertion *appleCred = (ASAuthorizationPlatformPublicKeyCredentialAssertion *)authorization.credential;
NSDictionary *clientJSON		=	[NSJSONSerialization JSONObjectWithData:appleCred.rawClientDataJSON options:NSJSONReadingMutableContainers error:nil];
NSLog(@"%s clientJSON  = %@",__FUNCTION__,clientJSON);
NSString *clientJSONString		=	[ServerUtilities encodeBase64URLData:appleCred.rawClientDataJSON];
NSString *authenticatorString	=	[ServerUtilities encodeBase64URLData:appleCred.rawAuthenticatorData];
NSString *signatureString		=	[ServerUtilities encodeBase64URLData:appleCred.signature];
NSDictionary *respDict			=	[NSDictionary dictionaryWithObjectsAndKeys	clientJSONString, @"clientDataJSON",
														authenticatorString, @"signature",
														signatureString, @"authenticatorData",	nil];
NSLog(@"%s  respDict - %@",__FUNCTION__,respDict);
NSString *credIDString	=	[ServerUtilities encodeBase64URLData:appleCred.credentialID];
NSDictionary *regDict	=	[NSDictionary dictionaryWithObjectsAndKeys:	credIDString, @"id",
															respDict, @"response",
															@"public-key",@"type",		nil];
					pubKeyDict				=	[NSDictionary dictionaryWithObjectsAndKeys:	credentialEmail,			kEMAIL,
																							regDict,					@"auth_cred", nil];
NSLog(@"%s  pubKeyDict - %@",__FUNCTION__,pubKeyDict);

//Post JSON serialized version of pubKeyDict to host

Xcode log

pubKeyDict - {
    "auth_cred" =     {
        id = "JNMBbZF_PQM7O64RY_MXQqhAyKk";
        response =         {
            authenticatorData = "MEYCIQDgVFINihy9nPuuRrZWkLPahEfLy3huCV9_seOuM5gSlgIhAP_99EVQjXhyJy37ccRxQenSxt7oPNrQ4VjVDSW5Ej8l";
            clientDataJSON = eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoidkhYcTdKTG05Z2dNU1BienNpZThFWUJsVW16V2N3WXlvOFBnT0FKMlR1b0VBbGVBbkQ0NGFNVDg3RG93YnZaZUNxLWxTdC1uNTFkUWJpVzgxNVhZRlEiLCJvcmlnaW4iOiJodHRwczovL2FuY2hvci1hd2F5LmNvbSJ9;
            signature = "WJ3tJNMYfNST99x-EAdNDrnsUOCxqFzvDgXCyTcTg6sdAAAAAA";
        };
        type = "public-key";
    };
    email = "my@email.com";
}
Django Python Host

auth_cred_json - {'id': 'JNMBbZF_PQM7O64RY_MXQqhAyKk', 'response': {'clientDataJSON': 'eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoidkhYcTdKTG05Z2dNU1BienNpZThFWUJsVW16V2N3WXlvOFBnT0FKMlR1b0VBbGVBbkQ0NGFNVDg3RG93YnZaZUNxLWxTdC1uNTFkUWJpVzgxNVhZRlEiLCJvcmlnaW4iOiJodHRwczovL2FuY2hvci1hd2F5LmNvbSJ9', 'signature': 'WJ3tJNMYfNST99x-EAdNDrnsUOCxqFzvDgXCyTcTg6sdAAAAAA', 'authenticatorData': 'MEYCIQDgVFINihy9nPuuRrZWkLPahEfLy3huCV9_seOuM5gSlgIhAP_99EVQjXhyJy37ccRxQenSxt7oPNrQ4VjVDSW5Ej8l'}, 'type': 'public-key', 'rawId': 'JNMBbZF_PQM7O64RY_MXQqhAyKk'}

AuthorizationCredential generated by 'parse_authentication_credential_json'

id='JNMBbZF_PQM7O64RY_MXQqhAyKk'
raw_id=b'$\\xd3\\x01m\\x91\\x7f=\\x03;;\\xae\\x11c\\xf3\\x17B\\xa8@\\xc8\\xa9' response=AuthenticatorAssertionResponse(
	client_data_json=b'{
		"type":"webauthn.get",
		"challenge":"vHXq7JLm9ggMSPbzsie8EYBlUmzWcwYyo8PgOAJ2TuoEAleAnD44aMT87DowbvZeCq-lSt-n51dQbiW815XYFQ",
		"origin":"https://anchor-away.com"
	}',
	authenticator_data=b"0F\\x02!\\x00\\xe0TR\\r\\x8a\\x1c\\xbd\\x9c\\xfb\\xaeF\\xb6V\\x90\\xb3\\xda\\x84G\\xcb\\xcbxn\\t_\\x7f\\xb1\\xe3\\xae3\\x98\\x12\\x96\\x02!\\x00\\xff\\xfd\\xf4EP\\x8dxr'-\\xfbq\\xc4qA\\xe9\\xd2\\xc6\\xde\\xe8<\\xda\\xd0\\xe1X\\xd5\\r%\\xb9\\x12?%",
	signature=b'X\\x9d\\xed$\\xd3\\x18|\\xd4\\x93\\xf7\\xdc~\\x10\\x07M\\x0e\\xb9\\xecP\\xe0\\xb1\\xa8\\\\\\xef\\x0e\\x05\\xc2\\xc97\\x13\\x83\\xab\\x1d\\x00\\x00\\x00\\x00',
	user_handle=None
	)


Replies

Apple's ASAuthentication's. authorizationController:didCompleteWithAuthorization: works just fine during the Passkey Authentication process. The rawAuthenticationData value attached to the ASAuthorizationPlatformPublicKeyCredentialAssertion credential consists of exactly 37 bytes like the WW3 Section 6.1 specification requires. In the case of Passkey Authentication it is only 37 bytes without either attestedCredentialData nor extensions. But during Passkey Registration process, the ASAuthorizationPlatformPublicKeyCredentialRegistration credential's rawAttestionObject does generate attestedCredentialData.

Believe it or not, my error was generated during the creation of the JSON dictionary to be posted during authentication. I mistakenly assigned the Base64URL authenticationString to the signature property and the Base64URL signatureString to the athenticatorData property.

Very stupid typo that cost me about two weeks of debugging work..

  • Sorry to hear it cost you so much time, but I'm glad you were able to solve this!

Add a Comment