I've tried over and over to get passkey auth to show the rp name instead of the rp id. Initially I had my backend url (rp id) set to a subdomain (api.project.ca) and my frontend was projectname.project.ca. I read somewhere that a subdomain mismatch might cause chrome to be overzealous in its protectiveness of the user and display the rp id as a precaution, but after reworking my infrastructure to throw a CDN in front of it and route it so all traffic goes to projectname.project.ca, it STILL won't show the rp name. Same in Edge. So idk what I'm doing wrong here, it seems like it should be a simple thing. I'm not 100% sure what code is even relevant here, but here's a bit of the angular service I have running the navigator.create:
public register(
email: string,
attestOptions: AttestationOptions,
): Observable<FidoState> {
return new Observable((observer) => {
observer.next(FidoState.PendingBrowserPasskey);
const options = this.mapAttestOptions(attestOptions);
navigator.credentials
.create({
publicKey: options,
})
.then((cred) => {
const credential = cred as PublicKeyCredential;
observer.next(FidoState.PendingRegistration);
this.completeWebAuthnRegistration(email, credential, observer);
return credential;
})
.catch((error) => {
console.error('Error getting attestation options', error);
observer.next(FidoState.Error);
});
});
}
private mapAttestOptions(
options: AttestationOptions,
): PublicKeyCredentialCreationOptions {
const challengeArray = coerceToArrayBuffer(options.challenge);
const userId = coerceToArrayBuffer(options.user.id);
const publicKey: PublicKeyCredentialCreationOptions = {
...options,
attestation: 'direct',
challenge: challengeArray,
user: {
...options.user,
id: userId,
},
authenticatorSelection: {
requireResidentKey: false,
userVerification: 'discouraged',
},
excludeCredentials: options.excludeCredentials.map((cred) => ({
type: 'public-key',
id: coerceToArrayBuffer(cred.id),
})),
pubKeyCredParams: options.pubKeyCredParams.map((param) => ({
type: 'public-key',
alg: param.alg,
})),
};
return publicKey;
}
The attestOptions is a json object I get from my C# backend in the code snippet below. I'm using the Fido2 v3.0.1 nuget package:
public async Task<AttestationOptionsResponse> GetAttestationOptions(string email)
{
Fido2User fidoUser = new()
{
Name = email,
DisplayName = email,
Id = Encoding.UTF8.GetBytes(email)
};
User? user = await db.Users.Include(u => u.Credentials).FirstOrDefaultAsync(u => u.Email == email);
List<PublicKeyCredentialDescriptor> storedCredentials =
[.. db
.Credentials
.Include(c => c.User)
.Where(c => c.User!.Email == email)
.Select(c => new PublicKeyCredentialDescriptor(c.CredentialId))];
CredentialCreateOptions options = fido.RequestNewCredential(fidoUser, storedCredentials, AuthenticatorSelection.Default, AttestationConveyancePreference.None);
return !await cache.StoreItemAsync(string.Format(CacheAttestStringFormat, email), options.ToJson())
? new AttestationOptionsResponse
{
Success = false,
ErrorCode = AttestationOptionsError.CacheError
}
: new AttestationOptionsResponse
{
Success = true,
Data = options.ToJson()
};
}
At this point, the registration technically works, but I really want the popup to show a friendly name, rather than a URL, and as per the MDN documentation, this should work. Can anyone tell me what I'm doing wrong here? Thanks for the help!
I originally tried with the rp id being a subdomain of the request URL, but changed it to fully match. The browser still shows the rp id, but I expect it to show the rp name.