mirror of
https://github.com/supabase/supabase.git
synced 2026-10-17 07:09:07 +03:00
fix: initial commit
This commit is contained in:
1 parent
91c6e7bc0a
commit
1d1aceeb79
4 files changed
+191
No files matched your search
@@ -606,6 +606,7 @@ export const auth = {
|
||||
items: [
|
||||
{ name: 'App Authenticator (TOTP)', url: '/guides/auth/auth-mfa/totp' },
|
||||
{ name: 'Phone', url: '/guides/auth/auth-mfa/phone' },
|
||||
{ name: 'WebAuthn (Security Keys)', url: '/guides/auth/auth-mfa/webauthn' },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
---
|
||||
id: 'auth-mfa-webauthn'
|
||||
title: 'Multi-Factor Authentication (WebAuthn)'
|
||||
description: 'Add an additional layer of security with passwordless authentication using biometrics and security keys.'
|
||||
---
|
||||
|
||||
## What is WebAuthn?
|
||||
|
||||
WebAuthn (Web Authentication) is a web standard that enables secure, passwordless authentication using public-key cryptography. It allows users to log in to websites and applications using built-in authenticators like fingerprint sensors, facial recognition, or external devices like security keys.
|
||||
|
||||
### When to Use WebAuthn?
|
||||
|
||||
Consider implementing WebAuthn when:
|
||||
|
||||
1. You want to enhance security beyond traditional passwords
|
||||
2. You aim to provide a more user-friendly authentication experience
|
||||
3. You need to reduce costs associated with SMS-based authentication
|
||||
4. You want to offer a hardware backup to existing authentication factors
|
||||
|
||||
WebAuthn is particularly suitable for security-conscious applications that require a user-friendly, passwordless solution.
|
||||
|
||||
### Pricing and Availability
|
||||
|
||||
WebAuthn MFA is part of the Auth Advanced MFA Add-on. Here's the breakdown of pricing:
|
||||
|
||||
- Cost: $75 per month for the first project in the organization
|
||||
- Additional projects: $10 per month each
|
||||
- Included in the Add-on: Phone and other advanced MFA features
|
||||
|
||||
## Types of Authenticators
|
||||
|
||||
An authenticator is a component that securely generates and stores cryptographic keys associated with a particular relying \***\*party (web page origin) and a credential \*\***ID (a unique identifier for the key). The authenticator uses these keys to authenticate the user to the relying party without directly storing user-specific data like the user ID.
|
||||
|
||||
There are two main types of authenticators:
|
||||
|
||||
1. **Platform Authenticators**: Built into the user's device
|
||||
- Examples:
|
||||
- Desktop: Touch ID (macOS), Windows Hello (Windows)
|
||||
- Mobile: Face ID (iOS), Biometrics (Android)
|
||||
2. **Roaming Authenticators**: Portable devices that work across multiple platforms
|
||||
- Examples: YubiKey, Google Titan Security Key, Solokeys
|
||||
|
||||
## Authentication Flows
|
||||
|
||||
WebAuthn involves two primary processes: Enrollment and Login. Both follow a similar pattern:
|
||||
|
||||
<Image
|
||||
alt="Diagram showing the flow of Multi-Factor authentication"
|
||||
src={{
|
||||
light: '/docs/img/guides/auth-mfa/auth-mfa-webauthn-flow.svg',
|
||||
dark: '/docs/img/guides/auth-mfa/auth-mfa-webauthn-flow.svg',
|
||||
}}
|
||||
containerClassName="max-w-[700px]"
|
||||
/>
|
||||
|
||||
### Step-by-Step Explanation
|
||||
|
||||
1. The user's device initiates the process by requesting a challenge from the server.
|
||||
2. The server generates and sends a unique challenge to the authenticator.
|
||||
3. The authenticator prompts the user for verification.
|
||||
4. The user provides the required verification (e.g., fingerprint, face scan).
|
||||
5. Upon successful user verification, the authenticator signs the challenge using its private key.
|
||||
6. The server validates the signed challenge using the corresponding public key.
|
||||
7. If the validation is successful, the authentication process is completed.
|
||||
|
||||
This flow applies to both enrollment (when setting up WebAuthn) and login (when using WebAuthn to authenticate).
|
||||
|
||||
**Navigator API**
|
||||
|
||||
In both flows, WebAuthn interacts with the browser through the Navigator API, specifically using the `navigator.credentials` interface. This API provides methods like `create()` for registering new credentials and `get()` for authenticating existing ones. By handling the secure communication with authenticators (such as hardware tokens or biometrics), the Navigator API simplifies the implementation of WebAuthn, allowing developers to focus on integrating strong, passwordless authentication into their web applications.
|
||||
|
||||
### Add enrollment flow
|
||||
|
||||
An enrollment flow provides a UI for users to set up additional authentication factors. Most applications add the enrollment flow in two places within their app:
|
||||
|
||||
1. Right after login or sign up.
|
||||
This allows users quickly set up Multi Factor Authentication (MFA) post login or account creation. Where possible, encourage all users to set up MFA. Many applications offer this as an opt-in step in an
|
||||
effort to reduce onboarding friction.
|
||||
2. From within a settings page.
|
||||
Allows users to set up, disable or modify their MFA settings.
|
||||
|
||||
As far as possible, maintain a generic flow that you can reuse in both cases with minor modifications.
|
||||
|
||||
You can enroll a WebAuthn factor with a single step:
|
||||
|
||||
```
|
||||
import { browserSupportsWebAuthn } from '@supabase/supabase-js'
|
||||
if (supabase.auth.browserSupportsWebAuthn()) {
|
||||
const { data, error } = await supabase.auth.mfa.enroll({ factorType: 'webauthn'})
|
||||
}) else {
|
||||
// Use another MFA method, such as MFA (Phone)
|
||||
// const { data, error } = await supabase.auth.mfa.enroll({ factorType: 'phone', phone: '<your-phone-number>' })
|
||||
};
|
||||
```
|
||||
|
||||
Or in multiple-steps by setting the `useMultiStepEnroll` parameter to true.
|
||||
|
||||
```
|
||||
const { data: { factorId }, error } = await supabase.auth.mfa.enroll({ factorType: 'webauthn', useMultiStep: true })
|
||||
|
||||
const { data: {options}, error } = await supabase.auth.mfa.challenge({ factorId })
|
||||
const options = await supabase.auth.generateRegistrationOptions{
|
||||
options,
|
||||
rp_name,
|
||||
user: {
|
||||
name,
|
||||
display_name,
|
||||
}
|
||||
authenticator_options: {
|
||||
attestation: 'direct', // or developer choice of 'enterprise'
|
||||
authenticatorAttachment,
|
||||
requireResidentKey,
|
||||
userVerification,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const credential = await navigator.credentials.create(data)
|
||||
const { data, error } = await supabase.auth.mfa.verify({ factorId, credential })
|
||||
```
|
||||
|
||||
`useMultiStep` informs Auth to abstract away the complexities of interacting with the browser API. Multi Step enrollment is recommended only for Advanced Users who require a heightened level of customization in their application
|
||||
|
||||
When using multi-step enroll there are a substantial number of fields to deal with. The fields are highlighted below:
|
||||
|
||||
| Field Type | Field Name | Configurability | Description |
|
||||
| ---------------- | ------------------------- | ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
|
||||
| Non-Configurable | `excludeCredentials` | Generated by auth server | Prevents enrollment of multiple credentials |
|
||||
| Non-Configurable | `rp.id` | Generated by auth server | Relying Party ID |
|
||||
| Non-Configurable | `challenge` | Generated by auth server | Unique challenge for the authentication attempt |
|
||||
| Configurable | `user.name` | Generated by auth server. Can be overridden by specifying `webauthn_user_name` in `user_metadata` | User's account name in the Relying Party's system |
|
||||
| Configurable | `user.displayName` | Generated by Auth server. Can be overridden by specifying `webauthn_display_name` in `user_metadata` | User's display name |
|
||||
| Configurable | `attestation` | Developer choice | Type of attestation requested (e.g., 'direct', 'indirect', 'none') |
|
||||
| Configurable | `authenticatorAttachment` | Developer choice | Preferred authenticator type ('platform' or 'cross-platform') |
|
||||
| Configurable | `requireResidentKey` | Developer choice | Whether a resident key is required (true or false) |
|
||||
| Configurable | `userVerification` | Developer choice | User verification preference ('required', 'preferred', 'discouraged') |
|
||||
| Configurable | `rp.name` | Developer choice | Human-readable name for the Relying Party |
|
||||
|
||||
While configurable fields can be set by developers, it's important to choose appropriate values based on your security requirements and user experience considerations.
|
||||
|
||||
Visit the MDN Documentation for a full description of each field [here](https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredentialCreationOptions)
|
||||
|
||||
## Add Login Flow
|
||||
|
||||
Once a user has registered a factor, they can proceed to log in. In single-step mode this is accomplished with:
|
||||
|
||||
```jsx
|
||||
const { data, error } = await supabase.auth.mfa.verify({ factorType: 'webauthn' })
|
||||
```
|
||||
|
||||
As with enrollment, developers have the option of performing verification over multiple steps
|
||||
|
||||
```
|
||||
const { data: factors, error } = await supabase.auth.mfa.listFactors()
|
||||
const { data, error } = await supabase.auth.mfa.challenge({
|
||||
factorId: factors.find((type) => type === 'webauthn'), // use first verified factor
|
||||
})
|
||||
|
||||
const credential = await generateAuthenticationOptions({authenticator: {userVerification, timeout, authenticatorAttachment}})
|
||||
|
||||
const { data: factor, error } = await supabase.auth.mfa.verify({ factorId, credential, useMultiStep: true, allowAnyCredential: true })
|
||||
```
|
||||
|
||||
In both modes, developers can set `allowAnyCredentials` to true to prioritize User Experience. Users often have multiple security keys for multi-factor authentication. These could be used for different services or different work contexts. These keys are physically identical, which can lead to confusion during the verification process where a user could use a key registered with service A to authenticate against service B
|
||||
|
||||
When a user has multiple registered security keys (e.g., known Key A and unknown Key B), they might accidentally use an unknown key during verification.
|
||||
|
||||
For example:
|
||||
|
||||
1. The app requests verification using known Key A.
|
||||
2. The user inserts unknown Key B.
|
||||
3. The app rejects the key, saying it's not recognized.
|
||||
|
||||
This creates a confusing experience for the user, who has the correct keys but in the wrong order.
|
||||
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml-stylesheet href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css" type="text/css"?>
|
||||
<svg aria-roledescription="sequence" role="graphics-document document" viewBox="-50 -10 1179 537" style="max-width: 100%;" xmlns="http://www.w3.org/2000/svg" width="100%" id="graph-div" height="100%" xmlns:xlink="http://www.w3.org/1999/xlink"><g><rect class="actor actor-bottom" ry="3" rx="3" name="User" height="65" width="150" stroke="#666" fill="#eaeaea" y="451" x="929"></rect><text style="text-anchor: middle; font-size: 16px; font-weight: 400;" class="actor actor-box" alignment-baseline="central" dominant-baseline="central" y="483.5" x="1004"><tspan dy="0" x="1004">User</tspan></text></g><g><rect class="actor actor-bottom" ry="3" rx="3" name="Authenticator" height="65" width="150" stroke="#666" fill="#eaeaea" y="451" x="575"></rect><text style="text-anchor: middle; font-size: 16px; font-weight: 400;" class="actor actor-box" alignment-baseline="central" dominant-baseline="central" y="483.5" x="650"><tspan dy="0" x="650">Authenticator</tspan></text></g><g><rect class="actor actor-bottom" ry="3" rx="3" name="Server" height="65" width="150" stroke="#666" fill="#eaeaea" y="451" x="264"></rect><text style="text-anchor: middle; font-size: 16px; font-weight: 400;" class="actor actor-box" alignment-baseline="central" dominant-baseline="central" y="483.5" x="339"><tspan dy="0" x="339">Server</tspan></text></g><g><rect class="actor actor-bottom" ry="3" rx="3" name="Device" height="65" width="150" stroke="#666" fill="#eaeaea" y="451" x="0"></rect><text style="text-anchor: middle; font-size: 16px; font-weight: 400;" class="actor actor-box" alignment-baseline="central" dominant-baseline="central" y="483.5" x="75"><tspan dy="0" x="75">User Device</tspan></text></g><g><line name="User" stroke="#999" stroke-width="0.5px" class="actor-line 200" y2="451" x2="1004" y1="65" x1="1004" id="actor7"></line><g id="root-7"><rect class="actor actor-top" ry="3" rx="3" name="User" height="65" width="150" stroke="#666" fill="#eaeaea" y="0" x="929"></rect><text style="text-anchor: middle; font-size: 16px; font-weight: 400;" class="actor actor-box" alignment-baseline="central" dominant-baseline="central" y="32.5" x="1004"><tspan dy="0" x="1004">User</tspan></text></g></g><g><line name="Authenticator" stroke="#999" stroke-width="0.5px" class="actor-line 200" y2="451" x2="650" y1="65" x1="650" id="actor6"></line><g id="root-6"><rect class="actor actor-top" ry="3" rx="3" name="Authenticator" height="65" width="150" stroke="#666" fill="#eaeaea" y="0" x="575"></rect><text style="text-anchor: middle; font-size: 16px; font-weight: 400;" class="actor actor-box" alignment-baseline="central" dominant-baseline="central" y="32.5" x="650"><tspan dy="0" x="650">Authenticator</tspan></text></g></g><g><line name="Server" stroke="#999" stroke-width="0.5px" class="actor-line 200" y2="451" x2="339" y1="65" x1="339" id="actor5"></line><g id="root-5"><rect class="actor actor-top" ry="3" rx="3" name="Server" height="65" width="150" stroke="#666" fill="#eaeaea" y="0" x="264"></rect><text style="text-anchor: middle; font-size: 16px; font-weight: 400;" class="actor actor-box" alignment-baseline="central" dominant-baseline="central" y="32.5" x="339"><tspan dy="0" x="339">Server</tspan></text></g></g><g><line name="Device" stroke="#999" stroke-width="0.5px" class="actor-line 200" y2="451" x2="75" y1="65" x1="75" id="actor4"></line><g id="root-4"><rect class="actor actor-top" ry="3" rx="3" name="Device" height="65" width="150" stroke="#666" fill="#eaeaea" y="0" x="0"></rect><text style="text-anchor: middle; font-size: 16px; font-weight: 400;" class="actor actor-box" alignment-baseline="central" dominant-baseline="central" y="32.5" x="75"><tspan dy="0" x="75">User Device</tspan></text></g></g><style>#graph-div{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#graph-div .error-icon{fill:#552222;}#graph-div .error-text{fill:#552222;stroke:#552222;}#graph-div .edge-thickness-normal{stroke-width:1px;}#graph-div .edge-thickness-thick{stroke-width:3.5px;}#graph-div .edge-pattern-solid{stroke-dasharray:0;}#graph-div .edge-thickness-invisible{stroke-width:0;fill:none;}#graph-div .edge-pattern-dashed{stroke-dasharray:3;}#graph-div .edge-pattern-dotted{stroke-dasharray:2;}#graph-div .marker{fill:#333333;stroke:#333333;}#graph-div .marker.cross{stroke:#333333;}#graph-div svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#graph-div p{margin:0;}#graph-div .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#graph-div text.actor>tspan{fill:black;stroke:none;}#graph-div .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#graph-div .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#graph-div .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#graph-div #arrowhead path{fill:#333;stroke:#333;}#graph-div .sequenceNumber{fill:white;}#graph-div #sequencenumber{fill:#333;}#graph-div #crosshead path{fill:#333;stroke:#333;}#graph-div .messageText{fill:#333;sLine truncated
|
||||
|
After Width: | Height: | Size: 23 KiB |
@@ -0,0 +1,13 @@
|
||||
sequenceDiagram
|
||||
participant Device as User Device
|
||||
participant Server
|
||||
participant Authenticator
|
||||
participant User
|
||||
|
||||
Device->>Server: 1. Request challenge
|
||||
Server-->>Authenticator: 2. Send challenge
|
||||
Authenticator->>User: 3. Request user verification
|
||||
User-->>Authenticator: 4. Provide verification (e.g., fingerprint)
|
||||
Authenticator-->>Server: 5. Sign challenge with private key
|
||||
Server-->>Server: 6. Validate signature with public key
|
||||
Server->>Device: 7. Complete authentication
|
||||
Reference in new issue
Block a user