mirror of
https://github.com/supabase/supabase.git
synced 2026-09-22 13:37:53 +08:00
24be387cdb
## What kind of change does this PR introduce? Docs update. Aligns documentation and style guides with the **Sign in / Sign out / Sign up** platform standard. Closes DOCS-1328. Related to [#49874](https://github.com/supabase/supabase/pull/49874). ## What is the current behavior? Docs style guides prefer _login_ / _log in_. Guide prose uses mixed login and sign in wording. ## What is the new behavior? - [WORD_LIST.md](apps/docs/WORD_LIST.md) and [copywriting.mdx](apps/design-system/content/docs/copywriting.mdx) document the sign in standard - Design-system auth examples updated - Guide prose and API reference spec descriptions updated ### Terminology **Standard:** Use _sign in_, _sign out_, and _sign up_ as verbs. Use _sign-in_, _sign-out_, and _sign-up_ as nouns and adjectives. Match Studio UI labels (**Sign in**, **Sign out**, **Sign up**). **Preserved intentionally:** | Category | Keep as-is | Example | | -------- | ---------- | ------- | | Feature name | social login | `/social-login`, `features.mdx` heading, OAuth provider section | | URL slugs | `login` in paths | `/phone-login`, `/login-flows`, `choosing-login-flow` | | CLI | `supabase login` / `supabase logout` | Reference ids `supabase-login` / `supabase-logout`; executable commands unchanged | | SDK methods | `logout()` | Kotlin/Swift method names in API reference titles and examples | | Third-party UI | Provider product labels | Facebook Login, Kakao Login, portal **Login** buttons | | Postgres | Database terminology | login privileges, login credentials, login via role | | Audit/logging | Log prose | "Generates the following **log** in the Postgres Logs" | | Code and routes | Paths and filenames | `app/login/`, `Login.tsx`, `demos/android-login` | | External URLs | Third-party login pages | `dash.cloudflare.com/login`, `console.neon.tech/login`, `vercel.com/login` | | API identifiers | Event and field names | Audit actions `login`/`logout`, `should_logout_user` | ## To test - Run `pnpm lint:mdx` in `apps/docs` - Spot-check `features.mdx`, `social-login.mdx`, and a provider guide (e.g. Facebook, Kakao) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Standardized authentication terminology across guides, reference material, CLI documentation, and copywriting guidance using “sign in,” “sign out,” and “sign up.” * Updated authentication instructions, headings, link text, examples, and SSO guidance for clearer, more consistent wording. * Corrected related grammar, spelling, hyphenation, and documentation links while preserving established product names and implementation commands. * **Style** * Refined code examples with consistent import ordering and spacing. * **Examples** * Updated authentication button and menu labels to “Sign in” and “Sign out.” <!-- end of auto-generated comment: release notes by coderabbit.ai -->
401 lines
11 KiB
Plaintext
401 lines
11 KiB
Plaintext
---
|
|
title: 'Passwordless email sign-in'
|
|
subtitle: 'Email sign-in using Magic Links or One-Time Passwords (OTPs)'
|
|
---
|
|
|
|
Supabase Auth provides several passwordless sign-in methods. Passwordless sign-in allows users to sign in without a password, by clicking a confirmation link or entering a verification code.
|
|
|
|
Passwordless sign-in can:
|
|
|
|
- Improve the user experience by not requiring users to create and remember a password
|
|
- Increase security by reducing the risk of password-related security breaches
|
|
- Reduce support burden of dealing with password resets and other password-related flows
|
|
|
|
Supabase Auth offers two passwordless sign-in methods that use the user's email address:
|
|
|
|
- [Magic Link](#with-magic-link)
|
|
- [OTP](#with-otp)
|
|
|
|
## With Magic Link
|
|
|
|
Magic Links are a form of passwordless sign-in where users click on a link sent to their email address to sign in to their accounts. Magic Links only work with email addresses and are one-time use only.
|
|
|
|
### Enabling Magic Link
|
|
|
|
Email authentication methods, including Magic Links, are enabled by default.
|
|
|
|
Configure the Site URL and any additional redirect URLs. These are the only URLs that are allowed as redirect destinations after the user clicks a Magic Link. You can change the URLs on the [URL Configuration page](/dashboard/project/_/auth/url-configuration) for hosted projects, in the `config.toml` [file](/docs/guides/local-development/cli/config#auth.additional_redirect_urls) for local development, or in the `.env` configuration file for [self-hosted Supabase](/docs/guides/self-hosting/docker).
|
|
|
|
By default, a user can only request a magic link once every <SharedData data="config">auth.rate_limits.magic_link.period</SharedData> and they expire after <SharedData data="config">auth.rate_limits.magic_link.validity</SharedData>.
|
|
|
|
### Signing in with Magic Link
|
|
|
|
Call the "sign in with OTP" method from the client library.
|
|
|
|
Though the method is labelled "OTP", it sends a Magic Link by default. The two methods differ only in the content of the confirmation email sent to the user.
|
|
|
|
If the user hasn't signed up yet, they are automatically signed up by default. To prevent this, set the `shouldCreateUser` option to `false`.
|
|
|
|
<Tabs
|
|
scrollable
|
|
size="small"
|
|
type="underlined"
|
|
defaultActiveId="js"
|
|
queryGroup="language"
|
|
>
|
|
<TabPanel id="js" label="JavaScript">
|
|
|
|
```js
|
|
import { createClient } from '@supabase/supabase-js'
|
|
|
|
const supabase = createClient('https://your-project-id.supabase.co', 'sb_publishable_...')
|
|
|
|
// ---cut---
|
|
async function signInWithEmail() {
|
|
const { data, error } = await supabase.auth.signInWithOtp({
|
|
email: 'valid.email@supabase.io',
|
|
options: {
|
|
// set this to false if you do not want the user to be automatically signed up
|
|
shouldCreateUser: false,
|
|
emailRedirectTo: 'https://example.com/welcome',
|
|
},
|
|
})
|
|
}
|
|
```
|
|
|
|
</TabPanel>
|
|
<TabPanel id="react-native" label="Expo React Native">
|
|
|
|
```ts
|
|
import { makeRedirectUri } from 'expo-auth-session'
|
|
|
|
const redirectTo = makeRedirectUri()
|
|
|
|
const { error } = await supabase.auth.signInWithOtp({
|
|
email: 'valid.email@supabase.io',
|
|
options: {
|
|
emailRedirectTo: redirectTo,
|
|
},
|
|
})
|
|
```
|
|
|
|
Read the [Deep Linking Documentation](/docs/guides/auth/native-mobile-deep-linking) to learn how to handle deep linking.
|
|
|
|
</TabPanel>
|
|
<$Show if="sdk:dart">
|
|
<TabPanel id="dart" label="Dart">
|
|
|
|
```dart
|
|
Future<void> signInWithEmail() async {
|
|
await supabase.auth.signInWithOtp(email: 'valid.email@supabase.io');
|
|
}
|
|
```
|
|
|
|
</TabPanel>
|
|
</$Show>
|
|
<$Show if="sdk:swift">
|
|
<TabPanel id="swift" label="Swift">
|
|
|
|
```swift
|
|
try await supabase.auth.signInWithOTP(
|
|
email: "valid.email@supabase.io",
|
|
redirectTo: URL(string: "https://example.com/welcome"),
|
|
// set this to false if you do not want the user to be automatically signed up
|
|
shouldCreateUser: false
|
|
)
|
|
```
|
|
|
|
</TabPanel>
|
|
</$Show>
|
|
<$Show if="sdk:kotlin">
|
|
<TabPanel id="kotlin" label="Kotlin">
|
|
|
|
```kotlin
|
|
suspend fun signInWithEmail() {
|
|
supabase.auth.signInWith(OTP) {
|
|
email = "valid.email@supabase.io"
|
|
}
|
|
}
|
|
```
|
|
|
|
</TabPanel>
|
|
</$Show>
|
|
<$Show if="sdk:python">
|
|
<TabPanel id="python" label="Python">
|
|
|
|
```python
|
|
response = supabase.auth.sign_in_with_otp({
|
|
'email': 'valid.email@supabase.io',
|
|
'options': {
|
|
# set this to false if you do not want the user to be automatically signed up
|
|
'should_create_user': False,
|
|
'email_redirect_to': 'https://example.com/welcome',
|
|
},
|
|
})
|
|
```
|
|
|
|
</TabPanel>
|
|
</$Show>
|
|
<$Show if="sdk:csharp">
|
|
<TabPanel id="csharp" label="C#">
|
|
|
|
```c#
|
|
var options = new SignInOptions { RedirectTo = "https://example.com/welcome" };
|
|
var didSendMagicLink = await supabase.Auth.SendMagicLink("valid.email@supabase.io", options);
|
|
```
|
|
|
|
</TabPanel>
|
|
</$Show>
|
|
</Tabs>
|
|
|
|
{/* supa-mdx-lint-disable-next-line Rule004ExcludeWords */}
|
|
|
|
That's it for the implicit flow.
|
|
|
|
If you're using PKCE flow, edit the Magic Link [email template](/docs/guides/auth/auth-email-templates) to send a token hash:
|
|
|
|
```html
|
|
<h2>Sign in to your account</h2>
|
|
|
|
<p>Use this link to sign in to your account:</p>
|
|
<p><a href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=email">Sign in</a></p>
|
|
```
|
|
|
|
At the `/auth/confirm` endpoint, exchange the hash for the session:
|
|
|
|
```js
|
|
import { createClient } from '@supabase/supabase-js'
|
|
|
|
const supabase = createClient('https://your-project-id.supabase.co', 'sb_publishable_...')
|
|
|
|
// ---cut---
|
|
const { error } = await supabase.auth.verifyOtp({
|
|
token_hash: 'hash',
|
|
type: 'email',
|
|
})
|
|
```
|
|
|
|
## With OTP
|
|
|
|
Email one-time passwords (OTP) are a form of passwordless sign-in where users key in a six-digit code sent to their email address to sign in to their accounts.
|
|
|
|
### Enabling email OTP
|
|
|
|
Email authentication methods, including Email OTPs, are enabled by default.
|
|
|
|
Email OTPs share an implementation with Magic Links. To send an OTP instead of a Magic Link, alter the **Magic Link** [email template](/dashboard/project/_/auth/templates/magic-link-or-otp). Refer to the [Email Templates guide](/docs/guides/auth/auth-email-templates) for more information.
|
|
|
|
Modify the template to include the `{{ .Token }}` variable, for example:
|
|
|
|
```html
|
|
<h2>One time login code</h2>
|
|
|
|
<p>Please enter this code: {{ .Token }}</p>
|
|
```
|
|
|
|
By default, a user can only request an OTP once every <SharedData data="config">auth.rate_limits.otp.period</SharedData>, and they expire after <SharedData data="config">auth.rate_limits.otp.validity</SharedData>. This is configurable via **Authentication > Sign In / Providers > Auth Providers > Email > Email OTP expiration**. An expiry duration of more than 86,400 seconds (one day) is strongly discouraged and can only be set via the [Management API](/docs/reference/api/v1-update-auth-service-config). Make sure to read the [security recommendations](/docs/guides/deployment/going-into-prod#security) before going into production.
|
|
|
|
<Admonition type="caution">
|
|
|
|
The **Email OTP Expiration** setting also governs the validity of Magic Links and other email links, including confirmation, password recovery, email change, and [invitation](/docs/guides/auth/users#inviting-users) links.
|
|
|
|
</Admonition>
|
|
|
|
### Signing in with email OTP
|
|
|
|
#### Step 1: Send the user an OTP code
|
|
|
|
Get the user's email and call the "sign in with OTP" method from your client library.
|
|
|
|
If the user hasn't signed up yet, they are automatically signed up by default. To prevent this, set the `shouldCreateUser` option to `false`.
|
|
|
|
<Tabs
|
|
scrollable
|
|
size="small"
|
|
type="underlined"
|
|
defaultActiveId="js"
|
|
queryGroup="language"
|
|
>
|
|
<TabPanel id="js" label="JavaScript">
|
|
|
|
```js
|
|
import { createClient } from '@supabase/supabase-js'
|
|
|
|
const supabase = createClient('https://your-project-id.supabase.co', 'sb_publishable_...')
|
|
|
|
// ---cut---
|
|
const { data, error } = await supabase.auth.signInWithOtp({
|
|
email: 'valid.email@supabase.io',
|
|
options: {
|
|
// set this to false if you do not want the user to be automatically signed up
|
|
shouldCreateUser: false,
|
|
},
|
|
})
|
|
```
|
|
|
|
</TabPanel>
|
|
<$Show if="sdk:dart">
|
|
<TabPanel id="dart" label="Dart">
|
|
|
|
```dart
|
|
Future<void> signInWithEmailOtp() async {
|
|
await supabase.auth.signInWithOtp(email: 'valid.email@supabase.io');
|
|
}
|
|
```
|
|
|
|
</TabPanel>
|
|
</$Show>
|
|
<$Show if="sdk:swift">
|
|
<TabPanel id="swift" label="Swift">
|
|
|
|
```swift
|
|
try await supabase.auth.signInWithOTP(
|
|
email: "valid.email@supabase.io",
|
|
// set this to false if you do not want the user to be automatically signed up
|
|
shouldCreateUser: false
|
|
)
|
|
```
|
|
|
|
</TabPanel>
|
|
</$Show>
|
|
<$Show if="sdk:kotlin">
|
|
<TabPanel id="kotlin" label="Kotlin">
|
|
|
|
```kotlin
|
|
suspend fun signInWithEmailOtp() {
|
|
supabase.auth.signInWith(OTP) {
|
|
email = "valid.email@supabase.io"
|
|
}
|
|
}
|
|
```
|
|
|
|
</TabPanel>
|
|
</$Show>
|
|
<$Show if="sdk:python">
|
|
<TabPanel id="python" label="Python">
|
|
|
|
```python
|
|
response = supabase.auth.sign_in_with_otp({
|
|
'email': 'valid.email@supabase.io',
|
|
'options': {
|
|
# set this to false if you do not want the user to be automatically signed up
|
|
'should_create_user': False,
|
|
},
|
|
})
|
|
```
|
|
|
|
</TabPanel>
|
|
</$Show>
|
|
<$Show if="sdk:csharp">
|
|
<TabPanel id="csharp" label="C#">
|
|
|
|
```c#
|
|
await supabase.Auth.SendMagicLink("valid.email@supabase.io");
|
|
```
|
|
|
|
</TabPanel>
|
|
</$Show>
|
|
</Tabs>
|
|
|
|
If the request is successful, you receive a response with `error: null` and a `data` object where both `user` and `session` are null. Let the user know to check their email inbox.
|
|
|
|
```json
|
|
{
|
|
"data": {
|
|
"user": null,
|
|
"session": null
|
|
},
|
|
"error": null
|
|
}
|
|
```
|
|
|
|
#### Step 2: Verify the OTP to create a session
|
|
|
|
Provide an input field for the user to enter their one-time code.
|
|
|
|
Call the "verify OTP" method from your client library with the user's email address, the code, and a type of `email`:
|
|
|
|
<Tabs
|
|
scrollable
|
|
size="small"
|
|
type="underlined"
|
|
defaultActiveId="js"
|
|
queryGroup="language"
|
|
>
|
|
<TabPanel id="js" label="JavaScript">
|
|
|
|
```js
|
|
import { createClient } from '@supabase/supabase-js'
|
|
|
|
const supabase = createClient('https://your-project-id.supabase.co', 'sb_publishable_...')
|
|
|
|
// ---cut---
|
|
const {
|
|
data: { session },
|
|
error,
|
|
} = await supabase.auth.verifyOtp({
|
|
email: 'email@example.com',
|
|
token: '123456',
|
|
type: 'email',
|
|
})
|
|
```
|
|
|
|
</TabPanel>
|
|
<$Show if="sdk:swift">
|
|
<TabPanel id="swift" label="Swift">
|
|
|
|
```swift
|
|
try await supabase.auth.verifyOTP(
|
|
email: email,
|
|
token: "123456",
|
|
type: .email
|
|
)
|
|
```
|
|
|
|
</TabPanel>
|
|
</$Show>
|
|
<$Show if="sdk:kotlin">
|
|
<TabPanel id="kotlin" label="Kotlin">
|
|
|
|
```kotlin
|
|
supabase.auth.verifyEmailOtp(type = OtpType.Email.EMAIL, email = "email", token = "151345")
|
|
```
|
|
|
|
</TabPanel>
|
|
</$Show>
|
|
<$Show if="sdk:python">
|
|
<TabPanel id="python" label="Python">
|
|
|
|
```python
|
|
response = supabase.auth.verify_otp({
|
|
'email': email,
|
|
'token': '123456',
|
|
'type': 'email',
|
|
})
|
|
```
|
|
|
|
</TabPanel>
|
|
</$Show>
|
|
<$Show if="sdk:csharp">
|
|
<TabPanel id="csharp" label="C#">
|
|
|
|
```c#
|
|
var session = await supabase.Auth.VerifyOTP("email@example.com", "123456", EmailOtpType.Email);
|
|
```
|
|
|
|
</TabPanel>
|
|
</$Show>
|
|
</Tabs>
|
|
|
|
If successful, the user is now signed in, and you receive a valid session that looks like:
|
|
|
|
```json
|
|
{
|
|
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNjI3MjkxNTc3LCJzdWIiOiJmYTA2NTQ1Zi1kYmI1LTQxY2EtYjk1NC1kOGUyOTg4YzcxOTEiLCJlbWFpbCI6IiIsInBob25lIjoiNjU4NzUyMjAyOSIsImFwcF9tZXRhZGF0YSI6eyJwcm92aWRlciI6InBob25lIn0sInVzZXJfbWV0YWRhdGEiOnt9LCJyb2xlIjoiYXV0aGVudGljYXRlZCJ9.1BqRi0NbS_yr1f6hnr4q3s1ylMR3c1vkiJ4e_N55dhM",
|
|
"token_type": "bearer",
|
|
"expires_in": 3600,
|
|
"refresh_token": "LSp8LglPPvf0DxGMSj-vaQ",
|
|
"user": {...}
|
|
}
|
|
```
|