Creates a new layout, moves two sample files to pages dir

This commit is contained in:
Terry Sutton committed 2022-11-14 21:48:19 -03:30
1 parent e46fc9d0e3
commit 58a4636601
4 files changed
+1214 -26

No files matched your search

+89
View File
@@ -0,0 +1,89 @@
import { MDXProvider } from '@mdx-js/react'
import { useEffect, FC } from 'react'
import Head from 'next/head'
import NavBar from '../components/Navigation/NavBar'
import SideBar from '../components/Navigation/SideBar'
import Footer from '../components/Footer'
import TableOfContents from '~/components/TableOfContents'
import { useRouter } from 'next/router'
import { getPageType } from '../lib/helpers'
import components from '~/components'
import { menuItems } from '../components/Navigation/Navigation.constants'
interface Props {
meta: { title: string; description?: string; hide_table_of_contents?: boolean }
children: any
toc?: any
currentPage: string
}
const Layout: FC<Props> = ({ meta, children, toc, currentPage }) => {
const { asPath } = useRouter()
const page = getPageType(asPath)
useEffect(() => {
const key = localStorage.getItem('supabaseDarkMode')
if (!key) {
// Default to dark mode if no preference config
document.documentElement.className = 'dark'
} else {
document.documentElement.className = key === 'true' ? 'dark' : ''
}
}, [])
const hasTableOfContents =
toc !== undefined && toc.json.filter((item) => item.lvl !== 1 && item.lvl <= 3).length > 0
console.log({ children })
return (
<>
<Head>
<title>{meta?.title} | Supabase</title>
<meta name="description" content={meta?.description} />
<meta content="width=device-width, initial-scale=1" name="viewport" />
<link rel="icon" href="/docs/favicon.ico" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content={meta?.title} />
<meta property="og:description" content={meta?.description} />
<meta property="og:title" content={meta?.title} />
</Head>
<main>
<NavBar currentPage={page} />
<div className="flex w-full flex-row">
<SideBar menuItems={menuItems[page]} />
<div className="main-content-pane docs-width grid md:grid-cols-12 gap-4 justify-between p-4 pb-8 w-full">
<div
className={`${
meta?.hide_table_of_contents || !hasTableOfContents
? 'col-span-12 xl:col-start-2 xl:col-span-10 2xl:col-start-3 2xl:col-span-8'
: 'col-span-12 lg:col-span-9'
} py-2 lg:py-4 px-2 lg:px-8 mx-auto`}
>
<article
className={`${
meta?.hide_table_of_contents || !hasTableOfContents ? 'xl:min-w-[880px]' : ''
} doc-content-container prose dark:prose-dark dark:bg-scale-200 width-full mt-8 2xl:max-w-[880px]`}
>
<MDXProvider components={components}>{children}</MDXProvider>
</article>
</div>
{hasTableOfContents && !meta?.hide_table_of_contents && (
<div
className={[
'border-scale-400 dark:bg-scale-200 table-of-contents-height border-l',
'thin-scrollbar overflow-y-auto sticky hidden xl:block md:col-span-3 px-2',
].join(' ')}
>
<TableOfContents toc={toc} />
</div>
)}
</div>
</div>
<Footer />
</main>
</>
)
}
export default Layout
+1 -26
View File
@@ -1,44 +1,19 @@
import nextMdx from '@next/mdx'
import bundleAnalyzer from '@next/bundle-analyzer'
import remarkGfm from 'remark-gfm'
import admonitions from 'remark-admonitions'
import rehypeSlug from 'rehype-slug'
import withTM from 'next-transpile-modules'
// import { remarkCodeHike } from '@code-hike/mdx'
// import theme from 'shiki/themes/dark-plus.json' assert { type: 'json' }
// import codeHikeTheme from './codeHikeTheme.js'
/**
* Rewrites and redirects are handled by
* apps/www nextjs config
*
* Do not add them in this config
*/
const withMDX = nextMdx({
extension: /\.mdx?$/,
options: {
remarkPlugins: [
// [
// remarkCodeHike,
// { theme: codeHikeTheme, autoImport: false },
// ],
remarkGfm,
[admonitions, {}],
],
remarkPlugins: [remarkGfm],
rehypePlugins: [rehypeSlug],
// This is required for `MDXProvider` component
providerImportSource: '@mdx-js/react',
},
})
const withBundleAnalyzer = bundleAnalyzer({
enabled: process.env.ANALYZE === 'true',
})
// /** @type {NextConfig} */
const nextConfig = {
// Append the default value with md extensions
@@ -0,0 +1,281 @@
import Layout from '~/layouts/DefaultLayout'
export const meta = {
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.
<video width="99%" muted playsInline controls="true">
<source
src="https://supabase.com/images/blog/lw5-one-more/auth-ui-demo.mp4"
type="video/mp4"
muted
playsInline
/>
</video>
## Set up Auth UI
Install the latest version of [supabase-js](/docs/reference/javascript) and the Auth UI package:
```bash
npm install @supabase/supabase-js @supabase/auth-ui-react
```
### Import the Auth component
Pass `supabaseClient` from `@supabase/supabase-js` as a prop to the component.
```js title=/src/index.js
import { createClient } from '@supabase/supabase-js'
import { Auth } from '@supabase/auth-ui-react'
const supabase = createClient('<INSERT PROJECT URL>', '<INSERT PROJECT ANON API KEY>')
const App = () => <Auth supabaseClient={supabase} />
```
This renders the Auth component without any styling.
We recommend using one of the predefined themes to style the UI.
Import the theme you want to use and pass it to the `appearence.theme` prop.
```js lines=4,11 title=/src/index.js
import {
Auth,
// Import predefined theme
ThemeSupa,
} from '@supabase/auth-ui-react'
const App = () => (
<Auth
supabaseClient={supabase}
{/* Apply predefined theme */}
appearance={{ theme: ThemeSupa }}
/>
)
```
## Customization
There are several ways to customize Auth UI:
- Use one of the [predefined themes](#predefined-themes) that comes with Auth UI
- Extend a theme by [overriding the variable tokens](#override-themes) in a theme
- [Create your own theme](#create-theme)
- [Use your own CSS classes](#custom-css-classes)
- [Use inline styles](#custom-inline-styles)
- [Use your own labels](#custom-labels)
### Predefined themes
Auth UI comes with several themes to customize the appearance. Each predefined theme comes with at least two variations, a `default` variation, and a `dark` variation. You can switch between these themes using the `theme` prop. Import the theme you want to use and pass it to the `appearence.theme` prop.
```js lines=2,10 title=/src/index.js
import { createClient } from '@supabase/supabase-js'
import { Auth, ThemeSupa } from '@supabase/auth-ui-react'
const supabase = createClient('<INSERT PROJECT URL>', '<INSERT PROJECT ANON API KEY>')
const App = () => (
<Auth
supabaseClient={supabase}
{/* Apply predefined theme */}
appearance={{ theme: ThemeSupa }}
/>
)
```
<Admonition type="info">
Currently there is only one predefined theme available, but we plan to add more.
</Admonition>
### Switch theme variations
Auth UI comes with two theme variations: `default` and `dark`. You can switch between these themes with the `theme` prop.
```js lines=11 title=/src/index.js
import { createClient } from '@supabase/supabase-js'
import { Auth, ThemeSupa } from '@supabase/auth-ui-react'
const supabase = createClient('<INSERT PROJECT URL>', '<INSERT PROJECT ANON API KEY>')
const App = () => (
<Auth
supabaseClient={supabase}
appearance={{ theme: ThemeSupa }}
{/* Set theme to dark */}
theme="dark"
/>
)
```
If you don't pass a value to `theme` it uses the `"default"` theme. You can pass `"dark"` to the theme prop to switch to the `dark` theme. If your theme has other variations, use the name of the variation in this prop.
### Override themes
Auth UI themes can be overridden using variable tokens. See the [list of variable tokens](https://github.com/supabase-community/auth-ui/blob/main/packages/react/common/theming/Themes.tsx).
```js lines=11-18 title=/src/index.js
import { createClient } from '@supabase/supabase-js'
import { Auth, ThemeSupa } from '@supabase/auth-ui-react'
const supabase = createClient('<INSERT PROJECT URL>', '<INSERT PROJECT ANON API KEY>')
const App = () => (
<Auth
supabaseClient={supabase}
appearance={{
theme: ThemeSupa,
variables: {
default: {
colors: {
brand: 'red',
brandAccent: 'darkred',
},
},
},
}}
/>
)
```
If you created your own theme, you may not need to override any of the them.
### Create your own theme {#create-theme}
You can create your own theme by following the same structure within a `appearance.theme` property.
See the list of [tokens within a theme](https://github.com/supabase-community/auth-ui/blob/main/packages/react/common/theming/Themes.tsx).
```js title=/src/index.js
import { createClient } from '@supabase/supabase-js'
import { Auth } from '@supabase/auth-ui-react'
const supabase = createClient(
'<INSERT PROJECT URL>',
'<INSERT PROJECT ANON API KEY>'
)
const customTheme = {
default: {
colors: {
brand: 'hsl(153 60.0% 53.0%)',
brandAccent: 'hsl(154 54.8% 45.1%)',
brandButtonText: 'white',
// ..
},
dark: {
colors: {
brandButtonText: 'white',
defaultButtonBackground: '#2e2e2e',
defaultButtonBackgroundHover: '#3e3e3e',
//..
},
},
// You can also add more theme variations with different names.
evenDarker: {
colors: {
brandButtonText: 'white',
defaultButtonBackground: '#1e1e1e',
defaultButtonBackgroundHover: '#2e2e2e',
//..
},
},
}
const App = () => (
<Auth
supabaseClient={supabase}
theme="default" // can also be "dark" or "evenDarker"
appearance={{ theme: customTheme}}
/>
)
```
You can swich between different variations of your theme with the ["theme" prop](#switch-theme-variations).
### Custom CSS classes {#custom-css-classes}
You can use custom CSS classes for the following elements:
`"button"`, `"container"`, `"anchor"`, `"divider"`, `"label"`, `"input"`, `"loader"`, `"message"`.
```js title=/src/index.js
import { createClient } from '@supabase/supabase-js'
import { Auth } from '@supabase/auth-ui-react'
const supabase = createClient('<INSERT PROJECT URL>', '<INSERT PROJECT ANON API KEY>')
const App = () => (
<Auth
supabaseClient={supabase}
appearance={{
className: {
anchor: 'my-awesome-anchor',
button: 'my-awesome-button',
//..
},
}}
/>
)
```
### Custom inline CSS {#custom-inline-styles}
You can use custom CSS inline styles for the following elements:
`"button"`, `"container"`, `"anchor"`, `"divider"`, `"label"`, `"input"`, `"loader"`, `"message"`.
```js title=/src/index.js
import { createClient } from '@supabase/supabase-js'
import { Auth } from '@supabase/auth-ui-react'
const supabase = createClient('<INSERT PROJECT URL>', '<INSERT PROJECT ANON API KEY>')
const App = () => (
<Auth
supabaseClient={supabase}
appearance={{
style: {
button: { background: 'red', color: 'white' },
anchor: { color: 'blue' },
//..
},
}}
/>
)
```
### Custom labels {#custom-labels}
You can use custom labels with `localization.variables`. See the [list of labels](https://github.com/supabase-community/auth-ui/blob/main/packages/react/common/lib/Localization/en.json) that can be overwritten.
```js title=/src/index.js
import { createClient } from '@supabase/supabase-js'
import { Auth } from '@supabase/auth-ui-react'
const supabase = createClient('<INSERT PROJECT URL>', '<INSERT PROJECT ANON API KEY>')
const App = () => (
<Auth
supabaseClient={supabase}
//highlight-start
localization={{
variables: {
sign_in: {
email_label: 'Your email address',
password_label: 'Your strong password',
},
},
}}
//highlight-end
/>
)
```
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -0,0 +1,843 @@
import Layout from '~/layouts/DefaultLayout'
export const meta = {
id: 'nextjs',
title: 'Supabase Auth with Next.js',
description: 'Authentication helpers for Next.js API routes, middleware, and SSR.',
sidebar_label: 'Next.js',
}
This submodule provides convenience helpers for implementing user authentication in Next.js applications.
## Install the Next.js helper library
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="npm"
>
<TabPanel id="npm" label="npm">
```sh
npm install @supabase/auth-helpers-nextjs
```
This library supports the following tooling versions:
- Node.js: `^10.13.0 || >=12.0.0`
- Next.js: `>=10`
- Note: Next.js 13 is supported except for the new `app` directory approach. We're working on adding support for this and you can follow along [here](https://github.com/supabase/auth-helpers/issues/341).
Additionally, install the **React Auth Helpers** for components and hooks that can be used across all React-based frameworks.
```sh
npm install @supabase/auth-helpers-react
```
</TabPanel>
<TabPanel id="yarn" label="Yarn">
```sh
yarn add @supabase/auth-helpers-nextjs
```
This library supports the following tooling versions:
- Node.js: `^10.13.0 || >=12.0.0`
- Next.js: `>=10`
- Note: Next.js 13 is supported except for the new `app` directory approach. We're working on adding support for this and you can follow along [here](https://github.com/supabase/auth-helpers/issues/341).
Additionally, install the **React Auth Helpers** for components and hooks that can be used across all React-based frameworks.
```sh
yarn add @supabase/auth-helpers-react
```
</TabPanel>
</Tabs>
## Set up environment variables
Retrieve your project URL and anon key in your project's [API settings](https://app.supabase.com/project/_/settings/api) in the Dashboard to set up the following environment variables. For local development you can set them in a `.env.local` file. See an [example](https://github.com/supabase/auth-helpers/blob/main/examples/nextjs/.env.local.example).
```bash title=.env.local
NEXT_PUBLIC_SUPABASE_URL=YOUR_SUPABASE_URL
NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
```
## Basic Setup
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="js"
>
<TabPanel id="js" label="JavaScript">
Wrap your `pages/_app.js` component with the `SessionContextProvider` component:
```jsx title=pages/_app.js
import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
import { SessionContextProvider } from '@supabase/auth-helpers-react'
function MyApp({ Component, pageProps }) {
const router = useRouter()
// Create a new supabase browser client on every first render.
const [supabaseClient] = useState(() => createBrowserSupabaseClient())
return (
<SessionContextProvider
supabaseClient={supabaseClient}
initialSession={pageProps.initialSession}
>
<Component {...pageProps} />
</SessionContextProvider>
)
}
```
</TabPanel>
<TabPanel id="ts" label="TypeScript">
Wrap your `pages/_app.tsx` component with the `SessionContextProvider` component:
```tsx lines=2,8 title=pages/_app.tsx
import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
import { SessionContextProvider, Session } from '@supabase/auth-helpers-react'
function MyApp({
Component,
pageProps,
}: AppProps<{
initialSession: Session
}>) {
// Create a new supabase browser client on every first render.
const [supabaseClient] = useState(() => createBrowserSupabaseClient())
return (
<SessionContextProvider
supabaseClient={supabaseClient}
initialSession={pageProps.initialSession}
>
<Component {...pageProps} />
</SessionContextProvider>
)
}
```
</TabPanel>
</Tabs>
You can now determine if a user is authenticated by checking that the `user` object returned by the `useUser()` hook is defined.
## Usage with TypeScript
You can pass types that were [generated with the Supabase CLI](/docs/reference/javascript/typescript-support#generating-types) to the Supabase Client to get enhanced type safety and auto completion:
### Browser client
Creating a new supabase client object:
```tsx
import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
import { Database } from '../database.types'
const supabaseClient = createBrowserSupabaseClient<Database>()
```
Retrieving a supabase client object from the SessionContext:
```tsx
import { useSupabaseClient } from '@supabase/auth-helpers-react'
import { Database } from '../database.types'
const supabaseClient = useSupabaseClient<Database>()
```
### Server client
```tsx
// Creating a new supabase server client object (e.g. in API route):
import type { NextApiRequest, NextApiResponse } from 'next'
import type { Database } from 'types_db'
export default async (req: NextApiRequest, res: NextApiResponse) => {
const supabaseServerClient = createServerSupabaseClient<Database>({
req,
res,
})
const {
data: { user },
} = await supabaseServerClient.auth.getUser()
res.status(200).json({ name: user?.name ?? '' })
}
```
## Client-side data fetching with RLS
For [row level security](/docs/learn/auth-deep-dive/auth-row-level-security) to work properly when fetching data client-side, you need to make sure to use the `supabaseClient` from the `useSupabaseClient` hook and only run your query once the user is defined client-side in the `useUser()` hook:
```jsx lines=10-17
import { Auth, ThemeSupa } from '@supabase/auth-ui-react'
import { useUser, useSupabaseClient } from '@supabase/auth-helpers-react'
import { useEffect, useState } from 'react'
const LoginPage = () => {
const supabaseClient = useSupabaseClient()
const user = useUser()
const [data, setData] = useState()
useEffect(() => {
async function loadData() {
const { data } = await supabaseClient.from('test').select('*')
setData(data)
}
// Only run query once user is logged in.
if (user) loadData()
}, [user])
if (!user)
return (
<Auth
redirectTo="http://localhost:3000/"
appearance={{ theme: ThemeSupa }}
supabaseClient={supabaseClient}
providers={['google', 'github']}
socialLayout="horizontal"
/>
)
return (
<>
<button onClick={() => supabaseClient.auth.signOut()}>Sign out</button>
<p>user:</p>
<pre>{JSON.stringify(user, null, 2)}</pre>
<p>client-side data fetching with RLS</p>
<pre>{JSON.stringify(data, null, 2)}</pre>
</>
)
}
export default LoginPage
```
## Server-side rendering (SSR)
Create a server supabase client to retrieve the logged in user's session:
```jsx title=pages/profile.js
import { createServerSupabaseClient } from '@supabase/auth-helpers-nextjs'
export default function Profile({ user }) {
return <div>Hello {user.name}</div>
}
export const getServerSideProps = async (ctx) => {
// Create authenticated Supabase Client
const supabase = createServerSupabaseClient(ctx)
// Check if we have a session
const {
data: { session },
} = await supabase.auth.getSession()
if (!session)
return {
redirect: {
destination: '/',
permanent: false,
},
}
return {
props: {
initialSession: session,
user: session.user,
},
}
}
```
## Server-side data fetching with RLS
You can use the server supabase client to run [row level security](/docs/learn/auth-deep-dive/auth-row-level-security) authenticated queries server-side:
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="js"
>
<TabPanel id="js" label="JavaScript">
```jsx
import { createServerSupabaseClient } from '@supabase/auth-helpers-nextjs'
export default function ProtectedPage({ user, data }) {
return (
<>
<div>Protected content for {user.email}</div>
<pre>{JSON.stringify(data, null, 2)}</pre>
<pre>{JSON.stringify(user, null, 2)}</pre>
</>
)
}
export const getServerSideProps = async (ctx) => {
// Create authenticated Supabase Client
const supabase = createServerSupabaseClient(ctx)
// Check if we have a session
const {
data: { session },
} = await supabase.auth.getSession()
if (!session)
return {
redirect: {
destination: '/',
permanent: false,
},
}
// Run queries with RLS on the server
const { data } = await supabase.from('users').select('*')
return {
props: {
initialSession: session,
user: session.user,
data: data ?? [],
},
}
}
```
</TabPanel>
<TabPanel id="ts" label="TypeScript">
```tsx
import { User, createServerSupabaseClient } from '@supabase/auth-helpers-nextjs'
import { GetServerSidePropsContext } from 'next'
export default function ProtectedPage({ user, data }: { user: User; data: any }) {
return (
<>
<div>Protected content for {user.email}</div>
<pre>{JSON.stringify(data, null, 2)}</pre>
<pre>{JSON.stringify(user, null, 2)}</pre>
</>
)
}
export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
// Create authenticated Supabase Client
const supabase = createServerSupabaseClient(ctx)
// Check if we have a session
const {
data: { session },
} = await supabase.auth.getSession()
if (!session)
return {
redirect: {
destination: '/',
permanent: false,
},
}
// Run queries with RLS on the server
const { data } = await supabase.from('users').select('*')
return {
props: {
initialSession: session,
user: session.user,
data: data ?? [],
},
}
}
```
</TabPanel>
</Tabs>
## Server-side data fetching to OAuth APIs using `provider token` {#oauth-provider-token}
When using third-party auth providers, sessions are initiated with an additional `provider_token` field which is persisted in the auth cookie and can be accessed within the session object. The `provider_token` can be used to make API requests to the OAuth provider's API endpoints on behalf of the logged-in user.
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="js"
>
<TabPanel id="js" label="JavaScript">
```jsx
import { createServerSupabaseClient } from '@supabase/auth-helpers-nextjs'
export default function ProtectedPage({ user, allRepos }) {
return (
<>
<div>Protected content for {user.email}</div>
<p>Data fetched with provider token:</p>
<pre>{JSON.stringify(allRepos, null, 2)}</pre>
<p>user:</p>
<pre>{JSON.stringify(user, null, 2)}</pre>
</>
)
}
export const getServerSideProps = async (ctx) => {
// Create authenticated Supabase Client
const supabase = createServerSupabaseClient(ctx)
// Check if we have a session
const {
data: { session },
} = await supabase.auth.getSession()
if (!session)
return {
redirect: {
destination: '/',
permanent: false,
},
}
// Retrieve provider_token & logged in user's third-party id from metadata
const { provider_token, user } = session
const userId = user.user_metadata.user_name
const allRepos = await (
await fetch(`https://api.github.com/search/repositories?q=user:${userId}`, {
method: 'GET',
headers: {
Authorization: `token ${provider_token}`,
},
})
).json()
return { props: { user, allRepos } }
}
```
</TabPanel>
<TabPanel id="ts" label="TypeScript">
```tsx
import { User, createServerSupabaseClient } from '@supabase/auth-helpers-nextjs'
import { GetServerSidePropsContext } from 'next'
export default function ProtectedPage({ user, allRepos }: { user: User; allRepos: any }) {
return (
<>
<div>Protected content for {user.email}</div>
<p>Data fetched with provider token:</p>
<pre>{JSON.stringify(allRepos, null, 2)}</pre>
<p>user:</p>
<pre>{JSON.stringify(user, null, 2)}</pre>
</>
)
}
export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
// Create authenticated Supabase Client
const supabase = createServerSupabaseClient(ctx)
// Check if we have a session
const {
data: { session },
} = await supabase.auth.getSession()
if (!session)
return {
redirect: {
destination: '/',
permanent: false,
},
}
// Retrieve provider_token & logged in user's third-party id from metadata
const { provider_token, user } = session
const userId = user.user_metadata.user_name
const allRepos = await (
await fetch(`https://api.github.com/search/repositories?q=user:${userId}`, {
method: 'GET',
headers: {
Authorization: `token ${provider_token}`,
},
})
).json()
return { props: { user, allRepos } }
}
```
</TabPanel>
</Tabs>
## Protecting API routes
Create a server supabase client to retrieve the logged in user's session:
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="js"
>
<TabPanel id="js" label="JavaScript">
```jsx title=pages/api/protected-route.js
import { createServerSupabaseClient } from '@supabase/auth-helpers-nextjs'
const ProtectedRoute = async (req, res) => {
// Create authenticated Supabase Client
const supabase = createServerSupabaseClient({ req, res })
// Check if we have a session
const {
data: { session },
} = await supabase.auth.getSession()
if (!session)
return res.status(401).json({
error: 'not_authenticated',
description: 'The user does not have an active session or is not authenticated',
})
// Run queries with RLS on the server
const { data } = await supabase.from('test').select('*')
res.json(data)
}
export default ProtectedRoute
```
</TabPanel>
<TabPanel id="ts" label="TypeScript">
```tsx title=pages/api/protected-route.ts
import { NextApiHandler } from 'next'
import { createServerSupabaseClient } from '@supabase/auth-helpers-nextjs'
const ProtectedRoute: NextApiHandler = async (req, res) => {
// Create authenticated Supabase Client
const supabase = createServerSupabaseClient({ req, res })
// Check if we have a session
const {
data: { session },
} = await supabase.auth.getSession()
if (!session)
return res.status(401).json({
error: 'not_authenticated',
description: 'The user does not have an active session or is not authenticated',
})
// Run queries with RLS on the server
const { data } = await supabase.from('test').select('*')
res.json(data)
}
export default ProtectedRoute
```
</TabPanel>
</Tabs>
## Auth with Next.js Middleware
As an alternative to protecting individual pages you can use a [Next.js Middleware](https://nextjs.org/docs/middleware) to protect the entire directory or those that match the config object. In the following example, all requests to `/middleware-protected/*` will check whether a user is signed in, if successful the request will be forwarded to the destination route, otherwise the user will be redirected:
```ts title=middleware.ts
import { createMiddlewareSupabaseClient } from '@supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export async function middleware(req: NextRequest) {
// We need to create a response and hand it to the supabase client to be able to modify the response headers.
const res = NextResponse.next()
// Create authenticated Supabase Client.
const supabase = createMiddlewareSupabaseClient({ req, res })
// Check if we have a session
const {
data: { session },
} = await supabase.auth.getSession()
// Check auth condition
if (session?.user.email?.endsWith('@gmail.com')) {
// Authentication successful, forward request to protected route.
return res
}
// Auth condition not met, redirect to home page.
const redirectUrl = req.nextUrl.clone()
redirectUrl.pathname = '/'
redirectUrl.searchParams.set(`redirectedFrom`, req.nextUrl.pathname)
return NextResponse.redirect(redirectUrl)
}
export const config = {
matcher: '/middleware-protected',
}
```
## Migration Guide
### Migrating to v0.5.X
To make these helpers more flexible as well as more maintainable and easier to upgrade for new versions of Next.js, we're stripping them down to the most useful part which is managing the cookies and giving you an authenticated supabase-js client in any environment (client, server, middleware/edge).
Therefore we're marking the `withApiAuth`, `withPageAuth`, and `withMiddlewareAuth` higher order functions as deprectaed and they will be removed in the next **minor** release (v0.6.X).
Please follow the steps below to update your API routes, pages, and middleware handlers. Thanks!
#### `withApiAuth` deprecated!
Use `createServerSupabaseClient` within your `NextApiHandler`:
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="before"
>
<TabPanel id="before" label="Before">
```tsx title=pages/api/protected-route.ts
import { withApiAuth } from '@supabase/auth-helpers-nextjs'
export default withApiAuth(async function ProtectedRoute(req, res, supabase) {
// Run queries with RLS on the server
const { data } = await supabase.from('test').select('*')
res.json(data)
})
```
</TabPanel>
<TabPanel id="after" label="After">
```tsx title=pages/api/protected-route.ts
import { NextApiHandler } from 'next'
import { createServerSupabaseClient } from '@supabase/auth-helpers-nextjs'
const ProtectedRoute: NextApiHandler = async (req, res) => {
// Create authenticated Supabase Client
const supabase = createServerSupabaseClient({ req, res })
// Check if we have a session
const {
data: { session },
} = await supabase.auth.getSession()
if (!session)
return res.status(401).json({
error: 'not_authenticated',
description: 'The user does not have an active session or is not authenticated',
})
// Run queries with RLS on the server
const { data } = await supabase.from('test').select('*')
res.json(data)
}
export default ProtectedRoute
```
</TabPanel>
</Tabs>
#### `withPageAuth` deprecated!
Use `createServerSupabaseClient` within `getServerSideProps`:
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="before"
>
<TabPanel id="before" label="Before">
```tsx title=pages/profile.tsx
import { withPageAuth, User } from '@supabase/auth-helpers-nextjs'
export default function Profile({ user }: { user: User }) {
return <pre>{JSON.stringify(user, null, 2)}</pre>
}
export const getServerSideProps = withPageAuth({ redirectTo: '/' })
```
</TabPanel>
<TabPanel id="after" label="After">
```tsx title=pages/profile.js
import { createServerSupabaseClient, User } from '@supabase/auth-helpers-nextjs'
import { GetServerSidePropsContext } from 'next'
export default function Profile({ user }: { user: User }) {
return <pre>{JSON.stringify(user, null, 2)}</pre>
}
export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
// Create authenticated Supabase Client
const supabase = createServerSupabaseClient(ctx)
// Check if we have a session
const {
data: { session },
} = await supabase.auth.getSession()
if (!session)
return {
redirect: {
destination: '/',
permanent: false,
},
}
return {
props: {
initialSession: session,
user: session.user,
},
}
}
```
</TabPanel>
</Tabs>
#### `withMiddlewareAuth` deprecated!
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="before"
>
<TabPanel id="before" label="Before">
```tsx title=middleware.ts
import { withMiddlewareAuth } from '@supabase/auth-helpers-nextjs'
export const middleware = withMiddlewareAuth({
redirectTo: '/',
authGuard: {
isPermitted: async (user) => {
return user.email?.endsWith('@gmail.com') ?? false
},
redirectTo: '/insufficient-permissions',
},
})
export const config = {
matcher: '/middleware-protected',
}
```
</TabPanel>
<TabPanel id="after" label="After">
```tsx title=middleware.ts
import { createMiddlewareSupabaseClient } from '@supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export async function middleware(req: NextRequest) {
// We need to create a response and hand it to the supabase client to be able to modify the response headers.
const res = NextResponse.next()
// Create authenticated Supabase Client.
const supabase = createMiddlewareSupabaseClient({ req, res })
// Check if we have a session
const {
data: { session },
} = await supabase.auth.getSession()
// Check auth condition
if (session?.user.email?.endsWith('@gmail.com')) {
// Authentication successful, forward request to protected route.
return res
}
// Auth condition not met, redirect to home page.
const redirectUrl = req.nextUrl.clone()
redirectUrl.pathname = '/'
redirectUrl.searchParams.set(`redirectedFrom`, req.nextUrl.pathname)
return NextResponse.redirect(redirectUrl)
}
export const config = {
matcher: '/middleware-protected',
}
```
</TabPanel>
</Tabs>
### Migrating to v0.4.X and supabase-js v2
With the update to `supabase-js` v2 the `auth` API routes are no longer required, therefore you can go ahead and delete your `auth` directory under the `/pages/api/` directory. Please refer to the [v2 migration guide](/docs/reference/javascript/upgrade-guide) for the full set of changes within supabase-js.
The `/api/auth/logout` API route has been removed, please use the `signout` method instead:
```jsx
<button
onClick={async () => {
await supabaseClient.auth.signOut()
router.push('/')
}}
>
Logout
</button>
```
The `supabaseClient` and `supabaseServerClient` have been removed in favor of the `createBrowserSupabaseClient` and `createServerSupabaseClient` methods. This allows you to provide the CLI-generated types to the client:
```tsx
// client-side
import type { Database } from 'types_db'
const [supabaseClient] = useState(() => createBrowserSupabaseClient<Database>())
// server-side API route
import type { NextApiRequest, NextApiResponse } from 'next'
import type { Database } from 'types_db'
export default async (req: NextApiRequest, res: NextApiResponse) => {
const supabaseServerClient = createServerSupabaseClient<Database>({
req,
res,
})
const {
data: { user },
} = await supabaseServerClient.auth.getUser()
res.status(200).json({ name: user?.name ?? '' })
}
```
- The `UserProvider` has been replaced by the `SessionContextProvider`. Make sure to wrap your `pages/_app.js` componenent with the `SessionContextProvider`. Then, throughout your application you can use the `useSessionContext` hook to get the `session` and the `useSupabaseClient` hook to get an authenticated `supabaseClient`.
- The `useUser` hook now returns the `user` object or `null`.
- Usage with TypeScript: You can pass types that were [generated with the Supabase CLI](/docs/reference/javascript/typescript-support#generating-types) to the Supabase Client to get enhanced type safety and auto completion:
Creating a new supabase client object:
```tsx
import { Database } from '../database.types'
const [supabaseClient] = useState(() => createBrowserSupabaseClient<Database>())
```
Retrieving a supabase client object from the SessionContext:
```tsx
import { useSupabaseClient } from '@supabase/auth-helpers-react'
import { Database } from '../database.types'
const supabaseClient = useSupabaseClient<Database>()
```
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page