refactor: move auth guides into content directory (#20777)

Begin the process of moving our MDX files into their own content directory.

Fixed a few minor bugs re: ToC and tabs not rerendering consistently on page navigation. (The ToC thing wasn't a problem before the refactor, the tabs thing is a problem on prod.)

Moved MDX files can't import their own components, so everything they require needs to be back in the component prop for mdx-remote's serializer. Cleaned this up a bit and lazy-loaded heavy/rare stuff. Also, the component prop doesn't take arbitrary objects (only actual components), so imported data has to be wrapped in a component.
This commit is contained in:
Charis
2024-02-07 12:25:08 -05:00
committed by GitHub
parent 961c8be87e
commit e20038e2f2
83 changed files with 924 additions and 1138 deletions
-4
View File
@@ -23,7 +23,3 @@ yarn-error.log*
**/*/generated/**/*
!**/*/generated/.gitkeep
!**/*/generated/**/.gitkeep
# Temporary to avoid storing a lot of duplicate Markdown
# before finalizing the codemod
/content
@@ -0,0 +1,11 @@
import { lazy, Suspense } from 'react'
const Generator = lazy(() => import('./AppleSecretGenerator'))
export function AppleSecretGenerator() {
return (
<Suspense>
<Generator />
</Suspense>
)
}
+2
View File
@@ -0,0 +1,2 @@
import { Mermaid } from 'mdx-mermaid/lib/Mermaid'
export default Mermaid
+11
View File
@@ -0,0 +1,11 @@
import { Suspense, lazy } from 'react'
const MermaidHeavy = lazy(() => import('./Mermaid'))
export function Mermaid(props) {
return (
<Suspense>
<MermaidHeavy {...props} />
</Suspense>
)
}
+14
View File
@@ -0,0 +1,14 @@
import { ReactNode } from 'react'
import { navDataForMdx } from './Navigation/NavigationMenu/NavigationMenu.constants'
export function NavData({
data,
children,
}: {
data: keyof typeof navDataForMdx
children: (navItems: (typeof navDataForMdx)[keyof typeof navDataForMdx]) => ReactNode
}) {
const navItems = navDataForMdx[data]
return children(navItems)
}
@@ -2114,3 +2114,9 @@ export const references = [
],
},
]
export const navDataForMdx = {
nativeMobileLoginItems: NativeMobileLoginItems,
phoneLoginsItems: PhoneLoginsItems,
socialLoginItems: SocialLoginItems,
}
+108 -75
View File
@@ -1,80 +1,100 @@
/**
* This entire file could do with a cleanup and better code-splitting, but one thing at a time.
*/
// Basic UI things
import Link from 'next/link'
import { Alert, Button, CodeBlock, GlassPanel, markdownComponents, Tabs } from 'ui'
import StepHikeCompact from '~/components/StepHikeCompact'
import {
Accordion,
Admonition,
Alert,
Button,
CodeBlock,
GlassPanel,
IconPanel,
markdownComponents,
Tabs,
ThemeImage,
} from 'ui'
import { Heading } from './CustomHTMLElements'
// Common components
import { CH } from '@code-hike/mdx/components'
import StepHikeCompact from '~/components/StepHikeCompact'
import ButtonCard from './ButtonCard'
// Other components
// Reference guide specific
// [Charis] I think we can factor these out so they aren't in the bundle for absolutely everything
import CliGlobalFlagsHandler from '~/components/reference/enrichments/cli/CliGlobalFlagsHandler'
import RefHeaderSection from './reference/RefHeaderSection'
import RefSubLayout from '~/layouts/ref/RefSubLayout'
import { Heading } from './CustomHTMLElements'
// Other components
import AuthProviders from '~/components/AuthProviders'
import { ProjectConfigVariables } from './ProjectConfigVariables'
import Options from '~/components/Options'
import Param from '~/components/Params'
// Data wrappers
import { NavData } from './NavData'
// Partials
import DatabaseSetup from './MDX/database_setup.mdx'
import GetSessionWarning from './MDX/get_session_warning.mdx'
import MigrationWarnings from './MDX/migration_warnings.mdx'
import ProjectSetup from './MDX/project_setup.mdx'
import QuickstartIntro from './MDX/quickstart_intro.mdx'
import SocialProviderSettingsSupabase from './MDX/social_provider_settings_supabase.mdx'
import SocialProviderSetup from './MDX/social_provider_setup.mdx'
import StorageManagement from './MDX/storage_management.mdx'
import MigrationWarnings from './MDX/migration_warnings.mdx'
import GetSessionWarning from './MDX/get_session_warning.mdx'
import { CH } from '@code-hike/mdx/components'
import RefHeaderSection from './reference/RefHeaderSection'
import { ProjectConfigVariables } from './ProjectConfigVariables'
// Ref version specific
import CliGlobalFlagsHandler from '~/components/reference/enrichments/cli/CliGlobalFlagsHandler'
import Options from '~/components/Options'
import Param from '~/components/Params'
import { Admonition, ThemeImage } from 'ui'
// Icons
import {
IconMenuJavascript,
IconMenuHome,
IconMenuGettingStarted,
IconMenuDatabase,
IconMenuRestApis,
IconMenuAuth,
IconMenuEdgeFunctions,
IconMenuRealtime,
IconMenuStorage,
IconMenuPlatform,
IconMenuResources,
IconMenuSelfHosting,
IconMenuIntegrations,
IconMenuFlutter,
IconMenuPython,
IconMenuCsharp,
IconMenuSwift,
IconMenuKotlin,
IconMenuApi,
IconMenuAuth,
IconMenuCli,
IconMenuCsharp,
IconMenuDatabase,
IconMenuEdgeFunctions,
IconMenuFlutter,
IconMenuGettingStarted,
IconMenuHome,
IconMenuIntegrations,
IconMenuJavascript,
IconMenuKotlin,
IconMenuPlatform,
IconMenuPython,
IconMenuRealtime,
IconMenuResources,
IconMenuRestApis,
IconMenuSelfHosting,
IconMenuStorage,
IconMenuSwift,
} from './Navigation/NavigationMenu/HomeMenuIcons'
// Heavy/rare (lazy-loaded)
import { AppleSecretGenerator } from './AppleSecretGenerator'
import { Mermaid } from './Mermaid'
const components = {
...markdownComponents,
Accordion,
Admonition,
Button,
ButtonCard,
CH,
CodeBlock,
GlassPanel,
Link,
QuickstartIntro,
DatabaseSetup,
ProjectSetup,
MigrationWarnings,
GetSessionWarning,
SocialProviderSetup,
SocialProviderSettingsSupabase,
StepHikeCompact,
ProjectConfigVariables,
StorageManagement,
Alert: (props: any) => (
<Alert {...props} className="not-prose">
{props.children}
</Alert>
),
Tabs: (props: any) => <Tabs wrappable {...props} />,
TabPanel: (props: any) => <Tabs.Panel {...props}>{props.children}</Tabs.Panel>,
AppleSecretGenerator,
AuthProviders,
Button,
ButtonCard,
CH,
CliGlobalFlagsHandler: () => <CliGlobalFlagsHandler />,
CodeBlock,
DatabaseSetup,
GetSessionWarning,
GlassPanel,
h2: (props: any) => (
<Heading tag="h2" {...props}>
{props.children}
@@ -90,32 +110,45 @@ const components = {
{props.children}
</Heading>
),
IconMenuApi,
IconMenuAuth,
IconMenuCli,
IconMenuCsharp,
IconMenuDatabase,
IconMenuEdgeFunctions,
IconMenuFlutter,
IconMenuGettingStarted,
IconMenuHome,
IconMenuIntegrations,
IconMenuJavascript,
IconMenuKotlin,
IconMenuPlatform,
IconMenuPython,
IconMenuRealtime,
IconMenuResources,
IconMenuRestApis,
IconMenuSelfHosting,
IconMenuStorage,
IconMenuSwift,
IconPanel,
Image: (props: any) => <ThemeImage fill className="object-contain" {...props} />,
RefSubLayout,
RefHeaderSection: (props: any) => <RefHeaderSection {...props} />,
CliGlobalFlagsHandler: () => <CliGlobalFlagsHandler />,
Link,
Mermaid,
MigrationWarnings,
NavData,
Options,
Param,
IconMenuJavascript,
IconMenuHome,
IconMenuGettingStarted,
IconMenuDatabase,
IconMenuRestApis,
IconMenuAuth,
IconMenuEdgeFunctions,
IconMenuRealtime,
IconMenuStorage,
IconMenuPlatform,
IconMenuResources,
IconMenuSelfHosting,
IconMenuIntegrations,
IconMenuFlutter,
IconMenuPython,
IconMenuCsharp,
IconMenuKotlin,
IconMenuSwift,
IconMenuApi,
IconMenuCli,
ProjectConfigVariables,
ProjectSetup,
QuickstartIntro,
RefHeaderSection: (props: any) => <RefHeaderSection {...props} />,
RefSubLayout,
SocialProviderSettingsSupabase,
SocialProviderSetup,
StepHikeCompact,
StorageManagement,
TabPanel: (props: any) => <Tabs.Panel {...props}>{props.children}</Tabs.Panel>,
Tabs: (props: any) => <Tabs wrappable {...props} />,
}
export default components
@@ -1,13 +1,10 @@
import Layout from '~/layouts/DefaultGuideLayout'
import AuthProviders from '~/components/AuthProviders'
export const meta = {
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',
}
---
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'
---
There are two parts to every Auth system:
@@ -149,7 +146,3 @@ When users sign up, Supabase assigns them a unique ID. You can reference this ID
type="video/mp4"
/>
</video>
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-captcha',
title: 'Enable Captcha Protection',
description: 'Add Captcha Protection to your Supabase project',
video: 'https://www.youtube.com/v/em1cpOAXknM',
}
---
id: 'auth-captcha'
title: 'Enable Captcha Protection'
description: 'Add Captcha Protection to your Supabase project'
video: 'https://www.youtube.com/v/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/).
@@ -99,7 +97,9 @@ We will pass it the sitekey we copied from the hCaptcha website as a property al
```jsx
<HCaptcha
sitekey="your-sitekey"
onVerify={(token) => { setCaptchaToken(token) }}
onVerify={(token) => {
setCaptchaToken(token)
}}
/>
```
@@ -176,7 +176,9 @@ We will pass it the sitekey we copied from the Cloudflare website as a property
```jsx
<Turnstile
siteKey="your-sitekey"
onSuccess={(token) => { setCaptchaToken(token) }}
onSuccess={(token) => {
setCaptchaToken(token)
}}
/>
```
@@ -196,7 +198,3 @@ To test locally, you will need to add localhost to the domain whitelist as per t
</Tabs>
Run the application and you should now be provided with a captcha challenge.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
title: 'Email Templates',
description: 'Learn how to manage the email templates in Supabase.',
subtitle: 'Learn how to manage the email templates in Supabase.',
}
---
title: 'Email Templates'
description: 'Learn how to manage the email templates in Supabase.'
subtitle: 'Learn how to manage the email templates in Supabase.'
---
You can customize the email messages used for the authentication flows. You can edit the following email templates:
@@ -18,14 +16,14 @@ You can customize the email messages used for the authentication flows. You can
The templating system provides the following variables for use:
| Name | Description |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `{{ .ConfirmationURL }}` | Contains the confirmation URL. For example, a signup confirmation URL would look like: `https://project-ref.supabase.co/auth/v1/verify?token={{ .TokenHash }}&type=signup&redirect_to=https://example.com/path` . |
| `{{ .Token }}` | Contains a 6-digit One-Time-Password (OTP) that can be used instead of the `{{. ConfirmationURL }}` . |
| `{{ .TokenHash }}` | Contains a hashed version of the `{{ .Token }}`. This is useful for constructing your own email link in the email template. |
| `{{ .SiteURL }}` | Contains your application's Site URL. This can be configured in your project's [authentication settings](/dashboard/project/_/auth/url-configuration). |
| `{{ .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.
| Name | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `{{ .ConfirmationURL }}` | Contains the confirmation URL. For example, a signup confirmation URL would look like: `https://project-ref.supabase.co/auth/v1/verify?token={{ .TokenHash }}&type=signup&redirect_to=https://example.com/path` . |
| `{{ .Token }}` | Contains a 6-digit One-Time-Password (OTP) that can be used instead of the `{{. ConfirmationURL }}` . |
| `{{ .TokenHash }}` | Contains a hashed version of the `{{ .Token }}`. This is useful for constructing your own email link in the email template. |
| `{{ .SiteURL }}` | Contains your application's Site URL. This can be configured in your project's [authentication settings](/dashboard/project/_/auth/url-configuration). |
| `{{ .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. |
## Limitations
@@ -59,19 +57,23 @@ If you intend to use [Server-side rendering](/docs/guides/auth/server-side-rende
You can customize the email link in the email template to redirect the user to a server-side endpoint successfully. For example:
```html
<a href="https://api.example.com/v1/authenticate?token_hash={{ .TokenHash }}&type=invite&redirect_to={{ .RedirectTo }}"
>Accept the invite
</a>
<a
href="https://api.example.com/v1/authenticate?token_hash={{ .TokenHash }}&type=invite&redirect_to={{ .RedirectTo }}"
>Accept the invite
</a>
```
When the user clicks on the link, the request will hit `https://api.example.com/v1/authenticate` and you can grab the `token_hash`, `type` and `redirect_to` query parameters from the URL. Then, you can call the [`verifyOtp`](/docs/reference/javascript/auth-verifyotp) method to get back an authenticated session before redirecting the user back to the client. Since the `verifyOtp` method makes a `POST` request to Supabase Auth to verify the user, the session will be returned in the response body, which can be read by the server. For example:
```js
const { token_hash, type } = Object.fromEntries(new URLSearchParams(window.location.search))
const { data: { session }, error } = await supabase.auth.verifyOtp({ token_hash, type })
const {
data: { session },
error,
} = await supabase.auth.verifyOtp({ token_hash, type })
// subsequently redirect the user back to the client using the redirect_to param
// ...
```
## Customization
@@ -98,7 +100,3 @@ Billy Team</p>
Billy Team</p>
{{ end }}
```
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-email',
title: 'Login With Email',
description: 'Use Supabase to Authenticate and Authorize your users using email.',
}
---
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.
@@ -20,7 +18,8 @@ See the [self-hosting docs](/docs/guides/hosting/overview#configuration) for det
</Admonition>
## Sign up the user
## Sign up the user
<Tabs
scrollable
size="small"
@@ -38,8 +37,8 @@ async function signUpNewUser() {
email: 'example@email.com',
password: 'example-password',
options: {
emailRedirectTo: 'https://example.com/welcome'
}
emailRedirectTo: 'https://example.com/welcome',
},
})
}
```
@@ -92,7 +91,7 @@ When your user signs in, call [signInWithPassword()](/docs/reference/javascript/
async function signInWithEmail() {
const { data, error } = await supabase.auth.signInWithPassword({
email: 'example@email.com',
password: 'example-password'
password: 'example-password',
})
}
```
@@ -161,7 +160,6 @@ Future<void> signOut() async {
</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
@@ -179,7 +177,3 @@ suspend fun logout() {
- [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)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'index',
title: 'Auth Helpers',
description: 'Server-Side Auth guides and utilities for working with Supabase.',
sidebar_label: 'Overview',
}
---
id: 'index'
title: 'Auth Helpers'
description: 'Server-Side Auth guides and utilities for working with Supabase.'
sidebar_label: 'Overview'
---
<Admonition type="caution">
@@ -52,7 +50,3 @@ The Auth Helpers are in `beta`. They are usable in their current state, but it's
- [Source code](https://github.com/supabase/auth-helpers)
- [Known bugs and issues](https://github.com/supabase/auth-helpers/issues)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-ui',
title: 'Auth UI',
description: 'A prebuilt, customizable React component for authenticating users.',
}
---
id: 'auth-ui'
title: 'Auth UI'
description: 'A prebuilt, customizable React component for authenticating users.'
---
Auth UI is a pre-built React component for authenticating users.
It supports custom themes and extensible styles to match your brand and aesthetic.
@@ -472,7 +470,3 @@ Setting `showLinks` to `false` will hide the following links:
- Already have an account? Sign in
- Send a magic link email
- Forgot your password?
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'flutter-auth-ui',
title: 'Flutter Auth UI',
description: 'Prebuilt, customizable Flutter widgets for authenticating users.',
}
---
id: 'flutter-auth-ui'
title: 'Flutter Auth UI'
description: 'Prebuilt, customizable Flutter widgets for authenticating users.'
---
Flutter Auth UI is a Flutter package containing pre-built widgets for authenticating users.
It is unstyled and can match your brand and aesthetic.
@@ -124,7 +122,3 @@ SupaSocialsAuth(
### Theming
This package uses plain Flutter components allowing you to control the appearance of the components using your own theme.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,12 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'nextjs-pages',
title: 'Supabase Auth with Next.js Pages Directory',
description:
'Authentication helpers for Next.js API routes, middleware, and SSR in the Pages Directory.',
sidebar_label: 'Next.js (pages)',
}
---
id: 'nextjs-pages'
title: 'Supabase Auth with Next.js Pages Directory'
description: 'Authentication helpers for Next.js API routes, middleware, and SSR in the Pages Directory.'
sidebar_label: 'Next.js (pages)'
---
<Admonition type="caution">
@@ -917,7 +914,3 @@ import { Database } from '../database.types'
const supabaseClient = useSupabaseClient<Database>()
```
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,12 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'nextjs',
title: 'Supabase Auth with the Next.js App Router',
description:
'Authentication and Authorization helpers for creating an authenticated Supabase client with the Next.js 13 App Router.',
sidebar_label: 'Next.js',
}
---
id: 'nextjs'
title: 'Supabase Auth with the Next.js App Router'
description: 'Authentication and Authorization helpers for creating an authenticated Supabase client with the Next.js 13 App Router.'
sidebar_label: 'Next.js'
---
<Admonition type="caution">
@@ -1315,7 +1312,3 @@ export default function() {
```
For an example of creating multiple Supabase clients, check [Singleton section](/docs/guides/auth/auth-helpers/nextjs#singleton) above.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'remix',
title: 'Supabase Auth with Remix',
description: 'Authentication helpers for loaders and actions in Remix.',
sidebar_label: 'Remix',
}
---
id: 'remix'
title: 'Supabase Auth with Remix'
description: 'Authentication helpers for loaders and actions in Remix.'
sidebar_label: 'Remix'
---
<Admonition type="caution">
@@ -784,7 +782,3 @@ supabaseClient.auth.signUp({
},
})
```
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'sveltekit',
title: 'Supabase Auth with SvelteKit',
description: 'Convenience helpers for implementing user authentication in SvelteKit.',
sidebar_label: 'SvelteKit',
}
---
id: 'sveltekit'
title: 'Supabase Auth with SvelteKit'
description: 'Convenience helpers for implementing user authentication in SvelteKit.'
sidebar_label: 'SvelteKit'
---
<Admonition type="caution">
@@ -1968,7 +1966,3 @@ export const GET: RequestHandler = withAuth(async ({ session, getSupabaseClient
- [Auth Helpers Source code](https://github.com/supabase/auth-helpers)
- [SvelteKit example](https://github.com/supabase/auth-helpers/tree/main/examples/sveltekit)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,12 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
import { CodeBlock, Admonition, Accordion } from 'ui'
import Image from 'next/image'
export const meta = {
id: 'auth-hooks',
title: 'Auth Hooks',
description: 'Use Supabase Postgres Functions to customize your authentication flow',
}
---
id: 'auth-hooks'
title: 'Auth Hooks'
description: 'Use Supabase Postgres Functions to customize your authentication flow'
---
Supabase allows you to use Postgres functions to alter the default Supabase Auth flow. Developers can use hooks to add custom behavior that's not supported natively.
@@ -35,12 +31,14 @@ A hook is a [Postgres Function](https://www.postgresql.org/docs/current/sql-crea
This function is invoked by Supabase Auth at its point in the flow by providing an event object. The object returned by the function gives instructions on how Supabase Auth should continue processing.
To access properties of the `event` argument, you can use the [JSON operators and functions](https://www.postgresql.org/docs/current/functions-json.html).
To access properties of the `event` argument, you can use the [JSON operators and functions](https://www.postgresql.org/docs/current/functions-json.html).
There are no restrictions as to what language can be used to write Auth Hooks. If [PL/pgSQL](https://www.postgresql.org/docs/current/plpgsql.html) is too difficult consider using the [plv8](/docs/guides/database/extensions/plv8) extension which lets you use JavaScript to define functions.
<Admonition type="caution">
If you're using the Supabase SQL Editor, there's an issue when using the `?` (*Does the string exist as a top-level key within the JSON value?*) operator. Use a direct connection to the database if you need to use it when defining a function.
If you're using the Supabase SQL Editor, there's an issue when using the `?` (*Does the string
exist as a top-level key within the JSON value?*) operator. Use a direct connection to the
database if you need to use it when defining a function.
</Admonition>
Here is an example hook signature:
@@ -79,7 +77,6 @@ You also need to grant usage to `supabase_auth_admin`:
grant usage on schema public to supabase_auth_admin;
```
Also revoke permissions from the `authenticated` and `anon` roles to ensure the function is not accessible by Supabase Serverless APIs.
```sql
@@ -421,6 +418,7 @@ Next, store the API key of our email API provider:
```sql
select vault.create_secret('my_api_key', 'my_api_key_name', 'description_of_my_api_key');
```
Create the hook:
```sql
@@ -697,6 +695,3 @@ revoke execute
</TabPanel>
</Tabs>
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-identity-linking',
title: 'Identity Linking',
description: 'Manage the identities associated with your user',
subtitle: 'Manage the identities associated with your user',
}
---
id: 'auth-identity-linking'
title: 'Identity Linking'
description: 'Manage the identities associated with your user'
subtitle: 'Manage the identities associated with your user'
---
## The user identity
@@ -20,9 +18,9 @@ The user identity object contains the following attributes:
| 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. |
| 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
@@ -64,6 +62,7 @@ Supabase Auth allows a user to initiate identity linking with a different email
```kotlin
supabase.auth.linkIdentity(Google)
```
</TabPanel>
</Tabs>
@@ -110,9 +109,6 @@ val googleIdentity = identities.first { it.provider == "google" }
//unlink the google identity from the user
supabase.auth.unlinkIdentity(googleIdentity.identityId!!)
```
</TabPanel>
</Tabs>
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,13 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
import { Mermaid } from 'mdx-mermaid/lib/Mermaid'
export const meta = {
id: 'auth-mfa',
title: 'Multi-Factor Authentication',
description:
'Add an additional layer of security to your apps with Supabase Auth multi-factor authentication.',
}
---
id: 'auth-mfa'
title: 'Multi-Factor Authentication'
description: 'Add an additional layer of security to your apps with Supabase Auth multi-factor authentication.'
---
Multi-factor authentication (MFA), sometimes called two-factor authentication (2FA), adds an additional layer of security to your application by verifying their identity through additional verification steps.
@@ -117,7 +112,6 @@ Enrolling a factor for use with MFA takes three steps:
immediately becomes active for the user account. If not, you should repeat
steps 2 and 3.
#### Example: React
Below is an example that creates a new `EnrollMFA` component that illustrates the important pieces of the MFA enrollment flow.
@@ -223,7 +217,6 @@ An unenrollment flow provides a UI for users to manage and unenroll factors link
When a user unenrolls a factor, call `supabase.auth.mfa.unenroll()` with the ID of the factor. For example, call `supabase.auth.mfa.unenroll({factorId: "d30fd651-184e-4748-a928-0a4b9be1d429"})` to unenroll a factor with ID `d30fd651-184e-4748-a928-0a4b9be1d429`.
#### Example: React
Below is an example that creates a new `UnenrollMFA` component that illustrates the important pieces of the MFA enrollment flow. Note that users can only unenroll a factor after completing the enrollment flow and obtaining an `aal2` JWT claim. Here are some points of note:
@@ -266,26 +259,22 @@ export function UnenrollMFA() {
<>
{error && <div className="error">{error}</div>}
<tbody>
<tr>
<td>Factor ID</td>
<td>Friendly Name</td>
<td>Factor Status</td>
</tr>
{factors.map(factor => (
<tr>
<td>{factor.id}</td>
<td>{factor.friendly_name}</td>
<td>{factor.factor_type}</td>
<td>{factor.status}</td>
<td>Factor ID</td>
<td>Friendly Name</td>
<td>Factor Status</td>
</tr>
))}
{factors.map((factor) => (
<tr>
<td>{factor.id}</td>
<td>{factor.friendly_name}</td>
<td>{factor.factor_type}</td>
<td>{factor.status}</td>
</tr>
))}
</tbody>
<input
type="text"
value={verifyCode}
onChange={(e) => setFactorId(e.target.value.trim())}
/>
<button onClick={()=> supabase.auth.mfa.unenroll({factorId})}>Unenroll</button>
<input type="text" value={verifyCode} onChange={(e) => setFactorId(e.target.value.trim())} />
<button onClick={() => supabase.auth.mfa.unenroll({ factorId })}>Unenroll</button>
</>
)
}
@@ -629,13 +618,10 @@ Currently recognized authentication methods are:
- `sso/saml` - any Single Sign On (SAML) method.
The following additional claims are available when using PKCE flow:
- `invite` - any sign in via an invitation.
- `magiclink` - any sign in via magic link. Excludes logins resulting from invocation of `signUp`.
- `email/signup` - any login resulting from an email signup.
- `email_change` - any login resulting from a change in email.
More authentication methods will be added over time as we increase the number of authentication methods supported by Supabase.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-smtp',
title: 'Configure a Custom SMTP',
description: 'Moving towards production: Configuring a custom SMTP provider',
}
---
id: 'auth-smtp'
title: 'Configure a Custom SMTP'
description: 'Moving towards production: Configuring a custom SMTP provider'
---
## Auth SMTP
@@ -38,6 +36,3 @@ You can use Supabase Auth with any major SMTP provider of your choosing. Some SM
- [Twilio SendGrid](https://docs.sendgrid.com/for-developers/sending-email/integrating-with-the-smtp-api)
- [AWS SES](https://docs.aws.amazon.com/ses/latest/dg/send-email-smtp.html)
- [Resend](https://resend.com/docs/dashboard/emails/introduction)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-user-management',
title: 'User Management',
description: 'Manage your users with Supabase Auth',
subtitle: 'Manage your users with Supabase Auth',
}
---
id: 'auth-user-management'
title: 'User Management'
description: 'Manage your users with Supabase Auth'
subtitle: 'Manage your users with Supabase Auth'
---
## The user object
@@ -81,7 +79,3 @@ Supabase Auth provides these [configuration options](/dashboard/project/_/settin
- **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.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'column-level-security',
title: 'Column Level Security',
description: 'Secure your data using Postgres Column Level Security.',
}
---
id: 'column-level-security'
title: 'Column Level Security'
description: 'Secure your data using Postgres Column Level Security.'
---
PostgreSQL's [Row Level Security (RLS)](https://www.postgresql.org/docs/current/ddl-rowsecurity.html) gives you granular control over who can access rows of data. However, it doesn't give you control over which columns they can access within rows. Sometimes you want to restrict access to specific columns in your database. Column Level Privileges allows you to do just that.
@@ -151,8 +149,4 @@ authenticated;
## Considerations when using column-level privileges
- If you turn off a column privilege you won't be able to use that column at all.
- All operations (insert, update, delete) as well as using `select *` will fail.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
```
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'redirect-urls',
title: 'Redirect URLs',
description: 'Set up redirect urls with Supabase Auth.',
subtitle: 'Set up redirect urls with Supabase Auth.',
}
---
id: 'redirect-urls'
title: 'Redirect URLs'
description: 'Set up redirect urls with Supabase Auth.'
subtitle: 'Set up redirect urls with Supabase Auth.'
---
## Overview
@@ -83,7 +81,3 @@ const { data, error } = await supabase.auth.signInWithOAuth({
For mobile applications you can use deep linking URIs. For example, for your `SITE_URL` you can specify something like `com.supabase://login-callback/` and for additional redirect URLs something like `com.supabase.staging://login-callback/` if needed.
Read more about deep linking and find code examples for different frameworks [here](/docs/guides/auth/native-mobile-deep-linking).
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -0,0 +1,6 @@
---
title: 'Enterprise Single Sign-On'
description: 'Learn about Single Sign-On support in Supabase Auth for enterprise applications'
---
Supabase Auth supports building enterprise applications that require Single Sign-On (SSO) authentication [with SAML 2.0](/docs/guides/auth/sso/auth-sso-saml).
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'managing-user-data',
title: 'Managing User Data',
description: 'Securing your user data with Row Level Security.',
}
---
id: 'managing-user-data'
title: 'Managing User Data'
description: 'Securing your user data with Row Level Security.'
---
For security purposes, the `auth` schema is not exposed on the auto-generated API.
@@ -241,7 +239,3 @@ create trigger on_auth_user_created
after insert on auth.users
for each row execute procedure public.handle_new_user();
```
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,13 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
import { IconPanel } from 'ui'
import Link from 'next/link'
import { NativeMobileLoginItems } from '~/components/Navigation/NavigationMenu/NavigationMenu.constants'
export const meta = {
title: 'Native Mobile Deep Linking',
description: 'Learn how to handle deep linking for OAuth and email based login.',
tocVideo: '8TZ6O1C8ujE',
}
---
title: 'Native Mobile Deep Linking'
description: 'Learn how to handle deep linking for OAuth and email based login.'
tocVideo: '8TZ6O1C8ujE'
---
In certain auth scenarios you will need to handle linking back into your application to finish the user's sign in.
@@ -115,6 +110,7 @@ In certain auth scenarios you will need to handle linking back into your applica
```
For the best user experience it is recommended to use universal links which require a more elaborate setup. You can find the detailed setup instructions in the [Expo docs](https://docs.expo.dev/guides/deep-linking/).
</TabPanel>
<TabPanel id="flutter" label="Flutter">
\*Currently supabase_flutter supports deep links on Android, iOS, Web, MacOS and Windows.
@@ -266,7 +262,7 @@ In certain auth scenarios you will need to handle linking back into your applica
...
```
At this point, you can register your own scheme.
At this point, you can register your own scheme.
On Windows, URL protocols are setup in the Windows registry.
This package won't do it for you.
@@ -306,6 +302,7 @@ In certain auth scenarios you will need to handle linking back into your applica
</TabPanel>
</Tabs>
</TabPanel>
<TabPanel id="swift" label="Swift">
@@ -344,10 +341,6 @@ In certain auth scenarios you will need to handle linking back into your applica
</dict>
</plist>
```
</TabPanel>
</Tabs>
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,12 +1,7 @@
import Layout from '~/layouts/DefaultGuideLayout'
import { IconPanel } from 'ui'
import Link from 'next/link'
import { NativeMobileLoginItems } from '~/components/Navigation/NavigationMenu/NavigationMenu.constants'
export const meta = {
title: 'Native Mobile Login',
description: 'Logging in with native mobile identity providers like Apple & Google.',
}
---
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.
@@ -25,21 +20,28 @@ There are several reasons why you might want to add social login to your applica
## Set up a native social login with Supabase Auth
<div className="grid grid-cols-12 gap-10 not-prose py-8">
{NativeMobileLoginItems.map((item) => (
<Link href={`${item.url}`} key={item.name} passHref className="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 data="nativeMobileLoginItems">
{(data) =>
data.map((item) => (
<Link
href={`${item.url}`}
key={item.name}
passHref
className="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
@@ -47,7 +49,3 @@ There are several reasons why you might want to add social login to your applica
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.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,12 +1,7 @@
import Layout from '~/layouts/DefaultGuideLayout'
import { IconPanel, GlassPanel, IconMail } from 'ui'
import Link from 'next/link'
import { PhoneLoginsItems } from '~/components/Navigation/NavigationMenu/NavigationMenu.constants'
export const meta = {
title: 'Passwordless Login',
description: 'Sign in your users without passwords via magic link email, or one-time passwords sent via email, SMS, or WhatsApp.',
}
---
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:
@@ -36,22 +31,21 @@ There are several reasons why you might want to add passwordless login to your a
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">
{PhoneLoginsItems.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 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>
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-email-otp',
title: 'Login With Email OTP',
description: 'Use Supabase to authenticate your users using one-time password emails.',
}
---
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 24 hours.
@@ -138,7 +136,3 @@ If successful the user will now be logged in and you should receive a valid sess
- [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)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-magic-link',
title: 'Login With Magic Link',
description: 'Use Supabase to authenticate your users using magic links.',
}
---
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 24 hours.
@@ -140,7 +138,3 @@ suspend fun logout() {
- [Supabase Account - Free Plan OK](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Supabase Flutter Client](https://github.com/supabase/supabase-flutter)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
import { Accordion } from 'ui'
export const meta = {
id: 'auth-passwords',
title: 'Passwords',
description: 'How to work with passwords in Supabase Auth',
}
---
id: 'auth-passwords'
title: 'Passwords'
description: 'How to work with passwords in Supabase Auth'
---
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.
@@ -52,28 +49,28 @@ Strong passwords are difficult to remember, so Supabase Auth provides you with A
To add a secure password reset flow to your application:
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.**
- 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.
- 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.
The email link sent to your users works in the same way as [passwordless authentication](/docs/guides/auth/passwordless-login):
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.
- 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.
- 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).
- 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).
### Example: Request a password reset email
@@ -153,7 +150,3 @@ The hash is stored in the `encrypted_password` column of the `auth.users` table.
### 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.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,16 +1,11 @@
import Layout from '~/layouts/DefaultGuideLayout'
import { IconPanel, GlassPanel, IconMail } from 'ui'
import Link from 'next/link'
import { PhoneLoginsItems } from '~/components/Navigation/NavigationMenu/NavigationMenu.constants'
export const meta = {
title: 'Phone Login',
description: 'Learn about logging in to your platform using SMS one-time passwords.',
}
---
title: 'Phone Login'
description: 'Learn about logging in to your platform using SMS one-time passwords.'
---
<Admonition type="tip">
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).
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).
</Admonition>
@@ -31,20 +26,23 @@ There are several reasons why you might want to add phone login to your applicat
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.
<div className="grid grid-cols-1 md:grid-cols-12 gap-10 not-prose py-8">
{PhoneLoginsItems.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 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
@@ -76,10 +74,7 @@ To sign up the user, call [`signUp()`](/docs/reference/javascript/auth-signup) w
<TabPanel id="js" label="JavaScript">
```js
const {
data,
error,
} = await supabase.auth.signUp({
const { data, error } = await supabase.auth.signUp({
phone: '+13334445555',
password: 'some-password',
})
@@ -217,7 +212,7 @@ To update a user's phone number, the user must be logged in. Call [`updateUser()
```js
const { data, error } = await supabase.auth.updateUser({
phone: '123456789'
phone: '123456789',
})
```
@@ -248,7 +243,7 @@ const {
} = await supabase.auth.verifyOtp({
phone: '+13334445555',
token: '123456',
type: 'sms'
type: 'sms',
})
```
@@ -294,7 +289,3 @@ 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.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-messagebird',
title: 'Phone Auth with MessageBird',
description: 'How to set up and use Mobile OTP with MessageBird and Supabase.',
}
---
id: 'auth-messagebird'
title: 'Phone Auth with MessageBird'
description: 'How to set up and use Mobile OTP with MessageBird and Supabase.'
---
<Admonition type="tip">
@@ -69,7 +67,3 @@ To implement Phone Login, see the docs on [using Phone Login](/docs/guides/auth/
- [MessageBird Signup](https://dashboard.messagebird.com/en/sign-up)
- [Supabase Dashboard](https://supabase.com/dashboard)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-twilio',
title: 'Phone Auth with Twilio',
description: 'How to set up and use Mobile OTP with Twilio and Supabase.',
video: 'https://www.youtube.com/v/akScoPO01bc',
}
---
id: 'auth-twilio'
title: 'Phone Auth with Twilio'
description: 'How to set up and use Mobile OTP with Twilio and Supabase.'
video: 'https://www.youtube.com/v/akScoPO01bc'
---
<Admonition type="tip">
@@ -151,7 +149,3 @@ To implement Phone Login, see the docs on [using Phone Login](/docs/guides/auth/
- [Twilio Signup](https://www.twilio.com/try-twilio)
- [Supabase Dashboard](https://supabase.com/dashboard)
- [Supabase Row Level Security](/docs/guides/auth#row-level-security)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-vonage',
title: 'Phone Auth with Vonage',
description: 'How to set up and use Mobile OTP with Vonage and Supabase.',
}
---
id: 'auth-vonage'
title: 'Phone Auth with Vonage'
description: 'How to set up and use Mobile OTP with Vonage and Supabase.'
---
<Admonition type="tip">
@@ -66,7 +64,3 @@ To implement Phone Login, see the docs on [using Phone Login](/docs/guides/auth/
- [Vonage Signup](https://dashboard.nexmo.com/sign-up)
- [Supabase Dashboard](https://supabase.com/dashboard)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
import StepHikeCompact from '~/components/StepHikeCompact'
export const meta = {
title: 'Use Supabase Auth with Next.js',
subtitle: 'Learn how to configure Supabase Auth for the Next.js App Router.',
breadcrumb: 'Auth Quickstarts',
}
---
title: 'Use Supabase Auth with Next.js'
subtitle: 'Learn how to configure Supabase Auth for the Next.js App Router.'
breadcrumb: 'Auth Quickstarts'
hideToc: true
---
<StepHikeCompact>
@@ -88,7 +86,3 @@ export const meta = {
</StepHikeCompact.Step>
</StepHikeCompact>
export const Page = ({ children }) => <Layout meta={meta} children={children} hideToc={true} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
import StepHikeCompact from '~/components/StepHikeCompact'
export const meta = {
title: 'Use Supabase Auth with React Native',
subtitle: 'Learn how to use Supabase Auth with React Native',
breadcrumb: 'Auth Quickstarts',
}
---
title: 'Use Supabase Auth with React Native'
subtitle: 'Learn how to use Supabase Auth with React Native'
breadcrumb: 'Auth Quickstarts'
hideToc: true
---
<StepHikeCompact>
@@ -268,7 +266,3 @@ export const meta = {
</StepHikeCompact.Step>
</StepHikeCompact>
export const Page = ({ children }) => <Layout meta={meta} children={children} hideToc={true} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
import StepHikeCompact from '~/components/StepHikeCompact'
export const meta = {
title: 'Use Supabase Auth with React',
subtitle: 'Learn how to use Supabase Auth with React.js.',
breadcrumb: 'Auth Quickstarts',
}
---
title: 'Use Supabase Auth with React'
subtitle: 'Learn how to use Supabase Auth with React.js.'
breadcrumb: 'Auth Quickstarts'
hideToc: true
---
<StepHikeCompact>
@@ -132,7 +130,3 @@ export const meta = {
</StepHikeCompact.Step>
</StepHikeCompact>
export const Page = ({ children }) => <Layout meta={meta} children={children} hideToc={true} />
export default Page
@@ -1,12 +1,10 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
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',
}
---
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.
@@ -38,10 +36,11 @@ using ( auth.uid() = user_id );
```sql
select *
from todos
where auth.uid() = todos.user_id; -- Policy is implicitly added.
where auth.uid() = todos.user_id;
-- Policy is implicitly added.
```
## Authenticated and anonymous roles
## Authenticated and anonymous roles
Supabase Auth maps every request to one of the roles:
@@ -56,7 +55,7 @@ on profiles for select
to authenticated, anon
using ( true );
-- OR
-- OR
create policy "Public profiles are viewable only by authenticated users"
on profiles for select
@@ -64,7 +63,6 @@ to authenticated
using ( true );
```
## Helper functions
Supabase provides some helper functions that make it easier to write Policies.
@@ -82,8 +80,8 @@ Returns the JWT of the user making the request. Anything that you store in the u
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"
```sql
create policy "User is in team"
on my_table
to authenticated
using ( team_id in (select auth.jwt() -> 'app_metadata' -> 'teams'));
@@ -91,7 +89,7 @@ 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.
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.
@@ -125,7 +123,7 @@ using ( true );
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
```sql
create schema private;
create table private.employees (
@@ -147,10 +145,9 @@ 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.
Row Level Security is extremely versatile, since it simply uses SQL to express access rules for your data.
### Using functions
@@ -291,7 +288,3 @@ to authenticated using (
- [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)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
import { Accordion } from 'ui'
export const meta = {
id: 'server-side-rendering',
title: 'Server-Side Rendering',
description: 'Render pages with user information on the server.',
}
---
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.
@@ -68,7 +65,7 @@ Supabase Auth supports two authentication flows: **Implicit** and **PKCE**. The
<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>
@@ -80,7 +77,7 @@ Supabase Auth supports two authentication flows: **Implicit** and **PKCE**. The
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=<...>
@@ -90,7 +87,7 @@ Supabase Auth supports two authentication flows: **Implicit** and **PKCE**. The
<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:
@@ -143,6 +140,7 @@ Supabase Auth supports two authentication flows: **Implicit** and **PKCE**. The
</Accordion.Item>
</div>
</Accordion>
## Bringing it together
@@ -240,7 +238,3 @@ Also be sure you set proper cache control headers. We recommend invalidating cac
### 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.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
title: 'Creating a Supabase client for SSR',
description: 'Configure Supabase client to use Cookies',
sidebar_label: 'Creating a client',
}
---
title: 'Creating a Supabase client for SSR'
description: 'Configure Supabase client to use Cookies'
sidebar_label: 'Creating a client'
---
### Install Supabase packages
@@ -868,7 +866,3 @@ app.post("/hello-world", async function (req, res, next) {
- Implement [Authentication using Email and Password](/docs/guides/auth/server-side/email-based-auth-with-pkce-flow-for-ssr)
- Implement [Authentication using OAuth](/docs/guides/auth/server-side/oauth-with-pkce-flow-for-ssr)
- [Learn more about SSR](/docs/guides/auth/server-side-rendering)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,12 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
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.',
}
---
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
@@ -306,7 +302,3 @@ Let's update the URL in our email templates to point to our new confirmation end
>
</p>
```
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
title: 'Migrating to the SSR package from Auth Helpers',
description: 'Step-by-step guide to migrating your app to the new SSR package',
sidebar_label: 'Migrating to SSR from Auth Helpers',
}
---
title: 'Migrating to the SSR package from Auth Helpers'
description: 'Step-by-step guide to migrating your app to the new SSR package'
sidebar_label: 'Migrating to SSR from Auth Helpers'
---
The new `ssr` package takes the core concepts of the Auth Helpers and makes them available to any server language or framework. This page will guide you through migrating from the Auth Helpers package to `ssr`.
@@ -50,7 +48,3 @@ Check out the [Creating a client](/docs/guides/auth/server-side/creating-a-clien
- Implement [Authentication using Email and Password](/docs/guides/auth/server-side/email-based-auth-with-pkce-flow-for-ssr)
- Implement [Authentication using OAuth](/docs/guides/auth/server-side/oauth-with-pkce-flow-for-ssr)
- [Learn more about SSR](/docs/guides/auth/server-side-rendering)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
import StepHikeCompact from '~/components/StepHikeCompact'
import { Accordion } from 'ui'
export const meta = {
title: 'Setting up Server-Side Auth for Next.js',
sidebar_label: 'Next.js guide',
}
---
title: 'Setting up Server-Side Auth for Next.js'
sidebar_label: 'Next.js guide'
hideToc: true
---
Next.js comes in two flavors: the [App Router](https://nextjs.org/docs/app) and the [Pages Router](https://nextjs.org/docs/pages). You can set up Server-Side Auth with either strategy. You can even use both in the same application.
@@ -997,22 +994,17 @@ You can use both the App and Pages Routers together.
Follow the instructions for both the App and Pages Routers. Whenever you need to connect to Supabase, import the `createClient` utility that you need:
| Router | Code location | Which `createClient` to use |
| ------------ | ------------------------------ | --------------------------- |
| App Router | Server Component | `server.ts` |
| | Client Component | `client.ts` |
| | Server Action or Route Handler | `actions.ts` |
| Pages Router | `getServerSideProps` | `server-props.ts` |
| | `getStaticProps` | `static-props.ts` |
| | Component | `component.ts` |
| | API route | `api.ts` |
| Router | Code location | Which `createClient` to use |
| ------------ | ------------------------------------------------- | --------------------------- |
| App Router | Server Component, Server Action, or Route Handler | `server.ts` |
| | Client Component | `client.ts` |
| Pages Router | `getServerSideProps` | `server-props.ts` |
| | `getStaticProps` | `static-props.ts` |
| | Component | `component.ts` |
| | API route | `api.ts` |
Remember to create the `middleware.ts` file for the App Router so the session refreshes for App Router pages.
</TabPanel>
</Tabs>
export const Page = ({ children }) => <Layout meta={meta} children={children} hideToc={true} />
export default Page
@@ -1,12 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
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.',
}
---
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.'
---
### Setting up SSR client
@@ -219,7 +215,3 @@ await supabase.auth.signInWithOAuth({
},
})
```
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,18 +1,24 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'index',
title: 'Server-Side Auth Overview',
description: 'Configure Supabase Auth to use Cookies',
sidebar_label: 'Overview',
}
---
id: 'index'
title: 'Server-Side Auth Overview'
description: 'Configure Supabase Auth to use Cookies'
sidebar_label: 'Overview'
---
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.
## Framework quickstarts
<div className="grid cols-[repeat(auto-fit,min-max(250px,1fr)] gap-6 mb-6 not-prose">
{quickstarts.map((item) => {
{[
{
title: 'Next.js',
href: '/guides/auth/server-side/nextjs',
description:
'Automatically configure Supabase to use cookies, making your user and their session available on the client and server.',
icon: '/docs/img/icons/nextjs-icon',
},
].map((item) => {
return (
<Link href={`${item.href}`} key={item.title} passHref>
<GlassPanel title={item.title} background={false} icon={item.icon}>
@@ -35,17 +41,3 @@ If you're currently using the [Auth Helpers package](https://github.com/supabase
- [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)
export const quickstarts = [
{
title: 'Next.js',
href: '/guides/auth/server-side/nextjs',
description:
'Automatically configure Supabase to use cookies, making your user and their session available on the client and server.',
icon: '/docs/img/icons/nextjs-icon',
},
]
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'sessions',
title: 'User Sessions',
description: 'Control and manage user sessions.',
}
---
id: 'sessions'
title: 'User Sessions'
description: 'Control and manage user sessions.'
---
Supabase Auth provides fine-grained control over your user's sessions.
@@ -16,9 +14,9 @@ A session is created when a user signs in. By default, it lasts indefinitely and
A session is represented by the Supabase Auth access token in the form of a JWT, and a refresh token which is a unique string.
Access tokens are designed to be short lived, usually between 5 minutes and 1 hour while refresh tokens never expire but can only be used once. You can exchange a refresh token only once to get a new access and refresh token pair.
Access tokens are designed to be short lived, usually between 5 minutes and 1 hour while refresh tokens never expire but can only be used once. You can exchange a refresh token only once to get a new access and refresh token pair.
This process is called **refreshing the session.**
This process is called **refreshing the session.**
A session terminates, depending on configuration, when:
@@ -54,7 +52,7 @@ Upon sign out, all refresh tokens and potentially other database objects related
<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.
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>
@@ -74,7 +72,7 @@ There are three ways to limit the lifetime of a session:
- Time-boxed sessions, which terminate after a fixed amount of time.
- Set an inactivity timeout, which terminates sessions that haven't been refreshed within the timeout duration.
- Enforce a single-session per user, which only keeps the most recently active session.
- Enforce a single-session per user, which only keeps the most recently active session.
To make sure that users are required to re-authenticate periodically, you can set a positive value for the **Time-box user sessions** option in the [Auth settings](/dashboard/project/_/settings/auth) for your project.
@@ -108,11 +106,11 @@ As your users continue using your app, refresh tokens are being constantly excha
The general rule is that a refresh token can only be used once. However, strictly enforcing this can cause certain issues to arise. There are two exceptions to this design to prevent the early and unexpected termination of user's sessions:
- A refresh token can be used more than once within a defined reuse interval. By default this is 10 seconds and we do not recommend changing this value. This exception is granted for legitimate situations such as:
- Using server-side rendering where the same refresh token needs to be reused on the server and soon after on the client
- To allow some leeway for bugs or issues with serializing access to the refresh token request
- Using server-side rendering where the same refresh token needs to be reused on the server and soon after on the client
- To allow some leeway for bugs or issues with serializing access to the refresh token request
- If the parent of the currently active refresh token for the user's session is being used, the active token will be returned. This exception solves an important and often common situation:
- All clients such as browsers, mobile or desktop apps, and even some servers are inherently unreliable due to network issues. A request does not indicate that they received a response or even processed the response they received.
- If a refresh token is revoked after being used only once, and the response wasn't received and processed by the client, when the client comes back online, it will attempt to use the refresh token that was already used. Since this might happen outside of the reuse interval, it can cause sudden and unexpected session termination.
- All clients such as browsers, mobile or desktop apps, and even some servers are inherently unreliable due to network issues. A request does not indicate that they received a response or even processed the response they received.
- If a refresh token is revoked after being used only once, and the response wasn't received and processed by the client, when the client comes back online, it will attempt to use the refresh token that was already used. Since this might happen outside of the reuse interval, it can cause sudden and unexpected session termination.
Should the reuse attempt not fall under these two exceptions, the whole session is regarded as terminated and all refresh tokens belonging to it are marked as revoked. You can disable this behavior in the Advanced Settings of the [Auth settings](/dashboard/project/_/settings/auth) page, though it is generally not recommended.
@@ -161,7 +159,3 @@ const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
The `customStorageObject` should implement the `getItem`, `setItem`, and `removeItem` methods from the [`Storage` interface](https://developer.mozilla.org/en-US/docs/Web/API/Storage). Async versions of these methods are also supported.
When using cookies to store access and refresh tokens, make sure that the [`Expires` or `Max-Age` attributes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#attributes) of the cookies is set to a timestamp very far into the future. Browsers will clear the cookies, but the session will remain active in Supabase Auth. Therefore it's best to let Supabase Auth control the validity of these tokens and instruct the browser to always store the cookies indefinitely.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,12 +1,7 @@
import Layout from '~/layouts/DefaultGuideLayout'
import { IconPanel } from 'ui'
import Link from 'next/link'
import { SocialLoginItems } from '~/components/Navigation/NavigationMenu/NavigationMenu.constants'
export const meta = {
title: 'Social Login',
description: 'Logging in with social accounts',
}
---
title: 'Social Login'
description: 'Logging in with social accounts'
---
Social Login (OAuth) is an open standard for authentication that allows users to log in to one website or application using their credentials from another website or application. OAuth allows users to grant third-party applications access to their online accounts without sharing their passwords.
OAuth is commonly used for things like logging in to a social media account from a third-party app. It is a secure and convenient way to authenticate users and share information between applications.
@@ -26,32 +21,37 @@ There are several reasons why you might want to add social login to your applica
Supabase supports a suite of social providers. Follow these guides to configure a social provider for your platform.
<div className="grid grid-cols-12 gap-10 not-prose py-8">
{SocialLoginItems.map((item) => (
<Link href={`${item.url}`} key={item.name} passHref className="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 data="socialLoginItems">
{(data) =>
data.map((item) => (
<Link
href={`${item.url}`}
key={item.name}
passHref
className="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
You can use the provider token and provider refresh token returned to make API calls to the OAuth provider. For example, you can use the Google provider token to access Google APIs on behalf of your user.
You can use the provider token and provider refresh token returned to make API calls to the OAuth provider. For example, you can use the Google provider token to access Google APIs on behalf of your user.
Supabase Auth does not manage refreshing the provider token for the user. Your application will need to use the provider refresh token to obtain a new provider token. If no provider refresh token is returned, then it could mean one of the following:
- The OAuth provider does not return a refresh token
- Additional scopes need to be specified in order for the OAuth provider to return a refresh token.
Provider tokens are intentionally not stored in your project's database. 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 want to use the provider token outside of the browser that completed the OAuth flow, it is recommended to send it to a trusted and secure server you control.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,12 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
import AppleSecretGenerator from '~/components/AppleSecretGenerator'
export const meta = {
id: 'auth-apple',
title: 'Login with Apple',
description: 'Use Sign in with Apple with Supabase',
tocVideo: '-tpcZzTdvN0',
}
---
id: 'auth-apple'
title: 'Login with Apple'
description: 'Use Sign in with Apple with Supabase'
tocVideo: '-tpcZzTdvN0'
---
Supabase Auth supports using [Sign in with Apple](https://developer.apple.com/sign-in-with-apple/) on the web and in native apps for iOS, macOS, watchOS or tvOS.
@@ -378,7 +375,3 @@ When developing with Expo, you can test Sign in with Apple via the Expo Go app,
</TabPanel>
</Tabs>
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-azure',
title: 'Login with Azure (Microsoft)',
description: 'Add Azure (Microsoft) OAuth to your Supabase project',
}
---
id: 'auth-azure'
title: 'Login with Azure (Microsoft)'
description: 'Add Azure (Microsoft) OAuth to your Supabase project'
---
To enable Azure (Microsoft) Auth for your project, you need to set up an Azure OAuth application and add the application credentials to your Supabase Dashboard.
@@ -227,7 +225,3 @@ suspend fun signInWithAzure() {
- [Azure Developer Account](https://portal.azure.com)
- [GitHub Discussion](https://github.com/supabase/gotrue/pull/54#issuecomment-757043573)
- [Potential Risk of Privilege Escalation in Azure AD Applications](https://msrc.microsoft.com/blog/2023/06/potential-risk-of-privilege-escalation-in-azure-ad-applications/)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-bitbucket',
title: 'Login with Bitbucket',
description: 'Add Bitbucket OAuth to your Supabase project',
}
---
id: 'auth-bitbucket'
title: 'Login with Bitbucket'
description: 'Add Bitbucket OAuth to your Supabase project'
---
To enable Bitbucket Auth for your project, you need to set up a BitBucket OAuth application and add the application credentials to your Supabase Dashboard.
@@ -117,7 +115,3 @@ suspend fun signOut() {
- [Supabase Account - Free Plan OK](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Bitbucket Account](https://bitbucket.org)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-discord',
title: 'Login with Discord',
description: 'Add Discord OAuth to your Supabase project',
}
---
id: 'auth-discord'
title: 'Login with Discord'
description: 'Add Discord OAuth to your Supabase project'
---
To enable Discord Auth for your project, you need to set up a Discord Application and add the Application OAuth credentials to your Supabase Dashboard.
@@ -119,7 +117,3 @@ suspend fun signOut() {
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Discord Account](https://discord.com)
- [Discord Developer Portal](https://discord.com/developers)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-facebook',
title: 'Login with Facebook',
description: 'Add Facebook OAuth to your Supabase project',
}
---
id: 'auth-facebook'
title: 'Login with Facebook'
description: 'Add Facebook OAuth to your Supabase project'
---
To enable Facebook Auth for your project, you need to set up a Facebook OAuth application and add the application credentials to your Supabase Dashboard.
@@ -136,7 +134,3 @@ You can read more about App Review [here](https://developers.facebook.com/docs/a
- [Supabase Account - Free Plan OK](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Facebook Developers Dashboard](https://developers.facebook.com/)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-figma',
title: 'Login with Figma',
description: 'Add Figma OAuth to your Supabase project',
}
---
id: 'auth-figma'
title: 'Login with Figma'
description: 'Add Figma OAuth to your Supabase project'
---
To enable Figma Auth for your project, you need to set up a Figma OAuth application and add the application credentials to your Supabase Dashboard.
@@ -117,7 +115,3 @@ suspend fun signOut() {
- [Supabase Account - Free Plan OK](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Figma Developers page](https://www.figma.com/developers)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-github',
title: 'Login with GitHub',
description: 'Add GitHub OAuth to your Supabase project',
}
---
id: 'auth-github'
title: 'Login with GitHub'
description: 'Add GitHub OAuth to your Supabase project'
---
To enable GitHub Auth for your project, you need to set up a GitHub OAuth application and add the application credentials to your Supabase Dashboard.
@@ -128,7 +126,3 @@ suspend fun signOut() {
- [Supabase Account - Free Plan OK](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [GitHub Developer Settings](https://github.com/settings/developers)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-gitlab',
title: 'Login with GitLab',
description: 'Add GitLab OAuth to your Supabase project',
}
---
id: 'auth-gitlab'
title: 'Login with GitLab'
description: 'Add GitLab OAuth to your Supabase project'
---
To enable GitLab Auth for your project, you need to set up a GitLab OAuth application and add the application credentials to your Supabase Dashboard.
@@ -114,7 +112,3 @@ suspend fun signOut() {
- [Supabase Account - Free Plan OK](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [GitLab Account](https://gitlab.com)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-google',
title: 'Login with Google',
description: 'Use Sign in with Google on the web, in native apps or with Chrome extensions',
tocVideo: 'vojHmGUGUGc',
}
---
id: 'auth-google'
title: 'Login with Google'
description: 'Use Sign in with Google on the web, in native apps or with Chrome extensions'
tocVideo: 'vojHmGUGUGc'
---
Supabase Auth supports Sign in with Google on the web, native Android applications and Chrome extensions.
@@ -424,7 +422,3 @@ Before you can use Sign in with Google, you need to obtain a [Google Cloud Platf
</TabPanel>
</Tabs>
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-kakao',
title: 'Login with Kakao',
description: 'Add Kakao OAuth to your Supabase project',
}
---
id: 'auth-kakao'
title: 'Login with Kakao'
description: 'Add Kakao OAuth to your Supabase project'
---
To enable Kakao Auth for your project, you need to set up an Kakao OAuth application and add the application credentials to your Supabase Dashboard.
@@ -137,7 +135,3 @@ suspend fun signOut() {
## Resources
- [Kakao Developers Portal](https://developers.kakao.com).
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-keycloak',
title: 'Login with Keycloak',
description: 'Add Keycloak OAuth to your Supabase project',
}
---
id: 'auth-keycloak'
title: 'Login with Keycloak'
description: 'Add Keycloak OAuth to your Supabase project'
---
To enable Keycloak Auth for your project, you need to set up an Keycloak OAuth application and add the application credentials to your Supabase Dashboard.
@@ -136,7 +134,3 @@ suspend fun signOut() {
- You can find the keycloak openid endpoint configuration under the realm settings.
![Keycloak OpenID Endpoint Configuration](/docs/img/guides/auth-keycloak/keycloak-openid-endpoint-config.png)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-linkedin',
title: 'Login with LinkedIn',
description: 'Add LinkedIn OAuth to your Supabase project',
}
---
id: 'auth-linkedin'
title: 'Login with LinkedIn'
description: 'Add LinkedIn OAuth to your Supabase project'
---
To enable LinkedIn Auth for your project, you need to set up a LinkedIn OAuth application and add the application credentials to your Supabase Dashboard.
@@ -140,7 +138,3 @@ Do reach out to support if you have any concerns around this change.
- [Supabase Account - Free Plan OK](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [LinkedIn Developer Dashboard](https://api.LinkedIn.com/apps)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-notion',
title: 'Login with Notion',
description: 'Add Notion OAuth to your Supabase project',
}
---
id: 'auth-notion'
title: 'Login with Notion'
description: 'Add Notion OAuth to your Supabase project'
---
To enable Notion Auth for your project, you need to set up a Notion Application and add the Application OAuth credentials to your Supabase Dashboard.
@@ -118,7 +116,3 @@ suspend fun signOut() {
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Notion Account](https://notion.so)
- [Notion Developer Portal](https://www.notion.so/my-integrations)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-slack',
title: 'Login with Slack',
description: 'Add Slack OAuth to your Supabase project',
}
---
id: 'auth-slack'
title: 'Login with Slack'
description: 'Add Slack OAuth to your Supabase project'
---
To enable Slack Auth for your project, you need to set up a Slack OAuth application and add the application credentials to your Supabase Dashboard.
@@ -135,7 +133,3 @@ suspend fun signOut() {
- [Supabase Account - Free Plan OK](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Slack Developer Dashboard](https://api.slack.com/apps)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-spotify',
title: 'Login with Spotify',
description: 'Add Spotify OAuth to your Supabase project',
}
---
id: 'auth-spotify'
title: 'Login with Spotify'
description: 'Add Spotify OAuth to your Supabase project'
---
To enable Spotify Auth for your project, you need to set up a Spotify OAuth application and add the application credentials to your Supabase Dashboard.
@@ -129,7 +127,3 @@ suspend fun signOut() {
- [Supabase Account - Free Plan OK](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Spotify Developer Dashboard](https://developer.spotify.com/dashboard/)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-twitch',
title: 'Login with Twitch',
description: 'Add Twitch OAuth to your Supabase project',
}
---
id: 'auth-twitch'
title: 'Login with Twitch'
description: 'Add Twitch OAuth to your Supabase project'
---
To enable Twitch Auth for your project, you need to set up a Twitch Application and add the Application OAuth credentials to your Supabase Dashboard.
@@ -133,7 +131,3 @@ suspend fun signOut() {
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Twitch Account](https://twitch.tv)
- [Twitch Developer Console](https://dev.twitch.tv/console)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-twitter',
title: 'Login with Twitter',
description: 'Add Twitter OAuth to your Supabase project',
}
---
id: 'auth-twitter'
title: 'Login with Twitter'
description: 'Add Twitter OAuth to your Supabase project'
---
To enable Twitter Auth for your project, you need to set up a Twitter OAuth application and add the application credentials in the Supabase Dashboard.
@@ -123,7 +121,3 @@ suspend fun signOut() {
- [Supabase Account - Free Plan OK](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Twitter Developer Dashboard](https://developer.twitter.com/en/portal/dashboard)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-workos',
title: 'Login with WorkOS',
description: 'Add WorkOS OAuth to your Supabase project',
}
---
id: 'auth-workos'
title: 'Login with WorkOS'
description: 'Add WorkOS OAuth to your Supabase project'
---
To enable WorkOS Auth for your project, you need to set up WorkOS OAuth application and add the application credentials to your Supabase Dashboard.
@@ -134,7 +132,3 @@ suspend fun signOut() {
## Resources
- [WorkOS Documentation](https://workos.com/docs/sso/guide)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-zoom',
title: 'Login with Zoom',
description: 'Add Zoom OAuth to your Supabase project',
}
---
id: 'auth-zoom'
title: 'Login with Zoom'
description: 'Add Zoom OAuth to your Supabase project'
---
To enable Zoom Auth for your project, you need to set up a Zoom OAuth application and add the application credentials to your Supabase Dashboard.
@@ -130,7 +128,3 @@ suspend fun signOut() {
- [Supabase Account - Free Plan OK](https://supabase.com)
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- [Zoom App Marketplace](https://marketplace.zoom.us/)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,11 +1,9 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-sso-saml',
title: 'Single Sign-On with SAML 2.0 for Projects',
description: 'Use Single Sign-On (SSO) authentication on your project with SAML 2.0',
video: 'https://www.youtube.com/v/em1cpOAXknM',
}
---
id: 'auth-sso-saml'
title: 'Single Sign-On with SAML 2.0 for Projects'
description: 'Use Single Sign-On (SSO) authentication on your project with SAML 2.0'
video: 'https://www.youtube.com/v/em1cpOAXknM'
---
<Admonition>
@@ -406,7 +404,3 @@ Should you run into this problem, it is most likely a misconfiguration issue **o
At this time it is not possible to extract the RSA private key used by your project's Supabase Auth server. This is done to keep the private key as secure as possible, given that SAML does not offer an easy way to rotate keys without disrupting service. (Please use a SAML 2.0 Metadata URL whenever possible for this reason!)
If you really need access to the key, please [open a support ticket](https://supabase.com/dashboard/support/new) and we'll try to support you as best as possible.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
+2
View File
@@ -12,6 +12,8 @@ interface Props {
}
children: any
toc?: any
// [Charis] Deprecate meta.hide_table_of_contents once the content migration is over
hideToc?: boolean
currentPage?: string
editLink?: string
}
+3 -1
View File
@@ -10,6 +10,7 @@ import { FooterHelpCalloutType } from '~/components/FooterHelpCallout'
import GuidesTableOfContents from '~/components/GuidesTableOfContents'
import useHash from '~/hooks/useHash'
import { LayoutMainContent } from '../DefaultLayout'
import { usePathname } from 'next/navigation'
interface Props {
meta: {
@@ -31,6 +32,7 @@ interface Props {
}
const Layout: FC<Props> = (props) => {
const pathname = usePathname()
const [hash] = useHash()
const articleRef = useRef()
@@ -61,7 +63,7 @@ const Layout: FC<Props> = (props) => {
return { text, link, level }
})
setTocList(newHeadings)
}, [])
}, [pathname]) // Needed to recalculate the ToC when the page changes
const hasTableOfContents = tocList.length > 0
const tocVideoPreview = `http://img.youtube.com/vi/${props.meta.tocVideo}/0.jpg`
+12 -93
View File
@@ -1,98 +1,17 @@
import fs from 'fs'
import { join } from 'path'
import matter from 'gray-matter'
import nonGeneratedReferencePages from 'data/nonGeneratedReferencePages'
import { REFERENCES } from '~/components/Navigation/NavigationMenu/NavigationMenu.constants'
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
const docsDirectory = process.cwd()
export const DOCS_DIRECTORY = join(dirname(fileURLToPath(import.meta.url)), '..')
export const GUIDES_DIRECTORY = join(DOCS_DIRECTORY, 'content/guides')
const getPathToGeneratedDoc = (slug: string) => {
const sections = slug.split('/')
const updatedSections = sections.slice()
updatedSections.splice(updatedSections.length - 1, 0, 'generated')
return updatedSections.join('/')
type GuideFrontmatter = {
title: string
description?: string
hideToc?: boolean
}
export function getDocsBySlug(slug: string) {
const realSlug = slug.replace(/\.mdx$/, '')
const formattedSlug =
(realSlug.includes('reference/javascript/') &&
!nonGeneratedReferencePages.includes(realSlug)) ||
(realSlug.includes('reference/dart/') && !nonGeneratedReferencePages.includes(realSlug)) ||
(realSlug.includes('reference/cli/') && !nonGeneratedReferencePages.includes(realSlug)) ||
(realSlug.includes('reference/api/') && !nonGeneratedReferencePages.includes(realSlug)) ||
(realSlug.includes('reference/auth/') && !nonGeneratedReferencePages.includes(realSlug)) ||
(realSlug.includes('reference/realtime/') && !nonGeneratedReferencePages.includes(realSlug)) ||
(realSlug.includes('reference/storage/') && !nonGeneratedReferencePages.includes(realSlug))
? getPathToGeneratedDoc(realSlug)
: realSlug
// files can either be .md or .mdx
// we need to check which one exists
let fullPath
let fullPathMD = join(docsDirectory, `${formattedSlug}.md`)
let fullPathMDX = join(docsDirectory, `${formattedSlug}.mdx`)
if (fs.existsSync(fullPathMD)) {
fullPath = fullPathMD
}
if (fs.existsSync(fullPathMDX)) {
fullPath = fullPathMDX
}
// if no match, 404
if (!fs.existsSync(fullPath)) {
console.log(`\nfile ${fullPath} not found, redirect to 404\n`)
fullPath = join(docsDirectory, 'pages/404.mdx')
}
const fileContents = fs.readFileSync(fullPath, 'utf8')
const { data, content } = matter(fileContents)
// Append the title as the h1 tag in the content
const formattedContent = data.title !== undefined ? `# ${data.title} \n\n${content}` : content
return { slug: realSlug, meta: data, content: formattedContent }
}
export function getAllDocs() {
const slugs = walk('docs')
const docs = slugs.map((slug) => getDocsBySlug(slug))
return docs
}
function walk(dir: string) {
let results: string[] = []
const list = fs.readdirSync(dir)
list.forEach(function (file) {
file = dir + '/' + file
let slugs = []
fs.readdirSync(dir).forEach((file) => {
let absolute = join(dir, file)
if (fs.statSync(absolute).isDirectory()) {
fs.readdirSync(absolute).forEach((subFile) => slugs.push(file.concat('/' + subFile)))
}
})
})
return results
}
// [Joshen] Initially tried to simplify the NavBar versioning by reading the directory
// but caused some issues on Vercel. Just gonna park this for now.
export function getLibraryVersions(slug: string) {
const paths = slug.split('/')
if (paths.includes('reference') && paths.length >= 3) {
const reference = REFERENCES[paths[2]]
if (reference?.library !== undefined) {
const path = `data/nav/${reference.library}`
const list = fs.readdirSync(path).map((file) => file.split('.')[0])
return list.reverse()
}
}
return []
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
return true
}
@@ -1,26 +0,0 @@
import { serialize } from 'next-mdx-remote/serialize'
import toc from 'markdown-toc'
import { getDocsBySlug } from '~/lib/docs'
async function generateOldRefMarkdown(slug) {
let doc = getDocsBySlug(slug)
const content = await serialize(doc.content ?? '', {
// MDX's available options, see the MDX docs for more info.
// https://mdxjs.com/packages/mdx/#compilefile-options
// Indicates whether or not to parse the frontmatter from the mdx source
})
return {
props: {
/*
* old reference docs are below
*/
...doc,
content,
toc: toc(doc.content, { maxdepth: 1, firsth1: false }),
},
}
}
export default generateOldRefMarkdown
+100
View File
@@ -0,0 +1,100 @@
import { type CodeHikeConfig, remarkCodeHike } from '@code-hike/mdx'
import matter from 'gray-matter'
import { serialize } from 'next-mdx-remote/serialize'
import type { GetStaticPaths, GetStaticProps, InferGetStaticPropsType } from 'next'
import { MDXRemote } from 'next-mdx-remote'
import type { SerializeOptions } from 'next-mdx-remote/dist/types'
import { existsSync } from 'node:fs'
import { readdir, readFile } from 'node:fs/promises'
import { join, extname, sep } from 'node:path'
import remarkGfm from 'remark-gfm'
import codeHikeTheme from 'config/code-hike.theme.json' assert { type: 'json' }
import components from '~/components'
import Layout from '~/layouts/DefaultGuideLayout'
import { GUIDES_DIRECTORY, isValidGuideFrontmatter } from '~/lib/docs'
export const getStaticPaths = (async () => {
const directory = join(GUIDES_DIRECTORY, 'auth')
const files = (await readdir(directory, { recursive: true }))
.filter((file) => extname(file) === '.mdx')
.map((file) => ({
params: {
slug: file.replace(/\.mdx$/, '').split(sep),
},
}))
// Index page isn't included in the directory
const indexFile = join(GUIDES_DIRECTORY, 'auth.mdx')
if (existsSync(indexFile)) {
files.push({ params: { slug: [] } })
}
return {
paths: files,
fallback: false,
}
}) satisfies GetStaticPaths
export const getStaticProps = (async ({ params }) => {
let relPath: string
switch (typeof params.slug) {
case 'string':
relPath = 'auth' + sep + params.slug
break
case 'object': // actually an array
relPath = 'auth' + sep + params.slug.join(sep)
break
case 'undefined':
relPath = 'auth'
}
const fullPath = join(GUIDES_DIRECTORY, relPath + '.mdx')
const mdx = await readFile(fullPath, 'utf-8')
const editLink = `https://github.com/supabase/supabase/blob/master/apps/docs/content/guides/${relPath}.mdx`
const { data: frontmatter, content } = matter(mdx)
if (!isValidGuideFrontmatter(frontmatter)) {
throw Error('Type of frontmatter is not valid')
}
const codeHikeOptions: CodeHikeConfig = {
theme: codeHikeTheme,
lineNumbers: true,
showCopyButton: true,
skipLanguages: [],
autoImport: false,
}
const mdxOptions: SerializeOptions = {
mdxOptions: {
useDynamicImport: true,
remarkPlugins: [remarkGfm, [remarkCodeHike, codeHikeOptions]],
},
}
const mdxSource = await serialize(content, mdxOptions)
return {
props: {
frontmatter,
mdxSource,
editLink,
},
}
}) satisfies GetStaticProps
export default function AuthGuide({
frontmatter,
mdxSource,
editLink,
}: InferGetStaticPropsType<typeof getStaticProps>) {
const { hideToc, ...meta } = frontmatter
return (
<Layout meta={meta} hideToc={hideToc} editLink={editLink}>
<MDXRemote {...mdxSource} components={components} />
</Layout>
)
}
@@ -1,12 +0,0 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
title: 'Enterprise Single Sign-On',
description: 'Learn about Single Sign-On support in Supabase Auth for enterprise applications',
}
Supabase Auth supports building enterprise applications that require Single Sign-On (SSO) authentication [with SAML 2.0](/docs/guides/auth/sso/auth-sso-saml).
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
+52 -14
View File
@@ -3,8 +3,12 @@
/**
* Copies MDX files from the `pages` directory to the `content` directory,
* replacing frontmatter in `meta` with YAML frontmatter.
*
* Also deletes import and export statements.
*/
let SUB_DIR = null
import { parse } from 'acorn'
import { fromMarkdown } from 'mdast-util-from-markdown'
import { mdxFromMarkdown } from 'mdast-util-mdx'
@@ -15,8 +19,13 @@ import { fileURLToPath } from 'node:url'
const __dirname = dirname(fileURLToPath(import.meta.url))
const ROOT_DIR = join(__dirname, '../..')
const PAGES_DIR = join(ROOT_DIR, 'pages/guides')
const CONTENT_DIR = join(ROOT_DIR, 'content/guides')
let PAGES_DIR = join(ROOT_DIR, 'pages/guides')
let CONTENT_DIR = join(ROOT_DIR, 'content/guides')
if (SUB_DIR) {
PAGES_DIR = join(PAGES_DIR, SUB_DIR)
CONTENT_DIR = join(CONTENT_DIR, SUB_DIR)
}
function convertToYaml(properties) {
let result = '---\n'
@@ -48,26 +57,55 @@ async function main() {
const meta = mdxTree.children.find(
(node) => node.type === 'mdxjsEsm' && node.value.trim().startsWith('export const meta')
)
if (!meta) return
const linesIncl = [meta.position.start.line, meta.position.end.line]
let yamlString = ''
if (meta) {
// @ts-ignore
const parsedMeta = parse(meta.value, { ecmaVersion: 2020, sourceType: 'module' })
yamlString = convertToYaml(
// @ts-ignore
parsedMeta.body[0].declaration.declarations[0].init.properties
)
}
const parsedMeta = parse(meta.value, { ecmaVersion: 2020, sourceType: 'module' })
const yamlString = convertToYaml(
parsedMeta.body[0].declaration.declarations[0].init.properties
const importStatements = mdxTree.children.filter(
(node) => node.type === 'mdxjsEsm' && node.value.trim().match(/^import \w+ from/)
)
const exportStatements = mdxTree.children.filter(
(node) =>
node.type === 'mdxjsEsm' &&
(node.value.trim().match(/^export const (?!meta)/) ||
node.value.trim().startsWith('export default'))
)
const positions = [meta, ...importStatements, ...exportStatements]
// @ts-ignore
.map(({ position }) => [position.start.line, position.end.line])
// splicing them out in reverse order means we don't have to worry about line numbers shifting
.sort((a, b) => b[0] - a[0])
let index = 0
while (index < positions.length - 1) {
const overlapsNext = positions[index][0] <= positions[index + 1][1]
if (overlapsNext) {
positions[index][0] = positions[index + 1][0]
positions.splice(index + 1, 1)
} else {
index++
}
}
const lines = content.split('\n')
lines.splice(
meta.position.start.line - 1,
meta.position.end.line - meta.position.start.line + 1
)
const contentWithoutMeta = lines.join('\n')
const contentWithFrontmatter = yamlString + contentWithoutMeta
for (const position of positions) {
lines.splice(position[0] - 1, position[1] - position[0] + 1)
}
const splicedLines = lines.join('\n')
const splicedWithFrontmatter = yamlString + splicedLines
const destinationPath = join(CONTENT_DIR, filename)
await mkdir(dirname(destinationPath), { recursive: true })
writeFile(destinationPath, contentWithFrontmatter)
writeFile(destinationPath, splicedWithFrontmatter)
})
)
} catch (err) {
+8 -1
View File
@@ -93,11 +93,18 @@ export async function fetchSources() {
'spec/common-cli-sections.json'
).load()
const guideSources = (await walk('pages'))
const pagesSources = (await walk('pages'))
.filter(({ path }) => /\.mdx?$/.test(path))
.filter(({ path }) => !ignoredFiles.includes(path))
.map((entry) => new MarkdownLoader('guide', entry.path).load())
const contentSources = (await walk('content'))
.filter(({ path }) => /\.mdx?$/.test(path))
.filter(({ path }) => !ignoredFiles.includes(path))
.map((entry) => new MarkdownLoader('guide', entry.path, { yaml: true }).load())
const guideSources = [...pagesSources, ...contentSources]
const partnerIntegrationSources = (await fetchPartners()).map((partner) =>
new IntegrationLoader(partner.slug, partner).load()
)
+24 -8
View File
@@ -2,6 +2,7 @@ import { createHash } from 'crypto'
import { ObjectExpression } from 'estree'
import { readFile } from 'fs/promises'
import GithubSlugger from 'github-slugger'
import matter from 'gray-matter'
import { Content, Root } from 'mdast'
import { fromMarkdown } from 'mdast-util-from-markdown'
import { MdxjsEsm, mdxFromMarkdown } from 'mdast-util-mdx'
@@ -118,15 +119,28 @@ export function parseHeading(heading: string): { heading: string; customAnchor?:
* It extracts metadata, strips it of all JSX,
* and splits it into sub-sections based on criteria.
*/
export function processMdxForSearch(content: string): ProcessedMdx {
const checksum = createHash('sha256').update(content).digest('base64')
export function processMdxForSearch(_content: string, options?: { yaml?: boolean }): ProcessedMdx {
const checksum = createHash('sha256').update(_content).digest('base64')
let frontmatter: Record<string, unknown> = {}
let content = _content
if (options?.yaml) {
const parsed = matter(_content)
frontmatter = parsed.data
content = parsed.content
}
const mdxTree = fromMarkdown(content, {
extensions: [mdxjs()],
mdastExtensions: [mdxFromMarkdown()],
})
const meta = extractMetaExport(mdxTree)
let meta: Record<string, unknown>
if (options?.yaml) {
meta = frontmatter
} else {
meta = extractMetaExport(mdxTree)
}
const serializableMeta: Json = meta && JSON.parse(JSON.stringify(meta))
@@ -194,15 +208,16 @@ export class MarkdownLoader extends BaseLoader {
constructor(
source: string,
public filePath: string
public filePath: string,
public options?: { yaml?: boolean }
) {
const path = filePath.replace(/^pages/, '').replace(/\.mdx?$/, '')
const path = filePath.replace(/^(pages|content)/, '').replace(/\.mdx?$/, '')
super(source, path)
}
async load() {
const contents = await readFile(this.filePath, 'utf8')
return [new MarkdownSource(this.source, this.path, contents)]
return [new MarkdownSource(this.source, this.path, contents, this.options)]
}
}
@@ -212,13 +227,14 @@ export class MarkdownSource extends BaseSource {
constructor(
source: string,
path: string,
public contents: string
public contents: string,
public options?: { yaml?: boolean }
) {
super(source, path)
}
process() {
const { checksum, meta, sections } = processMdxForSearch(this.contents)
const { checksum, meta, sections } = processMdxForSearch(this.contents, this.options)
this.checksum = checksum
this.meta = meta
+1
View File
@@ -7,6 +7,7 @@ module.exports = config({
'./components/**/*.tsx',
'./layouts/**/*.tsx',
'./src/**/*.{ts,tsx,mdx}',
'./content/**/*.{ts,tsx,mdx}',
'./docs/**/*.{tsx,mdx}',
],
plugins: [
+40 -5
View File
@@ -1,6 +1,15 @@
import * as TabsPrimitive from '@radix-ui/react-tabs'
import { useRouter } from 'next/router'
import { Children, PropsWithChildren, useEffect, useState } from 'react'
import {
Children,
type KeyboardEvent,
type MouseEvent,
PropsWithChildren,
useEffect,
useState,
} from 'react'
import { TAB_CHANGE_EVENT_NAME } from '../../lib/events'
import styleHandler from '../../lib/theme/styleHandler'
import { useTabGroup } from './TabsProvider'
@@ -61,6 +70,27 @@ const Tabs: React.FC<PropsWithChildren<TabsProps>> & TabsSubComponents = ({
children?.[0]?.props?.id
)
useEffect(() => {
/**
* [Charis] The query param change is done by manual manipulation of window
* location and history, not by router.push (I think to avoid full-page
* rerenders). This doesn't reliably trigger rerender of all tabs on the
* page, possibly because it bypasses `useRouter`. The only way I could
* find of avoiding the full-page rerender but still reacting reliably to
* search param changes was to fire a CustomEvent.
*/
function handleChange(e: CustomEvent) {
if (e.detail.queryGroup === queryGroup && tabIds.includes(e.detail.id)) {
setActiveTab(e.detail.id)
setGroupActiveId?.(e.detail.id)
}
}
window.addEventListener(TAB_CHANGE_EVENT_NAME, handleChange as EventListener)
return () => window.removeEventListener(TAB_CHANGE_EVENT_NAME, handleChange as EventListener)
}, [])
// If query param present for the query group, switch to that tab.
useEffect(() => {
if (queryTab) {
@@ -75,7 +105,7 @@ const Tabs: React.FC<PropsWithChildren<TabsProps>> & TabsSubComponents = ({
const active = activeId ?? groupActiveId ?? activeTab
function onTabClick(id: string) {
function onTabClick(currentTarget: EventTarget, id: string) {
setActiveTab(id)
setGroupActiveId?.(id)
@@ -83,6 +113,9 @@ const Tabs: React.FC<PropsWithChildren<TabsProps>> & TabsSubComponents = ({
const url = new URL(document.location.href)
url.searchParams.set(queryGroup, id)
window.history.replaceState(undefined, '', url)
currentTarget.dispatchEvent(
new CustomEvent(TAB_CHANGE_EVENT_NAME, { bubbles: true, detail: { queryGroup, id } })
)
}
onClick?.(id)
@@ -114,13 +147,15 @@ const Tabs: React.FC<PropsWithChildren<TabsProps>> & TabsSubComponents = ({
return (
<TabsPrimitive.Trigger
onKeyDown={(e: any) => {
onKeyDown={(e: KeyboardEvent<HTMLButtonElement>) => {
if (e.keyCode === 13) {
e.preventDefault()
onTabClick(tab.props.id)
onTabClick(e.currentTarget, tab.props.id)
}
}}
onClick={() => onTabClick(tab.props.id)}
onClick={(e: MouseEvent<HTMLButtonElement>) =>
onTabClick(e.currentTarget, tab.props.id)
}
key={`${tab.props.id}-tab-button`}
value={tab.props.id}
className={triggerClasses.join(' ')}
+1
View File
@@ -0,0 +1 @@
export const TAB_CHANGE_EVENT_NAME = 'sb_ui_component_tab_change'