Skip to content

Auth Passkey

Ben Bucksch edited this page Dec 10, 2024 · 19 revisions

Browser

Signup

  1. User creates an account at the site, or goes to his account settings | Add passkey
  2. Webpage of site calls browser API credentials.create() to create a new passkey.
  3. Browser passes on these parameters as-is and calls authenticator (meaning the password manager, typically built into the OS) using a native OS function.
  4. Authenticator creates a new public/key pair, stores it for this site, and returns the public key. This is the "passkey".
  5. Browser returns the passkey as-is as JSON string to the webpage.
  6. Webpage sends it to the server, using a site-specific API.
  7. Site stores the passkey for that user. From now on, the user can authenticate using that same passkey, on this site.

Auth

  1. User is on login webpage of site
  2. User enters or selects the username / email address to login as. (Optional: The webpage queries the browser about which passkeys were stored for this site. TODO correct?)
  3. Webpage calls server using site-specific API, with the username, for a passkey challenge
  4. Site retrieves passkey stored for that username, and returns a challenge for that passkey and that site. The challenge contains a random string and the expected public key.
  5. Webpage passes on that challenge as-is and calls browser API credentials.get() with the challenge.
  6. The browser passes on that challenge as-is calls the authenticator (meaning the password manager, typically built into the OS) using a native OS function.
  7. The authenticator uses the public key to find the private key in its internal storage, and cryptographically signs the challenge with that key.
  8. The browser passes on that response as-is as JSON string to the webpage
  9. The webpage returns the response as-is to the server using a site-specific API
  10. The site compares the signature and challenge with the challenge and public key it sent in step 4. If the signature is correct, this proves that the user was in possession of the private key, and he is authenticated.

OS authenticator API

The browser calls the following OS native functions of the authenticator to respond to get() and create(). The browser essentially simply passes on the information from the webpage to the authenticator, and back.

Signup

Auth

We should build a library around those calls. It would also allow other authenticators to be used, depending on user choice of authenticator software.

SASL

Initial login to IMAP using the SASL passkey mechanism proposed here.

Signup and passkey creation happens at the normal web frontend of the site. We assume that the browser will use the same authenticator (OS functions) as the mail app. The authenticator may also sync the passkeys between the user's devices.

Login to IMAP in the mail app happens only using passkeys, without web browser.

Initial Auth using Passkey

  1. The mail app has the IMAP server and username configured. We assume that username == email address.
  2. Mail app opens connection to IMAP server and starts login using the SASL passkey mechanism:
S: * OK ACME IMAP Server v1.23 is ready
C: 22 CAPABILITY
S: 22 CAPABILITY IMAP4 IMAP4rev1 IMAP4rev2 AUTH=PASSKEY AUTH=REMEMBERME
C: 23 AUTHENTICATE PASSKEY [email protected]
S: AEC6576576557=== (passkey challenge)
C: EAB675757GJvYgB== (passkey response)
S: 23 OK AUTHENTICATE completed
  1. During the SASL exchange, the mail app takes the challenge that the server sends and passes it on as-is to the OS authenticator API. The OS calls are the same that the browser does, as documented under Auth above. The mail app then passes on the result as-is with base64 encoding to the server as SASL response.

Re-login

The passkey authentication requires explicit approval from the end user. The authenticator will typically ask for a fingerprint, FaceID, or the unlock method for the phone. We obviously cannot ask that for every IMAP connection. So, there needs to be a way to re-login to the same IMAP server and account, after the passkey login worked.

We have multiple ways to achieve that. We should pick the best and standardize that.

Rememberme

  1. We ask the IMAP server for a token, using a new IMAP command:
C: 24 REMEMBERME
S: 24 OK [TOKEN] 66757657658576476
...
C: 99 BYE
  1. During the above exchange, the server generated a string which it will accept as login. This may be a random string that it saved in a database, or it may be a JWT token or other token that contains the username and is signed with the server's key, or it may be an app password that is generated via a REST API to the site's OAuth2 server.

  2. The mail app stores that token in its own password manager (instead of a password), for that user account.

  3. The mail app needs to open a new IMAP connection, either to open multiple IMAP connections, or to re-connect after a network drop, or to re-login the next day. We use the token that the server gave us to re-login:

S: * OK ACME IMAP Server v1.23 is ready
C: 22 CAPABILITY
S: 22 CAPABILITY IMAP4 IMAP4rev1 IMAP4rev2 AUTH=PASSKEY AUTH=REMEMBERME
C: 23 AUTHENTICATE REMEMEBERME 66757657658576476
S: 23 OK AUTHENTICATE completed
  1. The server validates the token, depending on the method chosen in step 2: either by checking the token in the database, or by verifying the signature on the JWT token or other token, or by checking the app password with the site's OAuth2 server. If valid, the user is logged in.

TLS Exported Authentication

TLS Token Binding

  • Wikipedia
  • RFC 8471
  • TODO I am not sure that I understood TLS Token Binding correctly.
  • TODO Is this implemented by TLS libraries? Are the APIs exposed by the common language libs?
  1. The mail app creates a private and public key and stores them in its password manager.
  2. The mail app sends the public key to the IMAP server using a new IMAP command for TLS Token Binding:
C: 24 TLSTOKENBINDING
S: 24 OK [TOKEN] EAB78656756756757565675756
...
C: 99 BYE
  1. The server stores the public key for this user account.
  2. The mail app needs to open a new IMAP connection. It uses the private key to sign that request:
S: * OK ACME IMAP Server v1.23 is ready
C: 22 CAPABILITY
S: 22 CAPABILITY IMAP4 IMAP4rev1 IMAP4rev2 AUTH=PASSKEY AUTH=TLSTOKENBINDING
C: 23 AUTHENTICATE TLSTOKENBINDING EAB78656756756757565675756
S: 23 OK AUTHENTICATE completed
  1. The server validates the token, using its TLS library, by checking that the signature is done with the public key stored by the server for this user account. If valid, the user is logged in.