Skip to content

Commit

Permalink
Merge branch 'supabase:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
nipundev authored Sep 4, 2023
2 parents cbf81f8 + f3caf7a commit ba18d7a
Show file tree
Hide file tree
Showing 32 changed files with 836 additions and 417 deletions.
5 changes: 4 additions & 1 deletion apps/docs/components/MDX/storage_management.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,16 @@ as $$
declare
status int;
content text;
avatar_name text;
begin
if coalesce(old.avatar_url, '') <> ''
and (tg_op = 'DELETE' or (old.avatar_url <> new.avatar_url)) then
-- extract avatar name
avatar_name := substring(old.avatar_url from '/([^\/]+)\?.*$');
select
into status, content
result.status, result.content
from public.delete_avatar(old.avatar_url) as result;
from public.delete_avatar(avatar_name) as result;
if status <> 200 then
raise warning 'Could not delete avatar: % %', status, content;
end if;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -495,22 +495,36 @@ export const auth = {
{ name: 'Managing User Data', url: '/guides/auth/managing-user-data' },
{ name: 'Multi-Factor Authentication', url: '/guides/auth/auth-mfa' },
{ name: 'Row Level Security', url: '/guides/auth/row-level-security' },
{ name: 'Server-side Rendering', url: '/guides/auth/server-side-rendering' },
],
},
{
name: 'Auth Helpers',
name: 'Server-side Auth',
url: undefined,
items: [
{ name: 'Overview', url: '/guides/auth/auth-helpers' },
{ name: 'Auth UI', url: '/guides/auth/auth-helpers/auth-ui' },
{ name: 'Flutter Auth UI', url: '/guides/auth/auth-helpers/flutter-auth-ui' },
{
name: 'Next.js',
url: '/guides/auth/auth-helpers/nextjs',
},
{ name: 'Remix', url: '/guides/auth/auth-helpers/remix' },
{ name: 'SvelteKit', url: '/guides/auth/auth-helpers/sveltekit' },
{ name: 'Server-side Rendering', url: '/guides/auth/server-side-rendering' },
{
name: 'Email Auth with PKCE flow for SSR',
url: '/guides/auth/server-side/email-based-auth-with-pkce-flow-for-ssr',
},
{
name: 'OAuth with PKCE flow for SSR',
url: '/guides/auth/server-side/oauth-with-pkce-flow-for-ssr',
},
],
},
{
name: 'Auth UI',
url: undefined,
items: [
{ name: 'Auth UI', url: '/guides/auth/auth-helpers/auth-ui' },
{ name: 'Flutter Auth UI', url: '/guides/auth/auth-helpers/flutter-auth-ui' },
],
},
{
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/layouts/SiteLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ const Container = memo(function Container(props) {
className={[
// 'overflow-x-auto',
'w-full h-screen transition-all ease-out',
'absolute lg:relative',
// 'absolute lg:relative',
mobileMenuOpen
? '!w-auto ml-[75%] sm:ml-[50%] md:ml-[33%] overflow-hidden'
: 'overflow-auto',
Expand Down
22 changes: 3 additions & 19 deletions apps/docs/pages/guides/auth/auth-helpers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,15 @@ import Layout from '~/layouts/DefaultGuideLayout'

export const meta = {
id: 'index',
title: 'Auth Helpers Overview',
description: 'A collection of framework-specific Auth utilities for working with Supabase.',
title: 'Server-Side Auth Overview',
description: 'Server-Side Auth guides and utilities for working with Supabase.',
sidebar_label: 'Overview',
}

A collection of framework-specific Auth utilities for working with Supabase.
Working with server-side frameworks is slightly different to client-side frameworks. In this section we cover the various ways of handling server-side authentication and demonstrate how to use the Supabase helper-libraries to make the process more seamless.

<div className="container" style={{ padding: 0 }}>
<div className="grid md:grid-cols-12 gap-4">
{/* Auth UI */}
<div className="col-span-6">
<ButtonCard
to={'/guides/auth/auth-helpers/auth-ui'}
title={'Auth UI'}
description={'A pre-built React component for authenticating users.'}
/>
</div>
{/* Flutter Auth UI */}
<div className="col-span-6">
<ButtonCard
to={'/guides/auth/auth-helpers/flutter-auth-ui'}
title={'Flutter Auth UI'}
description={'Pre-built Flutter widgets for authenticating users.'}
/>
</div>
{/* Next.js */}
<div className="col-span-6">
<ButtonCard
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
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.',
}

### Install Supabase Auth Helpers

The Auth Helpers will assist you in implementing user authentication within your server-side rendering (SSR) framework.

<Tabs scrollable size="small" type="underlined" defaultActiveId="nextjs">

<TabPanel id="nextjs" label="NextJS">

```bash
npm install @supabase/auth-helpers-nextjs @supabase/supabase-js
```

</TabPanel>

<TabPanel id="sveltekit" label="SvelteKit">

```bash
npm install @supabase/auth-helpers-sveltekit @supabase/supabase-js
```

</TabPanel>

</Tabs>

### Set environment variables

Create an `.env.local` file in your project root directory. You can get your `SITE_URL` and `ANON_KEY` from inside of the [dashboard](https://supabase.com/dashboard/project/_/settings/api).

<Tabs scrollable size="small" type="underlined" defaultActiveId="nextjs">

<TabPanel id="nextjs" label="NextJS">

```bash .env.local
NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
```

</TabPanel>
<TabPanel id="sveltekit" label="SvelteKit">

```bash .env.local
PUBLIC_SUPABASE_URL=your_supabase_project_url
PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
```

</TabPanel>
</Tabs>

### Setting up the Auth Helpers

When using the Supabase client on the server, you must perform extra steps to ensure the user's auth session remains active. Since the user's session is tracked in a cookie, we need to read this cookie and update it if necessary.

<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="nextjs"
>
<TabPanel id="nextjs" label="NextJS">
Next.js Server Components allow you to read a cookie but not write back to it. Middleware on the other hand allow you to both read and write to cookies.

Next.js [Middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) runs immediately before each route is rendered. We'll use Middleware to refresh the user's session before loading Server Component routes.

Create a new `middleware.js` file in the root of your project and populate with the following:

```js middleware.js
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'

export async function middleware(req) {
const res = NextResponse.next()
const supabase = createMiddlewareClient({ req, res })
await supabase.auth.getSession()
return res
}
```

</TabPanel>
<TabPanel id="sveltekit" label="SvelteKit">
Create a new `hooks.server.js` file in the root of your project and populate with the following:

```ts src/hooks.server.js
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public'
import { createSupabaseServerClient } from '@supabase/auth-helpers-sveltekit'
import type { Handle } from '@sveltejs/kit'

export const handle: Handle = async ({ event, resolve }) => {
event.locals.supabase = createSupabaseServerClient({
supabaseUrl: PUBLIC_SUPABASE_URL,
supabaseKey: PUBLIC_SUPABASE_ANON_KEY,
event,
})

event.locals.getSession = async () => {
const {
data: { session },
} = await event.locals.supabase.auth.getSession()
return session
}

return resolve(event, {
filterSerializedResponseHeaders(name) {
return name === 'content-range'
},
})
}
```

</TabPanel>
</Tabs>

### Create API endpoint for handling `token_hash`

In order to use the updated email links we will need to setup a endpoint for verifying the `token_hash` along with the `type` to exchange `token_hash` for the user's `session`, which is set as a cookie for future requests made to Supabase.

<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="nextjs"
>
<TabPanel id="nextjs" label="NextJS">
Create a new file at `app/auth/confirm/route.js` and populate with the following:

```js app/auth/confirm/route.js
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
import { cookies } from 'next/headers'
import { NextResponse } from 'next/server'

export async function GET(req) {
const { searchParams } = new URL(req.url)
const token_hash = searchParams.get('token_hash')
const type = searchParams.get('type')
const next = searchParams.get('next') ?? '/'

if (token_hash && type) {
const supabase = createRouteHandlerClient({ cookies })
const { error } = await supabase.auth.verifyOtp({ type, token_hash })
if (!error) {
return NextResponse.redirect(new URL(`/${next.slice(1)}`, req.url))
}
}

// return the user to an error page with some instructions
return NextResponse.redirect(new URL('/auth/auth-code-error', req.url))
}
```
</TabPanel>
<TabPanel id="sveltekit" label="SvelteKit">
Create a new file at `src/routes/auth/confirm/+server.js` and populate with the following:
```js src/routes/auth/confirm/+server.js
import { redirect } from '@sveltejs/kit';

export const GET = async (event) => {
const {
url,
locals: { supabase }
} = event;
const token_hash = url.searchParams.get('token') as string;
const type = url.searchParams.get('type') as string;
const next = url.searchParams.get('next') ?? '/';

if (token_hash && type) {
const { error } = await supabase.auth.verifyOtp({ token_hash, type });
if (!error) {
throw redirect(303, `/${next.slice(1)}`);
}
}

// return the user to an error page with some instructions
throw redirect(303, '/auth/auth-code-error');
};
```
</TabPanel>
</Tabs>
### Update email templates with URL for API endpoint
Let's update the URL in our email templates to point to our new confirmation endpoint for the user to get confirmed.
**Confirm signup template**
```html
<h2>Confirm your signup</h2>

<p>Follow this link to confirm your user:</p>
<p>
<a href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=email"
>Confirm your email</a
>
</p>
```
**Invite user template**
```html
<h2>You have been invited</h2>

<p>
You have been invited to create a user on {{ .SiteURL }}. Follow this link to accept the invite:
</p>

<p>
<a
href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=invite&next=/path-to-your-update-password-page"
>Accept the invite</a
>
</p>
```
**Magic Link template**
```html
<h2>Magic Link</h2>

<p>Follow this link to login:</p>
<p><a href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=email">Log In</a></p>
```
**Change Email Address template**
```html
<h2>Confirm Change of Email</h2>

<p>Follow this link to confirm the update of your email from {{ .Email }} to {{ .NewEmail }}:</p>
<p><a href="{{ .ConfirmationURL }}">Change Email</a></p>
```
**Reset Password template**
```html
<h2>Reset Password</h2>

<p>Follow this link to reset the password for your user:</p>
<p>
<a
href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=recovery&next=/path-to-your-update-password-page"
>Reset Password</a
>
</p>
```
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
Loading

0 comments on commit ba18d7a

Please sign in to comment.