WebAuthn authenticator attestation response id and rawId

1k views Asked by At

I would like to ask a question regarding id and rawId.

When implementing webauthn, in the authenticator attestation response, I see that we have both id and rawId . Reading the spec (https://www.w3.org/TR/webauthn-1/#dom-publickeycredential-rawid) , id is base64url(rawId)

{
  "type": "public-key",
  "id": "AV1--2gCLXLF9_5bGWDwZn6FP_OqAWfKY74mckatWMgN65o5OW8q2k9XVbYl8kAqPtpEoBlM0opKEjwDqYRBDIYbAl058O8ZQWS-r0M0L-9ikcu3tKuMxfFnRZ9gU6tnDH6QqzYwUg",
  "rawId": "AV1--2gCLXLF9_5bGWDwZn6FP_OqAWfKY74mckatWMgN65o5OW8q2k9XVbYl8kAqPtpEoBlM0opKEjwDqYRBDIYbAl058O8ZQWS-r0M0L-9ikcu3tKuMxfFnRZ9gU6tnDH6QqzYwUg",
  "response": {
    ...
  }
  ...
}

So here is my questions:

  • why we need id
  • why in the example above, my id is exactly like rawId?
    • does it's always the same? If it does, can we get rid of id in the response?
  • If I save the public key id to the database (later use that public key id to create the allowCredentials list), which one below should I follow?
    1. save id in the database, use id to create allowCredentials list
    2. save base64url(rawId) in the database, then use that base64 url encoded value to create allowCredentials list
2

There are 2 answers

0
truongnm On BEST ANSWER

Q1. Why we need id

The answer to why we have both id and rawId is because it's by design in the spec:

id is inherited from Credential, though PublicKeyCredential overrides Credential's getter, instead returning the base64url encoding of the data contained in the object’s identifier internal slot.
https://www.w3.org/TR/webauthn-2/#iface-pkcredential

Q2. why in the example above, my id is exactly like rawId? does it's always the same? If it does, can we get rid of id in the response?

We get back raw bytes for rawId, which must be compared to the bytes encoded in id as part of the initial verification of the registration response, so technically we can't drop it.

Besides, for whatever reason, if base64url encoding is inappropriate, RP might use unencoded raw value so rawId is returned as well.

Q3. If I save the public key id to the database (later use that public key id to create the allowCredentials list), which one below should I follow?

It's up to RP implementation, as said above, for whatever reason, if base64url encoding is inappropriate, RP might use unencoded raw value and encode it to whatever format RP prefers.

I store id because it's already encoded into a format that easy to copy/paste and compare in logs when troubleshooting. Later we will use that id to create the allowCredentials and send it back to the authenticator. No need to do the extra work of base64url(rawId), use the id is perfectly fine.

0
mackie On

In this case it's likely that the ArrayBuffer containing rawId has been converted to base64 for serialization purposes, hence why they look identical in your example.

As for which to use, I don't think it matters as long as the value is presented in the correct encoding or structure at the point of needing it.

The library I'm using provides and expects a byte[] but I store it as base64 in the DB. This is then converted to base64 again when serialized to send the value to the client side and then converted back to an ArrayBuffer on the client side before passing to navigator.credentials.get().

As for why it's like this, I suspect there was a usecase where converting id from a base64 string was not desirable so the PublicKeyCredential type added it's own ArrayBuffer representation of the [[identifier]] slot but that's just my conjecture.