Fly with Passkey × AWS Cognito = Passquito!
A PoC on passkey authentication inspired by aws-samples/amazon-cognito-passwordless-auth
.
Features:
- Rust × AWS Lambda → Snappy cold start!
- AWS Cognito Lambda triggers
- 💩 Ugly codebase
The usage scenarios consist of two major parts:
The registration scenarios have two variations:
The following sections in Web Authentication: An API for accessing Public Key Credentials Level 3 are recommended to read for better understanding of the scenarios:
-
Your app provides a form for a user to sign up.
-
The user fills the form with the username and the display name.
Neither the username nor the display name are necessarily unique. They are provided for the user to locate the passkey in user's device.
-
The user hits the sign up button.
-
Your app POSTs the username and the display name to the registration start endpoint (
/registration/start
). -
The registration start endpoint generates a unique ID for the user → the user ID.
-
The registration start endpoint creates a public key credential creation options which includes a challenge.
The user handle of the public key credential creation options is the user ID.
-
The registration start endpoint stores a new registration session in the session store.
The registration session includes the following parameters:
- session ID: the primary key
- user ID
- username
- display name
- challenge
-
The registration start endpoint returns the session ID and public key credential creation options to your app.
-
Your app initiates a public key creation with the public key credential creation options.
-
The user authorizes the public key creation.
-
User's authenticator creates a new key pair (a private key and a public key).
-
User's authenticator signs the challenge with the private key → the signature.
-
User's authenticator returns a public key credential which includes the public key and the signature to your app.
-
Your app POSTs the session ID and the public key credential to the registration finish endpoint (
/registration/finish
). -
The registration finish endpoint pops the registration session associated with the session ID from the session store.
-
The registration finish endpoint verifies the public key credential.
The following parameters are involved in the verification:
- challenge
- public key
- signature
-
The registration finish endpoint creates a new Cognito user with the following attributes:
username
: user IDpreferred_username
: usernamename
: display name
The Cognito user is provided with a random password, which is confirmed upon creation; i.e., the user never faces it.
-
The registration finish endpoint stores the public key along with the following parameters in the credential store:
- user ID: the primary key
- The credential ID of the public key: the primary key
- The sub attribute of the Cognito user
-
The registration finish endpoint returns an empty OK response to your app.
Sequence diagram:
sequenceDiagram
actor User
participant Authenticator
participant YourApp
participant RegistrationStartEndpoint
participant RegistrationFinishEndpoint
participant SessionStore
participant CredentialStore
participant Cognito
YourApp-)User: Provide the form
User-)YourApp: Fill the form
User-)YourApp: Hit the sign up button
activate YourApp
YourApp->>RegistrationStartEndpoint: POST /registration/start
activate RegistrationStartEndpoint
RegistrationStartEndpoint->>RegistrationStartEndpoint: Generate a user ID
RegistrationStartEndpoint->>RegistrationStartEndpoint: Create a public key credential creation options
RegistrationStartEndpoint->>SessionStore: Store a registration session
activate SessionStore
SessionStore-->>RegistrationStartEndpoint: Registration session
deactivate SessionStore
RegistrationStartEndpoint-->>YourApp: Session ID, public key credential creation options
deactivate RegistrationStartEndpoint
YourApp-)Authenticator: Initiate a public key creation
activate Authenticator
deactivate YourApp
Authenticator-)User: Ask for the authorization
User-)Authenticator: Authorize the public key creation
Authenticator->>Authenticator: Create a new key pair
Authenticator->>Authenticator: Sign the challenge
Authenticator-)YourApp: Public key credential
activate YourApp
deactivate Authenticator
YourApp->>RegistrationFinishEndpoint: POST /registration/finish
activate RegistrationFinishEndpoint
RegistrationFinishEndpoint->>SessionStore: Pop the registration session
activate SessionStore
SessionStore-->>RegistrationFinishEndpoint: Registration session
deactivate SessionStore
RegistrationFinishEndpoint->>RegistrationFinishEndpoint: Verify the public key credential
RegistrationFinishEndpoint->>Cognito: Create a new Cognito user
activate Cognito
Cognito-->>RegistrationFinishEndpoint: Cognito user
deactivate Cognito
RegistrationFinishEndpoint->>CredentialStore: Store the public key
activate CredentialStore
CredentialStore-->>RegistrationFinishEndpoint: OK
deactivate CredentialStore
RegistrationFinishEndpoint-->>YourApp: OK
deactivate RegistrationFinishEndpoint
YourApp-)User: OK
deactivate YourApp
TBD
The authentication scenarios have two variations:
The authentication sceanrios utilize the custom authentication challenge Lambda triggers of AWS Cognito to implement a custom authentication flow. Please refer to Section Custom authentication challenge Lambda triggers in Amazon Cognito Developer Guide for better understanding of the custom authentication flow.
-
Your app shows a web page for a user to sign in.
-
Your app sends a POST request to the discoverable endpoint (
/discoverable/start
). -
The discoverable endpoint creates a public key credential request options which include a challenge and a relying party ID.
-
The discoverable endpoint stores a new discoverable authentication session in the session store.
The discoverable authentication session includes the following parameters:
- challenge: the primary key
- public key credential request options
-
The discoverable endpoint returns the public key credential request options to your app.
-
Your app initiates a discoverable credential request with the public key credential request options.
-
User's authenticator asks the user to select a passkey from those associated with the relying party ID in user's authenticator.
A passkey includes a key pair of a private key and a public key.
-
The user selects a passkey.
-
User's authenticator asks the user to authorize the use of the passkey.
-
The user authorizes the use of the passkey.
-
User's authenticator signs the challenge with the private key → the signature.
-
User's authenticator returns a public key credential which includes the public key and the signature to your app.
-
Your app extracts the user handle from the public key credential, which is equal to the user ID.
-
Your app calls the InitiateAuth AWS Cognito API with the following parameters:
AuthFlow
:"CUSTOM_AUTH"
AuthParameters
:USERNAME
: user ID
-
AWS Cognito invokes the define auth challenge trigger.
-
The define auth challenge trigger initiates a custom authentication flow.
-
AWS Cognito invokes the create auth challenge trigger.
-
The create auth challenge trigger returns a dummy challenge parameter.
The true challenge was created at Step 3.
-
AWS Cognito returns a custom challenge to your app.
-
Your app calls the RespondToAuthChallenge AWS Cognito API with the following parameters:
ChallengeName
:"CUSTOM_CHALLENGE"
Session
: the session associated with the custom challengeChallengeResponses
:USERNAME
: user IDANSWER
: the public key credential
-
AWS Cognito invokes the verify auth challenge trigger.
-
The verify auth challenge trigger pops the discoverable authentication session associated with the challenge from the session store.
This is the discoverable authentication session stored at Step 4.
-
The verify auth challenge trigger queries the credential store for the public keys associated with the user ID.
-
The verify auth challenge trigger verifies the public key credential.
The following parameters are involved in the verification:
- challenge
- public keys
- signature
-
The verify auth challenge trigger updates the used public key in the credential store if necessary.
-
The verify auth challenge trigger accepts the public key credential.
-
AWS Cognito returns an access tokens to your app.
Sequence diagram:
sequenceDiagram
actor User
participant Authenticator
participant YourApp
participant DiscoverableEndpoint
participant Cognito
participant DefineAuthChallenge
participant CreateAuthChallenge
participant VerifyAuthChallenge
participant SessionStore
participant CredentialStore
YourApp-)User: Show the sign-in page
activate YourApp
YourApp->>DiscoverableEndpoint: POST /discoverable/start
activate DiscoverableEndpoint
DiscoverableEndpoint->>DiscoverableEndpoint: Create a public key credential request options
DiscoverableEndpoint->>SessionStore: Store a discoverable authentication session
activate SessionStore
SessionStore-->>DiscoverableEndpoint: Discoverable authentication session
deactivate SessionStore
DiscoverableEndpoint-->>YourApp: Public key credential request options
deactivate DiscoverableEndpoint
YourApp-)Authenticator: Initiate a discoverable credential request
activate Authenticator
deactivate YourApp
Authenticator-)User: Ask to select a passkey
User-)Authenticator: Select a passkey
Authenticator-)User: Ask to authorize the use of the passkey
User-)Authenticator: Authorize the use of the passkey
Authenticator->>Authenticator: Sign the challenge
Authenticator-)YourApp: Public key credential
activate YourApp
deactivate Authenticator
YourApp->>YourApp: Extract the user ID
YourApp->>Cognito: InitiateAuth
activate Cognito
Cognito->>DefineAuthChallenge: Invoke
activate DefineAuthChallenge
DefineAuthChallenge-->>Cognito: Initiate a custom authentication flow
deactivate DefineAuthChallenge
Cognito->>CreateAuthChallenge: Invoke
activate CreateAuthChallenge
CreateAuthChallenge-->>Cognito: Dummy challenge
deactivate CreateAuthChallenge
Cognito-->>YourApp: Custom challenge
deactivate Cognito
YourApp->>Cognito: RespondToAuthChallenge
activate Cognito
Cognito->>VerifyAuthChallenge: Invoke
activate VerifyAuthChallenge
VerifyAuthChallenge->>SessionStore: Pop the discoverable authentication session
activate SessionStore
SessionStore-->>VerifyAuthChallenge: Discoverable authentication session
deactivate SessionStore
VerifyAuthChallenge->>CredentialStore: Query public keys
activate CredentialStore
CredentialStore-->>VerifyAuthChallenge: Public keys
deactivate CredentialStore
VerifyAuthChallenge->>VerifyAuthChallenge: Verify the public key credential
opt The public key has been updated
VerifyAuthChallenge->>CredentialStore: Update the public key
activate CredentialStore
CredentialStore-->>VerifyAuthChallenge: OK
deactivate CredentialStore
end
VerifyAuthChallenge-->>Cognito: Accept
deactivate VerifyAuthChallenge
Cognito-->>YourApp: Access tokens
deactivate Cognito
YourApp-)User: OK
deactivate YourApp
This is not precisely a passkey authentication, but a passwordless authentication.
Except for the following materials licensed under CC BY-SA 4.0 (https://creativecommons.org/licenses/by-sa/4.0/):