Skip to content

Commit

Permalink
made counter optional (fixes Counters don't work on macbook #38)
Browse files Browse the repository at this point in the history
  • Loading branch information
dagnelies committed Nov 27, 2023
1 parent 55567c2 commit f7abde4
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 20 deletions.
40 changes: 27 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ It is an [open source](https://github.com/passwordless-id/webauthn), minimalisti
Try out the playground to see how this library works:

- [Testing Playground](https://webauthn.passwordless.id/demos/playground.html)

---
### *[Testing Playground](https://webauthn.passwordless.id/demos/playground.html)*

Other demos with minial examples:

Expand Down Expand Up @@ -210,8 +208,8 @@ The credential key is the most important part and should be stored in a database
},
```

Please note that unlike traditional systems, a user might have multiple credential keys, one per device.

*Please note that unlike traditional systems, you might allow a user to have multiple credential keys.
For example, if you allow the user to use multiple device-bound keys and/or registering keys for multiple platforms.*


Authentication
Expand Down Expand Up @@ -294,12 +292,15 @@ const expected = {
challenge: "56535b13-5d93-4194-a282-f234c1c24500", // whatever was randomly generated by the server.
origin: "http://localhost:8080",
userVerified: true, // should be set if `userVerification` was set to `required` in the authentication options (default)
counter: 0 // for better security, you should verify the authenticator "usage" counter increased since last time
counter: 123 // Optional. For device-bound credentials, you should verify the authenticator "usage" counter increased since last time.
}
```

> On iOS/MacOS, the `counter` value for the first authentication will be 0, while on Android/Windows the `counter` will start with 1.
> So using a counter value of `-1` seems best to cover the initial case. Please note that the specs do not mandate "+1" increases, it could theoretically increase by any amount.
Regarding the `counter`, it might or might not be implemented by the authenticator.
Typically, it's implemented by hardware-bound keys to detect and avoid the risk of cloning the authenticator and starts with 1 during registration.
On the opposite, for password managers syncing keys in the cloud, the counter is typically always 0 since in that case cloning is a "feature".
For example, device-bound keys on Android and Windows do have an increasing `counter`, USB security keys also, while MacOS/iOS do not.
Lastly, please note that the specs do not mandate "+1" increases, it could theoretically increase by any amount.

Often, it might also be more practical to use functions to verify challenge or origin. This is possible too:

Expand All @@ -308,7 +309,7 @@ const expected = {
challenge: async (challenge) => { /* async call to DB for example */ return true },
origin: (origin) => listOfAllowedOrigins.includes(origin),
userVerified: true, // no function allowed here
counter: 0 // no function allowed here
counter: 123 // optional, no function allowed here
}
```

Expand All @@ -334,8 +335,7 @@ Otherwise, your implementation might become vulnerable to replay attacks.

### There can be multiple credentials per user ID

Unlike traditional authentication, you can have multiple public/private key pairs per user: one per device.

Unlike traditional authentication, you might have multiple credential keys per user.

### Authentication does *not* provide `username` out of the box

Expand All @@ -344,9 +344,23 @@ Only `credentialId` is provided during the authentication.
So either you maintain a mapping `credentialId -> username` in your database, or you add the `username` in your frontend to backend communication.


### Let the platform choose the user
### Passkeys a.k.a "discoverable" credentials

If the credential is [discoverable](https://w3c.github.io/webauthn/#client-side-discoverable-public-key-credential-source), the credential id and user information is kept on the system. Although you can "discourage" it, there is no option in the spec to "forbid" it. For example, iOS always use "discoverable" keys, even if the option is set to `"discouraged"`

Afterwards, you can use `client.authenticate([], ...)` without specifying credential IDs. In that case, the platform will pop-up a default dialog to let you pick a user and perform authentication. Of course, the look and feel is platform specific.


### Disable synced credentials

That is sadly impossible, the spec authors refuse to add an option to disable syncing.

See:

- https://github.com/w3c/webauthn/issues/1714
- https://github.com/w3c/webauthn/issues/1739

You can *not* specify any credential ids during authentication. In that case, the platform will pop-up a default dialog to let you pick a user and perform authentication. Of course, the look and feel is platform specific.
Note that in practice, and although not precised by the specs, the "discoverable" property has an impact on syncing in the cloud. As of end 2023, for Microsoft and Android, when marked as `discoverable: "discouraged"`, they are not synced in the cloud ...but still discoverable. Since this is not covered by the specs, this might change in the future.


### This library simplifies a few things by using sensible defaults
Expand Down
Loading

0 comments on commit f7abde4

Please sign in to comment.