I am playing with a YubiKey and JavaScript in-browser WebAuthn APIs. I am trying a very minimal "Hello, World!" example which just tries to do a minimal: navigator.credentials.create
and then a navigator.credentials.get
(with minimal, even hard-coded values to just demonstration.
The create
works fine - but the get
always results in an error:
This security key doesn't look familiar.
I am having a hard time understanding what even theoretically might cause this. For example, to my knowledge the YubiKey doesn't actually store credentials on it - but rather requires the server to send back an encrypted opaque bundle containing the key.
But the get
API only requires a cryptographic challenge
. (I think this is just random data from the server, and not the encrypted key?) (I am basically sending all zeros for the challenge, for now).
Examples show the allowCredentails
(which is an optional field) sending one (or more) ID's back. It make sense that if the IDs themselves were the encrypted keys, it should use those. (But they're again optional) and I still get this error even if I specify the same Id (in a ByteArray) which was returned from the create
, or if I give NO Id's whatsoever.
So what could the problem be? Is it that I'm supposed to be sending something with the challenge? Is my id
requried, but perhaps malformed? I've noticed other examples sending many allowCredential
IDs (I have no idea where these are coming from, because only one matches the one from it's original enrollment).
Is there something I am fundamentally missing here?
The answer appears to be (from the best I could resolve):
Yubikey does not store credentials, so the ID (passed back from the server during login) must encode the (encrypted private key) credentials - to be handed back to they authenticator (Yubikey) itself. If this isn't done (correctly) you will get an error saying the key is not recognized. Since the spec allows for you to pass no allowCredentials
back in the get
(login) phase - I would assume this means there may be a facility for an authenticator to store a key on it - though this is not commonly done, (or by Yubikey)?
Either way - in my case, the ID must be passed back, and correctly. My particular issue was that the ID returned during create is expressed as string - which is kind of base64 encoded, but has some oddities in the encoding - and when I was converting it back to an ArrayBuffer for the get
- I didn't get it exactly right.
The nonstandard base64 format needs some characters to be replaced before the base64 conversion:
thing = thing.replace(/-/g, "+").replace(/_/g, "/");