Skip to content

Commit 2a496fb

Browse files
coroiutrmartin4
andauthored
Add new FIDO2/Passkey deep dive (#290)
* add introduction to passkeys * add glossary * add information about how the browser extension works * update titles to not take so much space in the menu * add introduction to the browser extension * add generic FIDO2 architecture overview * sort custom words * shorten browser extension title in menu * add naming conventions * use monospace in RP note * add browser extension architecture * reorganize * add new credentials page * remove unused SVG sprites * fix broken links * finish credentials page * add passkey provider overview page * change document to page * add operations page * change ceremony to operation * clarify custom webauthn api * add RP overview * add information about user presence and verification * clarify deep-dive intention * remove remark-deflist * clarify the definition of a credential * fix 'storage' being used too much * clarify storage * minor nits and fixes * always use CTAP2 * Added PRF extension docs. * Explain the thinking behind Fido2 and WebAuthn convention * Storage vs Discoverability matrix * Various smaller fixes after review * fix: login -> log in everywhere * fix: many small PR comments * chore: move overview into root page * chore: move implementation overviews into their respective roots * fix: remove capitalization in headings * fix: broken link * fix: update extension UP to reflect recent changes * fix: limit dt and dd css rule scope * feat: add links to specifications * feat: simplify info block * fix: remove empty page * fix: clarify reference * fix: add links to various references * feat: improve diagram to reflect js app duality * feat: update implementation details Discoverabilty, user presence and user verification was moved from the general provider page to the browser extension page. This better reflects that upcoming MAUI and native apps might actually deviate from this (e.g. MAUI apps will release with full support for UV). * fix: title case * fix: log in -> login --------- Co-authored-by: Todd Martin <[email protected]>
1 parent f5caf98 commit 2a496fb

File tree

13 files changed

+753
-1
lines changed

13 files changed

+753
-1
lines changed

custom-words.txt

+4-1
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ bytemark
77
clickjacking
88
CODEOWNERS
99
CQRS
10+
CTAP2
1011
dockerized
1112
Gitter
13+
HKDF
1214
hotfix
1315
hotfixed
1416
hotfixes
@@ -61,4 +63,5 @@ Xcodes.app
6163
xmldoc
6264
Yellowpages
6365
Yubico
64-
YubiKey
66+
YubiKey
67+
YubiKeys
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
---
2+
sidebar_position: 2
3+
---
4+
5+
# Credentials
6+
7+
## What is a credential?
8+
9+
In general, FIDO2 works by using public/private key pairs that are generated by a FIDO2
10+
authenticator. The private key is protected by the authenticator, and the public key is stored
11+
server-side by the Relying Party (RP). The authenticator uses the private key to sign challenges
12+
from a RP, and the RP uses the public key to verify the signature.
13+
14+
According to the [WebAuthn specification](https://www.w3.org/TR/webauthn-3/#public-key-credential),
15+
a credential is data one entity presents to another in order to authenticate the former to the
16+
latter. What the term "credential" refers to is generally determined by context, and can be one of
17+
the following:
18+
19+
<dl>
20+
<dt>A public key credential source</dt>
21+
<dd>The data structure that is protected by the authenticator,
22+
including the private key, and any metadata that is associated with it. For more information see
23+
[Public Key Credential Source](https://www.w3.org/TR/webauthn-3/#public-key-credential-source).</dd>
24+
25+
<dt>The attested credential public key (corresponding to a public key credential source)</dt>
26+
<dd>The public key that is stored server-side by the RP.</dd>
27+
28+
<dt>An authentication assertion</dt>
29+
<dd>The data structure that contains a cryptographic signature.</dd>
30+
</dl>
31+
32+
In this documentation, the term "credential" refers to the **public key credential source** i.e. the
33+
private key plus metadata.
34+
35+
## Data structure
36+
37+
### Required fields
38+
39+
Credentials are relatively simple data structures and only require the following fields:
40+
41+
- `credentialId` - Binary data sequence that uniquely identifies the credential. It is either a
42+
random value, or an encrypted version of the credential (see [Storage Modes](#storage-modality)).
43+
- `rpId` - The identifier of the RP for which the credential was created. By default assigned the
44+
effective domain of the application's origin (ex: bitwarden.com).
45+
- `privateKey` - Private key material, created when generating the public/private key pair.
46+
47+
### Notable optional fields
48+
49+
- `userHandle` - Binary data sequence that uniquely identifies the user account. This data is opaque
50+
for anyone other than the RP. It is used to associate a credential with a specific user account.
51+
- `userName` - A human-readable name for the user account. This can be used to display the user
52+
account in the authenticator's user interface (if supported).
53+
54+
## Storage modality
55+
56+
### Client-side credentials
57+
58+
The credential is stored in persistent storage embedded in the authenticator, client, or client
59+
device. This is rapidly becoming a common storage mode, as it is the only way to support synced
60+
credentials across multiple devices. Client-side storage is required.
61+
62+
Hardware security keys also support this storage mode, but they often have a limited capacity (e.g.
63+
the YubiKey 5 can hold up to 25 client-side credentials).
64+
65+
### Server-side credentials
66+
67+
The credential is encrypted by the authenticator and stored in a database managed by the RP. This is
68+
the traditional storage mode and is often used in 2FA flows.
69+
70+
This enables the authenticator to have unlimited storage capacity for credentials, since the data is
71+
stored by the RP instead of by the authenticator. However, this means that a credential stored in
72+
this way must be retrieved from the RP before the authenticator can use it.
73+
74+
## Discoverability
75+
76+
### Non-discoverable credentials
77+
78+
This is a credential whose `credentialId` must be provided in `allowCredentials` when calling
79+
`navigator.credentials.get` because it is not client-side discoverable. This is always the case when
80+
the credential is stored server-side, because the RP must first identify the user in order to
81+
discover the `credentialId` to supply in the `navigator.credentials.get` call.
82+
83+
### Client-side discoverable credentials
84+
85+
:::info
86+
87+
These credentials have been previously known as resident credentials or resident keys. Due to the
88+
phrases `ResidentKey` and `residentKey` being widely used in both the WebAuthn API and also in the
89+
Authenticator Model (e.g. in dictionary member names, algorithm variable names, and operation
90+
parameters) the usage of resident within their names has not been changed for backwards
91+
compatibility purposes.
92+
93+
:::
94+
95+
This credential is usable in authentication operations where the RP does not provide any
96+
`credentialId`, meaning that the RP does not need to identify the user first.
97+
98+
As a result, an authenticator that can handle discoverable credentials can create assertion
99+
signatures using just an `rpId`. This enables single-click authentication flows where the returned
100+
assertion data contains everything the RP needs to identify and authenticate the user.
101+
102+
This implies that the source of the credential must be stored either in the authenticator or the
103+
client platform (see [Client Side Credentials](#client-side-credentials)).
104+
105+
## Storage vs discoverability matrix
106+
107+
As described above, there is a relationship between storage modality and discoverability. This can
108+
be condensed into the matrix below, which shows the supported combinations of the two.
109+
110+
| | Non-Discoverable | Discoverable |
111+
| --------------- | ---------------- | ------------ |
112+
| **Client-Side** | Supported | Supported |
113+
| **Server-Side** | Supported | - |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
sidebar_position: 6
3+
---
4+
5+
# Glossary
6+
7+
This page contains some definitions of terms used throughout the passkey documentation. For a more
8+
comprehensive list, see the
9+
[FIDO Alliance Glossary](https://fidoalliance.org/specs/common-specs/fido-glossary-v2.1-rd-20210525.html).
10+
11+
## Definitions
12+
13+
<dl>
14+
<dt>Passkey</dt>
15+
<dd>Any passwordless FIDO2 credential.</dd>
16+
17+
<dt>Credential</dt>
18+
<dd>The technical term for a passkey. The two terms are used interchangeably.</dd>
19+
20+
<dt>Relying Party (RP)</dt>
21+
<dd>A web site or other entity that uses a FIDO2 protocol to authenticate users by asking for a passkey.</dd>
22+
23+
<dt>Passkey Provider (PP)</dt>
24+
<dd>An app and/or service that is responsible for storing and managing passkeys. Many operating systems include a default passkey provider (first-party), and many also support third-party providers (like Bitwarden).</dd>
25+
26+
<dt>Attestation</dt>
27+
<dd>The process used to create a new passkey.</dd>
28+
<dd>The process of communicating a cryptographic assertion to a relying party that a key presented during authenticator registration was created and protected by a genuine authenticator with verified characteristics.</dd>
29+
30+
<dt>Attestation Statement</dt>
31+
<dd>An optional statement about the exact type (make/model) of the authenticator and its capabilities.</dd>
32+
<dd>An optional statement provided by an authenticator which can be used by a Relying Party to identify and verify the provenance of the authenticator. Not to be confused with "attestation" which can be performed without providing a statement about the provenance of the authenticator.</dd>
33+
34+
<dt>Assertion</dt>
35+
<dd>The process used to log in.</dd>
36+
<dd>The act of proving ownership of the private key, commonly referred to as authentication.</dd>
37+
38+
<dt>Fallback</dt>
39+
<dd>Aborting the Bitwarden-side of the authentication process and handing off control to the native browser implementation of WebAuthn, enabling the user to use hardware security keys, etc.</dd>
40+
</dl>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
sidebar_position: 5
3+
---
4+
5+
# Implementations
6+
7+
Bitwarden uses passkeys to provide users with a secure and convenient alternative to passwords. In
8+
this context Bitwarden can act as both a Relying Party (RP) and a Passkey Provider (PP). In other
9+
words: You can use passkeys to log in to Bitwarden, and you can use Bitwarden to generate and store
10+
passkeys for logging into other applications.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# Browser extension
2+
3+
Bitwarden supports passkeys in all browser by implementing the WebAuthn API. The browser does not
4+
need to support WebAuthn natively, the extension will polyfill if necessary, effectively adding
5+
support for passkeys to any browser.
6+
7+
## Compatibility
8+
9+
There are currently no browser APIs that allow extensions to provide passkeys alongside the
10+
browser's native implementation. This means that the only way an extension can provide passkeys is
11+
by completely replacing the native implementation with its own. This is done by injecting a script
12+
into the page that replaces the native implementation with the extension's implementation. In
13+
practice, this is done by reassigning the `navigator.credentials.create` and
14+
`navigator.credentials.get` methods.
15+
16+
To avoid implementing support for the entire FIDO2 ecosystem (hardware keys, CaBLE, etc.), the
17+
extension retains the ability to trigger the native implementation (usually referred to as a
18+
"fallback"), if the user chooses to not proceed with Bitwarden. In practice this is done by storing
19+
references to the native functions in separate variables before reassigning.
20+
21+
## Architecture
22+
23+
Bitwarden implements a simplified version of the FIDO2 architecture based solely on the
24+
[WebAuthn API specification](https://www.w3.org/TR/webauthn-3/). This is because the embedded
25+
`FIDO2 Authenticator` will never be used in a standalone context and does not need to support the
26+
full CTAP2 protocol.
27+
28+
:::info
29+
30+
`FIDO2 Client` is analogous to `WebAuthn Client` in the [FIDO2 Overview](../..#diagram), but is
31+
named differently due to naming conflicts with the RP implementation also in the Bitwarden codebase.
32+
See [Naming Convention](../../naming-convention) for more information.
33+
34+
:::
35+
36+
```kroki type=plantuml
37+
@startuml
38+
skinparam BackgroundColor transparent
39+
skinparam componentStyle rectangle
40+
41+
title Browser extension FIDO2 architecture overview
42+
43+
component "Browser" {
44+
component "Web page" {
45+
component "JavaScript Application"
46+
component "Page Script" <<Injected>> {
47+
component "Custom WebAuthn API"
48+
component "Messenger" as pageScriptMessenger
49+
50+
[Custom WebAuthn API] -> [pageScriptMessenger]
51+
}
52+
53+
component "Content Script" {
54+
component "Messenger" as contentScriptMessenger
55+
}
56+
57+
component "User Agent WebAuthn API" <<Native>>
58+
59+
[JavaScript Application] -> [Custom WebAuthn API]
60+
[Custom WebAuthn API] --> [User Agent WebAuthn API]
61+
}
62+
63+
component "Extension" {
64+
component "Background Script"
65+
component "FIDO2 Client"
66+
component "FIDO2 Authenticator"
67+
component "FIDO2 User Interface"
68+
database "Vault"
69+
70+
[FIDO2 Client] -> [FIDO2 Authenticator]
71+
[FIDO2 Authenticator] -> [Vault]
72+
[FIDO2 Authenticator] --> [FIDO2 User Interface]
73+
}
74+
75+
[pageScriptMessenger] <--> [contentScriptMessenger]
76+
[contentScriptMessenger] --> [Background Script]
77+
[Background Script] -> [FIDO2 Client]
78+
}
79+
@enduml
80+
```
81+
82+
## Discoverability
83+
84+
The browser extension respects discoverability requirements from RPs by saving a client-side
85+
discoverability flag in the passkey metadata (called `discoverable`). If this flag is set to
86+
`false`, the RP will need to provide the `credentialId` to Bitwarden in order to perform an
87+
assertion. If the flag is set to `true` the passkey will be discoverable using the `rpId`. The
88+
`userHandle` is always returned if available.
89+
90+
## User presence
91+
92+
The browser extension always requires user presence. This means that Bitwarden will never respond to
93+
a request without guaranteeing that the user has first interacted with their device somehow, by
94+
confirming or denying the request.
95+
96+
## User verification
97+
98+
The browser extension does not yet fully support user verification. This is a limitation of the
99+
existing user verification services. Full support for user verification will be added soon, via the
100+
user's unlock methods, such as a PIN or biometric unlock, with the master password as a fallback.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
title: Provider
3+
sidebar_position: 1
4+
---
5+
6+
# Overview
7+
8+
Bitwarden can act as a Passkey Provider to generate and store passkeys for other applications.
9+
10+
## Storage
11+
12+
Passkeys are stored alongside and in the same way as passwords, using the same encryption and
13+
security. This includes both the private key and all related metadata. This data can be stored in
14+
both personal and organization vaults.
15+
16+
Bitwarden only supports the client-side storage modality.
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
title: Relying Party
3+
sidebar_position: 1
4+
---
5+
6+
# Overview
7+
8+
Bitwarden can act as a Relying Party (RP) for passkeys. This means that you can log in to Bitwarden
9+
using a passkey. Bitwarden supports passkeys as a second factor of authentication (2FA) and as a
10+
primary factor of authentication. When used as a primary factor of authentication, Bitwarden will
11+
not require you to enter a username or password when logging in. In cases where your passkey
12+
[supports encryption](prf.md), your passkey will be able to decrypt your data as well. If your
13+
passkey does not support encryption, Bitwarden will prompt you to enter a password to unlock your
14+
vault.
15+
16+
## Implementations
17+
18+
Bitwarden currently has two implementations for authenticating users using passkeys:
19+
20+
- Older 2FA "WebAuthn" implementation
21+
- Newer "Log in with Passkeys" implementation
22+
23+
Both implementations use the same FIDO2 technologies, but are completely separate and share almost
24+
no code. From a user perspective "Log in with Passkeys" is a first-factor authentication method,
25+
while the older "WebAuthn" implementation is a second-factor. "Log in with Passkeys" takes advantage
26+
of FIDO2 User Verification and therefore completely replaces the other existing 2FA methods.
27+
28+
The 2FA implementation will eventually be consolidated with the newer "Log in with Passkey"
29+
implementation.
30+
31+
## Storage modality
32+
33+
- The 2FA "WebAuthn" implementation supports both client-side and server-side storage mode.
34+
- The "Log in with Passkeys" implementation only supports the client-side storage mode.
35+
36+
## Discoverability
37+
38+
- The 2FA "WebAuthn" implementation does not support discoverability, as it is a second-factor
39+
authentication method and supports the server-side storage mode.
40+
- The "Log in with Passkeys" implementation requires discoverable credentials.

0 commit comments

Comments
 (0)