feat: new auth ia (#22812)
Co-authored-by: Kang Ming <kang.ming1996@gmail.com> Co-authored-by: Joel Lee <lee.yi.jie.joel@gmail.com>
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
import Link from 'next/link'
|
||||
import { Admonition } from 'ui'
|
||||
|
||||
const CostWarning = () => (
|
||||
<Admonition type="warning">
|
||||
<p>
|
||||
To keep SMS sending costs under control, make sure you adjust your project's rate limits
|
||||
and <Link href="/guides/auth/auth-captcha">configure CAPTCHA</Link>. See the{' '}
|
||||
<Link href="/guides/platform/going-into-prod">Production Checklist</Link> to learn more.
|
||||
</p>
|
||||
<p>
|
||||
Some countries have special regulations for services that send SMS messages to users, for
|
||||
example, India's TRAI DLT regulations. Remember to look up and follow the regulations of
|
||||
countries where you operate.
|
||||
</p>
|
||||
</Admonition>
|
||||
)
|
||||
|
||||
export { CostWarning }
|
||||
@@ -0,0 +1,86 @@
|
||||
import { useEffect, useReducer, useRef } from 'react'
|
||||
import { PhoneLoginsItems } from '../Navigation/NavigationMenu/NavigationMenu.constants'
|
||||
import { IconPanel } from 'ui-patterns/IconPanel'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogSection, Heading } from 'ui'
|
||||
import MessageBird from './MessageBirdConfig.mdx'
|
||||
import Twilio from './TwilioConfig.mdx'
|
||||
import Vonage from './VonageConfig.mdx'
|
||||
import TextLocal from './TextLocalConfig.mdx'
|
||||
|
||||
const reducer = (_, action: (typeof PhoneLoginsItems)[number] | undefined) => {
|
||||
const url = new URL(document.location.href)
|
||||
if (action) {
|
||||
url.searchParams.set('showSmsProvider', encodeURIComponent(action.name))
|
||||
} else {
|
||||
url.searchParams.delete('showSmsProvider')
|
||||
}
|
||||
window.history.replaceState(null, '', url)
|
||||
return action
|
||||
}
|
||||
|
||||
const AuthSmsProviderConfig = () => {
|
||||
const [selectedProvider, setSelectedProvider] = useReducer(reducer, undefined)
|
||||
|
||||
useEffect(() => {
|
||||
const providerName = new URLSearchParams(document.location.search ?? '').get('showSmsProvider')
|
||||
if (!providerName) return
|
||||
|
||||
const provider = PhoneLoginsItems.find((item) => item.name === decodeURIComponent(providerName))
|
||||
if (provider) setSelectedProvider(provider)
|
||||
}, [])
|
||||
|
||||
const headingRef = useRef<HTMLHeadingElement>(null)
|
||||
|
||||
return (
|
||||
<>
|
||||
<section aria-labelledby="sms-provider-configuration">
|
||||
<h3 className="sr-only" id="sms-provider-configuration">
|
||||
Configuring SMS Providers
|
||||
</h3>
|
||||
<div className="grid grid-cols-6 gap-10 not-prose py-8">
|
||||
{PhoneLoginsItems.map((provider) => (
|
||||
<button
|
||||
key={provider.name}
|
||||
className="col-span-6 xl:col-span-3"
|
||||
onClick={() => setSelectedProvider(provider)}
|
||||
>
|
||||
<IconPanel
|
||||
title={provider.name}
|
||||
icon={provider.icon}
|
||||
hasLightIcon={provider.hasLightIcon}
|
||||
/>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
<Dialog
|
||||
open={!!selectedProvider}
|
||||
onOpenChange={(open) => !open && setSelectedProvider(undefined)}
|
||||
>
|
||||
{selectedProvider && (
|
||||
<DialogContent
|
||||
className="!w-[min(90vw,80ch)] !max-w-[min(90vw,80ch)] !max-h-[90dvh] prose overflow-auto"
|
||||
onOpenAutoFocus={(evt) => {
|
||||
evt.preventDefault()
|
||||
headingRef.current?.focus()
|
||||
}}
|
||||
>
|
||||
<DialogHeader className="pb-0 [&>h3]:!m-0 [&>h3>a]:!hidden [&>h3:focus-visible]:outline-none">
|
||||
<Heading tag="h3" ref={headingRef} tabIndex={-1}>
|
||||
{selectedProvider.name}
|
||||
</Heading>
|
||||
</DialogHeader>
|
||||
<DialogSection className="[&>:first-child]:mt-0">
|
||||
{selectedProvider.name.toLowerCase().includes('messagebird') && <MessageBird />}
|
||||
{selectedProvider.name.toLowerCase().includes('twilio') && <Twilio />}
|
||||
{selectedProvider.name.toLowerCase().includes('vonage') && <Vonage />}
|
||||
{selectedProvider.name.toLowerCase().includes('textlocal') && <TextLocal />}
|
||||
</DialogSection>
|
||||
</DialogContent>
|
||||
)}
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default AuthSmsProviderConfig
|
||||
@@ -0,0 +1,48 @@
|
||||
import { CostWarning } from './AuthSmsProviderConfig.Warnings'
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You'll need:
|
||||
|
||||
- A MessageBird account (sign up [here](https://dashboard.messagebird.com/en/sign-up))
|
||||
- A Supabase project (create one [here](https://supabase.com/dashboard))
|
||||
- A mobile phone capable of receiving SMS
|
||||
|
||||
<CostWarning />
|
||||
|
||||
## Set up MessageBird as your SMS provider
|
||||
|
||||
Start by logging into your MessageBird account and verify the mobile number you'll be using to test with [here](https://dashboard.messagebird.com/en/getting-started/sms). This is the number that will be receiving the SMS OTPs.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
Navigate to the [dashboard settings](https://dashboard.messagebird.com/en/settings/sms) to set the default originator. The messagebird originator is the name or number from which the message is sent. For more information, you can refer to the messagebird article on choosing an originator [here](https://support.messagebird.com/hc/en-us/articles/115002628665-Choosing-an-originator)
|
||||
|
||||

|
||||
|
||||
You will need the following values to get started:
|
||||
|
||||
- Live API Key / Test API Key
|
||||
- MessageBird originator
|
||||
|
||||
Now go to the [Auth > Providers](https://supabase.com/dashboard/project/_/auth/providers) page in the Supabase dashboard and select "Phone" from the Auth Providers list.
|
||||
|
||||
You should see an option to enable the Phone provider.
|
||||
|
||||
Toggle it on, and copy the 2 values over from the Messagebird dashboard. Click save.
|
||||
|
||||
<Admonition>
|
||||
|
||||
If you use the Test API Key, the OTP will not be delivered to the mobile number specified but messagebird will log the response in the dashboard. If the Live API Key is used instead, the OTP will be delivered and there will be a deduction in your free credits.
|
||||
|
||||
</Admonition>
|
||||
|
||||
#### SMS custom template
|
||||
|
||||
The SMS message sent to a phone containing an OTP code can be customized. This is useful if you need to mention a brand name or display a website address.
|
||||
|
||||
Go to [Auth > Templates](https://supabase.com/dashboard/project/_/auth/templates) page in the Supabase dashboard.
|
||||
|
||||
Use the variable `.Code` in the template to display the code.
|
||||
@@ -0,0 +1,42 @@
|
||||
import { CostWarning } from './AuthSmsProviderConfig.Warnings'
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you begin, ensure you have the following:
|
||||
|
||||
- **A Textlocal account**: Sign up for [TextLocal](https://www.textlocal.com/signup) to start sending SMS messages.
|
||||
- **A Supabase project**: Necessary for SMS authentication integration. Create your project in the [Supabase Dashboard](https://supabase.com/dashboard).
|
||||
- **A mobile phone**: To receive SMS messages and test your setup.
|
||||
|
||||
<CostWarning />
|
||||
|
||||
## Setting up Textlocal as your SMS provider
|
||||
|
||||
To integrate Textlocal with Supabase:
|
||||
|
||||
1. [Get a Textlocal API key](#get-a-textlocal-api-key)
|
||||
2. [Customize your sender name](#customize-your-sender-name-optional)
|
||||
3. [Configure your Supabase project](#configure-supabase)
|
||||
|
||||
### Get a Textlocal API key
|
||||
|
||||
1. Log into your Textlocal account and go to `Settings` > `API Keys`.
|
||||
2. Generate a new API Key. Save your new API key in a safe location.
|
||||
|
||||
### Customize your sender name (Optional)
|
||||
|
||||
Textlocal defaults to `TXTLCL` as the sender name for all messages. You can customize this to better reflect your brand:
|
||||
|
||||
1. In your Textlocal dashboard, go to `Settings` > `All Settings` > `Sender Names`.
|
||||
2. Change your sender name.
|
||||
|
||||
### Configure Supabase
|
||||
|
||||
To set up Textlocal as your SMS provider in Supabase, follow these steps:
|
||||
|
||||
1. In your Supabase Dashboard, go to the [Auth Providers section](https://supabase.com/dashboard/project/_/auth/providers).
|
||||
2. From the list of available authentication providers, select `Phone`.
|
||||
3. Toggle `Enable Phone Provider`.
|
||||
4. Under `SMS Provider`, select `Textlocal.`
|
||||
5. Enter your Textlocal API Key and Sender Name.
|
||||
6. Customize your SMS Message (optional).
|
||||
130
apps/docs/components/AuthSmsProviderConfig/TwilioConfig.mdx
Normal file
130
apps/docs/components/AuthSmsProviderConfig/TwilioConfig.mdx
Normal file
@@ -0,0 +1,130 @@
|
||||
import { CostWarning } from './AuthSmsProviderConfig.Warnings'
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You'll need:
|
||||
|
||||
- Twilio account ([sign up](https://www.twilio.com/try-twilio))
|
||||
- Supabase project (create one [here](https://supabase.com/dashboard))
|
||||
- Mobile phone capable of receiving SMS
|
||||
|
||||
SMS Authentication can be done with either Twilio Verify or Twilio Programmable Messaging. [Twilio Verify](https://www.twilio.com/en-us/trusted-activation/verify) is a specialized OTP solution and is recommended for most developers that need over-the-phone authentication. Alternatively you can use [Twilio Programmable Messaging](https://www.twilio.com/docs/messaging) which offers generic SMS sending support.
|
||||
|
||||
<CostWarning />
|
||||
|
||||
## Twilio Verify
|
||||
|
||||
To set up Twilio Verify, you will need to:
|
||||
|
||||
1. Create a new [verification service](https://support.twilio.com/hc/en-us/articles/360033309133-Getting-Started-with-Twilio-Verify-V2) in the Twilio dashboard.
|
||||
2. [Switch Phone Provider to Twilio Verify](https://supabase.com/dashboard/project/_/auth/providers)
|
||||
3. Configure the Twilio Verify Service ID field using the Verification Service ID obtained in 1.
|
||||
|
||||
When using Twilio Verify, OTPs are generated by Twilio. This means that:
|
||||
|
||||
- Unlike other providers, the OTP expiry duration and message content fields are not configurable via the Supabase dashboard. Please head to Twilio Verify to configure these settings.
|
||||
- The token remains the same during its validity period until the verification is successful. This means if your user makes another request within that period, they will receive the same token.
|
||||
- Twilio Verify has a separate set of rate limits that apply. Visit Twilio's [Rate Limit and Timeouts page](https://www.twilio.com/docs/verify/api/rate-limits-and-timeouts) to find out more.
|
||||
|
||||
<Admonition type="caution">
|
||||
|
||||
At this time, Twilio Verify is only supported on the `whatsapp` and `sms` channels.
|
||||
|
||||
</Admonition>
|
||||
|
||||
## Twilio (Programmable Messaging)
|
||||
|
||||
In this section, you'll set up Twilio as an SMS provider:
|
||||
|
||||
What you'll need:
|
||||
|
||||
- A Twilio account (sign up here: https://www.twilio.com/try-twilio)
|
||||
- A Supabase project (create one here: https://supabase.com/dashboard)
|
||||
- A mobile phone capable of receiving SMS
|
||||
|
||||
<div className="video-container">
|
||||
<iframe
|
||||
src="https://www.youtube-nocookie.com/embed/akScoPO01bc"
|
||||
frameBorder="1"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
### Setting up your Twilio credentials
|
||||
|
||||
Start by logging into your Twilio account and starting a new project: https://www.twilio.com/console/projects/create
|
||||
|
||||
Give your project a name and verify the mobile number you'll be using to test with. This is the number that will be receiving the SMS OTPs.
|
||||
|
||||

|
||||

|
||||
|
||||
Select 'SMS', 'Identity & Verification', and 'With code' as options on the welcome form.
|
||||
|
||||

|
||||
|
||||
When you're back on the [Twilio console screen](https://www.twilio.com/console), you need to scroll down and click 'Get a trial phone number' - this is the number that you'll be sending SMSs from.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
You should now be able to see all three values you'll need to get started:
|
||||
|
||||
- Account SID
|
||||
- Auth Token
|
||||
- Sender Phone Number
|
||||
|
||||

|
||||
|
||||
Now go to the [Auth > Providers](https://supabase.com/dashboard/project/_/auth/providers) page in the Supabase dashboard and select "Phone" from the Auth Providers list.
|
||||
|
||||
You should see an option to enable the Phone provider.
|
||||
|
||||
Toggle it on, and copy the 3 values over from the Twilio dashboard. Click save.
|
||||
|
||||
Note: for "Twilio Message Service SID" you can use the Sender Phone Number generated above.
|
||||
|
||||

|
||||
|
||||
Now the backend should be setup, we can proceed to add our client-side code!
|
||||
|
||||
<Admonition type="note">
|
||||
The `Twilio Content SID` field is specific to Twilio Programmable Messaging (WhatsApp) senders.
|
||||
Disregard this field if you are only sending SMS messages. Refer to the section on WhatsApp OTP
|
||||
Logins for further details.
|
||||
</Admonition>
|
||||
|
||||
#### SMS custom template
|
||||
|
||||
The SMS message sent to a phone containing an OTP code can be customized. This is useful if you need to mention a brand name or display a website address.
|
||||
|
||||
Go to the [Auth > Providers](https://supabase.com/dashboard/project/_/auth/providers) page in the Supabase dashboard and select "Phone" from the Auth Providers list. Scroll to the very bottom of the "Phone" section to the "SMS Message" input - you can customize the SMS message here.
|
||||
|
||||
Use the variable `.Code` in the template to display the OTP code. Here's an example in the SMS template.
|
||||
|
||||

|
||||
|
||||
## WhatsApp OTP logins
|
||||
|
||||
In some cases, you may wish to use WhatsApp as a delivery channel instead. Here are some examples our users have cited:
|
||||
|
||||
- You want higher deliverability
|
||||
- You wish for a secure channel
|
||||
- Your users mostly use WhatsApp as a messaging platform
|
||||
|
||||
Twilio Verify can be used with WhatsApp OTPs with no additional configuration.
|
||||
|
||||
Complete the following steps to use WhatsApp OTP with Twilio Programmable Messaging:
|
||||
|
||||
1. Go through the [Twilio self sign up guide for WhatsApp](https://www.twilio.com/docs/whatsapp/self-sign-up).
|
||||
2. Register a Twilio Content Template via the [API](https://www.twilio.com/docs/content/whatsappauthentication) and note the corresponding Twilio Content SID. You can also use the Template Builder on the Twilio dashboard to create a Content Template
|
||||

|
||||
3. Register the Twilio Content SID on the Supabase dashboard under Authentication > Providers > Phone > Twilio Content SID. Ensure you have Twilio selected as your phone provider.
|
||||
|
||||
<Admonition>
|
||||
|
||||
You may only use one Twilio Content SID at a time. Supabase Auth will use the Content Template over the `SMS Message` field when sending WhatsApp messages. Use Twilio Verify if you need to use more than one message template.
|
||||
|
||||
</Admonition>
|
||||
49
apps/docs/components/AuthSmsProviderConfig/VonageConfig.mdx
Normal file
49
apps/docs/components/AuthSmsProviderConfig/VonageConfig.mdx
Normal file
@@ -0,0 +1,49 @@
|
||||
import { CostWarning } from './AuthSmsProviderConfig.Warnings'
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You'll need:
|
||||
|
||||
- A Vonage account (sign up here: https://dashboard.nexmo.com/sign-up)
|
||||
- A Supabase project (create one here: https://supabase.com/dashboard)
|
||||
- A mobile phone capable of receiving SMS
|
||||
|
||||
<CostWarning />
|
||||
|
||||
## Set up Vonage as an SMS provider
|
||||
|
||||
### Getting your Vonage credentials
|
||||
|
||||
Start by logging into your Vonage Dashboard at https://dashboard.nexmo.com/
|
||||
|
||||
You will see you API Key and API Secret here, which is actually all you need to get started.
|
||||
|
||||
In most countries, a phone number is actually optional and you can also use any Alphanumeric Sender ID of up to 11 characters length (8 for India) as a Sender ID (from). This means you do not need a number to test with in most cases.
|
||||
|
||||
To find out more about supported countries for Alphanumeric Sender ID, check this overview: https://help.nexmo.com/hc/en-us/articles/115011781468-SMS-Features-Overview-Outbound-only-
|
||||
|
||||
Hint: Some countries might need a Sender ID Registration to allow sending with an Alphanumeric Sender ID. You can find this information in the help article as well. If Alpha Sender IDs are not supported, you will need to buy a phone number.
|
||||
|
||||
### Getting a phone number (optional)
|
||||
|
||||
If you want a phone number to send SMS from, you can buy one from the Vonage Dashboard under Numbers > Buy Numbers (https://dashboard.nexmo.com/buy-numbers).
|
||||
|
||||
Select the country you want a number for. You will need a mobile phone number with SMS or SMS+Voice capability. After you have bought the number, you will be able to send SMS from it.
|
||||
|
||||
### Configure Supabase
|
||||
|
||||
Now go to the [Auth > Providers](https://supabase.com/dashboard/project/_/auth/providers) page in the Supabase dashboard and select "Phone" from the Auth Providers list.
|
||||
|
||||
You should see an option to enable the Phone provider.
|
||||
|
||||
Toggle it on, and copy the API key, API secret and phone number values over from the Vonage dashboard. Click save.
|
||||
|
||||
Now the backend should be setup, we can proceed to add our client-side code!
|
||||
|
||||
#### SMS custom template
|
||||
|
||||
The SMS message sent to a phone containing an OTP code can be customized. This is useful if you need to mention a brand name or display a website address.
|
||||
|
||||
Go to [Auth > Templates](https://supabase.com/dashboard/project/_/auth/templates) page in the Supabase dashboard.
|
||||
|
||||
Use the variable `.Code` in the template to display the code.
|
||||
7
apps/docs/components/AuthSmsProviderConfig/index.tsx
Normal file
7
apps/docs/components/AuthSmsProviderConfig/index.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
import dynamic from 'next/dynamic'
|
||||
|
||||
const LazyConfig = dynamic(() => import('./AuthSmsProviderConfig'))
|
||||
|
||||
const AuthSmsProviderConfig = () => <LazyConfig />
|
||||
|
||||
export { AuthSmsProviderConfig }
|
||||
@@ -83,10 +83,13 @@ const GuidesTableOfContents = ({
|
||||
.filter((heading) => heading.id)
|
||||
.map((heading) => {
|
||||
const text = heading.textContent.replace('#', '')
|
||||
const link = heading.querySelector('a').getAttribute('href')
|
||||
const link = heading.querySelector('a')?.getAttribute('href')
|
||||
if (!link) return null
|
||||
|
||||
const level = heading.tagName === 'H2' ? 2 : 3
|
||||
return { text, link, level }
|
||||
})
|
||||
.filter(Boolean)
|
||||
setTocList(newHeadings)
|
||||
})
|
||||
|
||||
|
||||
14
apps/docs/components/MDX/auth_rate_limits.mdx
Normal file
14
apps/docs/components/MDX/auth_rate_limits.mdx
Normal file
@@ -0,0 +1,14 @@
|
||||
| Endpoint | Path | Limited By | Rate Limit |
|
||||
| ------------------------------------------------ | -------------------------------------------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| All endpoints that send emails | `/auth/v1/signup` `/auth/v1/recover` `/auth/v1/user`[^1] | Sum of combined requests | Defaults to 30 emails per hour. As of 14th July 2023, this has been updated to 4 emails per hour. As of 21 Oct 2023, this has been updated to <SharedData data="config">auth.rate_limits.email.inbuilt_smtp_per_hour</SharedData> emails per hour. You can only change this with your own custom SMTP setup. |
|
||||
| All endpoints that send One-Time-Passwords (OTP) | `/auth/v1/otp` | Sum of combined requests | Defaults to <SharedData data="config">auth.rate_limits.otp.requests_per_hour</SharedData> OTPs per hour. Is customizable. |
|
||||
| Send OTPs or magiclinks | `/auth/v1/otp` | Last request | Defaults to <SharedData data="config">auth.rate_limits.otp.period</SharedData> window before a new request is allowed. Is customizable. |
|
||||
| Signup confirmation request | `/auth/v1/signup` | Last request | Defaults to <SharedData data="config">auth.rate_limits.signup_confirmation.period</SharedData> window before a new request is allowed. Is customizable. |
|
||||
| Password Reset Request | `/auth/v1/recover` | Last request | Defaults to <SharedData data="config">auth.rate_limits.password_reset.period</SharedData> window before a new request is allowed. Is customizable. |
|
||||
| Verification requests | `/auth/v1/verify` | IP Address | <SharedData data="config">auth.rate_limits.verification.requests_per_hour</SharedData> requests per hour (with bursts up to <SharedData data="config">auth.rate_limits.verification.requests_burst</SharedData> requests) |
|
||||
| Token refresh requests | `/auth/v1/token` | IP Address | <SharedData data="config">auth.rate_limits.token_refresh.requests_per_hour</SharedData> requests per hour (with bursts up to <SharedData data="config">auth.rate_limits.token_refresh.requests_burst</SharedData> requests) |
|
||||
| Create or Verify an MFA challenge | `/auth/v1/factors/:id/challenge` `/auth/v1/factors/:id/verify` | IP Address | <SharedData data="config">auth.rate_limits.mfa.requests_per_hour</SharedData> requests per hour (with bursts up to <SharedData data="config">auth.rate_limits.verification.mfa</SharedData> requests) |
|
||||
| Anonymous sign-ins | `/auth/v1/signup`[^2] | IP Address | <SharedData data="config">auth.rate_limits.anonymous_signin.requests_per_hour</SharedData> requests per hour (with bursts up to <SharedData data="config">auth.rate_limits.anonymous_signin.requests_burst</SharedData> requests) |
|
||||
|
||||
[^1]: The rate limit is only applied on `/auth/v1/user` if this endpoint is called to update the user's email address.
|
||||
[^2]: The rate limit is only applied on `/auth/v1/signup` if this endpoint is called without passing in an email or phone number in the request body.
|
||||
@@ -1,16 +1,49 @@
|
||||
---
|
||||
title: 'OAuth with PKCE flow for SSR'
|
||||
description: 'Learn how to configure OAuth authentication in your server-side rendering (SSR) application to work with the PKCE flow.'
|
||||
subtitle: 'Learn how to configure OAuth authentication in your server-side rendering (SSR) application to work with the PKCE flow.'
|
||||
---
|
||||
For a PKCE flow, for example in Server-Side Auth, you need an extra step to handle the code exchange. When calling `signInWithOAuth`, provide a `redirectTo` URL which points to a callback route. This redirect URL should be added to your [redirect allow list](/docs/guides/auth/redirect-urls).
|
||||
|
||||
### Setting up SSR client
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="client"
|
||||
queryGroup="environment"
|
||||
>
|
||||
<TabPanel id="client" label="Client">
|
||||
|
||||
Check out our [guide for creating a client](/docs/guides/auth/server-side/creating-a-client) to learn how to install the necessary packages, declare environment variables, and create a Supabase client configured for SSR in your framework.
|
||||
In the browser, `signInWithOAuth` automatically redirects to the OAuth provider's authentication endpoint, which then redirects to your endpoint.
|
||||
|
||||
### Create API endpoint for handling the `code` exchange
|
||||
```js
|
||||
await supabase.auth.signInWithOAuth({
|
||||
provider,
|
||||
options: {
|
||||
redirectTo: `http://example.com/auth/callback`,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
In order to use OAuth we will need to setup a endpoint for the `code` exchange, to exchange an auth `code` for the user's `session`, which is set as a cookie for future requests made to Supabase.
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="server" label="Server">
|
||||
|
||||
In the server, you need to handle the redirect to the OAuth provider's authentication endpoint. The `signInWithOAuth` method returns the endpoint URL, which you can redirect to.
|
||||
|
||||
```js
|
||||
const { data, error } = await supabase.auth.signInWithOAuth({
|
||||
provider,
|
||||
options: {
|
||||
redirectTo: 'http://example.com/auth/callback',
|
||||
},
|
||||
})
|
||||
|
||||
if (data.url) {
|
||||
redirect(data.url) // use the redirect API for your server framework
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
At the callback endpoint, handle the code exchange to save the user session.
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
@@ -204,48 +237,3 @@ app.get("/auth/callback", async function (req, res) {
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
Let's point our `.signInWithOAuth` method's redirect to the callback route we create above:
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="client"
|
||||
queryGroup="environment"
|
||||
>
|
||||
<TabPanel id="client" label="Client">
|
||||
|
||||
In the browser, `signInWithOAuth` automatically redirects to the SSO provider's authentication endpoint, which then redirects to your endpoint.
|
||||
|
||||
```js
|
||||
await supabase.auth.signInWithOAuth({
|
||||
provider,
|
||||
options: {
|
||||
redirectTo: `http://example.com/auth/callback`,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="server" label="Server">
|
||||
|
||||
In the server, you need to handle the redirect to the OAuth provider's authentication endpoint. The `signInWithOAuth` method returns the endpoint URL, which you can redirect to.
|
||||
|
||||
```js
|
||||
const { data, error } = await supabase.auth.signInWithOAuth({
|
||||
provider,
|
||||
options: {
|
||||
redirectTo: 'http://example.com/auth/callback',
|
||||
},
|
||||
})
|
||||
|
||||
if (data.url) {
|
||||
redirect(data.url) // use the redirect API for your server framework
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
</Tabs>
|
||||
@@ -11,7 +11,7 @@ create table profiles (
|
||||
constraint username_length check (char_length(username) >= 3)
|
||||
);
|
||||
-- Set up Row Level Security (RLS)
|
||||
-- See https://supabase.com/docs/guides/auth/row-level-security for more details.
|
||||
-- See https://supabase.com/docs/guides/database/postgres/row-level-security for more details.
|
||||
alter table profiles
|
||||
enable row level security;
|
||||
|
||||
|
||||
@@ -471,19 +471,19 @@ export const SocialLoginItems = [
|
||||
|
||||
export const PhoneLoginsItems = [
|
||||
{
|
||||
name: 'MessageBird SMS Login',
|
||||
name: 'MessageBird',
|
||||
icon: '/docs/img/icons/messagebird-icon',
|
||||
linkDescription: 'Communication between businesses and their customers — across any channel.',
|
||||
url: '/guides/auth/phone-login/messagebird',
|
||||
},
|
||||
{
|
||||
name: 'Twilio SMS Login',
|
||||
name: 'Twilio',
|
||||
icon: '/docs/img/icons/twilio-icon',
|
||||
url: '/guides/auth/phone-login/twilio',
|
||||
linkDescription: 'Customer engagement platform used by hundreds of thousands of businesses.',
|
||||
},
|
||||
{
|
||||
name: 'Vonage SMS Login',
|
||||
name: 'Vonage',
|
||||
icon: '/docs/img/icons/vonage-icon',
|
||||
url: '/guides/auth/phone-login/vonage',
|
||||
linkDescription:
|
||||
@@ -492,7 +492,7 @@ export const PhoneLoginsItems = [
|
||||
hasLightIcon: true,
|
||||
},
|
||||
{
|
||||
name: 'Textlocal SMS Login (Community Supported)',
|
||||
name: 'Textlocal (Community Supported)',
|
||||
icon: '/docs/img/icons/textlocal-icon',
|
||||
url: '/guides/auth/phone-login/textlocal',
|
||||
linkDescription: 'Textlocal is a cloud-based SMS platform offering bulk messaging services.',
|
||||
@@ -508,83 +508,71 @@ export const auth = {
|
||||
url: '/guides/auth',
|
||||
},
|
||||
{
|
||||
name: 'Redirect URLs',
|
||||
url: '/guides/auth/concepts/redirect-urls',
|
||||
name: 'Architecture',
|
||||
url: '/guides/auth/architecture',
|
||||
},
|
||||
{
|
||||
name: 'Quickstarts',
|
||||
name: 'Getting Started',
|
||||
items: [
|
||||
{ name: 'Next.js', url: '/guides/auth/quickstarts/nextjs', items: [] },
|
||||
{
|
||||
name: 'Next.js',
|
||||
url: '/guides/auth/quickstarts/nextjs',
|
||||
},
|
||||
{ name: 'React', url: '/guides/auth/quickstarts/react', items: [] },
|
||||
{
|
||||
name: 'React Native',
|
||||
url: '/guides/auth/quickstarts/react-native',
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Authentication',
|
||||
url: undefined,
|
||||
name: 'Concepts',
|
||||
items: [
|
||||
{ name: 'Anonymous Sign-Ins', url: '/guides/auth/auth-anonymous' },
|
||||
{ name: 'Email Login', url: '/guides/auth/auth-email' },
|
||||
{ name: 'Users', url: '/guides/auth/users' },
|
||||
{ name: 'Identities', url: '/guides/auth/identities' },
|
||||
{
|
||||
name: 'Passwordless Login',
|
||||
url: '/guides/auth/passwordless-login',
|
||||
name: 'Sessions',
|
||||
url: '/guides/auth/sessions',
|
||||
items: [
|
||||
{
|
||||
name: 'Email Magic Link',
|
||||
url: '/guides/auth/passwordless-login/auth-magic-link',
|
||||
},
|
||||
{
|
||||
name: 'Email OTP',
|
||||
url: '/guides/auth/passwordless-login/auth-email-otp',
|
||||
},
|
||||
{ name: 'Phone OTP', url: '/guides/auth/phone-login' },
|
||||
{ name: 'Implicit flow', url: '/guides/auth/sessions/implicit-flow' },
|
||||
{ name: 'PKCE flow', url: '/guides/auth/sessions/pkce-flow' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Flows (How-tos)',
|
||||
items: [
|
||||
{
|
||||
name: 'Server-Side Rendering',
|
||||
url: '/guides/auth/server-side',
|
||||
items: [
|
||||
{ name: 'Next.js guide', url: '/guides/auth/server-side/nextjs' },
|
||||
{
|
||||
name: 'SvelteKit guide',
|
||||
url: '/guides/auth/server-side/sveltekit',
|
||||
},
|
||||
{ name: 'Creating a client', url: '/guides/auth/server-side/creating-a-client' },
|
||||
{
|
||||
name: 'Migrating from Auth Helpers',
|
||||
url: '/guides/auth/server-side/migrating-to-ssr-from-auth-helpers',
|
||||
},
|
||||
{
|
||||
name: 'Advanced guide',
|
||||
url: '/guides/auth/server-side/advanced-guide',
|
||||
},
|
||||
],
|
||||
},
|
||||
{ name: 'Password-based', url: '/guides/auth/passwords' },
|
||||
{ name: 'Email (Magic Link or OTP)', url: '/guides/auth/auth-email-passwordless' },
|
||||
{
|
||||
name: 'Phone Login',
|
||||
url: '/guides/auth/phone-login',
|
||||
items: [...PhoneLoginsItems],
|
||||
},
|
||||
{
|
||||
name: 'Social Login (OAuth)',
|
||||
url: '/guides/auth/social-login',
|
||||
items: [
|
||||
{
|
||||
name: 'Native Mobile OAuth',
|
||||
url: '/guides/auth/native-mobile-deep-linking',
|
||||
},
|
||||
...SocialLoginItems,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Native Mobile Login',
|
||||
url: '/guides/auth/native-mobile-login',
|
||||
items: [
|
||||
...NativeMobileLoginItems,
|
||||
{
|
||||
name: 'OAuth Deep Linking',
|
||||
url: '/guides/auth/native-mobile-deep-linking',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'User Sessions',
|
||||
url: '/guides/auth/sessions',
|
||||
},
|
||||
{ name: 'Passwords', url: '/guides/auth/passwords' },
|
||||
{
|
||||
name: 'User Management',
|
||||
url: '/guides/auth/auth-user-management',
|
||||
items: [
|
||||
{
|
||||
name: 'Identity Linking',
|
||||
url: '/guides/auth/auth-identity-linking',
|
||||
},
|
||||
],
|
||||
items: [...SocialLoginItems],
|
||||
},
|
||||
{
|
||||
name: 'Enterprise SSO',
|
||||
@@ -596,57 +584,51 @@ export const auth = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{ name: 'Email Templates', url: '/guides/auth/auth-email-templates' },
|
||||
{ name: 'Auth Hooks', url: '/guides/auth/auth-hooks' },
|
||||
{ name: 'Anonymous Sign-Ins', url: '/guides/auth/auth-anonymous' },
|
||||
{ name: 'Mobile Deep Linking', url: '/guides/auth/native-mobile-deep-linking' },
|
||||
{
|
||||
name: 'Identity Linking',
|
||||
url: '/guides/auth/auth-identity-linking',
|
||||
},
|
||||
{ name: 'Multi-Factor Authentication', url: '/guides/auth/auth-mfa' },
|
||||
{
|
||||
name: 'Signout',
|
||||
url: '/guides/auth/signout',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Authorization',
|
||||
url: undefined,
|
||||
name: 'Configuration',
|
||||
items: [
|
||||
{ name: 'Enable Captcha Protection', url: '/guides/auth/auth-captcha' },
|
||||
{ name: 'Configuring Custom SMTP', url: '/guides/auth/auth-smtp' },
|
||||
{ name: 'Managing User Data', url: '/guides/auth/managing-user-data' },
|
||||
{ name: 'Multi-Factor Authentication', url: '/guides/auth/auth-mfa' },
|
||||
{ name: 'Row Level Security', url: '/guides/auth/row-level-security' },
|
||||
{
|
||||
name: 'General Configuration',
|
||||
url: '/guides/auth/general-configuration',
|
||||
},
|
||||
{ name: 'Email Templates', url: '/guides/auth/auth-email-templates' },
|
||||
{
|
||||
name: 'Redirect URLs',
|
||||
url: '/guides/auth/redirect-urls',
|
||||
},
|
||||
{ name: 'Auth Hooks', url: '/guides/auth/auth-hooks' },
|
||||
{ name: 'Custom SMTP', url: '/guides/auth/auth-smtp' },
|
||||
{ name: 'User Management', url: '/guides/auth/managing-user-data' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Security',
|
||||
items: [
|
||||
{ name: 'Password Security', url: '/guides/auth/password-security' },
|
||||
{ name: 'Rate Limits', url: '/guides/auth/rate-limits' },
|
||||
{ name: 'Bot Detection (CAPTCHA)', url: '/guides/auth/auth-captcha' },
|
||||
{ name: 'JWTs', url: '/guides/auth/jwts' },
|
||||
{ name: 'Row Level Security', url: '/guides/database/postgres/row-level-security' },
|
||||
{
|
||||
name: 'Column Level Security',
|
||||
url: '/guides/database/postgres/column-level-security',
|
||||
},
|
||||
{
|
||||
name: 'Custom Claims & RBAC',
|
||||
url: '/guides/auth/custom-claims-and-role-based-access-control-rbac',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Server-side Auth',
|
||||
url: undefined,
|
||||
items: [
|
||||
{ name: 'Overview', url: '/guides/auth/server-side/overview' },
|
||||
{
|
||||
name: 'Next.js guide',
|
||||
url: '/guides/auth/server-side/nextjs',
|
||||
},
|
||||
{
|
||||
name: 'SvelteKit guide',
|
||||
url: '/guides/auth/server-side/sveltekit',
|
||||
},
|
||||
{
|
||||
name: 'Creating a client',
|
||||
url: '/guides/auth/server-side/creating-a-client',
|
||||
},
|
||||
{
|
||||
name: 'Email Auth with PKCE flow',
|
||||
url: '/guides/auth/server-side/email-based-auth-with-pkce-flow-for-ssr',
|
||||
},
|
||||
{
|
||||
name: 'OAuth with PKCE flow',
|
||||
url: '/guides/auth/server-side/oauth-with-pkce-flow-for-ssr',
|
||||
},
|
||||
{
|
||||
name: 'Server-side Rendering',
|
||||
url: '/guides/auth/server-side-rendering',
|
||||
},
|
||||
{
|
||||
name: 'Migrating from Auth Helpers',
|
||||
url: '/guides/auth/server-side/migrating-to-ssr-from-auth-helpers',
|
||||
url: '/guides/database/postgres/custom-claims-and-role-based-access-control-rbac',
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -661,29 +643,6 @@ export const auth = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Deep Dive',
|
||||
url: undefined,
|
||||
items: [
|
||||
{
|
||||
name: 'Part One: JWTs',
|
||||
url: '/guides/auth/auth-deep-dive/auth-deep-dive-jwts',
|
||||
},
|
||||
{
|
||||
name: 'Part Two: Row Level Security',
|
||||
url: '/guides/auth/auth-deep-dive/auth-row-level-security',
|
||||
},
|
||||
{
|
||||
name: 'Part Three: Policies',
|
||||
url: '/guides/auth/auth-deep-dive/auth-policies',
|
||||
},
|
||||
{ name: 'Part Four: GoTrue', url: '/guides/auth/auth-deep-dive/auth-gotrue' },
|
||||
{
|
||||
name: 'Part Five: Google OAuth',
|
||||
url: '/guides/auth/auth-deep-dive/auth-google-oauth',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -770,6 +729,14 @@ export const database: NavMenuConstant = {
|
||||
name: 'Row Level Security',
|
||||
url: '/guides/database/postgres/row-level-security',
|
||||
},
|
||||
{
|
||||
name: 'Column Level Security',
|
||||
url: '/guides/database/postgres/column-level-security',
|
||||
},
|
||||
{
|
||||
name: 'Custom Claims & RBAC',
|
||||
url: '/guides/database/postgres/custom-claims-and-role-based-access-control-rbac',
|
||||
},
|
||||
{
|
||||
name: 'Managing Postgres Roles',
|
||||
url: '/guides/database/postgres/roles',
|
||||
|
||||
@@ -15,7 +15,7 @@ const TopNavBar: FC = () => {
|
||||
return (
|
||||
<nav
|
||||
aria-label="top bar"
|
||||
className="h-[var(--desktop-header-height,60px)] border-b backdrop-blur backdrop-filter bg bg-opacity-75"
|
||||
className="h-[var(--header-height,60px)] border-b backdrop-blur backdrop-filter bg bg-opacity-75"
|
||||
>
|
||||
<div className="px-5 max-w-7xl mx-auto flex gap-3 justify-between items-center h-full">
|
||||
<div className="lg:hidden">
|
||||
|
||||
@@ -1,19 +1,36 @@
|
||||
import { at } from 'lodash'
|
||||
import { ReactNode } from 'react'
|
||||
import { logConstants } from 'shared-data'
|
||||
import { config, logConstants } from 'shared-data'
|
||||
|
||||
const sharedData = {
|
||||
config,
|
||||
logConstants,
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper component to access data from the `shared-data` package within MDX
|
||||
* files.
|
||||
*
|
||||
* @param children - How to access the shared data. If it is a render function,
|
||||
* it takes the data object as a param. If it is a string, it
|
||||
* takes a path through the data object, formatted like
|
||||
* `a[0].b.c`. This path should lead to either a renderable
|
||||
* type or a nested object. If it leads to an object, the
|
||||
* return value is `${object.value} ${object.unit}`.
|
||||
*/
|
||||
function SharedData({
|
||||
data,
|
||||
children,
|
||||
}: {
|
||||
data: keyof typeof sharedData
|
||||
children: (selectedData: (typeof sharedData)[keyof typeof sharedData]) => ReactNode
|
||||
children: ((selectedData: (typeof sharedData)[keyof typeof sharedData]) => ReactNode) | string
|
||||
}) {
|
||||
const selectedData = sharedData[data]
|
||||
return children(selectedData)
|
||||
let selectedData = sharedData[data] as any
|
||||
return typeof children === 'string'
|
||||
? ((typeof (selectedData = at(selectedData, [children])[0]) === 'object'
|
||||
? `${selectedData.value ?? ''} ${selectedData.unit ?? ''}`.trim()
|
||||
: selectedData) as unknown as ReactNode)
|
||||
: children(selectedData)
|
||||
}
|
||||
|
||||
export { SharedData }
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
'use client'
|
||||
|
||||
import { useCallback, type ComponentProps } from 'react'
|
||||
import { Tabs as TabsPrimitive } from 'ui'
|
||||
import { withQueryParams } from 'ui-patterns/ComplexTabs'
|
||||
import { useCallback, type ComponentPropsWithoutRef, type PropsWithChildren } from 'react'
|
||||
import { Tabs as TabsPrimitive, type TabsProps } from 'ui'
|
||||
import { withQueryParams, withSticky, type QueryParamsProps } from 'ui-patterns/ComplexTabs'
|
||||
import { useTocRerenderTrigger } from '~/components/GuidesTableOfContents'
|
||||
|
||||
const TabsWithQueryParams = withQueryParams(TabsPrimitive)
|
||||
const TabsWithStickyAndQueryParams = withSticky<PropsWithChildren<TabsProps & QueryParamsProps>>(
|
||||
withQueryParams(TabsPrimitive)
|
||||
)
|
||||
|
||||
const TabPanel = TabsPrimitive.Panel
|
||||
const Tabs = ({ onChange, ...props }: ComponentProps<typeof TabsWithQueryParams>) => {
|
||||
const Tabs = ({
|
||||
onChange,
|
||||
stickyTabList,
|
||||
...props
|
||||
}: ComponentPropsWithoutRef<typeof TabsWithStickyAndQueryParams>) => {
|
||||
const rerenderToc = useTocRerenderTrigger()
|
||||
const onChangeInternal = useCallback(
|
||||
(...args: Parameters<typeof onChange>) => {
|
||||
@@ -18,7 +24,20 @@ const Tabs = ({ onChange, ...props }: ComponentProps<typeof TabsWithQueryParams>
|
||||
[rerenderToc, onChange]
|
||||
)
|
||||
|
||||
return <TabsWithQueryParams wrappable onChange={onChangeInternal} {...props} />
|
||||
if (stickyTabList && !stickyTabList.scrollMarginTop) {
|
||||
// Magic number is the height of tab list + paragraph margin, worth getting
|
||||
// rid of this?
|
||||
stickyTabList.scrollMarginTop = 'calc(var(--header-height) + 43px + 20px)'
|
||||
}
|
||||
|
||||
return (
|
||||
<TabsWithStickyAndQueryParams
|
||||
wrappable
|
||||
onChange={onChangeInternal}
|
||||
stickyTabList={stickyTabList}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Tabs, TabPanel }
|
||||
|
||||
@@ -24,20 +24,23 @@ import RefHeaderSection from './reference/RefHeaderSection'
|
||||
|
||||
// Other components
|
||||
import AuthProviders from '~/components/AuthProviders'
|
||||
import { CostWarning } from '~/components/AuthSmsProviderConfig/AuthSmsProviderConfig.Warnings'
|
||||
import Options from '~/components/Options'
|
||||
import Param from '~/components/Params'
|
||||
import { ProjectConfigVariables } from './ProjectConfigVariables'
|
||||
import { ProjectConfigVariables } from '~/components/ProjectConfigVariables'
|
||||
|
||||
// Data wrappers
|
||||
import { NavData } from './NavData'
|
||||
import { SharedData } from './SharedData'
|
||||
|
||||
// Partials
|
||||
import AuthRateLimits from './MDX/auth_rate_limits.mdx'
|
||||
import DatabaseSetup from './MDX/database_setup.mdx'
|
||||
import GetSessionWarning from './MDX/get_session_warning.mdx'
|
||||
import HuggingFaceDeployment from './MDX/ai/quickstart_hf_deployment.mdx'
|
||||
import KotlinProjectSetup from './MDX/kotlin_project_setup.mdx'
|
||||
import MigrationWarnings from './MDX/migration_warnings.mdx'
|
||||
import OAuthPkceFlow from './MDX/oauth_pkce_flow.mdx'
|
||||
import ProjectSetup from './MDX/project_setup.mdx'
|
||||
import QuickstartIntro from './MDX/quickstart_intro.mdx'
|
||||
import SocialProviderSettingsSupabase from './MDX/social_provider_settings_supabase.mdx'
|
||||
@@ -70,6 +73,7 @@ import { IconArrowDown, IconCheck } from 'ui'
|
||||
|
||||
// Heavy/rare (lazy-loaded)
|
||||
import { AppleSecretGenerator } from './AppleSecretGenerator'
|
||||
import { AuthSmsProviderConfig } from './AuthSmsProviderConfig'
|
||||
import { Extensions } from './Extensions'
|
||||
import { JwtGenerator } from './JwtGenerator'
|
||||
import { Mermaid } from './Mermaid'
|
||||
@@ -86,11 +90,14 @@ const components = {
|
||||
),
|
||||
AppleSecretGenerator,
|
||||
AuthProviders,
|
||||
AuthRateLimits,
|
||||
AuthSmsProviderConfig,
|
||||
Button,
|
||||
ButtonCard,
|
||||
CH,
|
||||
CliGlobalFlagsHandler: () => <CliGlobalFlagsHandler />,
|
||||
CodeBlock,
|
||||
CostWarning,
|
||||
DatabaseSetup,
|
||||
Extensions,
|
||||
GetSessionWarning,
|
||||
@@ -141,6 +148,7 @@ const components = {
|
||||
Mermaid,
|
||||
MigrationWarnings,
|
||||
NavData,
|
||||
OAuthPkceFlow,
|
||||
Options,
|
||||
Param,
|
||||
ProjectConfigVariables,
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { Fragment } from 'react'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import { CodeBlock, IconDatabase } from 'ui'
|
||||
import { CodeBlock, IconDatabase, Tabs } from 'ui'
|
||||
import components from '~/components'
|
||||
import Options from '~/components/Options'
|
||||
import Param from '~/components/Params'
|
||||
import { Tabs, TabPanel } from '~/components/Tabs'
|
||||
import RefDetailCollapse from '~/components/reference/RefDetailCollapse'
|
||||
import RefSubLayout from '~/layouts/ref/RefSubLayout'
|
||||
import { extractTsDocNode, generateParameters } from '~/lib/refGenerator/helpers'
|
||||
@@ -12,8 +11,6 @@ import { IRefFunctionSection } from './Reference.types'
|
||||
|
||||
const RefFunctionSection: React.FC<IRefFunctionSection> = (props) => {
|
||||
const item = props.spec.functions.find((x: any) => x.id === props.funcData.id)
|
||||
|
||||
// gracefully return nothing if function does not exist
|
||||
if (!item) return <></>
|
||||
|
||||
const hasTsRef = item['$ref'] || null
|
||||
@@ -100,7 +97,6 @@ const RefFunctionSection: React.FC<IRefFunctionSection> = (props) => {
|
||||
size="tiny"
|
||||
type="rounded-pills"
|
||||
scrollable
|
||||
queryGroup="example"
|
||||
>
|
||||
{item.examples &&
|
||||
item.examples.map((example, exampleIndex) => {
|
||||
@@ -123,12 +119,6 @@ const RefFunctionSection: React.FC<IRefFunctionSection> = (props) => {
|
||||
: example?.code?.startsWith('```kotlin')
|
||||
? 'kotlin'
|
||||
: 'js'
|
||||
// `
|
||||
// import { createClient } from '@supabase/supabase-js'
|
||||
|
||||
// // Create a single supabase client for interacting with your database
|
||||
// const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key')
|
||||
// `
|
||||
const staticExample = item.examples[exampleIndex]
|
||||
|
||||
const response = staticExample.response
|
||||
@@ -136,7 +126,7 @@ const RefFunctionSection: React.FC<IRefFunctionSection> = (props) => {
|
||||
const tables = staticExample?.data?.tables
|
||||
|
||||
return (
|
||||
<TabPanel
|
||||
<Tabs.Panel
|
||||
id={example.id}
|
||||
key={exampleIndex}
|
||||
label={example.name}
|
||||
@@ -217,7 +207,7 @@ const RefFunctionSection: React.FC<IRefFunctionSection> = (props) => {
|
||||
</div>
|
||||
</RefDetailCollapse>
|
||||
)}
|
||||
</TabPanel>
|
||||
</Tabs.Panel>
|
||||
)
|
||||
})}
|
||||
</Tabs>
|
||||
|
||||
@@ -20,7 +20,7 @@ The keys are both long-lived JWTs. If you decode these keys, you will see that t
|
||||
|
||||
## The `anon` key
|
||||
|
||||
The `anon` key has very few privileges. You can use it in your [RLS policies](/docs/guides/auth/row-level-security) to allow unauthenticated access. For example, this policy will allow unauthenticated access to the `profiles` table:
|
||||
The `anon` key has very few privileges. You can use it in your [RLS policies](/docs/guides/database/postgres/row-level-security) to allow unauthenticated access. For example, this policy will allow unauthenticated access to the `profiles` table:
|
||||
|
||||
```sql
|
||||
create policy "Allow public access" on profiles to anon for
|
||||
|
||||
@@ -45,7 +45,7 @@ alter table
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
With RLS enabled, you can create Policies that allow or disallow users to access and update data. We provide a detailed guide for creating Row Level Security Policies in our [Authorization documentation](/docs/guides/auth/row-level-security).
|
||||
With RLS enabled, you can create Policies that allow or disallow users to access and update data. We provide a detailed guide for creating Row Level Security Policies in our [Authorization documentation](/docs/guides/database/postgres/row-level-security).
|
||||
|
||||
<Admonition type="danger">
|
||||
|
||||
|
||||
@@ -3,147 +3,38 @@ id: 'auth'
|
||||
title: 'Auth'
|
||||
description: 'Use Supabase to Authenticate and Authorize your users.'
|
||||
subtitle: 'Use Supabase to authenticate and authorize your users.'
|
||||
video: 'https://www.youtube.com/v/6ow_jW4epf8'
|
||||
tocVideo: '6ow_jW4epf8'
|
||||
---
|
||||
|
||||
There are two parts to every Auth system:
|
||||
Supabase Auth makes it easy to implement authentication and authorization in your app. We provide client SDKs and API endpoints to help you create and manage users.
|
||||
|
||||
- **Authentication:** should this person be allowed in? If yes, who are they?
|
||||
- **Authorization:** once they are in, what are they allowed to do?
|
||||
Your users can use many popular Auth methods, including password, magic link, one-time password (OTP), social login, and single sign-on (SSO).
|
||||
|
||||
Supabase Auth is designed to work either as a standalone product, or deeply integrated with the other Supabase products.
|
||||
Postgres is at the heart of everything we do, and the Auth system follows this principle. We leverage Postgres' built-in Auth functionality wherever possible.
|
||||
## About authentication and authorization
|
||||
|
||||
Here's a quick, 2 minute tour of the Auth features built-in to Supabase:
|
||||
Authentication and authorization are the core responsibilities of any Auth system.
|
||||
|
||||
<div className="video-container">
|
||||
<iframe
|
||||
src="https://www.youtube-nocookie.com/embed/6ow_jW4epf8"
|
||||
frameBorder="1"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
</div>
|
||||
- **Authentication** means checking that a user is who they say they are.
|
||||
- **Authorization** means checking what resources a user is allowed to access.
|
||||
|
||||
## Authentication
|
||||
Supabase Auth uses [JSON Web Tokens (JWTs)](/docs/guides/auth/jwt) for authentication. Auth integrates with Supabase's database features, making it easy to use [Row Level Security (RLS)](/docs/guides/database/postgres/row-level-security) for authorization.
|
||||
|
||||
You can authenticate your users in several ways:
|
||||
## The Supabase ecosystem
|
||||
|
||||
- Password-based methods such as [email](/docs/guides/auth/auth-email) or [phone](/docs/guides/auth/phone-login) and password
|
||||
- Passwordless methods such as sending a magiclink or one-time password (OTP)
|
||||
- OAuth social providers
|
||||
- SAML SSO
|
||||
- [Anonymous Sign-ins](/docs/guides/auth/auth-anonymous)
|
||||
You can use Supabase Auth as a standalone product, but it's also built to integrate with the Supabase ecosystem.
|
||||
|
||||
### Providers
|
||||
Auth uses your project's Postgres database under the hood, storing user data and other Auth information in a special schema. You can connect this data to your own tables using triggers and foreign key references.
|
||||
|
||||
We provide a suite of Providers and login methods, as well as [Auth helpers](/docs/guides/auth/auth-helpers/).
|
||||
Auth also enables access control to your database's automatically generated [REST API](/docs/guides/api). When using Supabase SDKs, your data requests are automatically sent with the user's Auth Token. The Auth Token scopes database access on a row-by-row level when used along with [RLS policies](/docs/guides/database/postgres/row-level-security).
|
||||
|
||||
#### Social Auth
|
||||
## Providers
|
||||
|
||||
<div className="container" style={{ padding: 0 }}>
|
||||
<AuthProviders type="social" />
|
||||
</div>
|
||||
Supabase Auth works with many popular Auth methods, including Social and Phone Auth using third-party providers. See the following sections for a list of supported third-party providers.
|
||||
|
||||
#### Phone Auth
|
||||
### Social Auth
|
||||
|
||||
<div className="container" style={{ padding: 0 }}>
|
||||
<AuthProviders type="phone" />
|
||||
</div>
|
||||
<AuthProviders type="social" />
|
||||
|
||||
### Configure third-party providers
|
||||
### Phone Auth
|
||||
|
||||
You can enable third-party providers with the click of a button by navigating to Authentication > Providers > Auth Providers and inputting your `Client ID` and `Secret` for each.
|
||||
|
||||

|
||||
|
||||
### Redirect URLs and wildcards
|
||||
|
||||
We've moved the guide for setting up redirect URLs [here](/docs/guides/auth/concepts/redirect-urls).
|
||||
|
||||
#### [Netlify preview URLs](/docs/guides/auth/concepts/redirect-urls#netlify-preview-urls)
|
||||
|
||||
#### [Vercel preview URLs](/docs/guides/auth/concepts/redirect-urls#vercel-preview-urls)
|
||||
|
||||
#### [Mobile deep linking URIs](/docs/guides/auth/concepts/redirect-urls#mobile-deep-linking-uris)
|
||||
|
||||
## Authorization
|
||||
|
||||
When you need granular authorization rules, nothing beats PostgreSQL's Row Level Security (RLS).
|
||||
|
||||
Policies are PostgreSQL's rule engine. They are incredibly powerful and flexible, allowing you to write complex SQL rules which fit your unique business needs.
|
||||
|
||||
Get started with our [Row Level Security Guides](/docs/guides/auth/row-level-security).
|
||||
|
||||
### Row Level Security
|
||||
|
||||
Authentication only gets you so far. When you need granular authorization rules, nothing beats PostgreSQL's [Row Level Security (RLS)](https://www.postgresql.org/docs/current/ddl-rowsecurity.html). Supabase makes it simple to turn RLS on and off.
|
||||
|
||||
<video width="99%" muted playsInline controls={true}>
|
||||
<source
|
||||
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/rls-zoom2.mp4"
|
||||
type="video/mp4"
|
||||
/>
|
||||
</video>
|
||||
|
||||
### Policies
|
||||
|
||||
[Policies](https://www.postgresql.org/docs/current/sql-createpolicy.html) are PostgreSQL's rule engine. They are incredibly powerful and flexible, allowing you to write complex SQL rules which fit your unique business needs.
|
||||
|
||||
<video width="99%" muted playsInline controls={true}>
|
||||
<source
|
||||
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/policies-zoom2.mp4"
|
||||
type="video/mp4"
|
||||
/>
|
||||
</video>
|
||||
|
||||
With policies, your database becomes the rules engine. Instead of repetitively filtering your queries, like this ...
|
||||
|
||||
```js
|
||||
const loggedInUserId = 'd0714948'
|
||||
const { data, error } = await supabase
|
||||
.from('users')
|
||||
.select('user_id, name')
|
||||
.eq('user_id', loggedInUserId)
|
||||
|
||||
// console.log(data)
|
||||
// => { id: 'd0714948', name: 'Jane' }
|
||||
```
|
||||
|
||||
... you can simply define a rule on your database table, `(select auth.uid()) = user_id`, and your request will return the rows which pass the rule, even when you remove the filter from your middleware:
|
||||
|
||||
```js
|
||||
const { data, error } = await supabase.from('users').select('user_id, name')
|
||||
|
||||
// console.log(data)
|
||||
// Still => { id: 'd0714948', name: 'Jane' }
|
||||
```
|
||||
|
||||
### How it works
|
||||
|
||||
1. A user signs up. Supabase creates a new user in the `auth.users` table.
|
||||
2. Supabase returns a new JWT, which contains the user's `UUID`.
|
||||
3. Every request to your database also sends the JWT.
|
||||
4. Postgres inspects the JWT to determine the user making the request.
|
||||
5. The user's UID can be used in policies to restrict access to rows.
|
||||
|
||||
Supabase provides a special function in Postgres, `auth.uid()`, which extracts the user's UID from the JWT. This is especially useful when creating policies.
|
||||
|
||||
## User management
|
||||
|
||||
Supabase provides multiple endpoints to authenticate and manage your users:
|
||||
|
||||
- [Sign up](/docs/reference/javascript/auth-signup)
|
||||
- [Sign in with password](/docs/reference/javascript/auth-signinwithpassword)
|
||||
- [Sign in with passwordless / one-time password (OTP)](/docs/reference/javascript/auth-signinwithotp)
|
||||
- [Sign in with OAuth](/docs/reference/javascript/auth-signinwithoauth)
|
||||
- [Sign out](/docs/reference/javascript/auth-signout)
|
||||
|
||||
When users sign up, Supabase assigns them a unique ID. You can reference this ID anywhere in your database. For example, you might create a `profiles` table referencing `id` in the `auth.users` table using a `user_id` field.
|
||||
|
||||
<video width="99%" muted playsInline controls={true}>
|
||||
<source
|
||||
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/auth-zoom2.mp4"
|
||||
type="video/mp4"
|
||||
/>
|
||||
</video>
|
||||
<AuthProviders type="phone" />
|
||||
|
||||
34
apps/docs/content/guides/auth/_flow-template.mdx
Normal file
34
apps/docs/content/guides/auth/_flow-template.mdx
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
title: A DESCRIPTIVE TITLE
|
||||
subtitle: A DESCRIPTIVE SUBTITLE
|
||||
---
|
||||
|
||||
{/* Use this template to document Auth Flows. These should be how-to guides, walking the reader through the process of (1) enabling the feature and (2) triggering the flow from their code. */}
|
||||
|
||||
A brief description of what this flow does. Don't get into details: if the concepts require a lot of explanation, make a separate page under Concepts.
|
||||
|
||||
## Enabling WHATEVER_FLOW
|
||||
|
||||
If the feature is default on, say so. If the feature needs to be turned on in the Dashboard, say how. If the feature can be turned on in self-hosted, say how.
|
||||
|
||||
If configuration is required, say how. If configuration is extensive, you can link to a separate explanation under Configuration.
|
||||
|
||||
## Using the flow (1)
|
||||
|
||||
Step-by-step instructions on how to use the flow. This assumes configuration is already done, and just contains the application code needed by the developer.
|
||||
|
||||
This first section should contain the most basic example of the flow (e.g., a client-side sign in with password).
|
||||
|
||||
If multiple steps are required, use an ordered list. Always include code examples.
|
||||
|
||||
## Using the flow (2)
|
||||
|
||||
Step-by-step instructions on how to use the flow. This assumes configuration is already done, and just contains the application code needed by the developer.
|
||||
|
||||
This can contain another related flow (e.g., change password, or PKCE version of flow 1).
|
||||
|
||||
If multiple steps are required, use an ordered list. Always include code examples.
|
||||
|
||||
## Resources
|
||||
|
||||
{/* Optional section containing a list of links to learn more. */}
|
||||
70
apps/docs/content/guides/auth/architecture.mdx
Normal file
70
apps/docs/content/guides/auth/architecture.mdx
Normal file
@@ -0,0 +1,70 @@
|
||||
---
|
||||
title: 'Auth architecture'
|
||||
subtitle: 'The architecture behind Supabase Auth.'
|
||||
---
|
||||
|
||||
There are four major layers to Supabase Auth:
|
||||
|
||||
1. [Client layer.](#client-layer) This can be one of the Supabase client SDKs, or manually made HTTP requests using the HTTP client of your choice.
|
||||
1. Kong API gateway. This is shared between all Supabase products.
|
||||
1. [Auth service](#auth-service) (formerly known as GoTrue).
|
||||
1. [Postgres database.](#postgres) This is shared between all Supabase products.
|
||||
|
||||
<Image
|
||||
alt="Diagram showing the architecture of Supabase. The Kong API gateway sits in front of 7 services: GoTrue, PostgREST, Realtime, Storage, pg_meta, Functions, and pg_graphql. All the services talk to a single Postgres instance."
|
||||
src={{
|
||||
dark: '/docs/img/supabase-architecture.svg',
|
||||
light: '/docs/img/supabase-architecture--light.svg',
|
||||
}}
|
||||
/>
|
||||
|
||||
## Client layer
|
||||
|
||||
The client layer runs in your app. This could be running in many places, including:
|
||||
|
||||
- Your frontend browser code
|
||||
- Your backend server code
|
||||
- Your native application
|
||||
|
||||
The client layer provides the functions that you use to sign in and manage users. We recommend using the Supabase client SDKs, which handle:
|
||||
|
||||
- Configuration and authentication of HTTP calls to the Supabase Auth backend
|
||||
- Persistence, refresh, and removal of Auth Tokens in your app's storage medium
|
||||
- Integration with other Supabase products
|
||||
|
||||
But at its core, this layer manages the making of HTTP calls, so you could write your own client layer if you wanted to.
|
||||
|
||||
See the Client SDKs for more information:
|
||||
|
||||
- [JavaScript](/docs/reference/javascript/introduction)
|
||||
- [Flutter](/docs/reference/dart/introduction)
|
||||
- [Swift](/docs/reference/swift/introduction)
|
||||
- [Python](/docs/reference/python/introduction)
|
||||
- [C#](/docs/reference/csharp/introduction)
|
||||
- [Kotlin](/docs/reference/kotlin/introduction)
|
||||
|
||||
## Auth service
|
||||
|
||||
The [Auth service](https://github.com/supabase/auth) is an Auth API server written and maintained by Supabase. It is a fork of the GoTrue project, originally created by Netlify.
|
||||
|
||||
When you deploy a new Supabase project, we deploy an instance of this server alongside your database, and inject your database with the required Auth schema.
|
||||
|
||||
The Auth service is responsible for:
|
||||
|
||||
- Validating, issuing, and refreshing JWTs
|
||||
- Serving as the intermediary between your app and Auth information in the database
|
||||
- Communicating with external providers for Social Login and SSO
|
||||
|
||||
## Postgres
|
||||
|
||||
Supabase Auth uses the `auth` schema in your Postgres database to store user tables and other information. For security, this schema is not exposed on the auto-generated API.
|
||||
|
||||
You can connect Auth information to your own objects using [database triggers](/docs/guides/database/postgres/triggers) and [foreign keys](https://www.postgresql.org/docs/current/tutorial-fk.html). Make sure that any views you create for Auth data are adequately protected by [enabling RLS](/docs/guides/database/postgres/row-level-security) or [revoking grants](https://www.postgresql.org/docs/current/sql-revoke.html).
|
||||
|
||||
<Admonition type="danger">
|
||||
|
||||
Make sure any views you create for Auth data are protected.
|
||||
|
||||
Starting in Postgres version 15, views inherit the RLS policies of the underlying tables if created with `security_invoker`. Views in earlier versions, or those created without `security_invoker`, inherit the permissions of the owner, who can bypass RLS policies.
|
||||
|
||||
</Admonition>
|
||||
@@ -133,10 +133,10 @@ try await supabase.auth.updateUser(
|
||||
</TabPanel>
|
||||
<TabPanel id="kotlin" label="Kotlin">
|
||||
|
||||
You can use the [`updateUser()`](/docs/reference/kotlin/auth-updateuser) method to link an email or phone identity to the anonymous user.
|
||||
You can use the [`modifyUser()`](/docs/reference/kotlin/auth-updateuser) method to link an email or phone identity to the anonymous user.
|
||||
|
||||
```kotlin
|
||||
supabase.auth.updateUser {
|
||||
supabase.auth.modifyUser {
|
||||
email = "example@email.com"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -2,20 +2,11 @@
|
||||
id: 'auth-captcha'
|
||||
title: 'Enable Captcha Protection'
|
||||
description: 'Add Captcha Protection to your Supabase project'
|
||||
video: 'https://www.youtube.com/v/em1cpOAXknM'
|
||||
tocVideo: 'em1cpOAXknM'
|
||||
---
|
||||
|
||||
Supabase provides you with the option of adding captcha to your sign-in, sign-up, and password reset forms. This keeps your website safe from bots and malicious scripts. Supabase authentication has support for [hCaptcha](https://www.hcaptcha.com/) and [Cloudflare Turnstile](https://www.cloudflare.com/products/turnstile/).
|
||||
|
||||
<div className="video-container">
|
||||
<iframe
|
||||
src="https://www.youtube-nocookie.com/embed/em1cpOAXknM"
|
||||
frameBorder="1"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
## Sign up for Captcha
|
||||
|
||||
<Tabs
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
---
|
||||
id: 'auth-google-oauth'
|
||||
title: 'Part Five: Google OAuth'
|
||||
description: 'Supabase Deep Dive Part 5: Google OAuth Provider'
|
||||
video: 'https://www.youtube.com/v/_XM9ziOzWk4'
|
||||
---
|
||||
|
||||
### About
|
||||
|
||||
How to add Google OAuth Logins to your Supabase Application.
|
||||
|
||||
### Watch
|
||||
|
||||
<div className="video-container">
|
||||
<iframe
|
||||
src="https://www.youtube-nocookie.com/embed/_XM9ziOzWk4"
|
||||
frameBorder="1"
|
||||
allow="accelerometer;autoplay; clipboard-write encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
### Logging in with external OAuth providers
|
||||
|
||||
Connecting social logins such as Google, GitHub, or Facebook couldn't be easier. In this guide we'll walk you through the process of connecting Google, but the process is basically the same for all of the providers which includes: Azure, Bitbucket, GitHub, GitLab, Facebook, and Google.
|
||||
|
||||
First you'll need to create a Google project inside their [cloud console](https://console.cloud.google.com/home/dashboard), in other providers they may refer to this as an "app" and is usually available on the company's developer portal.
|
||||
|
||||

|
||||
|
||||
Once you have a project, type "OAuth" into the search bar and open up "OAuth Consent Screen"
|
||||
|
||||

|
||||
|
||||
Select 'External' and proceed to fill out the rest of the form fields
|
||||
|
||||

|
||||
|
||||
Next open up Credentials page on the left
|
||||
|
||||

|
||||
|
||||
And click to create a new set of credentials, select OAuth client ID as the option
|
||||
|
||||

|
||||
|
||||
Now choose Web Application (assuming you're creating a web app) and in the Authorized redirect URI section you need to add: `https://<your-ref>.supabase.co/auth/v1/callback`. You can find your Supabase URL in Settings > API inside the Supabase dashboard.
|
||||
|
||||

|
||||
|
||||
Now you can grab the client ID and secret from the popup, and insert them into the Google section inside the Supabase dashboard in Auth > Settings:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
Hit save. Now you should be able to navigate in the browser to:
|
||||
|
||||
```
|
||||
https://<your-ref>.supabase.co/auth/v1/authorize?provider=google
|
||||
```
|
||||
|
||||
And log in to your service using any Google or Gmail account.
|
||||
|
||||
You can additionally add a query parameter `redirect_to=` to the end of the URL for example:
|
||||
|
||||
```
|
||||
https://<your-ref>.supabase.co/auth/v1/authorize?provider=google&redirect_to=http://localhost:3000/welcome
|
||||
```
|
||||
|
||||
But make sure any URL you enter here is on the same host as the site url that you have entered on the Auth > Settings page on the Supabase dashboard. (There is additional functionality coming soon, where you'll be able to add additional URLs to the allow list).
|
||||
|
||||
If you want to redirect the user to a specific page in your website or app after a successful authentication.
|
||||
|
||||
You also have the option of requesting additional scopes from the OAuth provider. Let's say for example you want the ability to send emails on behalf of the user's gmail account. You can do this by adding the query parameter `scopes`, like:
|
||||
|
||||
```
|
||||
https://<your-ref>.supabase.co/auth/v1/authorize?provider=google&https://www.googleapis.com/auth/gmail.send
|
||||
```
|
||||
|
||||
Alternatively, if you're using the `supabase-js` client library, you can call the [`signInWithOAuth()` method](/docs/reference/javascript/auth-signinwithoauth):
|
||||
|
||||
```js
|
||||
const { data, error } = await supabase.auth.signInWithOAuth({ provider: 'google' })
|
||||
```
|
||||
|
||||
Note however that your app will usually have to be verified by Google before you can request advanced scopes such as this.
|
||||
|
||||
The only thing left to implement is the UI, but if you prefer to use something pre-built, we have a handy [Auth Widget](https://github.com/supabase/ui/#using-supabase-ui-auth), where you can enable/disable whichever auth providers you want to support.
|
||||
|
||||
For any support see [supabase.com/support](https://supabase.com/support) or for feature requests open an issue in the [backend](https://github.com/supabase/auth) or [frontend](https://github.com/supabase/auth-js) repos.
|
||||
|
||||
### Resources
|
||||
|
||||
- [JWT debugger](https://jwt.io)
|
||||
- [Supabase Auth API Reference](/docs/reference/javascript/auth-api)
|
||||
|
||||
### Next steps
|
||||
|
||||
- Watch [Part One: JWTs](./auth-deep-dive-jwts)
|
||||
- Watch [Part Two: Row Level Security](./auth-row-level-security)
|
||||
- Watch [Part Three: Policies](./auth-policies)
|
||||
- Watch [Part Four: GoTrue](./auth-gotrue)
|
||||
{/* <!-- - Watch [Part Five: Google Oauth](./auth-google-oauth) --> */}
|
||||
- Sign up for Supabase: [supabase.com/dashboard](https://supabase.com/dashboard)
|
||||
@@ -1,79 +0,0 @@
|
||||
---
|
||||
id: 'auth-gotrue'
|
||||
title: 'Part Four: GoTrue'
|
||||
description: 'Supabase Deep Dive Part 4: GoTrue Overview'
|
||||
---
|
||||
|
||||
### About
|
||||
|
||||
How to restrict table access to authenticated users, row level policies, and email domain based access.
|
||||
|
||||
### Watch
|
||||
|
||||
<div className="video-container">
|
||||
<iframe
|
||||
src="https://www.youtube.com/embed/neqfYym_84k"
|
||||
frameBorder="1"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
### GoTrue Server
|
||||
|
||||
GoTrue is an auth API server written in Go by the Netlify team, find the Supabase fork [here](https://github.com/supabase/gotrue). The list of available API endpoints is available [here](https://github.com/supabase/gotrue#endpoints).
|
||||
|
||||
When you deploy a new Supabase project, we deploy a new instance of this server alongside your database, and also inject your database with the required `auth` schema.
|
||||
|
||||
It makes it super easy to, for example, send magic link emails which your users can use to login:
|
||||
|
||||
```bash
|
||||
# replace <project-ref> with your own project reference
|
||||
# and SUPABASE_KEY with your anon api key
|
||||
curl -X POST 'https://<project-ref>.supabase.co/auth/v1/magiclink' \
|
||||
-H "apikey: SUPABASE_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"email": "someone@email.com"
|
||||
}'
|
||||
```
|
||||
|
||||
GoTrue is responsible for issuing access tokens for your users, sends confirmation, magic-link, and password recovery emails (by default we send these from a Supabase SMTP server, but you can easily plug in your own inside the dashboard at Project Settings > Auth) and also transacting with third party OAuth providers to get basic user data.
|
||||
|
||||
The community even recently built in the functionality to request custom OAuth scopes, if your users need to interact more closely with the provider. See the scopes parameter here: [https://github.com/supabase/gotrue#get-authorize](https://github.com/supabase/gotrue#get-authorize).
|
||||
|
||||
So let's say you want to send emails on behalf of a user via Gmail, you might request the gmail.send scope by directing them to:
|
||||
|
||||
```
|
||||
https://sjvwsaokcugktsdaxxze.supabase.co/auth/v1/authorize?provider=google&https://www.googleapis.com/auth/gmail.send
|
||||
```
|
||||
|
||||
You'll have to make sure your Google app is verified of course in order to request these advanced scopes.
|
||||
|
||||
[gotrue-js](https://github.com/supabase/gotrue-js) (and also [gotrue-csharp](https://github.com/supabase/gotrue-csharp), [gotrue-py](https://github.com/j0/gotrue-py), [gotrue-kt](https://github.com/supabase/gotrue-kt), and [gotrue-dart](https://github.com/supabase/gotrue-dart)) are all wrappers around the GoTrue API endpoints, and make for easier session management inside your client.
|
||||
|
||||
But all the functionality of gotrue-js is also available in supabase-js, which uses gotrue-js internally when you do things like:
|
||||
|
||||
```jsx
|
||||
const { user, session, error } = await supabase.auth.signInWithPassword({
|
||||
email: 'example@email.com',
|
||||
password: 'example-password',
|
||||
})
|
||||
```
|
||||
|
||||
If you want to request a feature, or contribute to the project directly, just head to https://github.com/supabase/gotrue and open some issues/PRs, we're always open to help.
|
||||
|
||||
In the next guide we'll be looking at how to setup external OAuth providers: Watch [Part Five: Google OAuth](./auth-google-oauth)
|
||||
|
||||
### Resources
|
||||
|
||||
- JWT debugger: https://jwt.io
|
||||
|
||||
### Next steps
|
||||
|
||||
- Watch [Part One: JWTs](./auth-deep-dive-jwts)
|
||||
- Watch [Part Two: Row Level Security](./auth-row-level-security)
|
||||
- Watch [Part Three: Policies](./auth-policies)
|
||||
{/* <!-- Watch [Part Four: GoTrue](./auth-gotrue) --> */}
|
||||
- Watch [Part Five: Google OAuth](./auth-google-oauth)
|
||||
- Sign up for Supabase: [supabase.com/dashboard](https://supabase.com/dashboard)
|
||||
@@ -1,186 +0,0 @@
|
||||
---
|
||||
id: 'auth-policies'
|
||||
title: 'Part Three: Policies'
|
||||
description: 'Supabase Auth Deep Dive Part 3: User Based Access Policies'
|
||||
video: 'https://www.youtube.com/v/0LvCOlELs5U'
|
||||
---
|
||||
|
||||
### About
|
||||
|
||||
How to restrict table access to authenticated users, row level policies, and email domain based access.
|
||||
|
||||
### Watch
|
||||
|
||||
<div className="video-container">
|
||||
<iframe
|
||||
src="https://www.youtube-nocookie.com/embed/0LvCOlELs5U"
|
||||
frameBorder="1"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
### User based row level policies
|
||||
|
||||
Now that we know how to restrict access to tables based on JWT roles, we can combine this with user management to give us much more control over what data your users can read to and write from your database.
|
||||
|
||||
We'll start with how user sessions work in Supabase, and later move on to writing user-centric policies.
|
||||
|
||||
Let's say we're signing a user up to our service for the first time. The typical way to do this is by invoking the following method in supabase-js:
|
||||
|
||||
```jsx
|
||||
// see full api reference here: /docs/reference/javascript/auth-signup
|
||||
supabase.auth.signUp({ email, password })
|
||||
```
|
||||
|
||||
By default this will send a confirmation email to the user. When the user clicks the link in the email, they will be redirected to your site (you need to provide your site url in Auth > Settings on the dashboard. By default this is http://localhost:3000) and the full URL including query params will look something like this:
|
||||
|
||||
```
|
||||
http://localhost:3000/#access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNjE2NDI5MDY0LCJzdWIiOiI1YTQzNjVlNy03YzdkLTRlYWYtYThlZS05ZWM5NDMyOTE3Y2EiLCJlbWFpbCI6ImFudEBzdXBhYmFzZS5pbyIsImFwcF9tZXRhZGF0YSI6eyJwcm92aWRlciI6ImVtYWlsIn0sInVzZXJfbWV0YWRhdGEiOnt9LCJyb2xlIjoiYXV0aGVudGljYXRlZCJ9.4IFzn4eymqUNYYo2AHLxNRL8m08G93Qcg3_fblGqDjo&expires_in=3600&refresh_token=RuioJv2eLV05lgH5AlJwTw&token_type=bearer&type=signup
|
||||
```
|
||||
|
||||
Let's break this up so that it's easier to read:
|
||||
|
||||
```jsx
|
||||
// the base url - whatever you set in the Auth Settings in supabase.com/dashboard dashboard
|
||||
http://localhost:3000/
|
||||
|
||||
// note we use the '#' (fragment) instead of '?' query param
|
||||
// the access token is a JWT issued to the user
|
||||
#access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNjE2NDI5MDY0LCJzdWIiOiI1YTQzNjVlNy03YzdkLTRlYWYtYThlZS05ZWM5NDMyOTE3Y2EiLCJlbWFpbCI6ImFudEBzdXBhYmFzZS5pbyIsImFwcF9tZXRhZGF0YSI6eyJwcm92aWRlciI6ImVtYWlsIn0sInVzZXJfbWV0YWRhdGEiOnt9LCJyb2xlIjoiYXV0aGVudGljYXRlZCJ9.4IFzn4eymqUNYYo2AHLxNRL8m08G93Qcg3_fblGqDjo
|
||||
|
||||
// valid for 60 minutes by default
|
||||
&expires_in=3600
|
||||
|
||||
// use to get a new access_token before 60 minutes expires
|
||||
&refresh_token=RuioJv2eLV05lgH5AlJwTw
|
||||
|
||||
// can use as the Authorization: Bearer header in requests to your API
|
||||
&token_type=bearer
|
||||
|
||||
// why was this token issued? was it a signup, login, password reset, or magic link?
|
||||
&type=signup
|
||||
```
|
||||
|
||||
If we put the access_token into [https://jwt.io](https://jwt.io) we'll see it decodes to:
|
||||
|
||||
```jsx
|
||||
{
|
||||
"aud": "authenticated",
|
||||
"exp": 1616429064,
|
||||
"sub": "5a4365e7-7c7d-4eaf-a8ee-9ec9432917ca",
|
||||
"email": "ant@supabase.io",
|
||||
"app_metadata": {
|
||||
"provider": "email"
|
||||
},
|
||||
"user_metadata": {},
|
||||
"role": "authenticated"
|
||||
}
|
||||
```
|
||||
|
||||
The `authenticated` role is special in Supabase, it tells the API that this is an authenticated user and will know to compare the JWT against any policies you've added to the requested resource (table or row).
|
||||
|
||||
The `sub` claim is usually what we use to match the JWT to rows in your database, since by default it is the unique identifier of the user in the `auth.users` table (as a side note - it's generally not recommended to alter the `auth` schema in any way in your Supabase database since the Auth API relies on it to function correctly).
|
||||
|
||||
For the curious, try heading to the SQL editor and querying:
|
||||
|
||||
```sql
|
||||
select * from auth.users;
|
||||
```
|
||||
|
||||
If supabase-js is loaded on your site (in this case http://localhost:3000) it automatically plucks the `access_token` out of the URL and initiates a session. You can retrieve the [session](/docs/reference/javascript/auth-getsession) to see if there is a valid session:
|
||||
|
||||
```jsx
|
||||
console.log(supabase.auth.getSession())
|
||||
```
|
||||
|
||||
<GetSessionWarning />
|
||||
|
||||
Now that we can use methods to issue JWTs to users, we want to start fetching resources specific to that user. So let's make some. Go to the SQL editor and run:
|
||||
|
||||
```sql
|
||||
create table my_scores (
|
||||
name text,
|
||||
score int,
|
||||
user_id uuid not null
|
||||
);
|
||||
|
||||
ALTER TABLE my_scores ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
insert into my_scores(name, score, user_id)
|
||||
values
|
||||
('Paul', 100, '5a4365e7-7c7d-4eaf-a8ee-9ec9432917ca'),
|
||||
('Paul', 200, '5a4365e7-7c7d-4eaf-a8ee-9ec9432917ca'),
|
||||
('Leto', 50, '9ec94326-2e2d-2ea2-22e3-3a535a4365e7');
|
||||
|
||||
-- use UUIDs from the auth.users table if you want to try it
|
||||
-- for yourself
|
||||
```
|
||||
|
||||
Now we'll write our policy, again in SQL, but note it's also possible to add via the dashboard in Auth > Policies:
|
||||
|
||||
```sql
|
||||
CREATE POLICY user_update_own_scores ON my_scores
|
||||
FOR ALL
|
||||
USING ((select auth.uid()) = user_id);
|
||||
```
|
||||
|
||||
Now, assuming you have an active session in your javascript/supabase-js environment you can do:
|
||||
|
||||
```jsx
|
||||
supabase.from('my_scores').select('*').then(console.log)
|
||||
```
|
||||
|
||||
and you should only receive scores belonging to the current logged in user. Alternatively you can use Bash like:
|
||||
|
||||
```bash
|
||||
curl 'https://sjvwsaokcugktsdaxxze.supabase.co/rest/v1/my_scores?select=*' \
|
||||
-H "apikey: <ANON_KEY>" \
|
||||
-H "Authorization: Bearer <ACCESS_TOKEN>"
|
||||
```
|
||||
|
||||
Note that the `anon key` (or `service role key`) is always needed to get past the API gateway. This can be passed in the `apikey` header or in a query param named `apikey`. It is passed automatically in supabase-js as long as you used it to instantiate the client.
|
||||
|
||||
There are some more notes here on how to structure your schema to best integrate with the `auth.users` table.
|
||||
|
||||
Once you get the hang of policies you can start to get a little bit fancy. Let's say I work at Blizzard and I only want Blizzard staff members to be able to update people's high scores, I can write something like:
|
||||
|
||||
```sql
|
||||
create policy "Only Blizzard staff can update leaderboard"
|
||||
on my_scores
|
||||
for update using (
|
||||
right((select auth.jwt() ->> 'email'), 13) = '@blizzard.com'
|
||||
);
|
||||
```
|
||||
|
||||
Supabase comes with two built-in helper functions: `auth.uid()` and `auth.jwt()`.
|
||||
|
||||
To create your own functions, navigate to the SQL editor and create a new query.
|
||||
|
||||
```sql
|
||||
create or replace function user_agent()
|
||||
returns text
|
||||
language sql
|
||||
as $$
|
||||
select nullif(current_setting('request.headers', true)::json->>'user-agent', '')::text;
|
||||
$$;
|
||||
```
|
||||
|
||||
See the full PostgreSQL policy docs here: [https://www.postgresql.org/docs/12/sql-createpolicy.html](https://www.postgresql.org/docs/12/sql-createpolicy.html)
|
||||
|
||||
You can get as creative as you like with these policies.
|
||||
|
||||
### Resources
|
||||
|
||||
- JWT debugger: https://jwt.io
|
||||
- PostgeSQL Policies: https://www.postgresql.org/docs/12/sql-createpolicy.html
|
||||
- PostgREST Row Level Security: https://postgrest.org/en/v7.0.0/auth.html
|
||||
|
||||
### Next steps
|
||||
|
||||
- Watch [Part One: JWTs](./auth-deep-dive-jwts)
|
||||
- Watch [Part Two: Row Level Security](./auth-row-level-security)
|
||||
{/* <!-- Watch [Part Three: Policies](./auth-policies) --> */}
|
||||
- Watch [Part Four: GoTrue](./auth-gotrue)
|
||||
- Watch [Part Five: Google Oauth](./auth-google-oauth)
|
||||
- Sign up for Supabase: [supabase.com/dashboard](https://supabase.com/dashboard)
|
||||
@@ -1,135 +0,0 @@
|
||||
---
|
||||
id: 'auth-row-level-security'
|
||||
title: 'Part Two: Row Level Security'
|
||||
description: 'Supabase Auth Deep Dive Part Two - Row Level Security'
|
||||
video: 'https://www.youtube.com/v/qY_iQ10IUhs'
|
||||
---
|
||||
|
||||
### About
|
||||
|
||||
Learn how to restrict access to your database tables by enabling Row Level Security and writing Postgres Policies in the Supabase Dashboard.
|
||||
|
||||
### Watch
|
||||
|
||||
<div className="video-container">
|
||||
<iframe
|
||||
src="https://www.youtube-nocookie.com/embed/qY_iQ10IUhs"
|
||||
frameBorder="1"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
### Securing Your Tables
|
||||
|
||||
In Supabase, you can access your data directly from the client (often the web browser), you do this by passing your Supabase URL and Anon key to supabase-js like so:
|
||||
|
||||
```js
|
||||
const supabase = createClient(
|
||||
'https://qwertyuiop.supabase.co',
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'
|
||||
)
|
||||
```
|
||||
|
||||
This raises an interesting question however: "if my anon key is in the client, then can't someone read my javascript and steal my key?", the answer is yes. And this is where Postgres policies come in.
|
||||
|
||||
Using Postgres's "Row-Level-Security" policies, we can set rules on what data the anon key is allowed or not allowed to access by default.
|
||||
|
||||
We can say for example that the anon key should only be able to read from a particular table, but not write, update, nor delete.
|
||||
|
||||
And these rules can be as complex as we want. We could say that the anon key can only delete rows which were inserted on a Thursday afternoon between 4 and 6pm, and where the id column is an even number. Pretty strange, but it shows the power of policies.
|
||||
|
||||
Let's say we create a leaderboard table. We want people on our website to be able to read the leaderboard, but not write, update, or delete from it. We start by defining our table in SQL and adding some dummy data:
|
||||
|
||||
```sql
|
||||
create table leaderboard (
|
||||
name text,
|
||||
score int
|
||||
);
|
||||
|
||||
insert into leaderboard
|
||||
(name, score)
|
||||
values
|
||||
('Paul', 100),
|
||||
('Leto', 50),
|
||||
('Chani', 200);
|
||||
```
|
||||
|
||||
Now let's set up a client to read the data, I've created a repl here to show a living example: [https://replit.com/@awalias/supabase-leaderboard-demo#index.js](https://replit.com/@awalias/supabase-leaderboard-demo#index.js). If you copy the snippet you can plug in your own Supabase URL and anon key.
|
||||
|
||||
You can see that it's possible to freely read from and write to the table by using:
|
||||
|
||||
```js
|
||||
// Writing
|
||||
const { data, error } = await supabase.from('leaderboard').insert({ name: 'Bob', score: 99999 })
|
||||
|
||||
// Reading
|
||||
const { data, error } = await supabase
|
||||
.from('leaderboard')
|
||||
.select('name, score')
|
||||
.order('score', { ascending: false })
|
||||
```
|
||||
|
||||
Now let's restrict access. We'll start by fully restricting the table. We can do this in the SQL editor by making a query:
|
||||
|
||||
```sql
|
||||
ALTER TABLE leaderboard ENABLE ROW LEVEL SECURITY;
|
||||
```
|
||||
|
||||
or via the Supabase Dashboard, by navigating to Auth > Policies, and clicking the red padlock on the leaderboard table, so that it turns white.
|
||||
|
||||

|
||||
|
||||
You'll notice that reading will return no rows and writing will now fail with an error like:
|
||||
|
||||
```jsx
|
||||
{
|
||||
hint: null,
|
||||
details: null,
|
||||
code: '42501',
|
||||
message: 'new row violates row-level security policy for table "leaderboard"'
|
||||
}
|
||||
```
|
||||
|
||||
Now we need to add a policy to enable reading of the table, for everyone who sends the anon key (JWT) in the `Authorization: Bearer` header.
|
||||
|
||||
In SQL this can be done with:
|
||||
|
||||
```sql
|
||||
CREATE POLICY anon_read_leaderboard ON leaderboard
|
||||
FOR SELECT
|
||||
TO 'anon'
|
||||
USING (true);
|
||||
```
|
||||
|
||||
`anon_read_leaderboard` here is just a name that you choose for your policy. `leaderboard` is the table name. `FOR SELECT` says that we only want this policy to apply for reads (or rather "selects" in SQL). `TO` means that this policy will only apply to the `anon` Postgres role. And finally the rule itself is `true'`, which means it will _allow_ any `selects` to the `anon` user.
|
||||
|
||||
If you'd prefer to use the dashboard to add your policy you can do so by clicking "Add Policy" in the Policies tab and making a policy like this:
|
||||
|
||||

|
||||
|
||||
<Admonition type="caution">
|
||||
|
||||
Please note that comments are not supported when using RLS policies via the dashboard.
|
||||
|
||||
</Admonition>
|
||||
|
||||
You should now be able to read from your leaderboard, but will still not be able to write, update, or delete from it, which is exactly what we wanted!
|
||||
|
||||
A quick reminder that you can always use your `service_role` API key to bypass these row level security policies. But be extra careful that you don't leak this key by including it in the client. This can be useful if you're building internal admin tools, or if you need to bulk insert or delete data via the API.
|
||||
|
||||
In the next guide we will look at using Policies in combination with User Accounts, so that you can restrict access to data on a User by User basis: Watch [Part Three: Policies](./auth-policies)
|
||||
|
||||
### Resources
|
||||
|
||||
- JWT debugger: https://jwt.io/
|
||||
- RESTED: https://github.com/RESTEDClient/RESTED
|
||||
|
||||
### Next steps
|
||||
|
||||
- Watch [Part One: JWTs](./auth-deep-dive-jwts)
|
||||
{/* <!-- - Watch [Part Two: Row Level Security](./auth-row-level-security) --> */}
|
||||
- Watch [Part Three: Policies](./auth-policies)
|
||||
- Watch [Part Four: GoTrue](./auth-gotrue)
|
||||
- Watch [Part Five: Google Oauth](./auth-google-oauth)
|
||||
- Sign up for Supabase: [supabase.com/dashboard](https://supabase.com/dashboard)
|
||||
281
apps/docs/content/guides/auth/auth-email-passwordless.mdx
Normal file
281
apps/docs/content/guides/auth/auth-email-passwordless.mdx
Normal file
@@ -0,0 +1,281 @@
|
||||
---
|
||||
title: 'Passwordless email logins'
|
||||
subtitle: 'Email logins using Magic Links or One-Time Passwords (OTPs)'
|
||||
---
|
||||
|
||||
Supabase Auth provides several passwordless login methods. Passwordless logins allow users to sign in without a password, by clicking a confirmation link or entering a verification code.
|
||||
|
||||
Passwordless login 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 login 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 login where users click on a link sent to their email address to log 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 [Auth Providers page](/dashboard/project/_/auth/providers) for hosted projects, or in the [configuration file](/docs/guides/cli/config#auth.additional_redirect_urls) for self-hosted projects.
|
||||
|
||||
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
|
||||
async function signInWithEmail() {
|
||||
const { data, error } = await supabase.auth.signInWithOtp({
|
||||
email: 'example@email.com',
|
||||
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: 'example@email.com',
|
||||
options: {
|
||||
emailRedirectTo: redirectTo,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
Read the [Deep Linking Documentation](/docs/guides/auth/native-mobile-deep-linking) to learn how to handle deep linking.
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="dart" label="Dart">
|
||||
|
||||
```dart
|
||||
Future<void> signInWithEmail() async {
|
||||
final AuthResponse res = await supabase.auth.signinwithotp(email: 'example@email.com');
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="swift" label="Swift">
|
||||
|
||||
```swift
|
||||
try await supabase.auth.signInWithOTP(
|
||||
email: "example@email.com",
|
||||
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>
|
||||
<TabPanel id="kotlin" label="Kotlin">
|
||||
|
||||
```kotlin
|
||||
suspend fun signInWithEmail() {
|
||||
supabase.auth.signInWith(OTP) {
|
||||
email = "example@email.com"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
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>Magic Link</h2>
|
||||
|
||||
<p>Follow this link to login:</p>
|
||||
<p><a href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=magiclink">Log In</a></p>
|
||||
```
|
||||
|
||||
At the `/auth/confirm` endpoint, exchange the hash for the session:
|
||||
|
||||
```js
|
||||
const { error } = await supabase.auth.verifyOtp({ token_hash, type })
|
||||
```
|
||||
|
||||
## With OTP
|
||||
|
||||
Email one-time passwords (OTP) are a form of passwordless login where users key in a six digit code sent to their email address to log 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. For a hosted Supabase project, go to [Email Templates](/dashboard/project/_/auth/templates) in the Dashboard. For a self-hosted project or local development, see the [Email Templates guide](/docs/guides/auth/auth-email-templates).
|
||||
|
||||
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>.
|
||||
|
||||
### 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
|
||||
const { data, error } = await supabase.auth.signInWithOtp({
|
||||
email: 'example@email.com',
|
||||
options: {
|
||||
// set this to false if you do not want the user to be automatically signed up
|
||||
shouldCreateUser: false,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="dart" label="Dart">
|
||||
|
||||
```dart
|
||||
Future<void> signInWithEmailOtp() async {
|
||||
final AuthResponse res = await supabase.auth.signInWithOtp(email: 'example@email.com');
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="swift" label="Swift">
|
||||
|
||||
```swift
|
||||
try await supabase.auth.signInWithOTP(
|
||||
email: "example@email.com",
|
||||
// set this to false if you do not want the user to be automatically signed up
|
||||
shouldCreateUser: false
|
||||
)
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="kotlin" label="Kotlin">
|
||||
|
||||
```kotlin
|
||||
suspend fun signInWithEmailOtp() {
|
||||
supabase.auth.signInWith(OTP) {
|
||||
email = "example@email.com"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</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
|
||||
const {
|
||||
data: { session },
|
||||
error,
|
||||
} = await supabase.auth.verifyOtp({
|
||||
email,
|
||||
token: '123456',
|
||||
type: 'email',
|
||||
})
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="swift" label="Swift">
|
||||
|
||||
```swift
|
||||
try await supabase.auth.verifyOTP(
|
||||
email: email,
|
||||
token: "123456",
|
||||
type: .email
|
||||
)
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="kotlin" label="Kotlin">
|
||||
|
||||
```kotlin
|
||||
supabase.auth.verifyEmailOtp(type = OtpType.Email.EMAIL, email = "email", token = "151345")
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
If successful, the user is now logged 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": {...}
|
||||
}
|
||||
```
|
||||
@@ -25,6 +25,14 @@ The templating system provides the following variables for use:
|
||||
| `{{ .RedirectTo }}` | Contains the redirect URL passed when `signUp`, `signInWithOtp`, `signInWithOAuth`, `resetPasswordForEmail` or `inviteUserByEmail` is called. The redirect URL allow list can be configured in your project's [authentication settings](/dashboard/project/_/auth/url-configuration). |
|
||||
| `{{ .Data }}` | Contains metadata from `auth.users.user_metadata`. Use this to personalize the email message. |
|
||||
|
||||
## Editing email templates
|
||||
|
||||
On self-hosted Supabase projects, edit your email templates on the [Email Templates](/dashboard/project/_/auth/templates) page. On self-hosted projects or in local development, edit your [configuration files](/docs/guides/cli/customizing-email-templates).
|
||||
|
||||
## Mobile Deep Linking
|
||||
|
||||
For mobile applications, you might need to link or redirect to a specific page within your app. See the [Mobile Deep Linking guide](/docs/guides/auth/native-mobile-deep-linking) to set this up.
|
||||
|
||||
## Limitations
|
||||
|
||||
### Email prefetching
|
||||
|
||||
@@ -1,213 +0,0 @@
|
||||
---
|
||||
id: 'auth-email'
|
||||
title: 'Login With Email'
|
||||
description: 'Use Supabase to Authenticate and Authorize your users using email.'
|
||||
---
|
||||
|
||||
Set up Email Logins for your Supabase application.
|
||||
|
||||
- Enable the email provider in your [Supabase Project](/dashboard/project/_/auth/providers)
|
||||
- Configure the Site URL and any additional redirect URLs in the [authentication management tab](/dashboard/project/_/auth/url-configuration).
|
||||
- The Site URL represents the default URL that the user will be redirected to after clicking on the email signup confirmation link.
|
||||
|
||||
<Admonition type="note" label="Self hosting">
|
||||
|
||||
For self-hosting, you can update your project configuration using the files and environment variables provided.
|
||||
|
||||
See the [self-hosting docs](/docs/guides/hosting/overview#configuration) for details.
|
||||
|
||||
</Admonition>
|
||||
|
||||
## Sign up the user
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
queryGroup="language"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
To sign up the user, call [signUp()](/docs/reference/javascript/auth-signup) with their email address and password:
|
||||
|
||||
```js
|
||||
async function signUpNewUser() {
|
||||
const { data, error } = await supabase.auth.signUp({
|
||||
email: 'example@email.com',
|
||||
password: 'example-password',
|
||||
options: {
|
||||
emailRedirectTo: 'https://example.com/welcome',
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="dart" label="Dart">
|
||||
|
||||
To sign up the user, call [signUp()](/docs/reference/dart/auth-signup) with their email address and password:
|
||||
|
||||
```dart
|
||||
Future<void> signUpNewUser() async {
|
||||
final AuthResponse res = await supabase.auth.signUp(
|
||||
email: 'example@email.com',
|
||||
password: 'example-password'
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="swift" label="Swift">
|
||||
|
||||
To sign up the user, call [signUp()](/docs/reference/swift/auth-signup) with their email address and password:
|
||||
|
||||
```swift
|
||||
let response = try await supabase.auth.signUp(
|
||||
email: "example@email.com",
|
||||
password: "example-password",
|
||||
redirectTo: URL(string: "https://example.com/welcome")
|
||||
)
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="kotlin" label="Kotlin">
|
||||
|
||||
To sign up the user, call [signUpWith(Email)](/docs/reference/kotlin/auth-signup) with their email address and password:
|
||||
|
||||
```kotlin
|
||||
suspend fun signUpNewUser() {
|
||||
supabase.auth.signUpWith(Email) {
|
||||
email = "example@email.com"
|
||||
password = "example-password"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
## Sign in the user
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
queryGroup="language"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
When your user signs in, call [signInWithPassword()](/docs/reference/javascript/auth-signinwithpassword) with their email address and password:
|
||||
|
||||
```js
|
||||
async function signInWithEmail() {
|
||||
const { data, error } = await supabase.auth.signInWithPassword({
|
||||
email: 'example@email.com',
|
||||
password: 'example-password',
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="dart" label="Dart">
|
||||
|
||||
When your user signs in, call [signInWithPassword()](/docs/reference/dart/auth-signinwithpassword) with their email address and password:
|
||||
|
||||
```dart
|
||||
Future<void> signInWithEmail() async {
|
||||
final AuthResponse res = await supabase.auth.signInWithPassword(
|
||||
email: 'example@email.com',
|
||||
password: 'example-password'
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="swift" label="Swift">
|
||||
|
||||
When your user signs in, call [signIn(email:password:)](/docs/reference/swift/auth-signinwithpassword) with their email address and password:
|
||||
|
||||
```swift
|
||||
try await supabase.auth.signIn(
|
||||
email: "example@email.com",
|
||||
password: "example-password"
|
||||
)
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="kotlin" label="Kotlin">
|
||||
|
||||
When your user signs in, call [signInWith(Email)](/docs/reference/kotlin/auth-signinwithpassword) with their email address and password:
|
||||
|
||||
```kotlin
|
||||
suspend fun signInWithEmail() {
|
||||
supabase.auth.signInWith(Email) {
|
||||
email = "example@email.com"
|
||||
password = "example-password"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
## Sign out the user
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
queryGroup="language"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
When your user signs out, call [signOut()](/docs/reference/javascript/auth-signout) to remove them from the browser session and any objects from localStorage:
|
||||
|
||||
```js
|
||||
async function signOut() {
|
||||
const { error } = await supabase.auth.signOut()
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="dart" label="Dart">
|
||||
|
||||
When your user signs out, call [signOut()](/docs/reference/dart/auth-signout) to remove them from the browser session and any objects from localStorage:
|
||||
|
||||
```dart
|
||||
Future<void> signOut() async {
|
||||
await supabase.auth.signOut();
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="swift" label="Swift">
|
||||
|
||||
When your user signs out, call [signOut()](/docs/reference/swift/auth-signout) to remove the session from the local storage:
|
||||
|
||||
```swift
|
||||
try await supabase.auth.signOut()
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="kotlin" label="Kotlin">
|
||||
|
||||
When your user signs out, call [logout()](/docs/reference/kotlin/auth-signout) to remove them from the browser session and any objects from localStorage:
|
||||
|
||||
```kotlin
|
||||
suspend fun logout() {
|
||||
supabase.auth.signOut()
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
## Resources
|
||||
|
||||
- [Supabase - Get started for free](https://supabase.com)
|
||||
- [Supabase JS Client](https://github.com/supabase/supabase-js)
|
||||
- [Supabase Flutter Client](https://github.com/supabase/supabase-flutter)
|
||||
- [Supabase Kotlin Client](https://github.com/supabase-community/supabase-kt)
|
||||
@@ -784,7 +784,7 @@ export const handle: Handle = sequence(supabase, authorization)
|
||||
|
||||
### Client-side data fetching with RLS
|
||||
|
||||
For [row level security](https://supabase.com/docs/guides/auth/row-level-security) to work properly when fetching data client-side, you need to use `supabaseClient` from `PageData` and only run your query once the session is defined client-side:
|
||||
For [row level security](https://supabase.com/docs/guides/database/postgres/row-level-security) to work properly when fetching data client-side, you need to use `supabaseClient` from `PageData` and only run your query once the session is defined client-side:
|
||||
|
||||
```svelte src/routes/+page.svelte
|
||||
<script lang="ts">
|
||||
|
||||
@@ -5,23 +5,6 @@ description: 'Manage the identities associated with your user'
|
||||
subtitle: 'Manage the identities associated with your user'
|
||||
---
|
||||
|
||||
## The user identity
|
||||
|
||||
The user identity represents an authentication method associated to the user. For example, if a user signs in using their email, an email identity will be associated with the user.
|
||||
|
||||
The user identity object contains the following attributes:
|
||||
|
||||
| Attributes | Type | Description |
|
||||
| --------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| id | `string` | The provider id returned by the provider. If the provider is an OAuth provider, the id refers to the user's account with the OAuth provider. If the provider is `email` or `phone`, the id is the user's id from the `auth.users` table. |
|
||||
| user_id | `string` | The user's id that the identity is linked to. |
|
||||
| identity_data | `object` | The identity metadata. |
|
||||
| identity_id | `string` | The unique id of the identity. |
|
||||
| provider | `string` | The provider name. |
|
||||
| created_at | `string` | The timestamp that the identity was created. |
|
||||
| last_sign_in_at | `string` | The timestamp that the identity was last used to sign in. |
|
||||
| updated_at | `string` | The timestamp that the identity was last updated. |
|
||||
|
||||
## Identity linking strategies
|
||||
|
||||
Currently, Supabase Auth supports 2 strategies to link an identity to a user:
|
||||
|
||||
@@ -17,7 +17,7 @@ Users with weak passwords or compromised social login accounts are prone to mali
|
||||
|
||||
## Overview
|
||||
|
||||
Supabase Auth implements only Time-based One Time Password(TOTP) multi-factor authentication. This type of multi-factor authentication uses a timed one-time password generated from an authenticator app in the control of users.
|
||||
Supabase Auth implements only Time-based One Time Password(TOTP) multi-factor authentication. This type of multi-factor authentication uses a timed one-time password generated from an authenticator app in the control of users. The [MFA API](/docs/reference/javascript/auth-mfa-api) is enabled on all Supabase projects by default.
|
||||
|
||||
Applications using MFA require two important flows:
|
||||
|
||||
|
||||
27
apps/docs/content/guides/auth/general-configuration.mdx
Normal file
27
apps/docs/content/guides/auth/general-configuration.mdx
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
id: 'auth-general-configuration'
|
||||
title: 'General configuration'
|
||||
subtitle: 'General configuration options for Supabase Auth'
|
||||
---
|
||||
|
||||
This section covers the [general configuration options](/dashboard/project/_/settings/auth) for Supabase Auth. If you are looking for another type of configuration, you may be interested in one of the following sections:
|
||||
|
||||
- [Provider-specific configuration](/dashboard/project/_/auth/providers)
|
||||
- [Rate limits](/dashboard/project/_/auth/rate-limits)
|
||||
- [Email Templates](/dashboard/project/_/auth/templates)
|
||||
- [Redirect URLs](/dashboard/project/_/auth/url-configuration)
|
||||
- [Auth Hooks](/dashboard/project/_/auth/hooks)
|
||||
|
||||
Supabase Auth provides these [general configuration options](/dashboard/project/_/settings/auth) to control user access to your application:
|
||||
|
||||
- **Allow new users to sign up**: Users will be able to sign up. If this config is disabled, only existing users can sign in.
|
||||
|
||||
- **Confirm Email**: Users will need to confirm their email address before signing in for the first time.
|
||||
|
||||
- Having **Confirm Email** disabled assumes that the user's email does not need to be verified in order to login and implicitly confirms the user's email in the database.
|
||||
- This option can be found in the email provider under the provider-specific configuration.
|
||||
{/* - If you previously relied on this config to autoconfirm a user's email address, you can switch to use **Allow unverified email sign in** instead. This new option allows the user to sign in with an unverified email which you can keep track of through the user object. It provides more versatility if you require your users to verify their email address in the future since you can structure your RLS policies to check the user's `email_verified` field. */}
|
||||
|
||||
- **Allow anonymous sign-ins**: Allow anonymous users to be created.
|
||||
|
||||
- **Allow manual linking**: Allow users to link their accounts manually.
|
||||
27
apps/docs/content/guides/auth/identities.mdx
Normal file
27
apps/docs/content/guides/auth/identities.mdx
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
title: 'Identities'
|
||||
---
|
||||
|
||||
An identity is an authentication method associated with a user. Supabase Auth supports the following types of identity:
|
||||
|
||||
- Email
|
||||
- Phone
|
||||
- OAuth
|
||||
- SAML
|
||||
|
||||
A user can have more than one identity. Anonymous users have no identity until they link an identity to their user.
|
||||
|
||||
## The user identity object
|
||||
|
||||
The user identity object contains the following attributes:
|
||||
|
||||
| Attributes | Type | Description |
|
||||
| --------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| id | `string` | The provider id returned by the provider. If the provider is an OAuth provider, the id refers to the user's account with the OAuth provider. If the provider is `email` or `phone`, the id is the user's id from the `auth.users` table. |
|
||||
| user_id | `string` | The user's id that the identity is linked to. |
|
||||
| identity_data | `object` | The identity metadata. For OAuth and SAML identities, this contains information about the user from the provider. |
|
||||
| identity_id | `string` | The unique id of the identity. |
|
||||
| provider | `string` | The provider name. |
|
||||
| created_at | `string` | The timestamp that the identity was created. |
|
||||
| last_sign_in_at | `string` | The timestamp that the identity was last used to sign in. |
|
||||
| updated_at | `string` | The timestamp that the identity was last updated. |
|
||||
@@ -1,32 +1,19 @@
|
||||
---
|
||||
id: 'auth-deep-dive-jwts'
|
||||
title: 'Part One: JWTs'
|
||||
description: 'Supabase Auth Deep Dive Part 1 - JWTs'
|
||||
video: 'https://www.youtube.com/v/v3Exg5YpJvE'
|
||||
id: 'auth-jwts'
|
||||
title: 'JWTs'
|
||||
subtitle: 'JSON Web Tokens'
|
||||
tocVideo: 'v3Exg5YpJvE'
|
||||
---
|
||||
|
||||
### About
|
||||
A [JSON Web Token](https://jwt.io/introduction) is a type of data structure, represented as a string, that usually contains identity and authorization information about a user. It encodes information about its lifetime and is signed with a cryptographic key to make it tamper-resistant.
|
||||
|
||||
An introduction to JWTs and how they are used in Supabase Auth.
|
||||
Supabase Access Tokens are JWTs. The JWT is sent along with every request to Supabase services. By verifying the token and inspecting the included claims, you can allow or deny access to resources. [Row Level Security](/docs/guides/database/postgres/row-level-security) policies are based on the information present in JWTs.
|
||||
|
||||
### Watch
|
||||
## Encoding and signing JWTs
|
||||
|
||||
<div className="video-container">
|
||||
<iframe
|
||||
src="https://www.youtube-nocookie.com/embed/v3Exg5YpJvE"
|
||||
frameBorder="1"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
</div>
|
||||
JWTs are encoded and signed as follows.
|
||||
|
||||
### What are JSON Web Tokens (JWTs)?
|
||||
|
||||
JWTs are JSON objects that are encoded and signed and sent around as a string. They are distributed to users of a service or website, who can later show the JWT to the website or service as proof that they have the right to access certain content.
|
||||
|
||||
What exactly do we mean when we say "encoded" and "signed"?
|
||||
|
||||
Well, the JSON object starts out looking something like this:
|
||||
The JSON object starts out looking something like this:
|
||||
|
||||
```js
|
||||
{
|
||||
@@ -54,7 +41,7 @@ Well, the JSON object starts out looking something like this:
|
||||
|
||||
Just note that the more data you store in your token, the longer the encoded string will be.
|
||||
|
||||
When we want to send the JWT to the user, we first encode the data using an algorithm such as `HS256`. There are many libraries (and several different algorithms) that can be used to do this encoding/decoding, such as [jsonwebtoken](https://www.npmjs.com/package/jsonwebtoken). I made a repl [here](https://replit.com/@awalias/jsonwebtokens#index.js) so you can try it for yourself. The signing is as simple as:
|
||||
When we want to send the JWT to the user, we first encode the data using an algorithm such as `HS256`. There are many libraries (and several different algorithms) that can be used to do this encoding/decoding, such as [jsonwebtoken](https://www.npmjs.com/package/jsonwebtoken). The signing is as simple as:
|
||||
|
||||
```js
|
||||
// from https://replit.com/@awalias/jsonwebtokens#index.js
|
||||
@@ -69,7 +56,7 @@ eyJhbGciOiJIUzI1NiJ9
|
||||
.zMcHjKlkGhuVsiPIkyAkB2rjXzyzJsMMgpvEGvGtjvA
|
||||
```
|
||||
|
||||
You will notice that the string is actually made up of three components, which we'll address one by one:
|
||||
You will notice that the string is actually made up of three components:
|
||||
|
||||
The first segment `eyJhbGciOiJIUzI1NiJ9` is known as the "header", and when decoded just tells us which algorithm was used to do the encoding:
|
||||
|
||||
@@ -108,7 +95,7 @@ You might wonder why JWTs are so popular all of a sudden. The answer is that wit
|
||||
|
||||
Note: One downside of JWTs is that they are not easily voidable, like session tokens. If a JWT is leaked to a malicious actor, they will be able to redeem it anywhere until the expiry date is reached – unless of course the system owner updates the `jwt_secret` (which will of course invalidate _everyone's_ existing tokens).
|
||||
|
||||
### JWTs in Supabase
|
||||
## JWTs in Supabase
|
||||
|
||||
In Supabase we issue JWTs for three different purposes:
|
||||
|
||||
@@ -138,7 +125,7 @@ If we put this token into https://jwt.io, we see it decodes to:
|
||||
|
||||
This JWT is signed by a `jwt_secret` specific to the developer's Supabase token (you can find this secret alongside this encoded "anon key" on your Dashboard under Settings > API page) and is required to get past the Supabase API gateway and access the developer's project.
|
||||
|
||||
The idea with this particular key is that it's safe to put into your client, meaning it's okay if your end users see this key – but _only_ if you first enable Row Level Security, which is the topic of [Part Two](./auth-row-level-security) in this series.
|
||||
The idea with this particular key is that it's safe to put into your client, meaning it's okay if your end users see this key – but _only_ if you first enable Row Level Security.
|
||||
|
||||
The second key, `service role key`, should only ever be used on one of your own servers or environments, and should never be shared with end users. You might use this token to do things like make batch inserts of data.
|
||||
|
||||
@@ -175,18 +162,8 @@ You'll notice that this token is quite a bit longer, since it contains informati
|
||||
}
|
||||
```
|
||||
|
||||
Now that you understand what JWTs are and where they're used in Supabase, you can explore how to use them in combination with Row Level Security to start restricting access to certain tables, rows, and columns in your Postgres database: [Part Two: Row Level Security](./auth-row-level-security)
|
||||
Now that you understand what JWTs are and where they're used in Supabase, you can explore how to use them in combination with Row Level Security to start restricting access to certain tables, rows, and columns in your Postgres database.
|
||||
|
||||
### Resources
|
||||
## Resources
|
||||
|
||||
- JWT debugger: https://jwt.io/
|
||||
|
||||
### Next steps
|
||||
|
||||
{/* <!-- - Watch [Part One: JWTs](./auth-deep-dive-jwts) --> */}
|
||||
|
||||
- [Part Two: Row Level Security](./auth-row-level-security)
|
||||
- [Part Three: Policies](./auth-policies)
|
||||
- [Part Four: GoTrue](./auth-gotrue)
|
||||
- [Part Five: Google Oauth](./auth-google-oauth)
|
||||
- Sign up for Supabase: [supabase.com/dashboard](https://supabase.com/dashboard)
|
||||
@@ -1,16 +1,16 @@
|
||||
---
|
||||
id: 'managing-user-data'
|
||||
title: 'Managing User Data'
|
||||
description: 'Securing your user data with Row Level Security.'
|
||||
id: 'user-management'
|
||||
title: 'User Management'
|
||||
subtitle: 'View, delete, and export user information.'
|
||||
---
|
||||
|
||||
For security purposes, the `auth` schema is not exposed on the auto-generated API.
|
||||
You can view your users on the [Users page](/dashboard/project/_/auth/users) of the Dashboard. You can also view the contents of the Auth schema in the [Table Editor](/dashboard/project/_/editor).
|
||||
|
||||
Even though Supabase provides an `auth.users` table, it can be helpful to create tables in the `public` schema for storing user data that you want to access via the API.
|
||||
## Accessing user data via API
|
||||
|
||||
## Creating user tables
|
||||
For security, the Auth schema is not exposed in the auto-generated API. If you want to access users data via the API, you can create your own user tables in the `public` schema.
|
||||
|
||||
When you create tables to store user data, it's helpful to reference the `auth.users` table in the primary key to ensure data integrity. Also specify the `on delete cascade` clause when referencing `auth.users`. Omitting it may cause problems when deleting users. For example, a `public.profiles` table might look like this:
|
||||
Make sure to protect the table by enabling [Row Level Security](/docs/guides/database/postgres/row-level-security). Reference the `auth.users` table to ensure data integrity. Specify `on delete cascade` in the reference. For example, a `public.profiles` table might look like this:
|
||||
|
||||
```sql
|
||||
create table public.profiles (
|
||||
@@ -32,132 +32,29 @@ Primary keys are **guaranteed not to change**. Columns, indices, constraints or
|
||||
|
||||
</Admonition>
|
||||
|
||||
## Deleting users
|
||||
|
||||
You may delete users directly or via the management console at Authentication > Users. Note that deleting a user from the `auth.users` table does not automatically sign out a user. As Supabase makes use of JSON Web Tokens (JWT), a user's JWT will remain "valid" until it has expired. Should you wish to immediately revoke access for a user, do consider making use of a Row Level Security policy as described below.
|
||||
|
||||
<Admonition type="caution">
|
||||
|
||||
You cannot delete a user if they are the owner of any objects in Supabase Storage.
|
||||
|
||||
You will encounter an error when you try to delete an Auth user that owns any Storage objects. If this happens, try deleting all the objects for that user, or reassign ownership to another user.
|
||||
|
||||
</Admonition>
|
||||
|
||||
## Exporting users
|
||||
|
||||
As Supabase is built on top of PostgreSQL, you can query the `auth.users` and `auth.identities` table via the `SQL Editor` tab to extract all users:
|
||||
To update your `public.profiles` table every time a user signs up, set up a trigger. If the trigger fails, it could block signups, so test your code thoroughly.
|
||||
|
||||
```sql
|
||||
select * from auth.users;
|
||||
-- inserts a row into public.profiles
|
||||
create function public.handle_new_user()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
security definer set search_path = ''
|
||||
as $$
|
||||
begin
|
||||
insert into public.profiles (id, first_name, last_name)
|
||||
values (new.id, new.raw_user_meta_data ->> 'first_name', new.raw_user_meta_data ->> 'last_name');
|
||||
return new;
|
||||
end;
|
||||
$$;
|
||||
|
||||
-- trigger the function every time a user is created
|
||||
create trigger on_auth_user_created
|
||||
after insert on auth.users
|
||||
for each row execute procedure public.handle_new_user();
|
||||
```
|
||||
|
||||
You can also opt to export the results via CSV through the dashboard if you wish:
|
||||

|
||||
|
||||
## Public access
|
||||
|
||||
Since Row Level Security is enabled, this table is accessible via the API but no data will be returned unless we set up some Policies.
|
||||
If we wanted the data to be _readable_ by everyone but only allow logged-in users to update their own data, the Policies would look like this:
|
||||
|
||||
```sql
|
||||
create policy "Public profiles are viewable by everyone."
|
||||
on profiles for select
|
||||
using ( true );
|
||||
|
||||
create policy "Users can insert their own profile."
|
||||
on profiles for insert
|
||||
with check ( (select auth.uid()) = id );
|
||||
|
||||
create policy "Users can update own profile."
|
||||
on profiles for update
|
||||
using ( (select auth.uid()) = id );
|
||||
```
|
||||
|
||||
## Private access
|
||||
|
||||
If the data should only be _readable_ by the user who owns the data, we just need to change the `for select` query above.
|
||||
|
||||
```sql
|
||||
create policy "Profiles are viewable by users who created them."
|
||||
on profiles for select
|
||||
using ( (select auth.uid()) = id );
|
||||
```
|
||||
|
||||
The nice thing about this pattern? We can now query this table via the API and we don't need to include data filters in our API queries - the Policies will handle that for us:
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
queryGroup="language"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
```js
|
||||
// This will return nothing while the user is logged out
|
||||
const { data } = await supabase.from('profiles').select('id, username, avatar_url, website')
|
||||
|
||||
// After the user is logged in, this will only return
|
||||
// the logged-in user's data - in this case a single row
|
||||
const { error } = await supabase.auth.signIn({ email })
|
||||
const { data: profile } = await supabase
|
||||
.from('profiles')
|
||||
.select('id, username, avatar_url, website')
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="swift" label="Swift">
|
||||
|
||||
```swift
|
||||
// This will return nothing while the user is logged out
|
||||
let profiles: [Profile] = try await supabase.from("profiles")
|
||||
.select("id, username, avatar_url, website")
|
||||
.execute()
|
||||
.value
|
||||
|
||||
// After the user is logged in, this will only return
|
||||
// the logged-in user's data - in this case a single row
|
||||
try await supabase.auth.signInWithOTP(email: email)
|
||||
let profile: Profile = try await supabase.from("profiles")
|
||||
.select("id, username, avatar_url, website")
|
||||
.single()
|
||||
.execute()
|
||||
.value
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="kotlin" label="Kotlin">
|
||||
|
||||
```kotlin
|
||||
// This will return nothing while the user is logged out
|
||||
val data = supabase.from("profiles").select(Columns.list("id", "username", "avatar_url", "website"))
|
||||
|
||||
// After the user is logged in, this will only return
|
||||
// the logged-in user's data - in this case a single row
|
||||
supabase.auth.signInWith(OTP) {
|
||||
this.email = email
|
||||
}
|
||||
val data = supabase.from("profiles").select(Columns.list("id", "username", "avatar_url", "website"))
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
<Admonition type="tip">
|
||||
|
||||
Security is handled at the database level by the RLS policy. The policy restricts the returned rows, so you don't _need_ a filter on `id`. Depending on your table size and query, you might still want to add a filter for performance. Postgres can use the filter to construct a more efficient query.
|
||||
|
||||
</Admonition>
|
||||
|
||||
## Bypassing Row Level Security
|
||||
|
||||
If you need to fetch a full list of user profiles, we supply a `service_key` which you can use with your API and Client Libraries to bypass Row Level Security.
|
||||
|
||||
Make sure you _NEVER_ expose this publicly. But it can be used on the server-side to fetch all of the profiles.
|
||||
|
||||
## Accessing user metadata
|
||||
## Adding and retrieving user metadata
|
||||
|
||||
You can assign metadata to users on sign up:
|
||||
|
||||
@@ -253,31 +150,24 @@ val metadata = user?.userMetadata
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
## Advanced techniques
|
||||
## Deleting users
|
||||
|
||||
### Using triggers
|
||||
You may delete users directly or via the management console at Authentication > Users. Note that deleting a user from the `auth.users` table does not automatically sign out a user. As Supabase makes use of JSON Web Tokens (JWT), a user's JWT will remain "valid" until it has expired. Should you wish to immediately revoke access for a user, do consider making use of a Row Level Security policy as described below.
|
||||
|
||||
If you want to add a row to your `public.profiles` table every time a user signs up, you can use triggers.
|
||||
If the trigger fails however, it could block the user sign ups - so make sure that the code is well-tested.
|
||||
<Admonition type="caution">
|
||||
|
||||
Example:
|
||||
You cannot delete a user if they are the owner of any objects in Supabase Storage.
|
||||
|
||||
You will encounter an error when you try to delete an Auth user that owns any Storage objects. If this happens, try deleting all the objects for that user, or reassign ownership to another user.
|
||||
|
||||
</Admonition>
|
||||
|
||||
## Exporting users
|
||||
|
||||
As Supabase is built on top of Postgres, you can query the `auth.users` and `auth.identities` table via the `SQL Editor` tab to extract all users:
|
||||
|
||||
```sql
|
||||
-- inserts a row into public.profiles
|
||||
create function public.handle_new_user()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
security definer set search_path = ''
|
||||
as $$
|
||||
begin
|
||||
insert into public.profiles (id, first_name, age)
|
||||
values (new.id, new.raw_user_meta_data ->> 'first_name', new.raw_user_meta_data['age']::integer);
|
||||
return new;
|
||||
end;
|
||||
$$;
|
||||
|
||||
-- trigger the function every time a user is created
|
||||
create trigger on_auth_user_created
|
||||
after insert on auth.users
|
||||
for each row execute procedure public.handle_new_user();
|
||||
select * from auth.users;
|
||||
```
|
||||
|
||||
You can then export the results as CSV.
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
---
|
||||
title: 'Native Mobile Deep Linking'
|
||||
description: 'Learn how to handle deep linking for OAuth and email based login.'
|
||||
subtitle: 'Set up Deep Linking for mobile applications.'
|
||||
tocVideo: '8TZ6O1C8ujE'
|
||||
---
|
||||
|
||||
In certain auth scenarios you will need to handle linking back into your application to finish the user's sign in.
|
||||
Many Auth methods involve a redirect to your app. For example:
|
||||
|
||||
### When do you need to setup deep links
|
||||
- Signup confirmation emails, Magic Link signins, and password reset emails contain a link that redirects to your app.
|
||||
- In OAuth signins, an automatic redirect occurs to your app.
|
||||
|
||||
- Magic link login.
|
||||
- Have `confirm email` enabled and are using email login.
|
||||
- Resetting password for email login.
|
||||
- Calling `.signInWithOAuth()` method.
|
||||
With Deep Linking, you can configure this redirect to open a specific page. This is necessary if, for example, you need to display a form for password reset, or to manually exchange a token hash.
|
||||
|
||||
## Setting up Deep Linking
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
---
|
||||
title: 'Native Mobile Login'
|
||||
description: 'Logging in with native mobile identity providers like Apple & Google.'
|
||||
---
|
||||
|
||||
Some native mobile operating systems, like iOS and Android, offer a built-in identity provider for convenient user authentication.
|
||||
|
||||
For iOS, apps that use a third-party or social login service to set up or authenticate the user’s primary account with the app must also offer Sign in with Apple as an equivalent option.
|
||||
|
||||
## Benefits
|
||||
|
||||
There are several reasons why you might want to add social login to your applications:
|
||||
|
||||
- **Improved user experience**: Users can register and log in to your application using their existing app store accounts, which can be faster and more convenient than creating a new account from scratch. This makes it easier for users to access your application, improving their overall experience.
|
||||
|
||||
- **Better user engagement**: You can access additional data and insights about your users, such as their interests, demographics, and social connections. This can help you tailor your content and marketing efforts to better engage with your users and provide a more personalized experience.
|
||||
|
||||
- **Increased security**: Social login can improve the security of your application by leveraging the security measures and authentication protocols of the social media platforms that your users are logging in with. This can help protect against unauthorized access and account takeovers.
|
||||
|
||||
## Set up a native social login with Supabase Auth
|
||||
|
||||
<div className="grid grid-cols-12 xs:gap-x-10 gap-y-10 not-prose py-8">
|
||||
<NavData data="nativeMobileLoginItems">
|
||||
{(data) =>
|
||||
data.map((item) => (
|
||||
<Link
|
||||
href={`${item.url}`}
|
||||
key={item.name}
|
||||
passHref
|
||||
className="col-span-12 xs:col-span-6 lg:col-span-4 xl:col-span-3"
|
||||
>
|
||||
<IconPanel
|
||||
title={item.name}
|
||||
span="col-span-6"
|
||||
icon={item.icon}
|
||||
isDarkMode={item.isDarkMode}
|
||||
hasLightIcon={item.hasLightIcon}
|
||||
>
|
||||
{item.description}
|
||||
</IconPanel>
|
||||
</Link>
|
||||
))
|
||||
}
|
||||
</NavData>
|
||||
</div>
|
||||
|
||||
## Provider tokens
|
||||
|
||||
Just like with Oauth, you will receive a copy of the provider token in case you need to use it further. For example, you can use the Google provider token to access Google APIs on behalf of your user.
|
||||
|
||||
Provider tokens are intentionally not stored in your project's database, however. This is because provider tokens give access to potentially sensitive user data in third-party systems. Different applications have different needs, and one application's OAuth scopes may be significantly more permissive than another. If you do want to use the provider token outside of the browser that completed the OAuth flow, you will have to send it manually to a secure server under your control.
|
||||
48
apps/docs/content/guides/auth/password-security.mdx
Normal file
48
apps/docs/content/guides/auth/password-security.mdx
Normal file
@@ -0,0 +1,48 @@
|
||||
---
|
||||
title: 'Password security'
|
||||
subtitle: 'Help your users to protect their password security'
|
||||
---
|
||||
|
||||
A password is more secure if it is harder to guess or brute-force. In theory, a password is harder to guess if it is longer. It is also harder to guess if it uses a larger set of characters (for example, digits, lowercase and uppercase letters, and symbols).
|
||||
|
||||
This table shows the _minimum_ number of guesses that need to be tried to access a user's account:
|
||||
|
||||
| Required characters | Length | Guesses |
|
||||
| -------------------------------------------- | ------ | ---------------- |
|
||||
| Digits only | 8 | ~ 2<sup>27</sup> |
|
||||
| Digits and letters | 8 | ~ 2<sup>41</sup> |
|
||||
| Digits, lower and uppercase letters | 8 | ~ 2<sup>48</sup> |
|
||||
| Digits, lower and uppercase letters, symbols | 8 | ~ 2<sup>52</sup> |
|
||||
|
||||
In reality though, passwords are not always generated at random. They often contain variations of names, words, dates, and common phrases. Malicious actors can use these properties to guess a password in fewer attempts.
|
||||
|
||||
There are hundreds of millions (and growing!) known passwords out there. Malicious actors can use these lists of leaked passwords to automate login attempts (known as credential stuffing) and steal or access sensitive user data.
|
||||
|
||||
## Password strength and leaked password protection
|
||||
|
||||
To help protect your users, Supabase Auth allows you fine-grained control over the strength of the passwords used on your project. You can configure these in your project's [Auth settings](/dashboard/project/_/settings/auth):
|
||||
|
||||
- Set a large minimum password length. Anything less than 8 characters is not recommended.
|
||||
- Set the required characters that must appear at least once in a user's password. Use the strongest option of requiring digits, lowercase and uppercase letters, and symbols.
|
||||
- Prevent the use of leaked passwords. Supabase Auth uses the open-source [HaveIBeenPwned.org Pwned Passwords API](https://haveibeenpwned.com/Passwords) to reject passwords that have been leaked and are known by malicious actors.
|
||||
|
||||
## Additional recommendations
|
||||
|
||||
In addition to choosing suitable password strength settings and preventing the use of leaked passwords, consider asking your users to:
|
||||
|
||||
- Use a password manager to store and generate passwords.
|
||||
- Avoid password reuse across websites and apps.
|
||||
- Avoid using personal information in passwords.
|
||||
- Use [Multi-Factor Authentication](/docs/guides/auth/auth-mfa).
|
||||
|
||||
## Frequently asked questions
|
||||
|
||||
### How are passwords stored?
|
||||
|
||||
Supabase Auth uses [bcrypt](https://en.wikipedia.org/wiki/Bcrypt), a strong password hashing function, to store hashes of users' passwords. Only hashed passwords are stored. You cannot impersonate a user with the password hash. Each hash is accompanied by a randomly generated salt parameter for extra security.
|
||||
|
||||
The hash is stored in the `encrypted_password` column of the `auth.users` table. The column's name is a misnomer (cryptographic hashing is not encryption), but is kept for backward compatibility.
|
||||
|
||||
### How will strengthened password requirements affect current users?
|
||||
|
||||
Existing users can still sign in with their current password even if it doesn't meet the new, strengthened password requirements. However, if their password falls short of these updated standards, they will encounter a `WeakPasswordError` during the `signInWithPassword` process, explaining why it's considered weak. This change is also applicable to new users and existing users changing their passwords, ensuring everyone adheres to the enhanced security standards.
|
||||
@@ -1,51 +0,0 @@
|
||||
---
|
||||
title: 'Passwordless Login'
|
||||
description: 'Sign in your users without passwords via magic link email, or one-time passwords sent via email, SMS, or WhatsApp.'
|
||||
---
|
||||
|
||||
Supabase supports various forms of passwordless authentication:
|
||||
|
||||
- [Email Magic Link](/docs/guides/auth/passwordless-login/auth-magic-link)
|
||||
- [Email one-time password (OTP)](/docs/guides/auth/passwordless-login/auth-email-otp)
|
||||
- [SMS & WhatsApp one-time password (OTP)](/docs/guides/auth/phone-login)
|
||||
|
||||
## Benefits
|
||||
|
||||
There are several reasons why you might want to add passwordless login to your applications:
|
||||
|
||||
- **Improved user experience**: Eliminating the need for users to remember and enter complex passwords can make it easier and more convenient for users to log in to your application. This can improve the overall user experience and make it more enjoyable for users to interact with your application.
|
||||
|
||||
- **Increased security**: Passwordless login can improve the security of your application by reducing the risk of password-related security breaches, such as password reuse and weak passwords. By using alternative forms of authentication, such as one-time codes or biometric factors, you can make it more difficult for unauthorized users to access your application.
|
||||
|
||||
- **Reduced support burden**: Passwordless login can also help reduce the support burden for your team by eliminating the need to handle password recovery flows or deal with other password-related issues. This can free up your team to focus on other important tasks and improve the efficiency of your operation.
|
||||
|
||||
## Passwordless login options with Supabase Auth
|
||||
|
||||
### Email based
|
||||
|
||||
- [Email Magic Link](/docs/guides/auth/passwordless-login/auth-magic-link)
|
||||
- [Email one-time password (OTP)](/docs/guides/auth/passwordless-login/auth-email-otp)
|
||||
|
||||
## Phone based
|
||||
|
||||
Supabase supports [Phone Login](/docs/guides/auth/phone-login) (SMS & WhatsApp) with several communications platforms. Follow the guides below to set up a provider with Supabase Auth.
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-12 gap-10 not-prose py-8">
|
||||
<NavData data="phoneLoginsItems">
|
||||
{(data) =>
|
||||
data.map((item) => (
|
||||
<Link href={`${item.url}`} key={item.name} passHref className="col-span-4">
|
||||
<IconPanel
|
||||
title={item.name}
|
||||
span="col-span-6"
|
||||
icon={item.icon}
|
||||
isDarkMode={item.isDarkMode}
|
||||
hasLightIcon={item.hasLightIcon}
|
||||
>
|
||||
{item.linkDescription}
|
||||
</IconPanel>
|
||||
</Link>
|
||||
))
|
||||
}
|
||||
</NavData>
|
||||
</div>
|
||||
@@ -1,163 +0,0 @@
|
||||
---
|
||||
id: 'auth-email-otp'
|
||||
title: 'Login With Email OTP'
|
||||
description: 'Use Supabase to authenticate your users using one-time password emails.'
|
||||
---
|
||||
|
||||
Email one-time passwords (OTP) are a form of passwordless login where users key in a six digit code sent to their email address to log in to their accounts. By default, a user can only request an OTP once every 60 seconds and they expire after 1 hour.
|
||||
|
||||
## Setting up email OTP
|
||||
|
||||
To set up email OTP for your Supabase app:
|
||||
|
||||
- Enable the email provider in your [Supabase Project](/dashboard/project/_/auth/providers)
|
||||
- The Site URL represents the default URL that the user will be redirected to after clicking on the email signup confirmation link.
|
||||
- If a user has not signed up yet, signing in with an OTP will automatically sign up the user. To prevent users from signing up this way, you can set the `shouldCreateUser` option to `false`.
|
||||
- Navigate to the [email template settings](/dashboard/project/_/auth/templates) and modify the template to include the `{{ .Token }}` variable, for example:
|
||||
|
||||
```html
|
||||
<h2>One time login code</h2>
|
||||
|
||||
<p>Please enter this code: {{ .Token }}</p>
|
||||
```
|
||||
|
||||
## Signing in a user with email OTP
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
queryGroup="language"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
When your user signs in, call [signInWithOtp()](/docs/reference/javascript/auth-signinwithotp) with their email address:
|
||||
|
||||
```js
|
||||
const { data, error } = await supabase.auth.signInWithOtp({
|
||||
email: 'example@email.com',
|
||||
options: {
|
||||
// set this to false if you do not want the user to be automatically signed up
|
||||
shouldCreateUser: false,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="dart" label="Dart">
|
||||
|
||||
When your user signs in, call [signInWithOtp()](/docs/reference/dart/auth-signinwithotp) with their email address:
|
||||
|
||||
```dart
|
||||
Future<void> signInWithEmailOtp() async {
|
||||
final AuthResponse res = await supabase.auth.signInWithOtp(email: 'example@email.com');
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="swift" label="Swift">
|
||||
When your user signs in, call [signInWithOTP()](/docs/reference/swift/auth-signinwithotp) with their email address:
|
||||
|
||||
```swift
|
||||
try await supabase.auth.signInWithOTP(
|
||||
email: "example@email.com",
|
||||
// set this to false if you do not want the user to be automatically signed up
|
||||
shouldCreateUser: false
|
||||
)
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="kotlin" label="Kotlin">
|
||||
|
||||
When your user signs in, call [signInWith(OTP)](/docs/reference/kotlin/auth-signinwithotp) with their email address:
|
||||
|
||||
```kotlin
|
||||
suspend fun signInWithEmailOtp() {
|
||||
supabase.auth.signInWith(OTP) {
|
||||
email = "example@email.com"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
If the request was successful, you receive a response with `error: null` and a `data` object where both `user` and `session` are null. In this case you can let the user know to check their email inbox.
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"user": null,
|
||||
"session": null
|
||||
},
|
||||
"error": null
|
||||
}
|
||||
```
|
||||
|
||||
## Verify OTP to create session
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
queryGroup="language"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
Provide an input field for the user to key in the one-time code. To verify the code and complete the user's sign in, call [verifyOtp()](/docs/reference/javascript/auth-verifyotp) with their email address, the code, and `type: "email"`:
|
||||
|
||||
```js
|
||||
const {
|
||||
data: { session },
|
||||
error,
|
||||
} = await supabase.auth.verifyOtp({
|
||||
email,
|
||||
token: '123456',
|
||||
type: 'email',
|
||||
})
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="swift" label="Swift">
|
||||
|
||||
Provide an input field for the user to key in the one-time code. To verify the code and complete the user's sign in, call [verifyOTP()](/docs/reference/swift/auth-verifyotp) with their email address, the code, and `type: .email`:
|
||||
|
||||
```swift
|
||||
try await supabase.auth.verifyOTP(
|
||||
email: email,
|
||||
token: "123456",
|
||||
type: .email
|
||||
)
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="kotlin" label="Kotlin">
|
||||
|
||||
Provide an input field for the user to key in the one-time code. To verify the code and complete the user's sign in, call [verifyEmailOtp()](/docs/reference/kotlin/auth-verifyotp) with their email address, the code, and `OtpType.Email.EMAIL` as the type:
|
||||
|
||||
```kotlin
|
||||
supabase.auth.verifyEmailOtp(type = OtpType.Email.EMAIL, email = "email", token = "151345")
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
If successful the user will now be logged in and you should receive a valid session like:
|
||||
|
||||
```json
|
||||
{
|
||||
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNjI3MjkxNTc3LCJzdWIiOiJmYTA2NTQ1Zi1kYmI1LTQxY2EtYjk1NC1kOGUyOTg4YzcxOTEiLCJlbWFpbCI6IiIsInBob25lIjoiNjU4NzUyMjAyOSIsImFwcF9tZXRhZGF0YSI6eyJwcm92aWRlciI6InBob25lIn0sInVzZXJfbWV0YWRhdGEiOnt9LCJyb2xlIjoiYXV0aGVudGljYXRlZCJ9.1BqRi0NbS_yr1f6hnr4q3s1ylMR3c1vkiJ4e_N55dhM",
|
||||
"token_type": "bearer",
|
||||
"expires_in": 3600,
|
||||
"refresh_token": "LSp8LglPPvf0DxGMSj-vaQ",
|
||||
"user": {...}
|
||||
}
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- [Supabase - Get started for free](https://supabase.com)
|
||||
- [Phone SMS & WhatsApp OTP](/docs/guides/auth/phone-login)
|
||||
- [Supabase JS Client](https://github.com/supabase/supabase-js)
|
||||
- [Supabase Flutter Client](https://github.com/supabase/supabase-flutter)
|
||||
@@ -1,163 +0,0 @@
|
||||
---
|
||||
id: 'auth-magic-link'
|
||||
title: 'Login With Magic Link'
|
||||
description: 'Use Supabase to authenticate your users using magic links.'
|
||||
---
|
||||
|
||||
Magic links are a form of passwordless logins where users click on a link sent to their email address to log in to their accounts. Magic links only work with email addresses and are one-time use only. By default, a user can only request a magic link once every 60 seconds and they expire after 1 hour.
|
||||
|
||||
Set up Magic Link logins for your Supabase application.
|
||||
|
||||
- Enable the email provider in your [Supabase Project](/dashboard/project/_/auth/providers)
|
||||
- Configure the Site URL and any additional redirect URLs in the [authentication management tab](/dashboard/project/_/auth/url-configuration).
|
||||
- The Site URL represents the default URL that the user will be redirected to after clicking on the email signup confirmation link.
|
||||
- If a user has not signed up yet, signing in with a magic link will automatically sign up the user. To prevent users from signing up through magic links, you can set the `shouldCreateUser` option to `false`.
|
||||
|
||||
## Sign in with magic link
|
||||
|
||||
<Admonition type="caution">
|
||||
|
||||
When using Magic Links in [the PKCE Flow](/blog/supabase-auth-sso-pkce#introducing-pkce), keep in mind that links can only be opened from the same browser that they are sent from (as defined in the PKCE spec). Consequently, a magic link sent from Chrome on Desktop will be invalid if used on a Mobile Device.
|
||||
|
||||
The same applies for Magic Links in mobile devices. Magic Links can only be opened on the device they are sent from.
|
||||
|
||||
</Admonition>
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
queryGroup="language"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
When your user signs in, call [signInWithOtp()](/docs/reference/javascript/auth-signinwithotp) with their email address:
|
||||
|
||||
```js
|
||||
async function signInWithEmail() {
|
||||
const { data, error } = await supabase.auth.signInWithOtp({
|
||||
email: 'example@email.com',
|
||||
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">
|
||||
Follow the [Expo Quickstart Guide](/docs/guides/getting-started/tutorials/with-expo-react-native#initialize-a-react-native-app) to initialize `supabase-js` with React Native.
|
||||
|
||||
To start the sign in process call [signInWithOtp()](/docs/reference/javascript/auth-signinwithotp) with their email address:
|
||||
|
||||
```ts
|
||||
import { makeRedirectUri } from 'expo-auth-session'
|
||||
|
||||
const redirectTo = makeRedirectUri()
|
||||
|
||||
const { error } = await supabase.auth.signInWithOtp({
|
||||
email: 'example@email.com',
|
||||
options: {
|
||||
emailRedirectTo: redirectTo,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
Read the [Deep Linking Documentation](/docs/guides/auth/native-mobile-deep-linking) to learn how to handle deep linking.
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="dart" label="Dart">
|
||||
|
||||
When your user signs in, call [signIn()](/docs/reference/dart/auth-signinwithotp) with their email address:
|
||||
|
||||
```dart
|
||||
Future<void> signInWithEmail() async {
|
||||
final AuthResponse res = await supabase.auth.signinwithotp(email: 'example@email.com');
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="swift" label="Swift">
|
||||
When your user signs in, call [signInWithOTP()](/docs/reference/swift/auth-signinwithotp) with their email address:
|
||||
|
||||
```swift
|
||||
try await supabase.auth.signInWithOTP(
|
||||
email: "example@email.com",
|
||||
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>
|
||||
<TabPanel id="kotlin" label="Kotlin">
|
||||
|
||||
To start the sign in process call [signInWith(OTP)](/docs/reference/kotlin/auth-signinwithotp) with their email address:
|
||||
|
||||
```kotlin
|
||||
suspend fun signInWithEmail() {
|
||||
supabase.auth.signInWith(OTP) {
|
||||
email = "example@email.com"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
queryGroup="language"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
When your user signs out, call [signOut()](/docs/reference/javascript/auth-signout) to remove them from the browser session and any objects from localStorage:
|
||||
|
||||
```js
|
||||
async function signOut() {
|
||||
const { error } = await supabase.auth.signOut()
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="dart" label="Dart">
|
||||
|
||||
When your user signs out, call [signOut()](/docs/reference/dart/auth-signout) to remove them from the browser session and any objects from localStorage:
|
||||
|
||||
```dart
|
||||
Future<void> signOut() async {
|
||||
await supabase.auth.signOut();
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="swift" label="Swift">
|
||||
When your user signs out, call [signOut()](/docs/reference/swift/auth-signout) to remove them from the local storage:
|
||||
|
||||
```swift
|
||||
try await supabase.auth.signOut()
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="kotlin" label="Kotlin">
|
||||
|
||||
When your user signs out, call [logout()](/docs/reference/kotlin/auth-signout) to remove them from the browser session and any objects from localStorage:
|
||||
|
||||
```kotlin
|
||||
suspend fun logout() {
|
||||
supabase.auth.signOut()
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
## Resources
|
||||
|
||||
- [Supabase - Get started for free](https://supabase.com)
|
||||
- [Supabase JS Client](https://github.com/supabase/supabase-js)
|
||||
- [Supabase Flutter Client](https://github.com/supabase/supabase-flutter)
|
||||
@@ -1,78 +1,502 @@
|
||||
---
|
||||
id: 'auth-passwords'
|
||||
title: 'Passwords'
|
||||
description: 'How to work with passwords in Supabase Auth'
|
||||
title: 'Password-based Auth'
|
||||
subtitle: 'Allow users to sign in with a password connected to their email or phone number.'
|
||||
---
|
||||
|
||||
Using passwords to authenticate users is a tried-and-tested method to give your users access to your application. Supabase Auth provides you with secure configuration options and uses best practices to store and verify your user's passwords.
|
||||
Users often expect to sign in to your site with a password. Supabase Auth helps you implement password-based auth safely, using secure configuration options and best practices for storing and verifying passwords.
|
||||
|
||||
## Password security
|
||||
Users can associate a password with their identity using their [email address](#with-email) or a [phone number](#with-phone).
|
||||
|
||||
A password is more secure if it is harder to guess or brute-force. In theory, a password is harder to guess if it is longer. It is also harder to guess if it uses a larger set of characters (for example, digits, lowercase and uppercase letters, and symbols).
|
||||
## With email
|
||||
|
||||
This table shows the _minimum_ number of guesses that need to be tried to access a user's account:
|
||||
### Enabling email and password-based authentication
|
||||
|
||||
| Required characters | Length | Guesses |
|
||||
| -------------------------------------------- | ------ | ---------------- |
|
||||
| Digits only | 8 | ~ 2<sup>27</sup> |
|
||||
| Digits and letters | 8 | ~ 2<sup>41</sup> |
|
||||
| Digits, lower and uppercase letters | 8 | ~ 2<sup>48</sup> |
|
||||
| Digits, lower and uppercase letters, symbols | 8 | ~ 2<sup>52</sup> |
|
||||
Email authentication is enabled by default.
|
||||
|
||||
In reality though, passwords are not always generated at random. They often contain variations of names, words, dates, and common phrases. Malicious actors can use these properties to guess a password in fewer attempts.
|
||||
You can configure whether users need to verify their email to sign in. On hosted Supabase projects, this is true by default. On self-hosted projects or in local development, this is false by default.
|
||||
|
||||
There are hundreds of millions (and growing!) known passwords out there. Malicious actors can use these lists of leaked passwords to automate login attempts (known as credential stuffing) and steal or access sensitive user data.
|
||||
Change this setting on the [Auth Providers page](/dashboard/project/_/auth/providers) for hosted projects, or in the [configuration file](/docs/guides/cli/config#auth.email.enable_confirmations) for self-hosted projects.
|
||||
|
||||
### Password strength and leaked password protection
|
||||
### Signing up with an email and password
|
||||
|
||||
To help protect your users, Supabase Auth allows you fine-grained control over the strength of the passwords used on your project. You can configure these in your project's [Auth settings](/dashboard/project/_/settings/auth):
|
||||
There are two possible flows for email signup: [implicit flow](/docs/guides/auth/session#implicit-flow) and [PKCE flow](/docs/guides/auth/session#pkce-flow). If you're using SSR, you're using the PKCE flow. If you're using client-only code, the default flow depends upon the client library. The implicit flow is the default in JavaScript and Dart, and the PKCE flow is the default in Swift.
|
||||
|
||||
- Set a large minimum password length. Anything less than 8 characters is not recommended.
|
||||
- Set the required characters that must appear at least once in a user's password. Use the strongest option of requiring digits, lowercase and uppercase letters, and symbols.
|
||||
- Prevent the use of leaked passwords. Supabase Auth uses the open-source [HaveIBeenPwned.org Pwned Passwords API](https://haveibeenpwned.com/Passwords) to reject passwords that have been leaked and are known by malicious actors.
|
||||
The instructions in this section assume that email confirmations are enabled.
|
||||
|
||||
### Additional recommendations
|
||||
<Tabs
|
||||
scrollable
|
||||
stickyTabList={{
|
||||
style: {
|
||||
top: 'var(--header-height)',
|
||||
backgroundColor: 'hsl(var(--background-default) / var(--tw-bg-opacity))',
|
||||
maskImage: 'none',
|
||||
borderBottom: '1px solid hsl(var(--border-default) / 1)',
|
||||
}
|
||||
}}
|
||||
size="large"
|
||||
type="underlined"
|
||||
queryGroup="flow"
|
||||
>
|
||||
<TabPanel id="implicit" label="Implicit flow">
|
||||
|
||||
In addition to choosing suitable password strength settings and preventing the use of leaked passwords, consider asking your users to:
|
||||
The implicit flow only works for client-only apps. Your site directly receives the access token after the user confirms their email.
|
||||
|
||||
- Use a password manager to store and generate passwords.
|
||||
- Avoid password reuse across websites and apps.
|
||||
- Avoid using personal information in passwords.
|
||||
- Use [Multi-Factor Authentication](/docs/guides/auth/auth-mfa).
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
queryGroup="language"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
## Resetting a user's password (forgot password)
|
||||
To sign up the user, call [signUp()](/docs/reference/javascript/auth-signup) with their email address and password.
|
||||
|
||||
Strong passwords are difficult to remember, so Supabase Auth provides you with APIs to build a secure password reset flow.
|
||||
You can optionally specify a URL to redirect to after the user clicks the confirmation link. This URL must be configured as a [Redirect URL](/docs/guides/auth/redirect-urls), which you can do in the [dashboard](/dashboard/project/_/auth/url-configuration) for hosted projects, or in the [configuration file](/docs/guides/cli/config#auth.additional_redirect_urls) for self-hosted projects.
|
||||
|
||||
### Overview
|
||||
If you don't specify a redirect URL, the user is automatically redirected to your site URL. This defaults to `localhost:3000`, but you can also configure this.
|
||||
|
||||
To add a secure password reset flow to your application:
|
||||
```js
|
||||
async function signUpNewUser() {
|
||||
const { data, error } = await supabase.auth.signUp({
|
||||
email: 'example@email.com',
|
||||
password: 'example-password',
|
||||
options: {
|
||||
emailRedirectTo: 'https://example.com/welcome',
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
1. Build the **password reset page**:
|
||||
- Should be publicly accessible and contain a form asking for the user's email address.
|
||||
- Use the [`supabase.auth.resetPasswordForEmail`](/docs/reference/javascript/auth-resetpasswordforemail) API to request a password reset link for a user's email address.
|
||||
- Specify the `redirectTo` parameter when calling this API to point to the URL of the **change password page.**
|
||||
2. Build the **password change page**:
|
||||
- Should be accessible only to authenticated users.
|
||||
- Add its URL to the allowed [Redirect URLs](/dashboard/project/_/auth/url-configuration) settings.
|
||||
- Show a form or other prompt asking the user to choose a new password.
|
||||
- Call the [`supabase.auth.updateUser`](/docs/reference/javascript/auth-updateuser) API to set a new password for the user.
|
||||
</TabPanel>
|
||||
<TabPanel id="dart" label="Dart">
|
||||
|
||||
The email link sent to your users works in the same way as [passwordless authentication](/docs/guides/auth/passwordless-login):
|
||||
To sign up the user, call [signUp()](/docs/reference/dart/auth-signup) with their email address and password:
|
||||
|
||||
1. After a user visits the **reset password page**, they are sent a reset password link.
|
||||
- If you use PKCE (default), this link only works on the device or browser where the original reset request was made. Display a message to the user to make sure they don't change devices or browsers.
|
||||
- If you use the implicit grant flow, the link can be opened on any device.
|
||||
2. A user clicks the link. They are taken to Supabase Auth, which validates the link.
|
||||
- If the link is valid, it redirects to the **change password page**, which you specified in the `redirectTo` parameter.
|
||||
- If the **change password page's** URL is not properly registered in the [Redirect URLs](/dashboard/project/_/auth/url-configuration) configuration, the user is taken to the default Site URL page.
|
||||
3. Supabase Auth redirects to the **change password page** including [session](/docs/guides/auth/sessions) information in the URL.
|
||||
- If you used PKCE (default), the redirect contains the `code` query param.
|
||||
- If you are not using official Supabase libraries, or have a custom setup, extract the `code` from the URL and call the [`supabase.auth.exchangeCodeForSession`](/docs/reference/javascript/auth-exchangecodeforsession) API.
|
||||
- Official Supabase libraries handle code the code exchange for you.
|
||||
- If you used the implicit flow, the redirect contains a URL fragment encoding the user's [session](/docs/guides/auth/sessions).
|
||||
```dart
|
||||
Future<void> signUpNewUser() async {
|
||||
final AuthResponse res = await supabase.auth.signUp(
|
||||
email: 'example@email.com',
|
||||
password: 'example-password'
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Example: Request a password reset email
|
||||
</TabPanel>
|
||||
<TabPanel id="swift" label="Swift">
|
||||
|
||||
To sign up the user, call [signUp()](/docs/reference/swift/auth-signup) with their email address and password.
|
||||
|
||||
You can optionally specify a URL to redirect to after the user clicks the confirmation link. This URL must be configured as a [Redirect URL](/docs/guides/auth/redirect-urls), which you can do in the [dashboard](/dashboard/project/_/auth/url-configuration) for hosted projects, or in the [configuration file](/docs/guides/cli/config#auth.additional_redirect_urls) for self-hosted projects.
|
||||
|
||||
If you don't specify a redirect URL, the user is automatically redirected to your site URL. This defaults to `localhost:3000`, but you can also configure this.
|
||||
|
||||
```swift
|
||||
let response = try await supabase.auth.signUp(
|
||||
email: "example@email.com",
|
||||
password: "example-password",
|
||||
redirectTo: URL(string: "https://example.com/welcome")
|
||||
)
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="kotlin" label="Kotlin">
|
||||
|
||||
To sign up the user, call [signUpWith(Email)](/docs/reference/kotlin/auth-signup) with their email address and password:
|
||||
|
||||
```kotlin
|
||||
suspend fun signUpNewUser() {
|
||||
supabase.auth.signUpWith(Email) {
|
||||
email = "example@email.com"
|
||||
password = "example-password"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="pkce" label="PKCE flow">
|
||||
|
||||
The PKCE flow allows for server-side authentication. Unlike the implicit flow, which directly provides your app with the access token after the user clicks the confirmation link, the PKCE flow requires an intermediate token exchange step before you can get the access token.
|
||||
|
||||
##### Step 1: Update signup confirmation email
|
||||
|
||||
Update your signup email template to send the token hash. See [Email Templates](/docs/guides/auth/email-templates) for how to configure your email templates.
|
||||
|
||||
Your signup email template should contain the following HTML:
|
||||
|
||||
```html
|
||||
<h2>Confirm your signup</h2>
|
||||
|
||||
<p>Follow this link to confirm your user:</p>
|
||||
<p>
|
||||
<a href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=email"
|
||||
>Confirm your email</a
|
||||
>
|
||||
</p>
|
||||
```
|
||||
|
||||
##### Step 2: Create token exchange endpoint
|
||||
|
||||
Create an API endpoint at `<YOUR_SITE_URL>/auth/confirm` to handle the token exchange.
|
||||
|
||||
<Tabs scrollable size="small" type="underlined" defaultActiveId="nextjs" queryGroup="framework">
|
||||
<TabPanel id="nextjs" label="Next.js">
|
||||
|
||||
Create a new file at `app/auth/confirm/route.ts` and populate with the following:
|
||||
|
||||
```ts app/auth/confirm/route.ts
|
||||
import { createServerClient, type CookieOptions } from '@supabase/ssr'
|
||||
import { type EmailOtpType } from '@supabase/supabase-js'
|
||||
import { cookies } from 'next/headers'
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const token_hash = searchParams.get('token_hash')
|
||||
const type = searchParams.get('type') as EmailOtpType | null
|
||||
const next = searchParams.get('next') ?? '/'
|
||||
const redirectTo = request.nextUrl.clone()
|
||||
redirectTo.pathname = next
|
||||
|
||||
if (token_hash && type) {
|
||||
const cookieStore = cookies()
|
||||
const supabase = createServerClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||||
{
|
||||
cookies: {
|
||||
get(name: string) {
|
||||
return cookieStore.get(name)?.value
|
||||
},
|
||||
set(name: string, value: string, options: CookieOptions) {
|
||||
cookieStore.set({ name, value, ...options })
|
||||
},
|
||||
remove(name: string, options: CookieOptions) {
|
||||
cookieStore.delete({ name, ...options })
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const { error } = await supabase.auth.verifyOtp({
|
||||
type,
|
||||
token_hash,
|
||||
})
|
||||
if (!error) {
|
||||
return NextResponse.redirect(redirectTo)
|
||||
}
|
||||
}
|
||||
|
||||
// return the user to an error page with some instructions
|
||||
redirectTo.pathname = '/auth/auth-code-error'
|
||||
return NextResponse.redirect(redirectTo)
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="sveltekit" label="SvelteKit">
|
||||
|
||||
Create a new file at `src/routes/auth/confirm/+server.ts` and populate with the following:
|
||||
|
||||
```ts src/routes/auth/confirm/+server.ts
|
||||
import { redirect } from '@sveltejs/kit'
|
||||
import { type EmailOtpType } from '@supabase/supabase-js'
|
||||
|
||||
export const GET = async (event) => {
|
||||
const {
|
||||
url,
|
||||
locals: { supabase },
|
||||
} = event
|
||||
const token_hash = url.searchParams.get('token_hash') as string
|
||||
const type = url.searchParams.get('type') as EmailOtpType | null
|
||||
const next = url.searchParams.get('next') ?? '/'
|
||||
|
||||
if (token_hash && type) {
|
||||
const { error } = await supabase.auth.verifyOtp({ token_hash, type })
|
||||
if (!error) {
|
||||
throw redirect(303, `/${next.slice(1)}`)
|
||||
}
|
||||
}
|
||||
|
||||
// return the user to an error page with some instructions
|
||||
throw redirect(303, '/auth/auth-code-error')
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="astro" label="Astro">
|
||||
|
||||
Create a new file at `src/pages/auth/confirm.ts` and populate with the following:
|
||||
|
||||
```ts src/pages/auth/confirm.ts
|
||||
import { createServerClient } from '@supabase/ssr'
|
||||
import { type EmailOtpType } from '@supabase/supabase-js'
|
||||
import { type APIRoute } from 'astro'
|
||||
|
||||
export const GET: APIRoute = async ({ request, cookies, redirect }) => {
|
||||
const requestUrl = new URL(request.url)
|
||||
const token_hash = requestUrl.searchParams.get('token_hash')
|
||||
const type = requestUrl.searchParams.get('type') as EmailOtpType | null
|
||||
const next = requestUrl.searchParams.get('next') || '/'
|
||||
|
||||
if (token_hash && type) {
|
||||
const supabase = createServerClient(
|
||||
import.meta.env.PUBLIC_SUPABASE_URL,
|
||||
import.meta.env.PUBLIC_SUPABASE_ANON_KEY,
|
||||
{
|
||||
cookies: {
|
||||
get(key) {
|
||||
return cookies.get(key)?.value
|
||||
},
|
||||
set(key, value, options) {
|
||||
cookies.set(key, value, options)
|
||||
},
|
||||
remove(key, options) {
|
||||
cookies.delete(key, options)
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const { error } = await supabase.auth.verifyOtp({
|
||||
type,
|
||||
token_hash,
|
||||
})
|
||||
|
||||
if (!error) {
|
||||
return redirect(next)
|
||||
}
|
||||
}
|
||||
|
||||
// return the user to an error page with some instructions
|
||||
return redirect('/auth/auth-code-error')
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="remix" label="Remix">
|
||||
|
||||
Create a new file at `app/routes/auth.confirm.tsx` and populate with the following:
|
||||
|
||||
```ts app/routes/auth.confirm.tsx
|
||||
import { redirect, type LoaderFunctionArgs } from '@remix-run/node'
|
||||
import { createServerClient, parse, serialize } from '@supabase/ssr'
|
||||
import { type EmailOtpType } from '@supabase/supabase-js'
|
||||
|
||||
export async function loader({ request }: LoaderFunctionArgs) {
|
||||
const requestUrl = new URL(request.url)
|
||||
const token_hash = requestUrl.searchParams.get('token_hash')
|
||||
const type = requestUrl.searchParams.get('type') as EmailOtpType | null
|
||||
const next = requestUrl.searchParams.get('next') || '/'
|
||||
const headers = new Headers()
|
||||
|
||||
if (token_hash && type) {
|
||||
const cookies = parse(request.headers.get('Cookie') ?? '')
|
||||
|
||||
const supabase = createServerClient(process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!, {
|
||||
cookies: {
|
||||
get(key) {
|
||||
return cookies[key]
|
||||
},
|
||||
set(key, value, options) {
|
||||
headers.append('Set-Cookie', serialize(key, value, options))
|
||||
},
|
||||
remove(key, options) {
|
||||
headers.append('Set-Cookie', serialize(key, '', options))
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const { error } = await supabase.auth.verifyOtp({
|
||||
type,
|
||||
token_hash,
|
||||
})
|
||||
|
||||
if (!error) {
|
||||
return redirect(next, { headers })
|
||||
}
|
||||
}
|
||||
|
||||
// return the user to an error page with instructions
|
||||
return redirect('/auth/auth-code-error', { headers })
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="express" label="Express">
|
||||
|
||||
Create a new route in your express app and populate with the following:
|
||||
|
||||
```js app.js
|
||||
...
|
||||
app.get("/auth/confirm", async function (req, res) {
|
||||
const token_hash = req.query.token_hash
|
||||
const type = req.query.type
|
||||
const next = req.query.next ?? "/"
|
||||
|
||||
if (token_hash && type) {
|
||||
const supabase = createClient({ req, res })
|
||||
const { error } = await supabase.auth.verifyOtp({
|
||||
type,
|
||||
token_hash,
|
||||
})
|
||||
if (!error) {
|
||||
res.redirect(303, `/${next.slice(1)}`)
|
||||
}
|
||||
}
|
||||
|
||||
// return the user to an error page with some instructions
|
||||
res.redirect(303, '/auth/auth-code-error')
|
||||
})
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
##### Step 3: Call the sign up function to initiate the flow
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
queryGroup="language"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
To sign up the user, call [signUp()](/docs/reference/javascript/auth-signup) with their email address and password:
|
||||
|
||||
```js
|
||||
async function signUpNewUser() {
|
||||
const { data, error } = await supabase.auth.signUp({
|
||||
email: 'example@email.com',
|
||||
password: 'example-password',
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="dart" label="Dart">
|
||||
|
||||
To sign up the user, call [signUp()](/docs/reference/dart/auth-signup) with their email address and password:
|
||||
|
||||
```dart
|
||||
Future<void> signUpNewUser() async {
|
||||
final AuthResponse res = await supabase.auth.signUp(
|
||||
email: 'example@email.com',
|
||||
password: 'example-password'
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="swift" label="Swift">
|
||||
|
||||
To sign up the user, call [signUp()](/docs/reference/swift/auth-signup) with their email address and password:
|
||||
|
||||
```swift
|
||||
let response = try await supabase.auth.signUp(
|
||||
email: "example@email.com",
|
||||
password: "example-password",
|
||||
)
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="kotlin" label="Kotlin">
|
||||
|
||||
To sign up the user, call [signUpWith(Email)](/docs/reference/kotlin/auth-signup) with their email address and password:
|
||||
|
||||
```kotlin
|
||||
suspend fun signUpNewUser() {
|
||||
supabase.auth.signUpWith(Email) {
|
||||
email = "example@email.com"
|
||||
password = "example-password"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
</TabPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Signing in with an email and password
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
queryGroup="language"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
When your user signs in, call [signInWithPassword()](/docs/reference/javascript/auth-signinwithpassword) with their email address and password:
|
||||
|
||||
```js
|
||||
async function signInWithEmail() {
|
||||
const { data, error } = await supabase.auth.signInWithPassword({
|
||||
email: 'example@email.com',
|
||||
password: 'example-password',
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="dart" label="Dart">
|
||||
|
||||
When your user signs in, call [signInWithPassword()](/docs/reference/dart/auth-signinwithpassword) with their email address and password:
|
||||
|
||||
```dart
|
||||
Future<void> signInWithEmail() async {
|
||||
final AuthResponse res = await supabase.auth.signInWithPassword(
|
||||
email: 'example@email.com',
|
||||
password: 'example-password'
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="swift" label="Swift">
|
||||
|
||||
When your user signs in, call [signIn(email:password:)](/docs/reference/swift/auth-signinwithpassword) with their email address and password:
|
||||
|
||||
```swift
|
||||
try await supabase.auth.signIn(
|
||||
email: "example@email.com",
|
||||
password: "example-password"
|
||||
)
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="kotlin" label="Kotlin">
|
||||
|
||||
When your user signs in, call [signInWith(Email)](/docs/reference/kotlin/auth-signinwithpassword) with their email address and password:
|
||||
|
||||
```kotlin
|
||||
suspend fun signInWithEmail() {
|
||||
supabase.auth.signInWith(Email) {
|
||||
email = "example@email.com"
|
||||
password = "example-password"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
### Resetting a password
|
||||
|
||||
#### Step 1: Create a reset password page
|
||||
|
||||
Create a **reset password** page. This page should be publicly accessible.
|
||||
|
||||
Collect the user's email address and request a password reset email. Specify the redirect URL, which should point to the URL of a **change password** page. This URL needs to be configured in your [redirect URLs](/docs/guides/auth/redirect-urls).
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
@@ -83,8 +507,6 @@ The email link sent to your users works in the same way as [passwordless authent
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
Supabase provides a convenient method [`supabase.auth.resetPasswordForEmail`](/docs/reference/javascript/auth-resetpasswordforemail) to reset a user password. This method takes a parameter of `redirectTo`, which is an absolute URL to the update password page. This URL must be saved in your allowed [Redirect URLs](https://supabase.com/dashboard/project/_/auth/url-configuration) list found at [Authentication > Redirect Configuration](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
|
||||
```js
|
||||
await supabase.auth.resetPasswordForEmail('hello@example.com', {
|
||||
redirectTo: 'http://example.com/account/update-password',
|
||||
@@ -94,8 +516,6 @@ await supabase.auth.resetPasswordForEmail('hello@example.com', {
|
||||
</TabPanel>
|
||||
<TabPanel id="swift" label="Swift">
|
||||
|
||||
Supabase provides a convenient method [`supabase.auth.resetPasswordForEmail`](/docs/reference/swift/auth-resetpasswordforemail) to reset a user password. This method takes a parameter of `redirectTo`, which is an absolute URL to the update password page. This URL must be saved in your allowed [Redirect URLs](https://supabase.com/dashboard/project/_/auth/url-configuration) list found at [Authentication > Redirect Configuration](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
|
||||
```swift
|
||||
try await supabase.auth.resetPasswordForEmail(
|
||||
"hello@example.com",
|
||||
@@ -106,8 +526,6 @@ try await supabase.auth.resetPasswordForEmail(
|
||||
</TabPanel>
|
||||
<TabPanel id="kotlin" label="Kotlin">
|
||||
|
||||
Supabase provides a convenient method [`.sendRecoveryEmail`](/docs/reference/kotlin/auth-resetpasswordforemail) to reset a user password. This method takes a parameter of `redirectUrl`, which is an absolute URL to the update password page. This URL must be saved in your allowed [Redirect URLs](https://supabase.com/dashboard/project/_/auth/url-configuration) list found at [Authentication > Redirect Configuration](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
|
||||
```kotlin
|
||||
supabase.gotrue.sendRecoveryEmail(
|
||||
email = "hello@example.com",
|
||||
@@ -120,7 +538,41 @@ If you are on one of the Kotlin targets that have built-in support for redirect
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
### Example: Updating a user's password
|
||||
#### Step 2: Create a change password page
|
||||
|
||||
Create a **change password** page at the URL you specified in the previous step. This page should be accessible only to authenticated users.
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
queryGroup="flow"
|
||||
>
|
||||
<TabPanel id="implicit" label="Implicit flow">
|
||||
|
||||
Collect the user's new password and call `updateUser` to update their password.
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="pkce" label="PKCE flow">
|
||||
|
||||
In the PKCE flow, the reset password link only works on the device or browser where the original request was made.
|
||||
|
||||
When the user clicks the link and is redirected to the change password page, their URL contains a `code` query param. If you aren't using the official Supabase libraries, extract the `code` from the URL and exchange it for a session. For example, in JavaScript, call [`supabase.auth.exchangeCodeForSession`](/docs/reference/javascript/auth-exchangecodeforsession). If you're using an official library, the code exchange is handled for you.
|
||||
|
||||
<Admonition type="tip">
|
||||
|
||||
If your page is server-rendered and the code isn't being automatically exchanged, check whether the Supabase client is being initialized eagerly. Your server-rendered page might be lazily initializing the Supabase client after user interaction, which means the automatic code exchange can't run.
|
||||
|
||||
You can either exchange the code yourself or initialize the Supabase client eagerly.
|
||||
|
||||
</Admonition>
|
||||
|
||||
Once you have a session, collect the user's new password and call `updateUser` to update their password.
|
||||
|
||||
</TabPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
@@ -131,8 +583,6 @@ If you are on one of the Kotlin targets that have built-in support for redirect
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
To update the password, call the [`supabase.auth.updateUser`](/docs/reference/javascript/auth-updateuser) method with the new password.
|
||||
|
||||
```js
|
||||
await supabase.auth.updateUser({ password: new_password })
|
||||
```
|
||||
@@ -140,8 +590,6 @@ await supabase.auth.updateUser({ password: new_password })
|
||||
</TabPanel>
|
||||
<TabPanel id="swift" label="Swift">
|
||||
|
||||
To update the password, call the [`supabase.auth.updateUser`](/docs/reference/swift/auth-updateuser) method with the new password.
|
||||
|
||||
```swift
|
||||
try await supabase.auth.updateUser(user: UserAttributes(password: newPassword))
|
||||
```
|
||||
@@ -149,8 +597,6 @@ try await supabase.auth.updateUser(user: UserAttributes(password: newPassword))
|
||||
</TabPanel>
|
||||
<TabPanel id="kotlin" label="Kotlin">
|
||||
|
||||
To update the password, call the [`.updateUser`](/docs/reference/kotlin/auth-updateuser) method with the new password.
|
||||
|
||||
```kotlin
|
||||
supabase.auth.updateUser {
|
||||
password = "new_password"
|
||||
@@ -160,14 +606,224 @@ supabase.auth.updateUser {
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
## Frequently asked questions
|
||||
### Email sending
|
||||
|
||||
### How are passwords stored?
|
||||
The signup confirmation and password reset flows require an SMTP server to send emails.
|
||||
|
||||
Supabase Auth uses [bcrypt](https://en.wikipedia.org/wiki/Bcrypt), a strong password hashing function, to store hashes of users' passwords. Only hashed passwords are stored. You cannot impersonate a user with the password hash. Each hash is accompanied by a randomly generated salt parameter for extra security.
|
||||
The Supabase platform comes with a default email-sending service for you to try out. The service has a rate limit of <SharedData data="config">auth.rate_limits.email.inbuilt_smtp_per_hour.value</SharedData> emails per hour, and availability is on a best-effort basis. For production use, you should consider configuring a custom SMTP server.
|
||||
|
||||
The hash is stored in the `encrypted_password` column of the `auth.users` table. The column's name is a misnomer (cryptographic hashing is not encryption), but is kept for backward compatibility.
|
||||
<Admonition type="tip">
|
||||
|
||||
### How will strengthened password requirements affect current users?
|
||||
Consider configuring a custom SMTP server for production.
|
||||
|
||||
Existing users can still sign in with their current password even if it doesn't meet the new, strengthened password requirements. However, if their password falls short of these updated standards, they will encounter a `WeakPasswordError` during the `signInWithPassword` process, explaining why it's considered weak. This change is also applicable to new users and existing users changing their passwords, ensuring everyone adheres to the enhanced security standards.
|
||||
</Admonition>
|
||||
|
||||
See the [Custom SMTP guide](/docs/guides/auth/auth-smtp) for instructions.
|
||||
|
||||
#### Local development with Inbucket
|
||||
|
||||
You can test email flows on your local machine. The Supabase CLI automatically captures emails sent locally by using [Inbucket](https://github.com/inbucket/bucket).
|
||||
|
||||
In your terminal, run `supabase status` to get the Inbucket URL. Go to this URL in your browser, and follow the instructions to find your emails.
|
||||
|
||||
## With phone
|
||||
|
||||
You can use a user's mobile phone number as an identifier, instead of an email address, when they sign up with a password.
|
||||
|
||||
This practice is usually discouraged because phone networks recycle mobile phone numbers. Anyone receiving a recycled phone number gets access to the original user's account. To mitigate this risk, [implement MFA](/docs/guides/auth/auth-mfa).
|
||||
|
||||
<Admonition type="danger">
|
||||
|
||||
Protect users who use a phone number as a password-based auth identifier by enabling MFA.
|
||||
|
||||
</Admonition>
|
||||
|
||||
### Enabling phone and password-based authentication
|
||||
|
||||
Enable phone authentication on the [Auth Providers page](/dashboard/project/_/auth/providers) for hosted Supabase projects.
|
||||
|
||||
For self-hosted projects or local development, use the [configuration file](/docs/guides/cli/config#auth.sms.enable_signup). See the configuration variables namespaced under `auth.sms`.
|
||||
|
||||
If you want users to confirm their phone number on signup, you need to set up an SMS provider. Each provider has its own configuration. Supported providers include MessageBird, Twilio, Vonage, and TextLocal (community-supported).
|
||||
|
||||
<AuthSmsProviderConfig />
|
||||
|
||||
### Signing up with a phone number and password
|
||||
|
||||
To sign up the user, call [`signUp()`](/docs/reference/javascript/auth-signup) with their phone number and password:
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
queryGroup="language"
|
||||
>
|
||||
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
```js
|
||||
const { data, error } = await supabase.auth.signUp({
|
||||
phone: '+13334445555',
|
||||
password: 'some-password',
|
||||
})
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="swift" label="Swift">
|
||||
|
||||
```swift
|
||||
try await supabase.auth.signUp(
|
||||
phone: "+13334445555",
|
||||
password: "some-password"
|
||||
)
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="kotlin" label="Kotlin">
|
||||
|
||||
```kotlin
|
||||
supabase.auth.signUpWith(Phone) {
|
||||
phone = "+13334445555"
|
||||
password = "some-password"
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="http" label="HTTP">
|
||||
|
||||
```bash
|
||||
curl -X POST 'https://cvwawazfelidkloqmbma.supabase.co/auth/v1/signup' \
|
||||
-H "apikey: SUPABASE_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"phone": "+13334445555",
|
||||
"password": "some-password"
|
||||
}'
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
If you have phone verification turned on, the user receives an SMS with a 6-digit pin that you must verify within 60 seconds:
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
queryGroup="language"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
You should present a form to the user so they can input the 6 digit pin, then send it along with the phone number to `verifyOtp`:
|
||||
|
||||
```js
|
||||
const {
|
||||
data: { session },
|
||||
error,
|
||||
} = await supabase.auth.verifyOtp({
|
||||
phone: '+13334445555',
|
||||
token: '123456',
|
||||
type: 'sms',
|
||||
})
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="swift" label="Swift">
|
||||
|
||||
You should present a form to the user so they can input the 6 digit pin, then send it along with the phone number to `verifyOTP`:
|
||||
|
||||
```swift
|
||||
try await supabase.auth.verifyOTP(
|
||||
phone: "+13334445555",
|
||||
token: "123456",
|
||||
type: .sms
|
||||
)
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="kotlin" label="Kotlin">
|
||||
|
||||
You should present a form to the user so they can input the 6 digit pin, then send it along with the phone number to `verifyPhoneOtp`:
|
||||
|
||||
```kotlin
|
||||
supabase.auth.verifyPhoneOtp(
|
||||
type = OtpType.Phone.SMS,
|
||||
phone = "+13334445555",
|
||||
token = "123456"
|
||||
)
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="http" label="HTTP">
|
||||
|
||||
```bash
|
||||
curl -X POST 'https://<PROJECT_REF>.supabase.co/auth/v1/verify' \
|
||||
-H "apikey: <SUPABASE_KEY>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"type": "sms",
|
||||
"phone": "+13334445555",
|
||||
"token": "123456"
|
||||
}'
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
### Signing in a with a phone number and password
|
||||
|
||||
Call the function to sign in with the user's phone number and password:
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
queryGroup="language"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
```js
|
||||
const { user, error } = await supabase.auth.signInWithPassword({
|
||||
phone: '+13334445555',
|
||||
password: 'some-password',
|
||||
})
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="swift" label="Swift">
|
||||
|
||||
```swift
|
||||
try await supabase.auth.signIn(
|
||||
phone: "+13334445555",
|
||||
password: "some-password"
|
||||
)
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="kotlin" label="Kotlin">
|
||||
|
||||
```kotlin
|
||||
supabase.auth.signInWith(Phone) {
|
||||
phone = "+13334445555"
|
||||
password = "some-password"
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="http" label="HTTP">
|
||||
|
||||
```bash
|
||||
curl -X POST 'https://cvwawazfelidkloqmbma.supabase.co/auth/v1/token?grant_type=password' \
|
||||
-H "apikey: SUPABASE_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"phone": "+13334445555",
|
||||
"password": "some-password"
|
||||
}'
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
@@ -3,176 +3,31 @@ title: 'Phone Login'
|
||||
description: 'Learn about logging in to your platform using SMS one-time passwords.'
|
||||
---
|
||||
|
||||
<Admonition type="tip">
|
||||
Phone Login is a method of authentication that allows users to log in to a website or application without using a password. The user authenticates through a one-time code sent via SMS.
|
||||
|
||||
Authenticating users via SMS can become expensive. Adjust your project's rate limits and [configure CAPTCHA](/docs/guides/auth/auth-captcha) to control the bill. Read more about this in the [Production Checklist](/docs/guides/platform/going-into-prod).
|
||||
Users can also log in with their phones using Native Mobile Login with the built-in identity provider. For Native Mobile Login with Android and iOS, see the [Social Login guides](/docs/guides/auth/social-login).
|
||||
|
||||
</Admonition>
|
||||
Phone OTP login can:
|
||||
|
||||
Phone Login is a method of authentication that allows users to log in to a website or application without using a password. Instead of entering a password, the user provides another form of authentication through a one-time code sent via SMS.
|
||||
- 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
|
||||
|
||||
## Benefits
|
||||
<CostWarning />
|
||||
|
||||
There are several reasons why you might want to add phone login to your applications:
|
||||
## Enabling Phone Login
|
||||
|
||||
- **Improved user experience**: By eliminating the need for users to remember and enter complex passwords, phone login can make it easier and more convenient for users to log in to your application. This can improve the overall user experience and make it more enjoyable for users to interact with your application.
|
||||
Enable phone authentication on the [Auth Providers page](/dashboard/project/_/auth/providers) for hosted Supabase projects.
|
||||
|
||||
- **Increased security**: Phone login can improve the security of your application by reducing the risk of password-related security breaches, such as password reuse and weak passwords. By using alternative forms of authentication, such as one-time codes or biometric factors, you can make it more difficult for unauthorized users to access your application.
|
||||
For self-hosted projects or local development, use the [configuration file](/docs/guides/cli/config#auth.sms.enable_signup). See the configuration variables namespaced under `auth.sms`.
|
||||
|
||||
- **Reduced support burden**: Phone login can also help reduce the support burden for your team by eliminating the need to handle password recovery flows or deal with other password-related issues. This can free up your team to focus on other important tasks and improve the efficiency of your operation.
|
||||
You also need to set up an SMS provider. Each provider has its own configuration. Supported providers include MessageBird, Twilio, Vonage, and TextLocal (community-supported).
|
||||
|
||||
## Set up a provider with Supabase Auth
|
||||
<AuthSmsProviderConfig />
|
||||
|
||||
To use Phone Login, you first need to set up a Phone provider. Supabase supports Phone Login with several communications platforms. Follow the guides below to set up your provider.
|
||||
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>.
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-12 gap-10 not-prose py-8">
|
||||
<NavData data="phoneLoginsItems">
|
||||
{(data) =>
|
||||
data.map((item) => (
|
||||
<Link href={`${item.url}`} key={item.name} passHref className="col-span-4">
|
||||
<IconPanel
|
||||
title={item.name}
|
||||
span="col-span-6"
|
||||
icon={item.icon}
|
||||
isDarkMode={item.isDarkMode}
|
||||
hasLightIcon={item.hasLightIcon}
|
||||
>
|
||||
{item.linkDescription}
|
||||
</IconPanel>
|
||||
</Link>
|
||||
))
|
||||
}
|
||||
</NavData>
|
||||
</div>
|
||||
|
||||
## Using Phone Login
|
||||
|
||||
You can use Phone Login to:
|
||||
|
||||
- [Sign up a user with phone number and password](/docs/guides/auth/phone-login#sign-up-a-user-with-phone-number-and-password)
|
||||
- [Sign in a user with a One Time Password (OTP)](/docs/guides/auth/phone-login#sign-in-a-user-with-otp)
|
||||
- [Update a user's phone number](/docs/guides/auth/phone-login#update-a-users-phone-number)
|
||||
|
||||
Each of these flows sends the user an SMS containing a six-digit PIN, which you must [verify](/docs/guides/auth/phone-login#verify-a-user) to complete the flow.
|
||||
|
||||
### Sign up a user with phone number and password
|
||||
|
||||
You can use a user's mobile phone number, instead of an email address, when they sign up with a password.
|
||||
|
||||
This practice is usually discouraged because phone networks often recycle mobile phone numbers. Anyone receiving a recycled phone number gets access to the original user's account. To mitigate this risk, [implement MFA](/docs/guides/auth/auth-mfa).
|
||||
|
||||
To sign up the user, call [`signUp()`](/docs/reference/javascript/auth-signup) with their phone number and password:
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
queryGroup="language"
|
||||
>
|
||||
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
```js
|
||||
const { data, error } = await supabase.auth.signUp({
|
||||
phone: '+13334445555',
|
||||
password: 'some-password',
|
||||
})
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="swift" label="Swift">
|
||||
|
||||
```swift
|
||||
try await supabase.auth.signUp(
|
||||
phone: "+13334445555",
|
||||
password: "some-password"
|
||||
)
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="kotlin" label="Kotlin">
|
||||
|
||||
```kotlin
|
||||
supabase.auth.signUpWith(Phone) {
|
||||
phone = "+13334445555"
|
||||
password = "some-password"
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="http" label="HTTP">
|
||||
|
||||
```bash
|
||||
curl -X POST 'https://cvwawazfelidkloqmbma.supabase.co/auth/v1/signup' \
|
||||
-H "apikey: SUPABASE_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"phone": "+13334445555",
|
||||
"password": "some-password"
|
||||
}'
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
The user receives an SMS with a 6-digit pin that you must [verify](/docs/guides/auth/phone-login#verify-a-user) within 60 seconds.
|
||||
|
||||
Once a user's phone number is verified, they can sign in using their phone number and password without requiring phone number re-verification at each sign-in:
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
queryGroup="language"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
```js
|
||||
const { user, error } = await supabase.auth.signInWithPassword({
|
||||
phone: '+13334445555',
|
||||
password: 'some-password',
|
||||
})
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="swift" label="Swift">
|
||||
|
||||
```swift
|
||||
try await supabase.auth.signIn(
|
||||
phone: "+13334445555",
|
||||
password: "some-password"
|
||||
)
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="kotlin" label="Kotlin">
|
||||
|
||||
```kotlin
|
||||
supabase.auth.signInWith(Phone) {
|
||||
phone = "+13334445555"
|
||||
password = "some-password"
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="http" label="HTTP">
|
||||
|
||||
```bash
|
||||
curl -X POST 'https://cvwawazfelidkloqmbma.supabase.co/auth/v1/token?grant_type=password' \
|
||||
-H "apikey: SUPABASE_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"phone": "+13334445555",
|
||||
"password": "some-password"
|
||||
}'
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
### Sign in a user with OTP
|
||||
## Signing in with phone OTP
|
||||
|
||||
With OTP, a user can sign in without setting a password on their account. They need to verify their phone number each time they sign in.
|
||||
|
||||
@@ -224,44 +79,9 @@ curl -X POST 'https://cvwawazfelidkloqmbma.supabase.co/auth/v1/otp' \
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
The user receives an SMS with a 6-digit pin that you must [verify](/docs/guides/auth/phone-login#verify-a-user) within 60 seconds.
|
||||
The user receives an SMS with a 6-digit pin that you must verify within 60 seconds.
|
||||
|
||||
### Update a user's phone number
|
||||
|
||||
To update a user's phone number, the user must be logged in. Call [`updateUser()`](/docs/reference/javascript/auth-updateuser) with their phone number:
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
queryGroup="language"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
```js
|
||||
const { data, error } = await supabase.auth.updateUser({
|
||||
phone: '123456789',
|
||||
})
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="swift" label="Swift">
|
||||
|
||||
```swift
|
||||
try await supabase.auth.updateUser(
|
||||
user: UserAttributes(
|
||||
phone: "123456789"
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
The user receives an SMS with a 6-digit pin that you must [verify](/docs/guides/auth/phone-login#verify-a-user) within 60 seconds.
|
||||
|
||||
### Verify a user
|
||||
## Verifying a phone OTP
|
||||
|
||||
To verify the one-time password (OTP) sent to the user's phone number, call [`verifyOtp()`](/docs/reference/javascript/auth-verifyotp) with the phone number and OTP:
|
||||
|
||||
@@ -342,3 +162,38 @@ If successful the user will now be logged in and you should receive a valid sess
|
||||
```
|
||||
|
||||
The access token can be sent in the Authorization header as a Bearer token for any CRUD operations on supabase-js. See our guide on [Row Level Security](/docs/guides/auth#row-level-security) for more info on restricting access on a user basis.
|
||||
|
||||
## Updating a phone number
|
||||
|
||||
To update a user's phone number, the user must be logged in. Call [`updateUser()`](/docs/reference/javascript/auth-updateuser) with their phone number:
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
queryGroup="language"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
```js
|
||||
const { data, error } = await supabase.auth.updateUser({
|
||||
phone: '123456789',
|
||||
})
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="swift" label="Swift">
|
||||
|
||||
```swift
|
||||
try await supabase.auth.updateUser(
|
||||
user: UserAttributes(
|
||||
phone: "123456789"
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
The user receives an SMS with a 6-digit pin that you must [verify](#verifying-a-phone-otp) within 60 seconds.
|
||||
|
||||
8
apps/docs/content/guides/auth/rate-limits.mdx
Normal file
8
apps/docs/content/guides/auth/rate-limits.mdx
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
title: 'Rate limits'
|
||||
subtitle: 'Rate limits protect your services from abuse'
|
||||
---
|
||||
|
||||
Supabase Auth enforces rate limits on endpoints to prevent abuse. Some rate limits are [customizable](/dashboard/project/_/auth/rate-limits).
|
||||
|
||||
<AuthRateLimits />
|
||||
@@ -7,7 +7,9 @@ subtitle: 'Set up redirect urls with Supabase Auth.'
|
||||
|
||||
## Overview
|
||||
|
||||
When using [passwordless sign-ins](/docs/reference/javascript/auth-signinwithotp) or [third-party providers](/docs/reference/javascript/auth-signinwithoauth#sign-in-using-a-third-party-provider-with-redirect), the Supabase client library methods provide a `redirectTo` parameter to specify where to redirect the user to after authentication. By default, the user will be redirected to the [`SITE_URL`](/docs/guides/auth/concepts/redirect-urls) but you can modify the `SITE_URL` or add additional redirect URLs to the [allow list](https://supabase.com/dashboard/project/_/auth/url-configuration). Once you've added necessary URLs to the allow list, you can specify the URL you want the user to be redirected to in the `redirectTo` parameter.
|
||||
When using [passwordless sign-ins](/docs/reference/javascript/auth-signinwithotp) or [third-party providers](/docs/reference/javascript/auth-signinwithoauth#sign-in-using-a-third-party-provider-with-redirect), the Supabase client library methods provide a `redirectTo` parameter to specify where to redirect the user to after authentication. By default, the user will be redirected to the [`SITE_URL`](/docs/guides/auth/redirect-urls) but you can modify the `SITE_URL` or add additional redirect URLs to the allow list. Once you've added necessary URLs to the allow list, you can specify the URL you want the user to be redirected to in the `redirectTo` parameter.
|
||||
|
||||
To edit the allow list, go to the [URL Configuration](/dashboard/project/_/auth/url-configuration) page. In local development or self-hosted projects, use the [configuration file](/docs/guides/cli/config#auth.additional_redirect_urls).
|
||||
|
||||
## Use wildcards in redirect URLs
|
||||
|
||||
@@ -1,310 +0,0 @@
|
||||
---
|
||||
id: 'row-level-security'
|
||||
title: 'Row Level Security'
|
||||
description: 'Using Row Level Security with Supabase Auth.'
|
||||
subtitle: 'Using Row Level Security with Supabase Auth.'
|
||||
tocVideo: 'Ow_Uzedfohk'
|
||||
---
|
||||
|
||||
[Postgres Row Level Security](/docs/guides/database/postgres/row-level-security) (RLS) is a feature of Postgres that allows you to control which users are permitted to perform SELECT/INSERT/UPDATE/DELETE statements on specific rows within tables and views. For example, you could restrict a `blog_post` table such that the current user is only allowed to UPDATE rows where their user id is set in the table's `author_id` column.
|
||||
|
||||
Supabase Auth is designed to work perfectly with RLS.
|
||||
|
||||
You can use RLS to create [Policies](https://www.postgresql.org/docs/current/sql-createpolicy.html) that are incredibly powerful and flexible, allowing you to write complex SQL rules which fit your unique business needs.
|
||||
|
||||
<div className="video-container">
|
||||
<iframe
|
||||
src="https://www.youtube-nocookie.com/embed/Ow_Uzedfohk"
|
||||
frameBorder="1"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
## Policies
|
||||
|
||||
Policies are easy to understand once you get the hang of them. Each policy is attached to a table, and the policy is executed every time a table is accessed. You can just think of them as adding a `WHERE` clause to every query. For example a policy like this ...
|
||||
|
||||
```sql
|
||||
create policy "Individuals can view their own todos."
|
||||
on todos for select
|
||||
using ( (select auth.uid()) = user_id );
|
||||
```
|
||||
|
||||
.. would translate to this whenever a user tries to select from the todos table:
|
||||
|
||||
```sql
|
||||
select *
|
||||
from todos
|
||||
where auth.uid() = todos.user_id;
|
||||
-- Policy is implicitly added.
|
||||
```
|
||||
|
||||
## Authenticated and unauthenticated roles
|
||||
|
||||
Supabase Auth maps every request to one of the roles:
|
||||
|
||||
- `anon`: an unauthenticated request (the user is not logged in)
|
||||
- `authenticated`: an authenticated request (the user is logged in)
|
||||
|
||||
These are actually [Postgres Roles](/docs/guides/database/postgres/roles), and so they have significant value for the [performance](/docs/guides/database/postgres/row-level-security#specify-roles-in-your-policies) of your RLS Policies. You can use these roles within your Policies using the `TO` clause:
|
||||
|
||||
```sql
|
||||
create policy "Profiles are viewable by everyone"
|
||||
on profiles for select
|
||||
to authenticated, anon
|
||||
using ( true );
|
||||
|
||||
-- OR
|
||||
|
||||
create policy "Public profiles are viewable only by authenticated users"
|
||||
on profiles for select
|
||||
to authenticated
|
||||
using ( true );
|
||||
```
|
||||
|
||||
<Admonition type="note" label="Anonymous user vs the anon key">
|
||||
|
||||
Using the `anon` Postgres role is different from an [anonymous user](/docs/guides/auth/auth-anonymous) in Supabase Auth. An anonymous user assumes the `authenticated` role to access the database and can be differentiated from a permanent user by checking the `is_anonymous` claim in the JWT.
|
||||
|
||||
</Admonition>
|
||||
|
||||
## Helper functions
|
||||
|
||||
Supabase provides some helper functions that make it easier to write Policies.
|
||||
|
||||
### `auth.uid()`
|
||||
|
||||
Returns the ID of the user making the request.
|
||||
|
||||
### `auth.jwt()`
|
||||
|
||||
Returns the JWT of the user making the request. Anything that you store in the user's `app_metadata` column or the `user_metadata` column will be accessible using this function. It's important to know the distinction between these two:
|
||||
|
||||
- `user_metadata` - can be updated by the authenticated user using the `supabase.auth.update()` function. It is not a good place to store authorization data.
|
||||
- `app_metadata` - cannot be updated by the user, so it's a good place to store authorization data.
|
||||
|
||||
The `auth.jwt()` function is extremely versatile. For example, if you store some team data inside `app_metadata`, you can use it to determine whether a particular user belongs to a team. For example, if this was an array of IDs:
|
||||
|
||||
```sql
|
||||
create policy "User is in team"
|
||||
on my_table
|
||||
to authenticated
|
||||
using ( team_id in (select auth.jwt() -> 'app_metadata' -> 'teams'));
|
||||
```
|
||||
|
||||
<Admonition type="caution">
|
||||
|
||||
Keep in mind that a JWT is not always "fresh". In the example above, even if you remove a user from a team and update the `app_metadata` field, that will not be reflected using `auth.jwt()` until the user's JWT is refreshed.
|
||||
|
||||
Also, if you are using Cookies for Auth, then you must be mindful of the JWT size. Some browsers are limited to 4096 bytes for each cookie, and so the total size of your JWT should be small enough to fit inside this limitation.
|
||||
|
||||
</Admonition>
|
||||
|
||||
## Important considerations
|
||||
|
||||
We recommend reading the [Row Level Security](/docs/guides/database/postgres/row-level-security) guide in the database section to learn more about Postgres RLS. When using Postgres on Supabase there are some important things to keep in mind to maintain data security.
|
||||
|
||||
### Never use a service key on the client
|
||||
|
||||
Supabase provides special "Service" keys, which can be used to bypass RLS. These should never be used in the browser or exposed to customers, but they are useful for administrative tasks.
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
Supabase will adhere to the RLS policy of the signed-in user, even if the client library is initialized with a Service Key.
|
||||
|
||||
</Admonition>
|
||||
|
||||
### Always enable RLS on public tables
|
||||
|
||||
You should _always_ enable RLS on tables created in a public schema. This is considered "default safe". Unfortunately this is not enabled by default on Postgres, so you will need to keep this in mind - especially if you are using the SQL Editor or database migrations. RLS is already enabled by default if you create a table using the Table Editor. If you want to allow public access to a table, just add a Policy with `true`:
|
||||
|
||||
```sql
|
||||
create policy "Allow public access"
|
||||
on my_table for select
|
||||
using ( true );
|
||||
```
|
||||
|
||||
### Protect views using `security_invoker`
|
||||
|
||||
Views bypass RLS by default because they are usually created with the `postgres` user. This is a feature of Postgres, which automatically creates views with `security definer`.
|
||||
|
||||
In Postgres 15 and above, you can make a view obey the RLS policies of the underlying tables when invoked by `anon` and `authenticated` roles by setting `security_invoker = true`.
|
||||
|
||||
```sql
|
||||
create view <VIEW_NAME>
|
||||
with(security_invoker = true)
|
||||
as select <QUERY>
|
||||
```
|
||||
|
||||
In older versions of Postgres, protect your views by revoking access from the `anon` and `authenticated` roles, or by putting them in an unexposed schema.
|
||||
|
||||
### Using external authorization systems
|
||||
|
||||
If you want to use another authorization method for your applications that's also fine. Supabase is "just Postgres", so if your application works with Postgres, then it also works with Supabase. If you take this path, don't put your tables in the `public` schema - instead create a new schema for your tables and functions:
|
||||
|
||||
```sql
|
||||
create schema private;
|
||||
|
||||
create table private.employees (
|
||||
id serial primary key,
|
||||
name text
|
||||
);
|
||||
```
|
||||
|
||||
If you do put anything in the `public` schema, make sure to enable RLS (you don't need to add any policies):
|
||||
|
||||
```sql
|
||||
create table profiles (
|
||||
id serial primary key,
|
||||
email text
|
||||
);
|
||||
|
||||
alter table profiles enable row level security;
|
||||
```
|
||||
|
||||
This makes the tables inaccessible via the [APIs](/docs/guides/api).
|
||||
|
||||
## Usage
|
||||
|
||||
Row Level Security is extremely versatile, since it simply uses SQL to express access rules for your data.
|
||||
|
||||
### Using functions
|
||||
|
||||
You can use any Postgres function inside a Policy. The [Helper Functions](#helper-functions) above are simply Postgres functions we've made available in the `auth` schema. This is an example which:
|
||||
|
||||
1. Creates a table called `profiles` in the public schema (default schema).
|
||||
2. Enables RLS.
|
||||
3. Creates a policy which allows logged in users to update their own data, using the `auth.uid()` function.
|
||||
|
||||
```sql
|
||||
-- 1. Create table
|
||||
create table profiles (
|
||||
id uuid references auth.users,
|
||||
avatar_url text
|
||||
);
|
||||
|
||||
-- 2. Enable RLS
|
||||
alter table profiles enable row level security;
|
||||
|
||||
-- 3. Create Policy
|
||||
create policy "Users can update their own profiles"
|
||||
on profiles for update
|
||||
to authenticated
|
||||
using (
|
||||
(select auth.uid()) = id
|
||||
);
|
||||
```
|
||||
|
||||
**Note:** If you want to use [upsert](/docs/reference/javascript/upsert) operations, the user needs to have `INSERT`, `UPDATE`, and `SELECT` permissions.
|
||||
|
||||
### Using joins
|
||||
|
||||
Policies can include table joins. This example shows how you can query "external" tables to build more advanced rules. RLS policies are executed on every access of the table, so be careful to make sure that policies are efficient.
|
||||
|
||||
```sql
|
||||
-- 1. Create a table of teams
|
||||
create table teams (
|
||||
id serial primary key,
|
||||
name text
|
||||
);
|
||||
|
||||
-- 2. Create many to many join
|
||||
create table members (
|
||||
team_id bigint references teams,
|
||||
user_id uuid references auth.users
|
||||
);
|
||||
|
||||
-- 3. Enable RLS
|
||||
alter table teams enable row level security;
|
||||
|
||||
-- 4. Create Policy
|
||||
create policy "Team members can update team details if they belong to the team"
|
||||
on teams
|
||||
for update using (
|
||||
(select auth.uid()) in (
|
||||
select user_id from members
|
||||
where team_id = id
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
An important note here: if RLS is also enabled for `members`, the user must also have read (`select`) access to members. Otherwise the joined query will not yield any results. Another alternative is to use a "security definer" function which is created by a user with [bypassrls](/docs/guides/database/postgres/row-level-security#specify-roles-in-your-policies) privileges.
|
||||
|
||||
### Using security definer functions
|
||||
|
||||
You can use `security definer` functions inside Policies. This is useful in a many-to-many relationships, and [important for performance](/docs/guides/database/postgres/row-level-security#use-security-definer-functions). Following the `teams` and `members` example from above, this example shows how you can use the security definer function in combination with a policy to control access to the `members` table.
|
||||
|
||||
```sql
|
||||
-- 1. Create a table of teams
|
||||
create table teams (
|
||||
id serial primary key,
|
||||
name text
|
||||
);
|
||||
|
||||
-- 2. Create many to many join
|
||||
create table members (
|
||||
team_id bigint references teams,
|
||||
user_id uuid references auth.users
|
||||
);
|
||||
|
||||
-- 2. Enable RLS
|
||||
alter table teams enable row level security;
|
||||
alter table members enable row level security;
|
||||
|
||||
-- 3. Create security definer function, which should be run as "postgres"
|
||||
create function private.get_teams_for_authenticated_user()
|
||||
returns setof bigint
|
||||
language sql
|
||||
security definer
|
||||
set search_path = ''
|
||||
stable
|
||||
as $$
|
||||
select team_id
|
||||
from public.members
|
||||
where user_id = auth.uid()
|
||||
$$;
|
||||
|
||||
-- 4. Create Policy
|
||||
create policy "Team members can update team members if they belong to the team."
|
||||
on members
|
||||
for all using (
|
||||
team_id in (
|
||||
select private.get_teams_for_authenticated_user()
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
### Using built-in functions
|
||||
|
||||
Postgres has a number of built-in functions. Most commonly you'll use `in()` and `any()` which will match a column's value to a list of values.
|
||||
|
||||
You can use any Postgres functions inside Policies. For example, we can use the `right(string, n)` function to match email domains:
|
||||
|
||||
```sql
|
||||
create policy "Only Supabase staff can update the leaderboard"
|
||||
on leaderboard
|
||||
to authenticated
|
||||
for update using (
|
||||
right((select auth.jwt() ->> 'email'), 13) = '@supabase.com'
|
||||
);
|
||||
```
|
||||
|
||||
### Using Multi-factor Authentication
|
||||
|
||||
RLS can be [combined with Multi-Factor Authentication](/docs/guides/auth/auth-mfa#enforce-rules-for-mfa-logins) in Supabase Auth. For example, you could restrict a user from updating their profile unless they have at least 2 levels of authentication (Assurance Level 2):
|
||||
|
||||
```sql
|
||||
create policy "Restrict updates."
|
||||
on profiles
|
||||
as restrictive
|
||||
for update
|
||||
to authenticated using (
|
||||
(select auth.jwt()->>'aal') = 'aal2'
|
||||
);
|
||||
```
|
||||
|
||||
## More resources
|
||||
|
||||
- [Testing your database](/docs/guides/database/testing)
|
||||
- Community repo on testing RLS using [pgTAP and dbdev](https://github.com/usebasejump/supabase-test-helpers/tree/main)
|
||||
@@ -1,240 +0,0 @@
|
||||
---
|
||||
id: 'server-side-rendering'
|
||||
title: 'Server-Side Rendering'
|
||||
description: 'Render pages with user information on the server.'
|
||||
---
|
||||
|
||||
Single-page apps with server-side rendering (SSR) is a popular way to optimize rendering performance and leverage advanced caching strategies.
|
||||
|
||||
Supabase Auth supports server-side rendering when you need access to user information, or your server needs to authorize API requests on behalf of your user to render content.
|
||||
|
||||
When a user authenticates with Supabase Auth, two pieces of information are issued by the server:
|
||||
|
||||
1. **Access token** in the form of a JWT.
|
||||
2. **Refresh token** which is a randomly generated string.
|
||||
|
||||
Most Supabase projects have their auth server listening on `<project-ref>.supabase.co/auth/v1`, thus the access token and refresh token are set as `sb-access-token` and `sb-refresh-token` cookies on the `<project-ref>.supabase.co` domain.
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
These cookie names are for internal Supabase use only and may change without warning. They are included in this guide for illustration purposes only.
|
||||
|
||||
</Admonition>
|
||||
|
||||
Web browsers limit access to cookies across domains, consistent with the [Same-Origin Policy (SOP)](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy).
|
||||
|
||||
Your web application cannot access these cookies, nor will these cookies be sent to your application's server.
|
||||
|
||||
## Understanding the authentication flow
|
||||
|
||||
When you call one of the `signIn` methods, the client library running in the browser sends the request to the Supabase Auth server. The Auth server determines whether to verify a phone number, email and password combination, a Magic Link, or use a social login (if you have any setup in your project).
|
||||
|
||||
Upon successful verification of the identity of the user, the Supabase Auth server redirects the user back to your single-page app.
|
||||
|
||||
<Admonition type="tip">
|
||||
|
||||
You can configure [redirects URLs](https://supabase.com/dashboard/project/_/auth/url-configuration) in the Supabase Dashboard. You can use [wildcard match patterns](/docs/guides/auth#redirect-urls-and-wildcards) like `*` and `**` to allow redirects to different forms of URLs.
|
||||
|
||||
</Admonition>
|
||||
|
||||
Supabase Auth supports two authentication flows: **Implicit** and **PKCE**. The **PKCE** flow is generally preferred when on the server. It introduces a few additional steps which guard against replay and URL capture attacks. Unlike the implicit flow, it also allows users to access the `access_token` and `refresh_token` on the server.
|
||||
|
||||
<Accordion
|
||||
type="default"
|
||||
openBehaviour="multiple"
|
||||
chevronAlign="right"
|
||||
justified
|
||||
size="medium"
|
||||
className="text-foreground-light"
|
||||
>
|
||||
<div className="border-b pb-3">
|
||||
<Accordion.Item
|
||||
header={<span className="text-foreground font-bold">Implicit</span>}
|
||||
id={`ssr-implicit-flow`}
|
||||
>
|
||||
|
||||
When using the implicit flow, a redirect URL will be returned with the following structure:
|
||||
```
|
||||
https://yourapp.com/...#access_token=<...>&refresh_token=<...>&...
|
||||
```
|
||||
|
||||
The first access and refresh tokens after a successful verification are contained in the URL fragment (anything after the `#` sign) of the redirect location. This is intentional and not configurable.
|
||||
|
||||
The client libraries are designed to listen for this type of URL, extract the access token, refresh token and some extra information from it, and finally persist it in local storage for further use by the library and your app.
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
Web browsers do not send the URL fragment to the server they're making the request to. Since you may not be hosting the single-page app on a server under your direct control (such as on GitHub Pages or other freemium hosting providers), we want to prevent hosting services from getting access to your user's authorization credentials by default.
|
||||
|
||||
Even if the server is under your direct control, `GET` requests and their full URLs are often logged. This approach also avoids leaking credentials in request or access logs. If you wish to obtain the `access_token` and `refresh_token` on a server, please consider using the PKCE flow.
|
||||
|
||||
</Admonition>
|
||||
|
||||
</Accordion.Item>
|
||||
</div>
|
||||
<div className="border-b pb-3">
|
||||
<Accordion.Item
|
||||
header={<span className="text-foreground font-bold">PKCE</span>}
|
||||
id={`ssr-pkce-flow`}
|
||||
>
|
||||
|
||||
When using the PKCE flow, a redirect URL will be returned with the following structure:
|
||||
```
|
||||
https://yourapp.com/...?code=<...>
|
||||
```
|
||||
The `code` parameter is commonly known as the Auth Code and can be exchanged for an access token by calling `exchangeCodeForSession(code)`.
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
For security purposes, the code has a validity of 5 minutes and can only be exchanged for an access token once. You will need to restart the authentication flow from scratch if you wish to obtain a new access token.
|
||||
|
||||
</Admonition>
|
||||
|
||||
As the flow is run server side, `localStorage` may not be available. You may configure the client library to use a custom storage adapter an alternate backing storage such as cookies by setting the `storage` option to an object with the following methods:
|
||||
|
||||
```js
|
||||
const customStorageAdapter: SupportedStorage = {
|
||||
getItem: (key) => {
|
||||
if (!supportsLocalStorage()) {
|
||||
// Configure alternate storage
|
||||
return null
|
||||
}
|
||||
return globalThis.localStorage.getItem(key)
|
||||
},
|
||||
setItem: (key, value) => {
|
||||
if (!supportsLocalStorage()) {
|
||||
// Configure alternate storage here
|
||||
return
|
||||
}
|
||||
globalThis.localStorage.setItem(key, value)
|
||||
},
|
||||
removeItem: (key) => {
|
||||
if (!supportsLocalStorage()) {
|
||||
// Configure alternate storage here
|
||||
return
|
||||
}
|
||||
globalThis.localStorage.removeItem(key)
|
||||
},
|
||||
}
|
||||
```
|
||||
You may also configure the client library to automatically exchange it for a session after a successful redirect. This can be done by setting the `detectSessionInUrl` option to `true`.
|
||||
|
||||
Putting it all together, your client library initialization may look like this:
|
||||
```js
|
||||
const supabase = createClient(
|
||||
'https://xyzcompany.supabase.co',
|
||||
'public-anon-key',
|
||||
{
|
||||
...
|
||||
auth: {
|
||||
...
|
||||
detectSessionInUrl: true,
|
||||
flowType: 'pkce',
|
||||
storage: customStorageAdapter,
|
||||
}
|
||||
...
|
||||
}
|
||||
)
|
||||
```
|
||||
[Learn more](https://oauth.net/2/pkce/) about the PKCE flow.
|
||||
|
||||
</Accordion.Item>
|
||||
</div>
|
||||
|
||||
</Accordion>
|
||||
|
||||
## Bringing it together
|
||||
|
||||
As seen from the authentication flow, the initial request after successful login made by the browser to your app's server after user login **does not contain any information about the user**. This is because first the client-side JavaScript library must run before it makes the access and refresh token available to your server.
|
||||
|
||||
It is very important to make sure that the redirect route right after login works without any server-side rendering. Other routes requiring authorization do not have the same limitation, provided you send the access and refresh tokens to your server.
|
||||
|
||||
This is traditionally done by setting cookies. Here's an example you can add to the root of your application:
|
||||
|
||||
```typescript
|
||||
supabase.auth.onAuthStateChange((event, session) => {
|
||||
if (event === 'SIGNED_OUT') {
|
||||
// delete cookies on sign out
|
||||
const expires = new Date(0).toUTCString()
|
||||
document.cookie = `my-access-token=; path=/; expires=${expires}; SameSite=Lax; secure`
|
||||
document.cookie = `my-refresh-token=; path=/; expires=${expires}; SameSite=Lax; secure`
|
||||
} else if (event === 'SIGNED_IN' || event === 'TOKEN_REFRESHED') {
|
||||
const maxAge = 100 * 365 * 24 * 60 * 60 // 100 years, never expires
|
||||
document.cookie = `my-access-token=${session.access_token}; path=/; max-age=${maxAge}; SameSite=Lax; secure`
|
||||
document.cookie = `my-refresh-token=${session.refresh_token}; path=/; max-age=${maxAge}; SameSite=Lax; secure`
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
This uses the standard [`document.cookie` API](https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie) to set cookies on all paths of your app's domain. All subsequent requests made by the browser to your app's server include the `my-access-token` and `my-refresh-token` cookies (the names of the cookies and additional parameters can be changed).
|
||||
|
||||
In your server-side rendering code you can now access user and session information:
|
||||
|
||||
```typescript
|
||||
const refreshToken = req.cookies['my-refresh-token']
|
||||
const accessToken = req.cookies['my-access-token']
|
||||
|
||||
if (refreshToken && accessToken) {
|
||||
await supabase.auth.setSession({
|
||||
refresh_token: refreshToken,
|
||||
access_token: accessToken,
|
||||
{
|
||||
auth: { persistSession: false },
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// make sure you handle this case!
|
||||
throw new Error('User is not authenticated.')
|
||||
}
|
||||
|
||||
// returns user information
|
||||
await supabase.auth.getUser()
|
||||
```
|
||||
|
||||
Use `setSession({ access_token, refresh_token })` instead of `setSession(refreshToken)` or `getUser(accessToken)` as refresh tokens or access tokens alone do not properly identify a user session.
|
||||
|
||||
Access tokens are valid only for a short amount of time.
|
||||
|
||||
Even though refresh tokens are long-lived, there is no guarantee that a user has an active session. They may have logged out and your application failed to remove the `my-refresh-token` cookie, or some other failure occurred that left a stale refresh token in the browser. Furthermore, a refresh token can only be used a few seconds after it was first used. Only use a refresh token if the access token is about to expire, which will avoid the introduction of difficult to diagnose logout bugs in your app.
|
||||
|
||||
A good practice is to handle unauthorized errors by deferring rendering the page in the browser instead of in the server. Some user information is contained in the access token though, so in certain cases, you may be able to use this potentially stale information to render a page.
|
||||
|
||||
## Frequently asked questions
|
||||
|
||||
### No session on the server side with Next.js route prefetching?
|
||||
|
||||
When you use route prefetching in Next.js using `<Link href="/...">` components or the `Router.push()` APIs can send server-side requests before the browser processes the access and refresh tokens. This means that those requests may not have any cookies set and your server code will render unauthenticated content.
|
||||
|
||||
To improve experience for your users, we recommend redirecting users to one specific page after sign-in that does not include any route prefetching from Next.js. Once the Supabase client library running in the browser has obtained the access and refresh tokens from the URL fragment, you can send users to any pages that use prefetching.
|
||||
|
||||
### How do I make the cookies `HttpOnly`?
|
||||
|
||||
This is not necessary. Both the access token and refresh token are designed to be passed around to different components in your application. The browser-based side of your application needs access to the refresh token to properly maintain a browser session anyway.
|
||||
|
||||
### My server is getting invalid refresh token errors. What's going on?
|
||||
|
||||
It is likely that the refresh token sent from the browser to your server is stale. Make sure the `onAuthStateChange` listener callback is free of bugs and is registered relatively early in your application's lifetime
|
||||
|
||||
When you receive this error on the server-side, try to defer rendering to the browser where the client library can access an up-to-date refresh token and present the user with a better experience.
|
||||
|
||||
### Should I set a shorter `Max-Age` parameter on the cookies?
|
||||
|
||||
The `Max-Age` or `Expires` cookie parameters only control whether the browser sends the value to the server. Since a refresh token represents the long-lived authentication session of the user on that browser, setting a short `Max-Age` or `Expires` parameter on the cookies only results in a degraded user experience.
|
||||
|
||||
The only way to ensure that a user has logged out or their session has ended is to get the user's details with `getUser()`.
|
||||
|
||||
### What should I use for the `SameSite` property?
|
||||
|
||||
Make sure you [understand the behavior of the property in different situations](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite) as some properties can degrade the user experience.
|
||||
|
||||
A good default is to use `Lax` which sends cookies when users are navigating to your site. Cookies typically require the `Secure` attribute, which only sends them over HTTPS. However, this can be a problem when developing on `localhost`.
|
||||
|
||||
### Can I use server-side rendering with a CDN or cache?
|
||||
|
||||
Yes, but you need to be careful to include at least the refresh token cookie value in the cache key. Otherwise you may be accidentally serving pages with data belonging to different users!
|
||||
|
||||
Also be sure you set proper cache control headers. We recommend invalidating cache keys every hour or less.
|
||||
|
||||
### Which authentication flows have PKCE support?
|
||||
|
||||
At present, PKCE is supported on the Magic Link, OAuth, Sign Up, and Password Recovery routes. These correspond to the `signInWithOtp`, `signInWithOAuth`, `signUp`, and `resetPasswordForEmail` methods on the Supabase client library. When using PKCE with Phone and Email OTPs, there is no behavior change with respect to the implicit flow - an access token will be returned in the body when a request is successful.
|
||||
@@ -1,11 +1,27 @@
|
||||
---
|
||||
id: 'index'
|
||||
title: 'Server-Side Auth Overview'
|
||||
description: 'Configure Supabase Auth to use Cookies'
|
||||
sidebar_label: 'Overview'
|
||||
title: 'Server-Side Rendering'
|
||||
subtitle: 'How SSR works with Supabase Auth.'
|
||||
---
|
||||
|
||||
When using Supabase with server-side languages and frameworks - such as Next.js, SvelteKit and Remix - it is important to configure your Supabase client to use cookies for storing user sessions. We have developed an `@supabase/ssr` package to make this process as simple as possible. This package is currently in `beta`. Adoption is recommended but be aware that the API is still unstable and may have breaking changes in the future.
|
||||
SSR frameworks move rendering and data fetches to the server, to reduce client bundle size and execution time.
|
||||
|
||||
Supabase Auth is fully compatible with SSR. You need to make a few changes to the configuration of your Supabase client, to store the user session in cookies instead of local storage. After setting up your Supabase client, follow the instructions for any flow in the How-To guides.
|
||||
|
||||
<Admonition type="tip">
|
||||
|
||||
Make sure to use the PKCE flow instructions where those differ from the implicit flow instructions. If no difference is mentioned, don't worry about this.
|
||||
|
||||
</Admonition>
|
||||
|
||||
## `@supabase/ssr`
|
||||
|
||||
We have developed an [`@supabase/ssr`](https://www.npmjs.com/package/@supabase/ssr) package to make setting up the Supabase client as simple as possible. This package is currently in beta. Adoption is recommended but be aware that the API is still unstable and may have breaking changes in the future.
|
||||
|
||||
<Admonition type="tip">
|
||||
|
||||
If you're currently using the [Auth Helpers package](https://github.com/supabase/auth-helpers), the [docs are still available](/docs/guides/auth/auth-helpers), however we recommend migrating to the new `@supabase/ssr` package as this will be the recommended path moving forward.
|
||||
|
||||
</Admonition>
|
||||
|
||||
## Framework quickstarts
|
||||
|
||||
@@ -35,16 +51,3 @@ When using Supabase with server-side languages and frameworks - such as Next.js,
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
If you're currently using the [Auth Helpers package](https://github.com/supabase/auth-helpers), the [docs are still available](/docs/guides/auth/auth-helpers), however we recommend migrating to the new `@supabase/ssr` package as this will be the recommended path moving forward.
|
||||
|
||||
</Admonition>
|
||||
|
||||
## Next steps
|
||||
|
||||
- [Create a Supabase client for SSR](/docs/guides/auth/server-side/creating-a-client)
|
||||
- [Email Auth with PKCE flow for SSR](/docs/guides/auth/server-side/email-based-auth-with-pkce-flow-for-ssr)
|
||||
- [OAuth with PKCE flow for SSR](/docs/guides/auth/server-side/oauth-with-pkce-flow-for-ssr)
|
||||
- [Learn more about SSR](/docs/guides/auth/server-side-rendering)
|
||||
67
apps/docs/content/guides/auth/server-side/advanced-guide.mdx
Normal file
67
apps/docs/content/guides/auth/server-side/advanced-guide.mdx
Normal file
@@ -0,0 +1,67 @@
|
||||
---
|
||||
title: 'Advanced guide'
|
||||
subtitle: 'Details about SSR Auth flows and implementation for advanced users.'
|
||||
---
|
||||
|
||||
When a user authenticates with Supabase Auth, two pieces of information are issued by the server:
|
||||
|
||||
1. **Access token** in the form of a JWT.
|
||||
2. **Refresh token** which is a randomly generated string.
|
||||
|
||||
The default behavior if you're not using SSR is to store this information in local storage. Local storage isn't accessible by the server, so for SSR, the tokens instead need to be stored in a secure cookie. The cookie can then be passed back and forth between your app code in the client and your app code in the server.
|
||||
|
||||
If you're not using SSR, you might also be using the [implicit flow](/docs/guides/auth/sessions/implicit-flow) to get the access and refresh tokens. The server can't access the tokens in this flow, so for SSR, you should change to the [PKCE flow](/docs/guides/auth/sessions/pkce-flow). You can change the flow type when initiating your Supabase client if your client library provides this option.
|
||||
|
||||
<Admonition type="tip">
|
||||
|
||||
In the `@supabase/ssr` package, Supabase clients are initiated to use the PKCE flow by default. They are also automatically configured to handle the saving and retrieval of session information in cookies.
|
||||
|
||||
</Admonition>
|
||||
|
||||
## How it works
|
||||
|
||||
In the PKCE flow, a redirect is made to your app, with an Auth Code contained in the URL. When you exchange this code using `exchangeCodeForSession`, you receive the session information, which contains the access and refresh tokens.
|
||||
|
||||
To maintain the session, these tokens must be stored in a storage medium securely shared between client and server, which is traditionally cookies. Whenever the session is refreshed, the auth and refresh tokens in the shared storage medium must be updated. Supabase client libraries provide a customizable `storage` option when a client is initiated, allowing you to change where tokens are stored.
|
||||
|
||||
For an implementation example, see the [@supabase/ssr](https://github.com/supabase/auth-helpers/blob/main/packages/ssr/src/index.ts) package.
|
||||
|
||||
## Frequently asked questions
|
||||
|
||||
### No session on the server side with Next.js route prefetching?
|
||||
|
||||
When you use route prefetching in Next.js using `<Link href="/...">` components or the `Router.push()` APIs can send server-side requests before the browser processes the access and refresh tokens. This means that those requests may not have any cookies set and your server code will render unauthenticated content.
|
||||
|
||||
To improve experience for your users, we recommend redirecting users to one specific page after sign-in that does not include any route prefetching from Next.js. Once the Supabase client library running in the browser has obtained the access and refresh tokens from the URL fragment, you can send users to any pages that use prefetching.
|
||||
|
||||
### How do I make the cookies `HttpOnly`?
|
||||
|
||||
This is not necessary. Both the access token and refresh token are designed to be passed around to different components in your application. The browser-based side of your application needs access to the refresh token to properly maintain a browser session anyway.
|
||||
|
||||
### My server is getting invalid refresh token errors. What's going on?
|
||||
|
||||
It is likely that the refresh token sent from the browser to your server is stale. Make sure the `onAuthStateChange` listener callback is free of bugs and is registered relatively early in your application's lifetime
|
||||
|
||||
When you receive this error on the server-side, try to defer rendering to the browser where the client library can access an up-to-date refresh token and present the user with a better experience.
|
||||
|
||||
### Should I set a shorter `Max-Age` parameter on the cookies?
|
||||
|
||||
The `Max-Age` or `Expires` cookie parameters only control whether the browser sends the value to the server. Since a refresh token represents the long-lived authentication session of the user on that browser, setting a short `Max-Age` or `Expires` parameter on the cookies only results in a degraded user experience.
|
||||
|
||||
The only way to ensure that a user has logged out or their session has ended is to get the user's details with `getUser()`.
|
||||
|
||||
### What should I use for the `SameSite` property?
|
||||
|
||||
Make sure you [understand the behavior of the property in different situations](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite) as some properties can degrade the user experience.
|
||||
|
||||
A good default is to use `Lax` which sends cookies when users are navigating to your site. Cookies typically require the `Secure` attribute, which only sends them over HTTPS. However, this can be a problem when developing on `localhost`.
|
||||
|
||||
### Can I use server-side rendering with a CDN or cache?
|
||||
|
||||
Yes, but you need to be careful to include at least the refresh token cookie value in the cache key. Otherwise you may be accidentally serving pages with data belonging to different users!
|
||||
|
||||
Also be sure you set proper cache control headers. We recommend invalidating cache keys every hour or less.
|
||||
|
||||
### Which authentication flows have PKCE support?
|
||||
|
||||
At present, PKCE is supported on the Magic Link, OAuth, Sign Up, and Password Recovery routes. These correspond to the `signInWithOtp`, `signInWithOAuth`, `signUp`, and `resetPasswordForEmail` methods on the Supabase client library. When using PKCE with Phone and Email OTPs, there is no behavior change with respect to the implicit flow - an access token will be returned in the body when a request is successful.
|
||||
@@ -1,305 +0,0 @@
|
||||
---
|
||||
title: 'Email Auth with PKCE flow for SSR'
|
||||
description: 'Learn how to configure email authentication in your server-side rendering (SSR) application to work with the PKCE flow.'
|
||||
subtitle: 'Learn how to configure email authentication in your server-side rendering (SSR) application to work with the PKCE flow.'
|
||||
---
|
||||
|
||||
### Setting up SSR client
|
||||
|
||||
Check out our [guide for creating a client](/docs/guides/auth/server-side/creating-a-client) to learn how to install the necessary packages, declare environment variables, and create a Supabase client configured for SSR in your framework.
|
||||
|
||||
### Create API endpoint for handling `token_hash`
|
||||
|
||||
In order to use the updated email links we will need to setup a endpoint for verifying the `token_hash` along with the `type` to exchange `token_hash` for the user's `session`, which is set as a cookie for future requests made to Supabase.
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="nextjs"
|
||||
queryGroup="framework"
|
||||
>
|
||||
<TabPanel id="nextjs" label="Next.js">
|
||||
|
||||
Create a new file at `app/auth/confirm/route.ts` and populate with the following:
|
||||
|
||||
```ts app/auth/confirm/route.ts
|
||||
import { createServerClient, type CookieOptions } from '@supabase/ssr'
|
||||
import { type EmailOtpType } from '@supabase/supabase-js'
|
||||
import { cookies } from 'next/headers'
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const token_hash = searchParams.get('token_hash')
|
||||
const type = searchParams.get('type') as EmailOtpType | null
|
||||
const next = searchParams.get('next') ?? '/'
|
||||
const redirectTo = request.nextUrl.clone()
|
||||
redirectTo.pathname = next
|
||||
|
||||
if (token_hash && type) {
|
||||
const cookieStore = cookies()
|
||||
const supabase = createServerClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||||
{
|
||||
cookies: {
|
||||
get(name: string) {
|
||||
return cookieStore.get(name)?.value
|
||||
},
|
||||
set(name: string, value: string, options: CookieOptions) {
|
||||
cookieStore.set({ name, value, ...options })
|
||||
},
|
||||
remove(name: string, options: CookieOptions) {
|
||||
cookieStore.delete({ name, ...options })
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const { error } = await supabase.auth.verifyOtp({
|
||||
type,
|
||||
token_hash,
|
||||
})
|
||||
if (!error) {
|
||||
return NextResponse.redirect(redirectTo)
|
||||
}
|
||||
}
|
||||
|
||||
// return the user to an error page with some instructions
|
||||
redirectTo.pathname = '/auth/auth-code-error'
|
||||
return NextResponse.redirect(redirectTo)
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="sveltekit" label="SvelteKit">
|
||||
|
||||
Create a new file at `src/routes/auth/confirm/+server.ts` and populate with the following:
|
||||
|
||||
```ts src/routes/auth/confirm/+server.ts
|
||||
import { redirect } from '@sveltejs/kit'
|
||||
import { type EmailOtpType } from '@supabase/supabase-js'
|
||||
|
||||
export const GET = async (event) => {
|
||||
const {
|
||||
url,
|
||||
locals: { supabase },
|
||||
} = event
|
||||
const token_hash = url.searchParams.get('token_hash') as string
|
||||
const type = url.searchParams.get('type') as EmailOtpType | null
|
||||
const next = url.searchParams.get('next') ?? '/'
|
||||
|
||||
if (token_hash && type) {
|
||||
const { error } = await supabase.auth.verifyOtp({ token_hash, type })
|
||||
if (!error) {
|
||||
throw redirect(303, `/${next.slice(1)}`)
|
||||
}
|
||||
}
|
||||
|
||||
// return the user to an error page with some instructions
|
||||
throw redirect(303, '/auth/auth-code-error')
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="astro" label="Astro">
|
||||
|
||||
Create a new file at `src/pages/auth/confirm.ts` and populate with the following:
|
||||
|
||||
```ts src/pages/auth/confirm.ts
|
||||
import { createServerClient } from '@supabase/ssr'
|
||||
import { type EmailOtpType } from '@supabase/supabase-js'
|
||||
import { type APIRoute } from 'astro'
|
||||
|
||||
export const GET: APIRoute = async ({ request, cookies, redirect }) => {
|
||||
const requestUrl = new URL(request.url)
|
||||
const token_hash = requestUrl.searchParams.get('token_hash')
|
||||
const type = requestUrl.searchParams.get('type') as EmailOtpType | null
|
||||
const next = requestUrl.searchParams.get('next') || '/'
|
||||
|
||||
if (token_hash && type) {
|
||||
const supabase = createServerClient(
|
||||
import.meta.env.PUBLIC_SUPABASE_URL,
|
||||
import.meta.env.PUBLIC_SUPABASE_ANON_KEY,
|
||||
{
|
||||
cookies: {
|
||||
get(key) {
|
||||
return cookies.get(key)?.value
|
||||
},
|
||||
set(key, value, options) {
|
||||
cookies.set(key, value, options)
|
||||
},
|
||||
remove(key, options) {
|
||||
cookies.delete(key, options)
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const { error } = await supabase.auth.verifyOtp({
|
||||
type,
|
||||
token_hash,
|
||||
})
|
||||
|
||||
if (!error) {
|
||||
return redirect(next)
|
||||
}
|
||||
}
|
||||
|
||||
// return the user to an error page with some instructions
|
||||
return redirect('/auth/auth-code-error')
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="remix" label="Remix">
|
||||
|
||||
Create a new file at `app/routes/auth.confirm.tsx` and populate with the following:
|
||||
|
||||
```ts app/routes/auth.confirm.tsx
|
||||
import { redirect, type LoaderFunctionArgs } from '@remix-run/node'
|
||||
import { createServerClient, parse, serialize } from '@supabase/ssr'
|
||||
import { type EmailOtpType } from '@supabase/supabase-js'
|
||||
|
||||
export async function loader({ request }: LoaderFunctionArgs) {
|
||||
const requestUrl = new URL(request.url)
|
||||
const token_hash = requestUrl.searchParams.get('token_hash')
|
||||
const type = requestUrl.searchParams.get('type') as EmailOtpType | null
|
||||
const next = requestUrl.searchParams.get('next') || '/'
|
||||
const headers = new Headers()
|
||||
|
||||
if (token_hash && type) {
|
||||
const cookies = parse(request.headers.get('Cookie') ?? '')
|
||||
|
||||
const supabase = createServerClient(process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!, {
|
||||
cookies: {
|
||||
get(key) {
|
||||
return cookies[key]
|
||||
},
|
||||
set(key, value, options) {
|
||||
headers.append('Set-Cookie', serialize(key, value, options))
|
||||
},
|
||||
remove(key, options) {
|
||||
headers.append('Set-Cookie', serialize(key, '', options))
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const { error } = await supabase.auth.verifyOtp({
|
||||
type,
|
||||
token_hash,
|
||||
})
|
||||
|
||||
if (!error) {
|
||||
return redirect(next, { headers })
|
||||
}
|
||||
}
|
||||
|
||||
// return the user to an error page with instructions
|
||||
return redirect('/auth/auth-code-error', { headers })
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="express" label="Express">
|
||||
|
||||
Create a new route in your express app and populate with the following:
|
||||
|
||||
```js app.js
|
||||
...
|
||||
app.get("/auth/confirm", async function (req, res) {
|
||||
const token_hash = req.query.token_hash
|
||||
const type = req.query.type
|
||||
const next = req.query.next ?? "/"
|
||||
|
||||
if (token_hash && type) {
|
||||
const supabase = createClient({ req, res })
|
||||
const { error } = await supabase.auth.verifyOtp({
|
||||
type,
|
||||
token_hash,
|
||||
})
|
||||
if (!error) {
|
||||
res.redirect(303, `/${next.slice(1)}`)
|
||||
}
|
||||
}
|
||||
|
||||
// return the user to an error page with some instructions
|
||||
res.redirect(303, '/auth/auth-code-error')
|
||||
})
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
### Update email templates with URL for API endpoint
|
||||
|
||||
Let's update the URL in our email templates to point to our new confirmation endpoint for the user to get confirmed.
|
||||
|
||||
**Confirm signup template**
|
||||
|
||||
```html
|
||||
<h2>Confirm your signup</h2>
|
||||
|
||||
<p>Follow this link to confirm your user:</p>
|
||||
<p>
|
||||
<a href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=email"
|
||||
>Confirm your email</a
|
||||
>
|
||||
</p>
|
||||
```
|
||||
|
||||
**Invite user template**
|
||||
|
||||
```html
|
||||
<h2>You have been invited</h2>
|
||||
|
||||
<p>
|
||||
You have been invited to create a user on {{ .SiteURL }}. Follow this link to accept the invite:
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<a
|
||||
href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=invite&next=/path-to-your-update-password-page"
|
||||
>Accept the invite</a
|
||||
>
|
||||
</p>
|
||||
```
|
||||
|
||||
**Magic Link template**
|
||||
|
||||
```html
|
||||
<h2>Magic Link</h2>
|
||||
|
||||
<p>Follow this link to login:</p>
|
||||
<p><a href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=magiclink">Log In</a></p>
|
||||
```
|
||||
|
||||
**Change Email Address template**
|
||||
|
||||
```html
|
||||
<h2>Confirm Change of Email</h2>
|
||||
|
||||
<p>Follow this link to confirm the update of your email from {{ .Email }} to {{ .NewEmail }}:</p>
|
||||
<p>
|
||||
<a href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=email_change">
|
||||
Change Email
|
||||
</a>
|
||||
</p>
|
||||
```
|
||||
|
||||
**Reset Password template**
|
||||
|
||||
```html
|
||||
<h2>Reset Password</h2>
|
||||
|
||||
<p>Follow this link to reset the password for your user:</p>
|
||||
<p>
|
||||
<a
|
||||
href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=recovery&next=/path-to-your-update-password-page"
|
||||
>Reset Password</a
|
||||
>
|
||||
</p>
|
||||
```
|
||||
@@ -1,7 +1,5 @@
|
||||
---
|
||||
id: 'sessions'
|
||||
title: 'User Sessions'
|
||||
description: 'Control and manage user sessions.'
|
||||
title: 'User sessions'
|
||||
---
|
||||
|
||||
Supabase Auth provides fine-grained control over your user's sessions.
|
||||
@@ -30,31 +28,14 @@ A session terminates, depending on configuration, when:
|
||||
|
||||
Every access token contains a `session_id` claim, a UUID, uniquely identifying the session of the user. You can correlate this ID with the primary key of the `auth.sessions` table.
|
||||
|
||||
## Sign out and scopes
|
||||
## Initiating a session
|
||||
|
||||
Supabase Auth allows you to specify three different scopes for when a user invokes the [sign out API](/docs/reference/javascript/auth-signout) in your application:
|
||||
A session is initiated when a user signs in. The session is stored in the `auth.sessions` table, and your app should receive the access and refresh tokens.
|
||||
|
||||
- `global` (default) when all sessions active for the user are terminated.
|
||||
- `local` which only terminates the current session for the user but keep sessions on other devices or browsers active.
|
||||
- `others` to terminate all but the current session for the user.
|
||||
There are two flows for initiating a session and receiving the tokens:
|
||||
|
||||
You can invoke these by providing the `scope` option:
|
||||
|
||||
```typescript
|
||||
// defaults to the global scope
|
||||
await supabase.auth.signOut()
|
||||
|
||||
// sign out from the current session only
|
||||
await supabase.auth.signOut({ scope: 'local' })
|
||||
```
|
||||
|
||||
Upon sign out, all refresh tokens and potentially other database objects related to the affected sessions are destroyed and the client library removes the session stored in the browser.
|
||||
|
||||
<Admonition type="caution">
|
||||
|
||||
Access tokens of revoked sessions remain valid until their expiry time, encoded in the `exp` claim. The user won't be immediately logged out and will only be logged out when the access token expires.
|
||||
|
||||
</Admonition>
|
||||
- [Implicit flow](/docs/guides/auth/sessions/implicit-flow)
|
||||
- [PKCE flow](/docs/guides/auth/sessions/pkce-flow)
|
||||
|
||||
## Limiting session lifetime and number of allowed sessions per user
|
||||
|
||||
|
||||
33
apps/docs/content/guides/auth/sessions/implicit-flow.mdx
Normal file
33
apps/docs/content/guides/auth/sessions/implicit-flow.mdx
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
title: 'Implicit flow'
|
||||
subtitle: 'About authenticating with implicit flow.'
|
||||
---
|
||||
|
||||
The implicit flow is one of two ways that a user can authenticate and your app can receive the necessary access and refresh tokens.
|
||||
|
||||
The flow is an implementation detail handled for you by Supabase Auth, but understanding the difference between implicit and [PKCE flow](/guides/auth/sessions/pkce-flow) is important for understanding the difference between client-only and server-side auth.
|
||||
|
||||
## How it works
|
||||
|
||||
After a successful signin, the user is redirected to your app with a URL that looks like this:
|
||||
|
||||
```
|
||||
https://yourapp.com/...#access_token=<...>&refresh_token=<...>&...
|
||||
```
|
||||
|
||||
The access and refresh tokens are contained in the URL fragment.
|
||||
|
||||
The client libraries:
|
||||
|
||||
- Detect this type of URL
|
||||
- Extract the access token, refresh token, and some extra information
|
||||
- Persist this information to local storage for further use by the library and your app
|
||||
|
||||
## Limitations
|
||||
|
||||
The implicit flow only works on the client. Web browsers do not send the URL fragment to the server by design. This is a security feature:
|
||||
|
||||
- You may be hosting your single-page app on a third-party server. The third-party service shouldn't get access to your user's credentials.
|
||||
- Even if the server is under your direct control, `GET` requests and their full URLs are often logged. This approach avoids leaking credentials in request or access logs.
|
||||
|
||||
If you wish to obtain the access token and refresh token on a server, use the [PKCE flow](/docs/guides/auth/sessions/pkce-flow).
|
||||
83
apps/docs/content/guides/auth/sessions/pkce-flow.mdx
Normal file
83
apps/docs/content/guides/auth/sessions/pkce-flow.mdx
Normal file
@@ -0,0 +1,83 @@
|
||||
---
|
||||
title: 'PKCE flow'
|
||||
subtitle: 'About authenticating with PKCE flow.'
|
||||
---
|
||||
|
||||
The Proof Key for Code Exchange (PKCE) flow is one of two ways that a user can authenticate and your app can receive the necessary access and refresh tokens.
|
||||
|
||||
The flow is an implementation detail handled for you by Supabase Auth, but understanding the difference between PKCE and [implicit flow](/docs/guides/auth/sessions/implicit-flow) is important for understanding the difference between client-only and server-side auth.
|
||||
|
||||
## How it works
|
||||
|
||||
After a successful verification, the user is redirected to your app with a URL that looks like this:
|
||||
|
||||
```
|
||||
https://yourapp.com/...?code=<...>
|
||||
```
|
||||
|
||||
The `code` parameter is commonly known as the Auth Code and can be exchanged for an access token by calling `exchangeCodeForSession(code)`.
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
For security purposes, the code has a validity of 5 minutes and can only be exchanged for an access token once. You will need to restart the authentication flow from scratch if you wish to obtain a new access token.
|
||||
|
||||
</Admonition>
|
||||
|
||||
As the flow is run server side, `localStorage` may not be available. You may configure the client library to use a custom storage adapter and an alternate backing storage such as cookies by setting the `storage` option to an object with the following methods:
|
||||
|
||||
```js
|
||||
const customStorageAdapter: SupportedStorage = {
|
||||
getItem: (key) => {
|
||||
if (!supportsLocalStorage()) {
|
||||
// Configure alternate storage
|
||||
return null
|
||||
}
|
||||
return globalThis.localStorage.getItem(key)
|
||||
},
|
||||
setItem: (key, value) => {
|
||||
if (!supportsLocalStorage()) {
|
||||
// Configure alternate storage here
|
||||
return
|
||||
}
|
||||
globalThis.localStorage.setItem(key, value)
|
||||
},
|
||||
removeItem: (key) => {
|
||||
if (!supportsLocalStorage()) {
|
||||
// Configure alternate storage here
|
||||
return
|
||||
}
|
||||
globalThis.localStorage.removeItem(key)
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
You may also configure the client library to automatically exchange it for a session after a successful redirect. This can be done by setting the `detectSessionInUrl` option to `true`.
|
||||
|
||||
Putting it all together, your client library initialization may look like this:
|
||||
|
||||
```js
|
||||
const supabase = createClient(
|
||||
'https://xyzcompany.supabase.co',
|
||||
'public-anon-key',
|
||||
{
|
||||
...
|
||||
auth: {
|
||||
...
|
||||
detectSessionInUrl: true,
|
||||
flowType: 'pkce',
|
||||
storage: customStorageAdapter,
|
||||
}
|
||||
...
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
## Limitations
|
||||
|
||||
Behind the scenes, the code exchange requires a code verifier. Both the code in the URL and the code verifier are sent back to the Auth server for a successful exchange.
|
||||
|
||||
The code verifier is created and stored locally when the Auth flow is first initiated. That means the code exchange must be initiated on the same browser and device where the flow was started.
|
||||
|
||||
## Resources
|
||||
|
||||
- [OAuth 2.0 guide](https://oauth.net/2/pkce/) to PKCE flow
|
||||
77
apps/docs/content/guides/auth/signout.mdx
Normal file
77
apps/docs/content/guides/auth/signout.mdx
Normal file
@@ -0,0 +1,77 @@
|
||||
---
|
||||
title: 'Signing out'
|
||||
subtitle: 'Signing out a user'
|
||||
---
|
||||
|
||||
Signing out a user works the same way no matter what method they used to sign in.
|
||||
|
||||
Call the sign out method from the client library. It removes the active session and clears Auth data from the storage medium.
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
queryGroup="language"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
```js
|
||||
async function signOut() {
|
||||
const { error } = await supabase.auth.signOut()
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="dart" label="Dart">
|
||||
|
||||
```dart
|
||||
Future<void> signOut() async {
|
||||
await supabase.auth.signOut();
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="swift" label="Swift">
|
||||
|
||||
```swift
|
||||
try await supabase.auth.signOut()
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="kotlin" label="Kotlin">
|
||||
|
||||
```kotlin
|
||||
suspend fun logout() {
|
||||
supabase.auth.signOut()
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
## Sign out and scopes
|
||||
|
||||
Supabase Auth allows you to specify three different scopes for when a user invokes the [sign out API](/docs/reference/javascript/auth-signout) in your application:
|
||||
|
||||
- `global` (default) when all sessions active for the user are terminated.
|
||||
- `local` which only terminates the current session for the user but keep sessions on other devices or browsers active.
|
||||
- `others` to terminate all but the current session for the user.
|
||||
|
||||
You can invoke these by providing the `scope` option:
|
||||
|
||||
```typescript
|
||||
// defaults to the global scope
|
||||
await supabase.auth.signOut()
|
||||
|
||||
// sign out from the current session only
|
||||
await supabase.auth.signOut({ scope: 'local' })
|
||||
```
|
||||
|
||||
Upon sign out, all refresh tokens and potentially other database objects related to the affected sessions are destroyed and the client library removes the session stored in the local storage medium.
|
||||
|
||||
<Admonition type="caution">
|
||||
|
||||
Access Tokens of revoked sessions remain valid until their expiry time, encoded in the `exp` claim. The user won't be immediately logged out and will only be logged out when the Access Token expires.
|
||||
|
||||
</Admonition>
|
||||
@@ -47,6 +47,8 @@ When developing with Expo, you can test Sign in with Apple via the Expo Go app,
|
||||
|
||||
This call takes the user to Apple's consent screen. Once the flow ends, the user's profile information is exchanged and validated with Supabase Auth before it redirects back to your web application with an access and refresh token representing the user's session.
|
||||
|
||||
<OAuthPkceFlow />
|
||||
|
||||
### Configuration [#configuration-web]
|
||||
|
||||
You will require the following information:
|
||||
|
||||
@@ -153,6 +153,8 @@ suspend fun signInWithAzure() {
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
<OAuthPkceFlow />
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
|
||||
@@ -79,6 +79,8 @@ suspend fun signInWithBitbucket() {
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
<OAuthPkceFlow />
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
|
||||
@@ -78,6 +78,8 @@ suspend fun signInWithDiscord() {
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
<OAuthPkceFlow />
|
||||
|
||||
If your user is already signed in, Discord prompts the user again for authorization.
|
||||
|
||||
<Tabs
|
||||
|
||||
@@ -95,6 +95,8 @@ suspend fun signInWithFacebook() {
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
<OAuthPkceFlow />
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
|
||||
@@ -79,6 +79,8 @@ suspend fun signInWithFigma() {
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
<OAuthPkceFlow />
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
|
||||
@@ -90,6 +90,8 @@ suspend fun signInWithGithub() {
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
<OAuthPkceFlow />
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
|
||||
@@ -76,6 +76,8 @@ suspend fun signInWithGitLab() {
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
<OAuthPkceFlow />
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
|
||||
@@ -47,6 +47,8 @@ Before you can use Sign in with Google, you need to obtain a [Google Cloud Platf
|
||||
|
||||
This call takes the user to Google's consent screen. When the flow ends, the user's profile information is exchanged and validated with Supabase Auth before it redirects back to your web application with an access and refresh token representing the user's session.
|
||||
|
||||
<OAuthPkceFlow />
|
||||
|
||||
You can additionally extract the `provider_token` from the session (on initial login only) which is the OAuth 2.0 access token issued by Google that grants your application access to the Google services for the authenticated users. Please store this token in local storage, cookies or in your database or server.
|
||||
|
||||
Google does not send out a refresh token by default, so you will need to pass parameters like these to `signInWithOAuth()` in order to extract the `provider_refresh_token`:
|
||||
|
||||
@@ -101,6 +101,8 @@ suspend fun signInWithKakao() {
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
<OAuthPkceFlow />
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
|
||||
@@ -99,6 +99,8 @@ suspend fun signInWithKeycloak() {
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
<OAuthPkceFlow />
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
|
||||
@@ -87,6 +87,8 @@ suspend fun signInWithKaLinkedIn() {
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
<OAuthPkceFlow />
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
|
||||
@@ -79,6 +79,8 @@ suspend fun signInWithNotion() {
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
<OAuthPkceFlow />
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
|
||||
@@ -97,6 +97,8 @@ suspend fun signInWithSlack() {
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
<OAuthPkceFlow />
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
|
||||
@@ -91,6 +91,8 @@ suspend fun signInWithSpotify() {
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
<OAuthPkceFlow />
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
|
||||
@@ -94,6 +94,8 @@ suspend fun signInWithTwitch() {
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
<OAuthPkceFlow />
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
|
||||
@@ -85,6 +85,8 @@ suspend fun signInWithTwitter() {
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
<OAuthPkceFlow />
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
|
||||
@@ -98,6 +98,8 @@ Refer to the [WorkOS Documentation](https://workos.com/docs/reference/sso/author
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
<OAuthPkceFlow />
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
|
||||
@@ -92,6 +92,8 @@ suspend fun signInWithZoom() {
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
<OAuthPkceFlow />
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
|
||||
@@ -1,10 +1,32 @@
|
||||
---
|
||||
id: 'auth-user-management'
|
||||
title: 'User Management'
|
||||
description: 'Manage your users with Supabase Auth'
|
||||
subtitle: 'Manage your users with Supabase Auth'
|
||||
title: 'Users'
|
||||
---
|
||||
|
||||
A **user** in Supabase Auth is someone with a user ID, stored in the Auth schema. Once someone is a user, they can be issued an Access Token, which can be used to access Supabase endpoints. The token is tied to the user, so you can restrict access to resources via [RLS policies](/docs/guides/database/postgres/row-level-security).
|
||||
|
||||
## Permanent and anonymous users
|
||||
|
||||
Supabase distinguishes between permanent and anonymous users.
|
||||
|
||||
- **Permanent users** are tied to a piece of Personally Identifiable Information (PII), such as an email address, a phone number, or a third-party identity. They can use these identities to sign back into their account after signing out.
|
||||
- **Anonymous users** aren't tied to any identities. They have a user ID and a personalized Access Token, but they have no way of signing back in as the same user if they are signed out.
|
||||
|
||||
Anonymous users are useful for:
|
||||
|
||||
- E-commerce applications, to create shopping carts before checkout
|
||||
- Full-feature demos without collecting personal information
|
||||
- Temporary or throw-away accounts
|
||||
|
||||
See the [Anonymous Signins guide](/docs/guides/auth/auth-anonymous) to learn more about anonymous users.
|
||||
|
||||
<Admonition type="caution" title="Anonymous users do not use the anon role">
|
||||
|
||||
Just like permanent users, anonymous users use the **authenticated** role for database access.
|
||||
|
||||
The **anon** role is for those who aren't signed in at all and are not tied to any user ID. We refer to these as unauthenticated or public users.
|
||||
|
||||
</Admonition>
|
||||
|
||||
## The user object
|
||||
|
||||
The user object stores all the information related to a user in your application. The user object can be retrieved using one of these methods:
|
||||
@@ -50,32 +72,8 @@ The user object contains the following attributes:
|
||||
| identities | `UserIdentity[]` | Contains an object array of identities linked to the user. |
|
||||
| created_at | `string` | The timestamp that the user was created. |
|
||||
| updated_at | `string` | The timestamp that the user was last updated. |
|
||||
| is_anonymous | `boolean` | Is true if the user is an anonymous user. |
|
||||
|
||||
## Configuration
|
||||
## Resources
|
||||
|
||||
Supabase Auth provides these [configuration options](/dashboard/project/_/settings/auth) to control user access to your application:
|
||||
|
||||
- **Allow new users to sign up**: Users will be able to sign up. If this config is disabled, only existing users can sign in.
|
||||
- **Allow unverified email sign in**: Users will not need to have a verified email address to sign in.
|
||||
|
||||
- If enabled, you can structure your RLS policies to provide different access controls to a user with an unverified email and a user with a verified email. For example,
|
||||
|
||||
```sql
|
||||
-- Allows a user to read all posts regardless of whether they have a verified email address
|
||||
create policy "policy_name"
|
||||
ON public.posts
|
||||
for select to authenticated using (
|
||||
true
|
||||
);
|
||||
|
||||
-- Only allow users with a verified email address to insert posts
|
||||
create policy "policy_name"
|
||||
ON public.posts
|
||||
for insert to authenticated with check (
|
||||
(select auth.jwt()->>'email_verified') is true
|
||||
);
|
||||
```
|
||||
|
||||
- **Confirm Email (Deprecated)**: Users will need to confirm their email address before signing in for the first time.
|
||||
- Having **Confirm Email** disabled assumes that the user's email does not need to be verified in order to login and implicitly confirms the user's email in the database.
|
||||
- If you previously relied on this config to autoconfirm a user's email address, you can switch to use **Allow unverified email sign in** instead. This new option allows the user to sign in with an unverified email which you can keep track of through the user object. It provides more versatility if you require your users to verify their email address in the future since you can structure your RLS policies to check the user's `email_verified` field.
|
||||
- [User Management guide](/docs/guides/auth/managing-user-data)
|
||||
@@ -20,7 +20,7 @@ Custom Claims are special attributes attached to a user that you can use to cont
|
||||
|
||||
To implement Role-Based Access Control (RBAC) with `custom claims`, use a [Custom Access Token Auth Hook](/docs/guides/auth/auth-hooks#hook-custom-access-token). This hook runs before a token is issued. You can use it to add additional claims to the user's JWT.
|
||||
|
||||
This guide uses the [Slack Clone example](https://github.com/supabase/supabase/tree/master/examples/slack-clone/nextjs-slack-clone) to demonstrate how to add a `user_role` claim and use it in your [Row Level Security (RLS) policies](/docs/guides/auth/row-level-security).
|
||||
This guide uses the [Slack Clone example](https://github.com/supabase/supabase/tree/master/examples/slack-clone/nextjs-slack-clone) to demonstrate how to add a `user_role` claim and use it in your [Row Level Security (RLS) policies](/docs/guides/database/postgres/row-level-security).
|
||||
|
||||
## Create a table to track user roles and permissions
|
||||
|
||||
@@ -235,7 +235,7 @@ $$ language plpgsql security definer set search_path = '';
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
You can read more about using functions in RLS policies in the [RLS guide](/docs/guides/auth/row-level-security#using-functions).
|
||||
You can read more about using functions in RLS policies in the [RLS guide](/docs/guides/database/postgres/row-level-security#using-functions).
|
||||
|
||||
</Admonition>
|
||||
|
||||
@@ -272,6 +272,6 @@ You now have a robust system in place to manage user roles and permissions withi
|
||||
## More resources
|
||||
|
||||
- [Auth Hooks](/docs/guides/auth/auth-hooks)
|
||||
- [Row Level Security](/docs/guides/auth/row-level-security)
|
||||
- [RLS Functions](/docs/guides/auth/row-level-security#using-functions)
|
||||
- [Row Level Security](/docs/guides/database/postgres/row-level-security)
|
||||
- [RLS Functions](/docs/guides/database/postgres/row-level-security#using-functions)
|
||||
- [Next.js Slack Clone Example](https://github.com/supabase/supabase/tree/master/examples/slack-clone/nextjs-slack-clone)
|
||||
@@ -5,7 +5,7 @@ description: 'Managing access to your Postgres database and configuring permissi
|
||||
subtitle: 'Managing access to your Postgres database and configuring permissions.'
|
||||
---
|
||||
|
||||
Postgres manages database access permissions using the concept of roles. Generally you wouldn't use these roles for your own application - they are mostly for configuring _system access_ to your database. If you want to configure _application access_, then you should use [Row Level Security](/docs/guides/database/postgres/row-level-security) (RLS). You can also implement [Role-based Access Control](/docs/guides/auth/custom-claims-and-role-based-access-control-rbac) on top of RLS.
|
||||
Postgres manages database access permissions using the concept of roles. Generally you wouldn't use these roles for your own application - they are mostly for configuring _system access_ to your database. If you want to configure _application access_, then you should use [Row Level Security](/docs/guides/database/postgres/row-level-security) (RLS). You can also implement [Role-based Access Control](/docs/guides/database/postgres/custom-claims-and-role-based-access-control-rbac) on top of RLS.
|
||||
|
||||
## Users vs roles
|
||||
|
||||
|
||||
@@ -9,10 +9,16 @@ When you need granular authorization rules, nothing beats Postgres's [Row Level
|
||||
|
||||
## Row Level Security in Supabase
|
||||
|
||||
RLS is incredibly powerful and flexible, allowing you to write complex SQL rules that fit your unique business needs. RLS can be [combined with Supabase Auth](/docs/guides/auth/row-level-security) for end-to-end user security from the browser to the database.
|
||||
RLS is incredibly powerful and flexible, allowing you to write complex SQL rules that fit your unique business needs. RLS can be [combined with Supabase Auth](/docs/guides/database/postgres/row-level-security) for end-to-end user security from the browser to the database.
|
||||
|
||||
RLS is a Postgres primitive and can provide "[defense in depth](<https://en.wikipedia.org/wiki/Defense_in_depth_(computing)>)" to protect your data from malicious actors even when accessed through third-party tooling.
|
||||
|
||||
<Admonition type="info">
|
||||
|
||||
You should _always_ enable RLS on tables created in a public schema. This is done for you when you create a table with the Table Editor. If you create one in raw SQL or with the SQL Editor, remember to enable RLS yourself.
|
||||
|
||||
</Admonition>
|
||||
|
||||
## Policies
|
||||
|
||||
[Policies](https://www.postgresql.org/docs/current/sql-createpolicy.html) are Postgres's rule engine. Policies are easy to understand once you get the hang of them. Each policy is attached to a table, and the policy is executed every time a table is accessed.
|
||||
@@ -44,11 +50,40 @@ alter table "table_name" enable row level security;
|
||||
|
||||
Once you have enabled RLS, no data will be accessible via the [API](/docs/guides/api) when using the public `anon` key, until you create policies.
|
||||
|
||||
## Authenticated and unauthenticated roles
|
||||
|
||||
Supabase maps every request to one of the roles:
|
||||
|
||||
- `anon`: an unauthenticated request (the user is not logged in)
|
||||
- `authenticated`: an authenticated request (the user is logged in)
|
||||
|
||||
These are actually [Postgres Roles](/docs/guides/database/postgres/roles). You can use these roles within your Policies using the `TO` clause:
|
||||
|
||||
```sql
|
||||
create policy "Profiles are viewable by everyone"
|
||||
on profiles for select
|
||||
to authenticated, anon
|
||||
using ( true );
|
||||
|
||||
-- OR
|
||||
|
||||
create policy "Public profiles are viewable only by authenticated users"
|
||||
on profiles for select
|
||||
to authenticated
|
||||
using ( true );
|
||||
```
|
||||
|
||||
<Admonition type="note" label="Anonymous user vs the anon key">
|
||||
|
||||
Using the `anon` Postgres role is different from an [anonymous user](/docs/guides/auth/auth-anonymous) in Supabase Auth. An anonymous user assumes the `authenticated` role to access the database and can be differentiated from a permanent user by checking the `is_anonymous` claim in the JWT.
|
||||
|
||||
</Admonition>
|
||||
|
||||
## Creating policies
|
||||
|
||||
Policies are simply SQL logic that you attach to a Postgres table. You can attach as many policies as you want to each table.
|
||||
|
||||
Supabase provides some [helpers](/docs/guides/auth/row-level-security#helper-functions) that simplify RLS if you're using Supabase Auth. We'll use these helpers to illustrate some basic policies:
|
||||
Supabase provides some [helpers](#helper-functions) that simplify RLS if you're using Supabase Auth. We'll use these helpers to illustrate some basic policies:
|
||||
|
||||
### SELECT policies
|
||||
|
||||
@@ -161,9 +196,77 @@ to authenticated -- the Postgres Role (recommended)
|
||||
using ( (select auth.uid()) = user_id ); -- the actual Policy
|
||||
```
|
||||
|
||||
### Views
|
||||
|
||||
Views bypass RLS by default because they are usually created with the `postgres` user. This is a feature of Postgres, which automatically creates views with `security definer`.
|
||||
|
||||
In Postgres 15 and above, you can make a view obey the RLS policies of the underlying tables when invoked by `anon` and `authenticated` roles by setting `security_invoker = true`.
|
||||
|
||||
```sql
|
||||
create view <VIEW_NAME>
|
||||
with(security_invoker = true)
|
||||
as select <QUERY>
|
||||
```
|
||||
|
||||
In older versions of Postgres, protect your views by revoking access from the `anon` and `authenticated` roles, or by putting them in an unexposed schema.
|
||||
|
||||
## Helper functions
|
||||
|
||||
Supabase provides some helper functions that make it easier to write Policies.
|
||||
|
||||
### `auth.uid()`
|
||||
|
||||
Returns the ID of the user making the request.
|
||||
|
||||
### `auth.jwt()`
|
||||
|
||||
Returns the JWT of the user making the request. Anything that you store in the user's `app_metadata` column or the `user_metadata` column will be accessible using this function. It's important to know the distinction between these two:
|
||||
|
||||
- `user_metadata` - can be updated by the authenticated user using the `supabase.auth.update()` function. It is not a good place to store authorization data.
|
||||
- `app_metadata` - cannot be updated by the user, so it's a good place to store authorization data.
|
||||
|
||||
The `auth.jwt()` function is extremely versatile. For example, if you store some team data inside `app_metadata`, you can use it to determine whether a particular user belongs to a team. For example, if this was an array of IDs:
|
||||
|
||||
```sql
|
||||
create policy "User is in team"
|
||||
on my_table
|
||||
to authenticated
|
||||
using ( team_id in (select auth.jwt() -> 'app_metadata' -> 'teams'));
|
||||
```
|
||||
|
||||
<Admonition type="caution">
|
||||
|
||||
Keep in mind that a JWT is not always "fresh". In the example above, even if you remove a user from a team and update the `app_metadata` field, that will not be reflected using `auth.jwt()` until the user's JWT is refreshed.
|
||||
|
||||
Also, if you are using Cookies for Auth, then you must be mindful of the JWT size. Some browsers are limited to 4096 bytes for each cookie, and so the total size of your JWT should be small enough to fit inside this limitation.
|
||||
|
||||
</Admonition>
|
||||
|
||||
### MFA
|
||||
|
||||
The `auth.jwt()` function can be used to check for [Multi-Factor Authentication](/docs/guides/auth/auth-mfa#enforce-rules-for-mfa-logins). For example, you could restrict a user from updating their profile unless they have at least 2 levels of authentication (Assurance Level 2):
|
||||
|
||||
```sql
|
||||
create policy "Restrict updates."
|
||||
on profiles
|
||||
as restrictive
|
||||
for update
|
||||
to authenticated using (
|
||||
(select auth.jwt()->>'aal') = 'aal2'
|
||||
);
|
||||
```
|
||||
|
||||
## Bypassing Row Level Security
|
||||
|
||||
You can create [Postgres Roles](/docs/guides/database/postgres/roles) which can bypass Row Level Security using the "bypass RLS" privilege:
|
||||
Supabase provides special "Service" keys, which can be used to bypass RLS. These should never be used in the browser or exposed to customers, but they are useful for administrative tasks.
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
Supabase will adhere to the RLS policy of the signed-in user, even if the client library is initialized with a Service Key.
|
||||
|
||||
</Admonition>
|
||||
|
||||
You can also create new [Postgres Roles](/docs/guides/database/postgres/roles) which can bypass Row Level Security using the "bypass RLS" privilege:
|
||||
|
||||
```sql
|
||||
grant bypassrls on "table_name" to "role_name";
|
||||
@@ -380,3 +483,8 @@ This prevents the policy `( (select auth.uid()) = user_id )` from running for an
|
||||
| Test | Before (ms) | After (ms) | % Improvement | Change |
|
||||
| --------------------------------------------------------------------------------------------- | ----------- | ---------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [test6-To-role](https://github.com/GaryAustin1/RLS-Performance/tree/main/tests/test6-To-role) | 170 | < 0.1 | 99.78% | <details className="cursor-pointer">Before:<br/>No `TO` policy<br/><br/>After:<br/>`TO authenticated` (anon accessing)</details> |
|
||||
|
||||
## Resources
|
||||
|
||||
- [Testing your database](/docs/guides/database/testing)
|
||||
- Community repo on testing RLS using [pgTAP and dbdev](https://github.com/usebasejump/supabase-test-helpers/tree/main)
|
||||
|
||||
@@ -30,7 +30,7 @@ Importantly, this is done _inside_ the `Deno.serve()` callback argument, so that
|
||||
|
||||
## Fetching the user
|
||||
|
||||
After initializing a Supabase client with the Auth context, you can use `getUser()` to fetch the user object, and run queries in the context of the user with [Row Level Security (RLS)](/docs/guides/auth/row-level-security) policies enforced.
|
||||
After initializing a Supabase client with the Auth context, you can use `getUser()` to fetch the user object, and run queries in the context of the user with [Row Level Security (RLS)](/docs/guides/database/postgres/row-level-security) policies enforced.
|
||||
|
||||
```js mark=12:13
|
||||
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'
|
||||
@@ -57,7 +57,7 @@ Deno.serve(async (req: Request) => {
|
||||
|
||||
## Row Level Security
|
||||
|
||||
After initializing a Supabase client with the Auth context, all queries will be executed with the context of the user. For database queries, this means [Row Level Security](/docs/guides/auth/row-level-security) will be enforced.
|
||||
After initializing a Supabase client with the Auth context, all queries will be executed with the context of the user. For database queries, this means [Row Level Security](/docs/guides/database/postgres/row-level-security) will be enforced.
|
||||
|
||||
```js mark=12
|
||||
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'
|
||||
|
||||
@@ -16,8 +16,8 @@ Deno.env.get(MY_SECRET_NAME)
|
||||
Edge Functions have access to these secrets by default:
|
||||
|
||||
- `SUPABASE_URL`: The API gateway for your Supabase project.
|
||||
- `SUPABASE_ANON_KEY`: The `anon` key for your Supabase API. This is safe to use in a browser when you have [Row Level Security](/docs/guides/auth/row-level-security) enabled.
|
||||
- `SUPABASE_SERVICE_ROLE_KEY`: The `service_role` key for your Supabase API. This is safe to use in Edge Functions, but it should NEVER be used in a browser. This key will bypass [Row Level Security](/docs/guides/auth/row-level-security).
|
||||
- `SUPABASE_ANON_KEY`: The `anon` key for your Supabase API. This is safe to use in a browser when you have [Row Level Security](/docs/guides/database/postgres/row-level-security) enabled.
|
||||
- `SUPABASE_SERVICE_ROLE_KEY`: The `service_role` key for your Supabase API. This is safe to use in Edge Functions, but it should NEVER be used in a browser. This key will bypass [Row Level Security](/docs/guides/database/postgres/row-level-security).
|
||||
- `SUPABASE_DB_URL`: The URL for your [PostgreSQL database](/docs/guides/database). You can use this to connect directly to your database.
|
||||
|
||||
## Local secrets
|
||||
|
||||
@@ -108,7 +108,7 @@ Build passwordless logins via magic links for your application or website.[Docs]
|
||||
|
||||
### Authorization via Row Level Security
|
||||
|
||||
Control the data each user can access with Postgres Policies. [Docs](/docs/guides/auth/row-level-security).
|
||||
Control the data each user can access with Postgres Policies. [Docs](/docs/guides/database/postgres/row-level-security).
|
||||
|
||||
### Captcha protection
|
||||
|
||||
@@ -116,7 +116,7 @@ Add Captcha to your sign-in, sign-up, and password reset forms. [Docs](/docs/gui
|
||||
|
||||
### Server-side Auth
|
||||
|
||||
Helpers for implementing user authentication in popular server-side languages and frameworks like Next.js, SvelteKit and Remix. [Docs](/docs/guides/auth/server-side/overview).
|
||||
Helpers for implementing user authentication in popular server-side languages and frameworks like Next.js, SvelteKit and Remix. [Docs](/docs/guides/auth/server-side).
|
||||
|
||||
<br />
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ After developing your project and deciding it's Production Ready, you should run
|
||||
|
||||
- Ensure RLS is enabled
|
||||
- Tables that do not have RLS enabled with reasonable policies allow any client to access and modify their data. This is unlikely to be what you want in the majority of cases.
|
||||
- [Learn more about RLS](/docs/guides/auth/row-level-security).
|
||||
- [Learn more about RLS](/docs/guides/database/postgres/row-level-security).
|
||||
- Enable replication on tables containing sensitive data by enabling Row Level Security (RLS) and setting row security policies:
|
||||
- Go to the Authentication > Policies page in the Supabase Dashboard to enable RLS and create security policies.
|
||||
- Go to the Database > Publications page in the Supabase Dashboard to manage replication tables.
|
||||
@@ -63,17 +63,7 @@ After developing your project and deciding it's Production Ready, you should run
|
||||
|
||||
- The table below shows the rate limit quotas on the following authentication endpoints. You can configure the auth rate limits for your project [here](/dashboard/project/_/auth/rate-limits).
|
||||
|
||||
| Endpoint | Path | Limited By | Rate Limit |
|
||||
| ------------------------------------------------ | -------------------------------------------------------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| All endpoints that send emails | `/auth/v1/signup` `/auth/v1/recover` `/auth/v1/user`[^1] | Sum of combined requests | Defaults to 30 emails per hour. As of 14th July 2023, this has been updated to 4 emails per hour. As of 21 Oct 2023, this has been updated to 3 emails per hour. You can only change this with your own custom SMTP setup. |
|
||||
| All endpoints that send One-Time-Passwords (OTP) | `/auth/v1/otp` | Sum of combined requests | Defaults to 30 OTPs per hour. Is customizable. |
|
||||
| Send OTPs or magiclinks | `/auth/v1/otp` | Last request | Defaults to 60 seconds window before a new request is allowed. Is customizable. |
|
||||
| Signup confirmation request | `/auth/v1/signup` | Last request | Defaults to 60 seconds window before a new request is allowed. Is customizable. |
|
||||
| Password Reset Request | `/auth/v1/recover` | Last request | Defaults to 60 seconds window before a new request is allowed. Is customizable. |
|
||||
| Verification requests | `/auth/v1/verify` | IP Address | 360 requests per hour (with bursts up to 30 requests) |
|
||||
| Token refresh requests | `/auth/v1/token` | IP Address | 1800 requests per hour (with bursts up to 30 requests) |
|
||||
| Create or Verify an MFA challenge | `/auth/v1/factors/:id/challenge` `/auth/v1/factors/:id/verify` | IP Address | 15 requests per minute (with bursts up to 30 requests) |
|
||||
| Anonymous sign-ins | `/auth/v1/signup`[^2] | IP Address | 30 requests per hour (with bursts up to 30 requests) |
|
||||
<AuthRateLimits />
|
||||
|
||||
### Realtime quotas
|
||||
|
||||
@@ -94,6 +84,3 @@ After developing your project and deciding it's Production Ready, you should run
|
||||
## Next steps
|
||||
|
||||
This checklist is always growing so be sure to check back frequently, and also feel free to suggest additions and amendments by making a PR on [GitHub](https://github.com/supabase/supabase).
|
||||
|
||||
[^1]: The rate limit is only applied on `/auth/v1/user` if this endpoint is called to update the user's email address.
|
||||
[^2]: The rate limit is only applied on `/auth/v1/signup` if this endpoint is called without passing in an email or phone number in the request body.
|
||||
|
||||
@@ -67,7 +67,7 @@ Channels provide a generic networking solution. Supabase Realtime is designed to
|
||||
|
||||
### Postgres changes
|
||||
|
||||
The Postgres Changes extension listens for database changes and sends them to clients. Clients are required to subscribe with a JWT dictating which changes they are allowed to receive based on the database's [Row Level Security](/docs/guides/auth/row-level-security).
|
||||
The Postgres Changes extension listens for database changes and sends them to clients. Clients are required to subscribe with a JWT dictating which changes they are allowed to receive based on the database's [Row Level Security](/docs/guides/database/postgres/row-level-security).
|
||||
|
||||
```js
|
||||
const allChanges = client
|
||||
@@ -83,6 +83,6 @@ const allChanges = client
|
||||
.subscribe()
|
||||
```
|
||||
|
||||
Anyone with access to a valid JWT signed with the project's JWT secret is able to listen to your database's changes, unless tables have [Row Level Security](/docs/guides/auth/row-level-security) enabled and policies in place.
|
||||
Anyone with access to a valid JWT signed with the project's JWT secret is able to listen to your database's changes, unless tables have [Row Level Security](/docs/guides/database/postgres/row-level-security) enabled and policies in place.
|
||||
|
||||
Clients can choose to receive `INSERT`, `UPDATE`, `DELETE`, or `*` (all) changes for all changes in a schema, a table in a schema, or a column's value in a table. Your clients should only listen to tables in the `public` schema and you must first enable the tables you want your clients to listen to.
|
||||
|
||||
@@ -62,7 +62,7 @@ In this example we'll set up a database table, secure it with Row Level Security
|
||||
|
||||
<StepHikeCompact.Details title="Allow anonymous access">
|
||||
|
||||
In this example we'll turn on [Row Level Security](/docs/guides/auth/row-level-security) for this table and allow anonymous access. In production, be sure to secure your application with the appropriate permissions.
|
||||
In this example we'll turn on [Row Level Security](/docs/guides/database/postgres/row-level-security) for this table and allow anonymous access. In production, be sure to secure your application with the appropriate permissions.
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
import { useState, useCallback, useEffect } from 'react'
|
||||
|
||||
const useHash = () => {
|
||||
const [hash, setHash] = useState(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
const url = new URL(window.location.href)
|
||||
return url.hash.substring(1) // Removes the leading '#'
|
||||
}
|
||||
return undefined
|
||||
})
|
||||
const [hash, setHash] = useState(() =>
|
||||
typeof window !== 'undefined' ? window.location.hash.split('#')[1] : undefined
|
||||
)
|
||||
|
||||
const hashChangeHandler = useCallback(() => {
|
||||
const url = new URL(window.location.href)
|
||||
setHash(url.hash.substring(1))
|
||||
setHash(window.location.hash.split('#')[1])
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
@@ -23,9 +18,7 @@ const useHash = () => {
|
||||
|
||||
const updateHash = useCallback(
|
||||
(newHash) => {
|
||||
if (newHash !== hash) {
|
||||
window.location.hash = newHash
|
||||
}
|
||||
if (newHash !== hash) window.location.hash = newHash
|
||||
},
|
||||
[hash]
|
||||
)
|
||||
|
||||
@@ -160,7 +160,7 @@ const MobileHeader = memo(function MobileHeader({ menuId }: { menuId: MenuId })
|
||||
'transition-all ease-out z-10',
|
||||
'top-0',
|
||||
mobileMenuOpen && 'absolute',
|
||||
'flex items-center h-[var(--mobile-header-height,40px)]',
|
||||
'flex items-center h-[var(--header-height,40px)]',
|
||||
mobileMenuOpen ? 'gap-0' : 'gap-3'
|
||||
)}
|
||||
>
|
||||
@@ -269,8 +269,8 @@ const HeaderLogo = memo(function HeaderLogo() {
|
||||
|
||||
const Container = memo(function Container({
|
||||
children,
|
||||
style,
|
||||
}: PropsWithChildren<{ style?: CSSProperties }>) {
|
||||
className,
|
||||
}: PropsWithChildren<{ className?: string }>) {
|
||||
const mobileMenuOpen = useMenuMobileOpen()
|
||||
|
||||
return (
|
||||
@@ -283,9 +283,9 @@ const Container = memo(function Container({
|
||||
// 'absolute lg:relative',
|
||||
mobileMenuOpen ? 'ml-[75%] sm:ml-[50%] md:ml-[33%] overflow-hidden' : 'overflow-auto',
|
||||
// desktop override any margin styles
|
||||
'lg:ml-0'
|
||||
'lg:ml-0',
|
||||
className
|
||||
)}
|
||||
style={style}
|
||||
>
|
||||
<div className="flex flex-col relative">{children}</div>
|
||||
</div>
|
||||
@@ -362,14 +362,7 @@ function MainSkeleton({ children, menuId }: PropsWithChildren<{ menuId: MenuId }
|
||||
return (
|
||||
<div className="flex flex-row h-full">
|
||||
<NavContainer menuId={menuId} />
|
||||
<Container
|
||||
style={
|
||||
{
|
||||
'--desktop-header-height': '60px',
|
||||
'--mobile-header-height': '40px',
|
||||
} as CSSProperties
|
||||
}
|
||||
>
|
||||
<Container className="[--header-height:40px] lg:[--header-height:60px]">
|
||||
<div className={['lg:sticky top-0 z-10 overflow-hidden'].join(' ')}>
|
||||
<TopNavBar />
|
||||
</div>
|
||||
|
||||
@@ -143,7 +143,7 @@ const Layout: FC<Props> = (props) => {
|
||||
className={cn(
|
||||
'col-span-3 self-start',
|
||||
'hidden md:block md:col-span-3',
|
||||
'sticky top-[calc(var(--mobile-header-height,40px)+2rem)] lg:top-[calc(var(--desktop-header-height,60px)+2rem)]',
|
||||
'sticky top-[calc(var(--header-height,40px)+2rem)]',
|
||||
'max-h-[calc(100vh-60px-5rem)]'
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { serialize } from 'next-mdx-remote/serialize'
|
||||
import type { SerializeOptions } from 'next-mdx-remote/dist/types'
|
||||
import { existsSync } from 'node:fs'
|
||||
import { readdir, readFile } from 'node:fs/promises'
|
||||
import { dirname, join, extname, sep } from 'node:path'
|
||||
import { dirname, join, extname, sep, basename } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import rehypeKatex from 'rehype-katex'
|
||||
@@ -19,11 +19,26 @@ type GuideFrontmatter = {
|
||||
title: string
|
||||
description?: string
|
||||
hideToc?: boolean
|
||||
tocVideo?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the frontmatter for guide MDX files.
|
||||
*
|
||||
* @throws Throws if frontmatter is invalid.
|
||||
*/
|
||||
export function isValidGuideFrontmatter(obj: object): obj is GuideFrontmatter {
|
||||
if (!('title' in obj) || typeof obj.title !== 'string') return false
|
||||
if ('description' in obj && typeof obj.description !== 'string') return false
|
||||
if (!('title' in obj) || typeof obj.title !== 'string') {
|
||||
throw Error(
|
||||
// @ts-expect-error - Getting undefined for unknown property is desired here.
|
||||
`Invalid guide frontmatter: Title must exist and be a string. Received: ${obj.title}`
|
||||
)
|
||||
}
|
||||
if ('description' in obj && typeof obj.description !== 'string') {
|
||||
throw Error(
|
||||
`Invalid guide frontmatter: Description must be a string. Received: ${obj.description}`
|
||||
)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -31,7 +46,7 @@ export async function getGuidesStaticPaths(section: string) {
|
||||
const directory = join(GUIDES_DIRECTORY, section)
|
||||
|
||||
const files = (await readdir(directory, { recursive: true }))
|
||||
.filter((file) => extname(file) === '.mdx')
|
||||
.filter((file) => extname(file) === '.mdx' && !basename(file).startsWith('_'))
|
||||
.map((file) => ({
|
||||
params: {
|
||||
slug: file.replace(/\.mdx$/, '').split(sep),
|
||||
@@ -81,7 +96,8 @@ export async function getGuidesStaticProps(
|
||||
|
||||
const { data: frontmatter, content } = matter(mdx)
|
||||
if (!isValidGuideFrontmatter(frontmatter)) {
|
||||
throw Error('Type of frontmatter is not valid')
|
||||
// Will have thrown
|
||||
return
|
||||
}
|
||||
|
||||
const codeHikeOptions: CodeHikeConfig = {
|
||||
|
||||
@@ -48,7 +48,7 @@ export default function Config() {
|
||||
className={cn(
|
||||
'col-span-3 self-start',
|
||||
'hidden md:block md:col-span-3',
|
||||
'sticky top-[calc(var(--mobile-header-height,40px)+2rem)] lg:top-[calc(var(--desktop-header-height,60px)+2rem)]',
|
||||
'sticky top-[calc(var(--header-height,40px)+2rem)]',
|
||||
'max-h-[calc(100vh-60px-5rem)]'
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -119,7 +119,7 @@ functions:
|
||||
- **Confirm email** determines if users need to confirm their email address after signing up.
|
||||
- If **Confirm email** is enabled, a `user` is returned but `session` is null.
|
||||
- If **Confirm email** is disabled, both a `user` and a `session` are returned.
|
||||
- By default, when the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/guides/auth/concepts/redirect-urls). You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
- By default, when the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/guides/auth/redirect-urls). You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
- If signUp() is called for an existing confirmed user:
|
||||
- When both **Confirm email** and **Confirm phone** (even when phone provider is disabled) are enabled in [your project](/dashboard/project/_/auth/providers), an obfuscated/fake user object is returned.
|
||||
- When either **Confirm email** or **Confirm phone** (even when phone provider is disabled) is disabled, the error message, `User already registered` is returned.
|
||||
@@ -193,7 +193,7 @@ functions:
|
||||
- This method is used for passwordless sign-ins where a OTP is sent to the user's email or phone number.
|
||||
- If you're using an email, you can configure whether you want the user to receive a magiclink or a OTP.
|
||||
- If you're using phone, you can configure whether you want the user to receive a OTP.
|
||||
- The magic link's destination URL is determined by the [`SITE_URL`](https://supabase.com/docs/guides/auth/concepts/redirect-urls). You can modify the `SITE_URL` or add additional redirect URLs in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
- The magic link's destination URL is determined by the [`SITE_URL`](https://supabase.com/docs/guides/auth/redirect-urls). You can modify the `SITE_URL` or add additional redirect URLs in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
examples:
|
||||
- id: sign-in-with-email.
|
||||
name: Sign in with email.
|
||||
@@ -236,7 +236,7 @@ functions:
|
||||
name: Sign in using a third-party provider with redirect
|
||||
isSpotlight: false
|
||||
description: |
|
||||
When the third-party provider successfully authenticates the user, the provider will redirect the user to the URL specified in the `redirectTo` parameter. This parameter defaults to the [`SITE_URL`](https://supabase.com/docs/guides/auth/concepts/redirect-urls). It does not redirect the user immediately after invoking this method.
|
||||
When the third-party provider successfully authenticates the user, the provider will redirect the user to the URL specified in the `redirectTo` parameter. This parameter defaults to the [`SITE_URL`](https://supabase.com/docs/guides/auth/redirect-urls). It does not redirect the user immediately after invoking this method.
|
||||
You can modify the `SITE_URL` or add additional redirect URLs in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
js: |
|
||||
```js
|
||||
|
||||
@@ -123,7 +123,7 @@ functions:
|
||||
- **Confirm email** determines if users need to confirm their email address after signing up.
|
||||
- If **Confirm email** is enabled, a `user` is returned but `session` is null.
|
||||
- If **Confirm email** is disabled, both a `user` and a `session` are returned.
|
||||
- When the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/guides/auth/concepts/redirect-urls) by default. You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
- When the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/guides/auth/redirect-urls) by default. You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
- If SignUp() is called for an existing confirmed user:
|
||||
- When both **Confirm email** and **Confirm phone** (even when phone provider is disabled) are enabled in [your project](/dashboard/project/_/auth/providers), an obfuscated/fake user object is returned.
|
||||
- When either **Confirm email** or **Confirm phone** (even when phone provider is disabled) is disabled, the error message, `User already registered` is returned.
|
||||
@@ -162,7 +162,7 @@ functions:
|
||||
- This method is used for passwordless sign-ins where a OTP is sent to the user's email or phone number.
|
||||
- If you're using an email, you can configure whether you want the user to receive a magiclink or a OTP.
|
||||
- If you're using phone, you can configure whether you want the user to receive a OTP.
|
||||
- The magic link's destination URL is determined by the [`SITE_URL`](https://supabase.com/docs/guides/auth/concepts/redirect-urls). You can modify the `SITE_URL` or add additional redirect urls in [your project](https://supabase.com/dashboard/project/_/auth/settings).
|
||||
- The magic link's destination URL is determined by the [`SITE_URL`](https://supabase.com/docs/guides/auth/redirect-urls). You can modify the `SITE_URL` or add additional redirect urls in [your project](https://supabase.com/dashboard/project/_/auth/settings).
|
||||
examples:
|
||||
- id: sign-in-with-email
|
||||
name: Send Magic Link.
|
||||
|
||||
@@ -60,7 +60,7 @@ functions:
|
||||
- **Confirm email** determines if users need to confirm their email address after signing up.
|
||||
- If **Confirm email** is enabled, a `user` is returned but `session` is null.
|
||||
- If **Confirm email** is disabled, both a `user` and a `session` are returned.
|
||||
- When the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/guides/auth/concepts/redirect-urls) by default. You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
- When the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/guides/auth/redirect-urls) by default. You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
- If signUp() is called for an existing confirmed user:
|
||||
- If **Confirm email** is enabled in [your project](https://supabase.com/dashboard/project/_/auth/providers), an obfuscated/fake user object is returned.
|
||||
- If **Confirm email** is disabled, the error message, `User already registered` is returned.
|
||||
@@ -141,7 +141,7 @@ functions:
|
||||
- This method is used for passwordless sign-ins where a OTP is sent to the user's email or phone number.
|
||||
- If you're using an email, you can configure whether you want the user to receive a magiclink or a OTP.
|
||||
- If you're using phone, you can configure whether you want the user to receive a OTP.
|
||||
- The magic link's destination URL is determined by the [`SITE_URL`](https://supabase.com/docs/guides/auth/concepts/redirect-urls). You can modify the `SITE_URL` or add additional redirect urls in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
- The magic link's destination URL is determined by the [`SITE_URL`](https://supabase.com/docs/guides/auth/redirect-urls). You can modify the `SITE_URL` or add additional redirect urls in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
examples:
|
||||
- id: sign-in-with-email
|
||||
name: Sign in with email.
|
||||
|
||||
@@ -117,7 +117,7 @@ functions:
|
||||
- **Confirm email** determines if users need to confirm their email address after signing up.
|
||||
- If **Confirm email** is enabled, a `user` is returned but `session` is null.
|
||||
- If **Confirm email** is disabled, both a `user` and a `session` are returned.
|
||||
- When the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/guides/auth/concepts/redirect-urls) by default. You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
- When the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/guides/auth/redirect-urls) by default. You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
- If signUp() is called for an existing confirmed user:
|
||||
- When both **Confirm email** and **Confirm phone** (even when phone provider is disabled) are enabled in [your project](/dashboard/project/_/auth/providers), an obfuscated/fake user object is returned.
|
||||
- When either **Confirm email** or **Confirm phone** (even when phone provider is disabled) is disabled, the error message, `User already registered` is returned.
|
||||
@@ -276,7 +276,7 @@ functions:
|
||||
- This method is used for passwordless sign-ins where an OTP is sent to the user's email or phone number.
|
||||
- If you're using an email, you can configure whether you want the user to receive a magiclink or an OTP.
|
||||
- If you're using phone, you can configure whether you want the user to receive an OTP.
|
||||
- The magic link's destination URL is determined by the [`SITE_URL`](https://supabase.com/docs/guides/auth/concepts/redirect-urls). You can modify the `SITE_URL` or add additional redirect urls in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
- The magic link's destination URL is determined by the [`SITE_URL`](https://supabase.com/docs/guides/auth/redirect-urls). You can modify the `SITE_URL` or add additional redirect urls in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
params:
|
||||
- name: email
|
||||
isOptional: true
|
||||
|
||||
@@ -232,7 +232,7 @@ functions:
|
||||
- **Confirm email** determines if users need to confirm their email address after signing up.
|
||||
- If **Confirm email** is enabled, a `user` is returned but `session` is null.
|
||||
- If **Confirm email** is disabled, both a `user` and a `session` are returned.
|
||||
- When the user confirms their email address, they are redirected to the [`SITE_URL`](/docs/guides/auth/concepts/redirect-urls) by default. You can modify your `SITE_URL` or add additional redirect URLs in [your project](/dashboard/project/_/auth/url-configuration).
|
||||
- When the user confirms their email address, they are redirected to the [`SITE_URL`](/docs/guides/auth/redirect-urls) by default. You can modify your `SITE_URL` or add additional redirect URLs in [your project](/dashboard/project/_/auth/url-configuration).
|
||||
- If signUp() is called for an existing confirmed user:
|
||||
- When both **Confirm email** and **Confirm phone** (even when phone provider is disabled) are enabled in [your project](/dashboard/project/_/auth/providers), an obfuscated/fake user object is returned.
|
||||
- When either **Confirm email** or **Confirm phone** (even when phone provider is disabled) is disabled, the error message, `User already registered` is returned.
|
||||
@@ -571,7 +571,7 @@ functions:
|
||||
- If the user doesn't exist, `signInWithOtp()` will signup the user instead. To restrict this behavior, you can set `shouldCreateUser` in `SignInWithPasswordlessCredentials.options` to `false`.
|
||||
- If you're using an email, you can configure whether you want the user to receive a magiclink or a OTP.
|
||||
- If you're using phone, you can configure whether you want the user to receive a OTP.
|
||||
- The magic link's destination URL is determined by the [`SITE_URL`](/docs/guides/auth/concepts/redirect-urls).
|
||||
- The magic link's destination URL is determined by the [`SITE_URL`](/docs/guides/auth/redirect-urls).
|
||||
- See [redirect URLs and wildcards](/docs/guides/auth#redirect-urls-and-wildcards) to add additional redirect URLs to your project.
|
||||
- Magic links and OTPs share the same implementation. To send users a one-time code instead of a magic link, [modify the magic link email template](/dashboard/project/_/auth/templates) to include `{{ .Token }}` instead of `{{ .ConfirmationURL }}`.
|
||||
- See our [Twilio Phone Auth Guide](/docs/guides/auth/phone-login/twilio) for details about configuring WhatsApp sign in.
|
||||
@@ -632,7 +632,7 @@ functions:
|
||||
name: Sign in using a third-party provider with redirect
|
||||
isSpotlight: false
|
||||
description: |
|
||||
- When the third-party provider successfully authenticates the user, the provider redirects the user to the URL specified in the `redirectTo` parameter. This parameter defaults to the [`SITE_URL`](/docs/guides/auth/concepts/redirect-urls). It does not redirect the user immediately after invoking this method.
|
||||
- When the third-party provider successfully authenticates the user, the provider redirects the user to the URL specified in the `redirectTo` parameter. This parameter defaults to the [`SITE_URL`](/docs/guides/auth/redirect-urls). It does not redirect the user immediately after invoking this method.
|
||||
- See [redirect URLs and wildcards](/docs/guides/auth#redirect-urls-and-wildcards) to add additional redirect URLs to your project.
|
||||
code: |
|
||||
```js
|
||||
|
||||
@@ -2098,7 +2098,7 @@ functions:
|
||||
- **Confirm email** determines if users need to confirm their email address after signing up.
|
||||
- If **Confirm email** is enabled, the return value is the user and you won't be logged in automatically.
|
||||
- If **Confirm email** is disabled, the return value is null and you will be logged in instead.
|
||||
- When the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/guides/auth/concepts/redirect-urls) by default. You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
- When the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/guides/auth/redirect-urls) by default. You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
- To learn how to handle OTP links & OAuth refer to [initializing](/docs/reference/kotlin/initializing)
|
||||
- If signUpWith() is called for an existing confirmed user:
|
||||
- If **Confirm email** is enabled in [your project](https://supabase.com/dashboard/project/_/auth/providers), an obfuscated/fake user object is returned.
|
||||
@@ -2202,7 +2202,7 @@ functions:
|
||||
- If the user doesn't exist, `sendOtpTo()` will signup the user instead. To restrict this behavior, you can set `createUser` to `false`.
|
||||
- If you're using an email, you can configure whether you want the user to receive a magiclink or a OTP.
|
||||
- If you're using phone, you can configure whether you want the user to receive a OTP.
|
||||
- The magic link's destination URL is determined by the [`SITE_URL`](/docs/guides/auth/concepts/redirect-urls).
|
||||
- The magic link's destination URL is determined by the [`SITE_URL`](/docs/guides/auth/redirect-urls).
|
||||
- See [redirect URLs and wildcards](/docs/guides/auth/overview#redirect-urls-and-wildcards) to add additional redirect URLs to your project.
|
||||
- To learn how to handle OTP links & OAuth refer to [initializing](/docs/reference/kotlin/initializing)
|
||||
- Magic links and OTPs share the same implementation. To send users a one-time code instead of a magic link, [modify the magic link email template](https://supabase.com/dashboard/project/_/auth/templates) to include `{{ .Token }}` instead of `{{ .ConfirmationURL }}`.
|
||||
@@ -2256,7 +2256,7 @@ functions:
|
||||
name: Create a custom url
|
||||
isSpotlight: false
|
||||
description: |
|
||||
- When the third-party provider successfully authenticates the user, the provider redirects the user to the URL specified in the `redirectUrl` parameter. This parameter defaults to the [`SITE_URL`](/docs/guides/auth/concepts/redirect-urls). It does not redirect the user immediately after invoking this method.
|
||||
- When the third-party provider successfully authenticates the user, the provider redirects the user to the URL specified in the `redirectUrl` parameter. This parameter defaults to the [`SITE_URL`](/docs/guides/auth/redirect-urls). It does not redirect the user immediately after invoking this method.
|
||||
- See [redirect URLs and wildcards](/docs/guides/auth/overview#redirect-urls-and-wildcards) to add additional redirect URLs to your project.
|
||||
- oAuthUrl() provides the URL which needs to be opened in a browser.
|
||||
- The redirectTo URL needs to be setup correctly in your project under Authentication -> URL Configuration -> Redirect URLs.
|
||||
|
||||
@@ -2811,7 +2811,7 @@ functions:
|
||||
- **Confirm email** determines if users need to confirm their email address after signing up.
|
||||
- If **Confirm email** is enabled, the return value is the user and you won't be logged in automatically.
|
||||
- If **Confirm email** is disabled, the return value is null and you will be logged in instead.
|
||||
- When the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/guides/auth/concepts/redirect-urls) by default. You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
- When the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/guides/auth/redirect-urls) by default. You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
- To learn how to handle OTP links & OAuth refer to [initializing](/docs/reference/kotlin/initializing)
|
||||
- If signUpWith() is called for an existing confirmed user:
|
||||
- When both **Confirm email** and **Confirm phone** (even when phone provider is disabled) are enabled in [your project](/dashboard/project/_/auth/providers), an obfuscated/fake user object is returned.
|
||||
@@ -3050,7 +3050,7 @@ functions:
|
||||
- The method `signUpWith(OTP)` does the exact same thing as `signInWith(OTP)`, so it doesn't matter which one you use.
|
||||
- If you're using an email, you can configure whether you want the user to receive a magiclink or a OTP.
|
||||
- If you're using phone, you can configure whether you want the user to receive a OTP.
|
||||
- The magic link's destination URL is determined by the [`SITE_URL`](/docs/guides/auth/concepts/redirect-urls).
|
||||
- The magic link's destination URL is determined by the [`SITE_URL`](/docs/guides/auth/redirect-urls).
|
||||
- See [redirect URLs and wildcards](/docs/guides/auth/overview#redirect-urls-and-wildcards) to add additional redirect URLs to your project.
|
||||
- To learn how to handle OTP links & OAuth refer to [initializing](/docs/reference/kotlin/initializing)
|
||||
- Magic links and OTPs share the same implementation. To send users a one-time code instead of a magic link, [modify the magic link email template](https://supabase.com/dashboard/project/_/auth/templates) to include `{{ .Token }}` instead of `{{ .ConfirmationURL }}`.
|
||||
@@ -3148,7 +3148,7 @@ functions:
|
||||
name: Create a custom url
|
||||
isSpotlight: false
|
||||
description: |
|
||||
- When the third-party provider successfully authenticates the user, the provider redirects the user to the URL specified in the `redirectUrl` parameter. This parameter defaults to the [`SITE_URL`](/docs/guides/auth/concepts/redirect-urls). It does not redirect the user immediately after invoking this method.
|
||||
- When the third-party provider successfully authenticates the user, the provider redirects the user to the URL specified in the `redirectUrl` parameter. This parameter defaults to the [`SITE_URL`](/docs/guides/auth/redirect-urls). It does not redirect the user immediately after invoking this method.
|
||||
- See [redirect URLs and wildcards](/docs/guides/auth/overview#redirect-urls-and-wildcards) to add additional redirect URLs to your project.
|
||||
- getOAuthUrl() provides the URL which needs to be opened in a browser.
|
||||
- The redirectTo URL needs to be setup correctly in your project under Authentication -> URL Configuration -> Redirect URLs.
|
||||
|
||||
@@ -60,7 +60,7 @@ functions:
|
||||
- **Confirm email** determines if users need to confirm their email address after signing up.
|
||||
- If **Confirm email** is enabled, a `user` is returned but `session` is null.
|
||||
- If **Confirm email** is disabled, both a `user` and a `session` are returned.
|
||||
- By default, when the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/guides/auth/concepts/redirect-urls). You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
- By default, when the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/guides/auth/redirect-urls). You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
- If sign_up() is called for an existing confirmed user:
|
||||
- When both **Confirm email** and **Confirm phone** (even when phone provider is disabled) are enabled in [your project](/dashboard/project/_/auth/providers), an obfuscated/fake user object is returned.
|
||||
- When either **Confirm email** or **Confirm phone** (even when phone provider is disabled) is disabled, the error message, `User already registered` is returned.
|
||||
@@ -91,7 +91,7 @@ functions:
|
||||
- id: sign-up-with-redirect
|
||||
name: Sign up with a redirect URL
|
||||
description: |
|
||||
- See [redirect URLs and wildcards](/docs/guides/auth/concepts/redirect-urls) to add additional redirect URLs to your project.
|
||||
- See [redirect URLs and wildcards](/docs/guides/auth/redirect-urls) to add additional redirect URLs to your project.
|
||||
code: |
|
||||
```
|
||||
res = supabase.auth.sign_up(
|
||||
@@ -132,7 +132,7 @@ functions:
|
||||
- If the user doesn't exist, `sign_in_with_otp()` will signup the user instead. To restrict this behavior, you can set `should_create_user` in `SignInWithPasswordlessCredentials.options` to `false`.
|
||||
- If you're using an email, you can configure whether you want the user to receive a magiclink or a OTP.
|
||||
- If you're using phone, you can configure whether you want the user to receive a OTP.
|
||||
- The magic link's destination URL is determined by the [`SITE_URL`](/docs/guides/auth/concepts/redirect-urls).
|
||||
- The magic link's destination URL is determined by the [`SITE_URL`](/docs/guides/auth/redirect-urls).
|
||||
- See [redirect URLs and wildcards](/docs/guides/auth/overview#redirect-urls-and-wildcards) to add additional redirect URLs to your project.
|
||||
- Magic links and OTPs share the same implementation. To send users a one-time code instead of a magic link, [modify the magic link email template](https://supabase.com/dashboard/project/_/auth/templates) to include `{{ .Token }}` instead of `{{ .ConfirmationURL }}`.
|
||||
examples:
|
||||
@@ -178,7 +178,7 @@ functions:
|
||||
name: Sign in using a third-party provider with redirect
|
||||
isSpotlight: false
|
||||
description: |
|
||||
- When the third-party provider successfully authenticates the user, the provider redirects the user to the URL specified in the `redirectTo` parameter. This parameter defaults to the [`SITE_URL`](/docs/guides/auth/concepts/redirect-urls). It does not redirect the user immediately after invoking this method.
|
||||
- When the third-party provider successfully authenticates the user, the provider redirects the user to the URL specified in the `redirectTo` parameter. This parameter defaults to the [`SITE_URL`](/docs/guides/auth/redirect-urls). It does not redirect the user immediately after invoking this method.
|
||||
- See [redirect URLs and wildcards](/docs/guides/auth/overview#redirect-urls-and-wildcards) to add additional redirect URLs to your project.
|
||||
code: |
|
||||
```
|
||||
|
||||
@@ -85,7 +85,7 @@ functions:
|
||||
- **Confirm email** determines if users need to confirm their email address after signing up.
|
||||
- If **Confirm email** is enabled, a `user` is returned but `session` is null.
|
||||
- If **Confirm email** is disabled, both a `user` and a `session` are returned.
|
||||
- When the user confirms their email address, they are redirected to the [`SITE_URL`](/docs/guides/auth/concepts/redirect-urls) by default. You can modify your `SITE_URL` or add additional redirect URLs in [your project](/dashboard/project/_/auth/url-configuration).
|
||||
- When the user confirms their email address, they are redirected to the [`SITE_URL`](/docs/guides/auth/redirect-urls) by default. You can modify your `SITE_URL` or add additional redirect URLs in [your project](/dashboard/project/_/auth/url-configuration).
|
||||
- If signUp() is called for an existing confirmed user:
|
||||
- If **Confirm email** is enabled in [your project](/dashboard/project/_/auth/providers), an obfuscated/fake user object is returned.
|
||||
- If **Confirm email** is disabled, the error message, `User already registered` is returned.
|
||||
@@ -168,7 +168,7 @@ functions:
|
||||
- If the user doesn't exist, `signInWithOTP()` will signup the user instead. To restrict this behavior, you can set `shouldCreateUser` to `false`.
|
||||
- If you're using an email, you can configure whether you want the user to receive a magiclink or a OTP.
|
||||
- If you're using phone, you can configure whether you want the user to receive a OTP.
|
||||
- The magic link's destination URL is determined by the [`SITE_URL`](/docs/guides/auth/concepts/redirect-urls).
|
||||
- The magic link's destination URL is determined by the [`SITE_URL`](/docs/guides/auth/redirect-urls).
|
||||
- See [redirect URLs and wildcards](/docs/guides/auth#redirect-urls-and-wildcards) to add additional redirect URLs to your project.
|
||||
- Magic links and OTPs share the same implementation. To send users a one-time code instead of a magic link, [modify the magic link email template](/dashboard/project/_/auth/templates) to include `{{ .Token }}` instead of `{{ .ConfirmationURL }}`.
|
||||
- When using magic links, specify a `redirectTo` that matches a configured url scheme in your iOS app, so Supabase can correctly redirect back to your app.
|
||||
@@ -227,7 +227,7 @@ functions:
|
||||
name: Sign in using a third-party provider with redirect
|
||||
isSpotlight: false
|
||||
description: |
|
||||
- When the third-party provider successfully authenticates the user, the provider redirects the user to the URL specified in the `redirectTo` parameter. This parameter defaults to the [`SITE_URL`](/docs/guides/auth/concepts/redirect-urls). It does not redirect the user immediately after invoking this method.
|
||||
- When the third-party provider successfully authenticates the user, the provider redirects the user to the URL specified in the `redirectTo` parameter. This parameter defaults to the [`SITE_URL`](/docs/guides/auth/redirect-urls). It does not redirect the user immediately after invoking this method.
|
||||
- See [redirect URLs and wildcards](/docs/guides/auth/overview#redirect-urls-and-wildcards) to add additional redirect URLs to your project.
|
||||
- getOAuthSignInURL() provides the URL which needs to be opened in a SFSafariViewController instance.
|
||||
- The redirectTo URL needs to be setup correctly in your project under Authentication -> URL Configuration -> Redirect URLs.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user