Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to deal with discoverable credentials? #1764

Closed
dagnelies opened this issue Jul 6, 2022 · 10 comments
Closed

How to deal with discoverable credentials? #1764

dagnelies opened this issue Jul 6, 2022 · 10 comments

Comments

@dagnelies
Copy link

Hi,

until now, I understood how the normal authentication works:

Browser->Server: get challenge/credentialIds for "john.doe@example.com"
Browser->Server: here is the signed challenge + payload
Server: verifies challenge, signature, payload...

However, I fail to understand how it would work for discoverable credentials.

Browser->Server: get challenge for ???
Browser->Server: here is a signed random challenge for we don't know who yet
Server: verify what ???

Basically, you cannot associate the challenge to a specific user anymore. So, do you ignore the challenge completely? Do you store like a bunch of them? Do you use something like expiring JWTs as challenge?

And as last dumb question: what is the purpose of these discoverable credentials anyway? I mean, it's fairly common to let the user type its username/email or pick from a list trivially filled by autofill.

@Firstyear
Copy link
Contributor

This is probably something for the adoption group.

Anyway, when you do discoverable, the client works out what credential to use. It fills that in (and can pop up with conditional UI stuff). Then in the authentication this contains the client ID which is a unique identifier. You can use that unique identifier to work out the account associated with that credential and then verify the public key.

The idea of them is that the user doesn't need to enter a username at all, they just present their token/credential, and it identifies AND authenticates the user.

If you need worked code examples, look at https://github.com/kanidm/webauthn-rs which is a really complete library. Alternately consider using an existing library.

@dagnelies
Copy link
Author

dagnelies commented Jul 6, 2022

I guess I should paraphrase my problem.

In the default use case, with predetermined username, it's straightforward. When a login attempt occurs, on the server side you associate a challenge to this user, then this challenge is signed using webauthn on the client side, then you verify server side that the signature and challenge match.

However, in the case of discoverable credentials, this association username <-> challenge does not yet exist. So what do you do? Do you maintain some giant pool of anonymous challenges to check from or what? After all, these challenges are there to avoid replay attacks.

@emlun
Copy link
Member

emlun commented Jul 6, 2022

However, in the case of discoverable credentials, this association username <-> challenge does not yet exist. So what do you do? Do you maintain some giant pool of anonymous challenges to check from or what? After all, these challenges are there to avoid replay attacks.

Essentially, yes. If you already know the username before issuing the challenge, then you should of course verify that the response signed the correct challenge for that user. For username-less authentication with a discoverable credential you only really need to verify that the signed challenge was issued recently and hasn't already been used.

In my implementations I've associated each new challenge with a "request ID" which is sent to the client along with the challenge and returned to the server with the signed response. The server then looks up the request ID and removes it from the state map in memory, then verifies that the signed response matches the challenge that was stored for that request ID. This ensures that at most one authentication attempt is allowed for each challenge.

So to paraphrase your first post it's something like this:

Browser->Server: get challenge for ???
Server->Browser: here is challenge X
Browser->Server: here is a signed challenge X + payload for "john.doe@example.com"
Server: verifies challenge X exists in memory, signature valid, public key belongs to "john.doe@example.com", payload...
Server: (even if previous step failed) delete challenge X from memory

@dagnelies
Copy link
Author

Thanks, that answers the question and provides guidance.

@timcappalli
Copy link
Member

Hi @dagnelies, please use FIDO-DEV for implementation / deployment questions. This repo is for spec related work.

@ndpar
Copy link

ndpar commented Jul 6, 2022

@emlun Out of curiosity, can you use the challenge as "request ID"?

@Firstyear
Copy link
Contributor

Firstyear commented Jul 7, 2022

@emlun Out of curiosity, can you use the challenge as "request ID"?

No. You need to bind it to the session for that browser client.

@ndpar
Copy link

ndpar commented Jul 7, 2022

In my implementations I've associated each new challenge with a "request ID" which is sent to the client along with the challenge and returned to the server with the signed response. The server then looks up the request ID and removes it from the state map in memory, then verifies that the signed response matches the challenge that was stored for that request ID. This ensures that at most one authentication attempt is allowed for each challenge.

@Firstyear I don't see why challenge cannot be used as a value for the "request ID" in this particular implementation. Both are opaque unique strings. Surely you can associate C with C and the algorithm still works. I'm just trying to understand why the "request ID" has to be different than the challenge.

@dagnelies
Copy link
Author

I was a bit confused by that too. In the end I noticed I missed the simplest way though. To establish a session directly, via sessionID cookie, and store the challenge server side as part of this session. Done.

@emlun
Copy link
Member

emlun commented Jul 7, 2022

can you use the challenge as "request ID"?

Perhaps, as long as you only allow one authentication attempt per challenge and verify that the correct user owns the signing public key, but I'm not sure there aren't subtle pitfalls.

For any further questions, please continue the discussion on the public-webauthn@w3c.org or fido-dev@fidoalliance.org mail list instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants