Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions content/docs/auth/guides/manage-auth-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ The Neon API also provides endpoints for managing auth configuration at the bran
| `/email_and_password` | GET, PATCH | Configure email/password authentication |
| `/users` | POST, DELETE, PUT | Create, delete, and manage user roles |
| `/plugins` | GET, PATCH | View and configure [auth plugins](/docs/auth/guides/plugins) |
| `/plugins/magic-link` | PATCH | Configure the [Magic Link plugin](/docs/auth/guides/plugins/magic-link) |
| `/webhooks` | GET, PUT | Configure webhook notifications |
| `/allow_localhost` | GET, PATCH | Toggle localhost access for development |
| `/send_test_email` | POST | Send a test email to verify email configuration |
Expand Down
3 changes: 2 additions & 1 deletion content/docs/auth/guides/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@ The following Better Auth plugins are currently supported in Neon Auth:
| [Admin](/docs/auth/guides/plugins/admin) | ✅ Supported |
| [Email OTP](/docs/auth/guides/plugins/email-otp) | ✅ Supported |
| [JWT](/docs/auth/guides/plugins/jwt) | ✅ Supported |
| [Magic Link](/docs/auth/guides/plugins/magic-link) | ✅ Supported |
| [Organization](/docs/auth/guides/plugins/organization) | ⚠️ Partial (JWT token claims under development) |
| [Open API](/docs/auth/guides/plugins/openapi) | ✅ Supported |

For more runnable Neon Auth samples, see [Example applications](/docs/auth/overview#example-applications). The **Organization** plugin demo is **[neon-auth-orgs-example](https://github.com/neondatabase/neon-js/tree/main/examples/neon-auth-orgs-example)**; see the [Organization plugin](/docs/auth/guides/plugins/organization) page for context.
For more runnable Neon Auth samples, see [Example applications](/docs/auth/overview#example-applications). The **Organization** plugin demo is **[neon-auth-orgs-example](https://github.com/neondatabase/neon-js/tree/main/examples/neon-auth-orgs-example)** and the **Magic Link** demo is **[neon-auth-magic-link-example](https://github.com/neondatabase/neon-js/tree/main/examples/neon-auth-magic-link-example)**.

For the latest status (including what’s coming next), see the [Neon Auth roadmap](/docs/auth/roadmap).

Expand Down
130 changes: 130 additions & 0 deletions content/docs/auth/guides/plugins/magic-link.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
---
title: Magic Link
subtitle: Passwordless sign-in via email magic links
summary: >-
Covers the setup of Magic Link functionality in Neon Auth, enabling users to
sign in by clicking a link sent to their email, with no password required.
enableTableOfContents: true
updatedOn: '2026-04-14T00:00:00.000Z'
---

<FeatureBetaProps feature_name="Neon Auth with Better Auth" />

Neon Auth is built on [Better Auth](https://www.better-auth.com/) and supports the [Magic Link](https://www.better-auth.com/docs/plugins/magic-link) plugin through the Neon SDK. You don't need to install or configure the Better Auth Magic Link plugin directly.

Magic Link lets users sign in by clicking a link sent to their email. No password is required. The flow works like this:

1. The user enters their email address.
2. Neon Auth sends an email containing a unique, time-limited link.
3. The user clicks the link, which verifies the token, creates a session, and redirects them to your app.

## Prerequisites

- A Neon project with **Auth enabled**
- The **Magic Link plugin enabled** (see [Enable Magic Link](#enable-magic-link) below)

## Enable Magic Link

<Tabs labels={["Console", "API"]}>

<TabItem>

1. Open the [Neon Console](https://console.neon.tech).
2. Select your project and go to **Auth** > **Plugins**.
3. Toggle **Magic Link** on.
4. Configure the options:
- **Link Expiration** (5-1440 minutes, default: 5) controls how long a magic link stays valid.
- **Allow New User Registration**, when disabled, restricts magic links to existing users only (sign-in only, no sign-up).

![Neon Console Auth Plugins tab with Magic Link settings](/docs/auth/neon_auth_plugins_magic_link.png)

</TabItem>

<TabItem>

Send a `PATCH` request to configure the Magic Link plugin. All request body fields are optional; send only the fields you want to change.

```bash shouldWrap
curl -X PATCH \
"https://console.neon.tech/api/v2/projects/{project_id}/branches/{branch_id}/auth/plugins/magic-link" \
-H "Authorization: Bearer $NEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"enabled": true,
"expires_in": 5,
"disable_sign_up": false
}'
```

| Field | Type | Default | Description |
| ----------------- | ------- | ------- | ------------------------------------------------------- |
| `enabled` | boolean | `false` | Whether the Magic Link plugin is active |
| `expires_in` | integer | `5` | Minutes before the magic link expires (5-1440) |
| `disable_sign_up` | boolean | `false` | When `true`, magic links only work for existing users |

</TabItem>

</Tabs>

## Use Magic Link with SDK methods

Build a custom magic link flow using the [Neon SDK](/docs/reference/javascript-sdk). Call `signIn.magicLink()` with the user's email and a callback URL. Neon Auth sends the email and redirects the user to `callbackURL` after they click the link.

```ts shouldWrap filename="src/send-magic-link.ts"
import { authClient } from '@/lib/auth/client';

export async function sendMagicLink(email: string) {
const { error } = await authClient.signIn.magicLink({
email,
callbackURL: '/dashboard',
});

if (error) throw error;
}
```

After calling `signIn.magicLink()`, show the user a "check your email" message. For a complete working example with error handling, resend, and state management, see the [magic link example app](https://github.com/neondatabase/neon-js/tree/main/examples/neon-auth-magic-link-example) in the neon-js repository.

## Use Magic Link with UI components

If you're already using Neon Auth UI components, you can enable Magic Link with a single prop instead of building a custom form. Pass the `magicLink` prop to `NeonAuthUIProvider`:

```tsx shouldWrap filename="app/layout.tsx"
'use client';

import { authClient } from '@/lib/auth/client';
import { NeonAuthUIProvider } from '@neondatabase/neon-js/auth/react/ui';
import '@neondatabase/neon-js/ui/css';
import './globals.css';

export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) {
return (
<html lang="en" suppressHydrationWarning>
<body className={'antialiased'}>
<NeonAuthUIProvider
authClient={authClient}
magicLink // [!code ++]
>
{children}
</NeonAuthUIProvider>
</body>
</html>
);
}
```

Users can now sign in with a magic link by selecting the option on the sign-in screen and entering their email.

> If you haven't set up Neon Auth UI components yet, see the [UI components reference](/docs/auth/reference/ui-components) for setup, or the [Next.js](/docs/auth/quick-start/nextjs-api-only) or [React](/docs/auth/quick-start/react) quick start for building custom forms instead.

## Webhooks

When a magic link needs to be delivered, Neon Auth fires the `send.magic_link` webhook event with `link_type: "sign-in"`. If you subscribe to this event, Neon Auth skips its built-in email and your webhook handler is responsible for delivering the link (for example, via a custom email template or SMS).

See the [Webhooks guide](/docs/auth/guides/webhooks) for configuration details and payload format.

## Email provider configuration

For production environments, we strongly recommend using a dedicated email provider. The default shared SMTP should be used only during development. See the [Email provider configuration guide](/docs/auth/production-checklist#email-provider) for setup instructions.

<NeedHelp/>
4 changes: 2 additions & 2 deletions content/docs/auth/guides/webhooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,14 +144,14 @@ The `user` object fields are all optional and vary by event. Available fields: `

| Field | Type | Description |
| ------------ | ------------ | --------------------------------------------- |
| `link_type` | string | `"email-verification"` or `"forget-password"` |
| `link_type` | string | `"sign-in"`, `"email-verification"`, or `"forget-password"` |
| `link_url` | string | Full verification URL with embedded token |
| `token` | string | Raw token for building custom redirect URLs |
| `expires_at` | ISO datetime | Expiry time |
| `ip_address` | string | Requester's IP address |
| `user_agent` | string | Requester's user agent |

Magic links do not include a `delivery_preference` field. Your webhook handler determines the delivery channel.
The `"sign-in"` link type is sent when a user signs in via the [Magic Link plugin](/docs/auth/guides/plugins/magic-link). Magic links do not include a `delivery_preference` field. Your webhook handler determines the delivery channel.

### `user.before_create` and `user.created` event data

Expand Down
2 changes: 1 addition & 1 deletion content/docs/auth/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ See [Branching authentication](/docs/auth/branching-authentication) for details

## Example applications

Beyond the quick starts on this site, the [neondatabase/neon-js](https://github.com/neondatabase/neon-js) monorepo ships **more runnable Neon Auth and `neon-js` samples** under [`examples/`](https://github.com/neondatabase/neon-js/tree/main/examples). It includes Next.js and React apps, cross-subdomain setups, the Organization plugin, alternative UI stacks, and Data API patterns. Each folder includes its own README (many workflows use **bun** from the repository root). Browse there when you want a full project to clone next to the guides here.
Beyond the quick starts on this site, the [neondatabase/neon-js](https://github.com/neondatabase/neon-js) monorepo ships **more runnable Neon Auth and `neon-js` samples** under [`examples/`](https://github.com/neondatabase/neon-js/tree/main/examples), including the [Organization plugin](https://github.com/neondatabase/neon-js/tree/main/examples/neon-auth-orgs-example) and [Magic Link](https://github.com/neondatabase/neon-js/tree/main/examples/neon-auth-magic-link-example) demos, Next.js and React apps, cross-subdomain setups, alternative UI stacks, and Data API patterns. Each folder includes its own README (many workflows use **bun** from the repository root). Browse there when you want a full project to clone next to the guides here.

## Availability

Expand Down
30 changes: 30 additions & 0 deletions content/docs/auth/reference/ui-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ function App() {
| `avatar` | `AvatarOptions` | Avatar upload and display configuration | `avatar={{ size: 256, extension: 'webp' }}` |
| `additionalFields` | `AdditionalFields` | Custom fields for sign-up and account settings | See example below |
| `credentials.forgotPassword` | `boolean` | Enable forgot password flow | `credentials={{ forgotPassword: true }}` |
| `magicLink` | `boolean` | Enable passwordless magic link sign-in option | `magicLink` |

### Enable OAuth Providers

Expand Down Expand Up @@ -206,6 +207,35 @@ function App() {
}
```

### Next.js App Router

Create a route file at `app/auth/[path]/page.tsx` to render `AuthView` for sign-in, sign-up, and other auth views:

```tsx
import { AuthView } from '@neondatabase/neon-js/auth/react/ui';
import { authViewPaths } from '@neondatabase/neon-js/auth/react/ui/server';

export const dynamicParams = false;

export function generateStaticParams() {
return Object.values(authViewPaths).map((path) => ({ path }));
}

export default async function AuthPage({
params,
}: {
params: Promise<{ path: string }>;
}) {
const { path } = await params;

return (
<main className="flex min-h-screen items-center justify-center p-4">
<AuthView path={path} />
</main>
);
}
```

### User Menu

```tsx
Expand Down
2 changes: 1 addition & 1 deletion content/docs/auth/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ Neon Auth is built on [Better Auth](https://www.better-auth.com/). Not all Bette
| [Admin](/docs/auth/guides/plugins/admin) | ✅ Supported |
| [Organization](/docs/auth/guides/plugins/organization) | ⚠️ Partial (JWT token claims under development) |
| [JWT](/docs/auth/guides/plugins/jwt) | ✅ Supported |
| [Magic Link](/docs/auth/guides/plugins/magic-link) | ✅ Supported |
| [Open API](/docs/auth/guides/plugins/openapi) | ✅ Supported |

See [Set up OAuth](/docs/auth/guides/setup-oauth) for Neon-specific OAuth configuration (including Vercel). Email flows such as verification and password reset are covered in [Email verification](/docs/auth/guides/email-verification), [Password reset](/docs/auth/guides/password-reset), and [User management](/docs/auth/guides/user-management).
Expand All @@ -75,7 +76,6 @@ Branch-aware auth (separate auth state per Neon branch) is supported; see [Branc

| Plugin | Status |
| ----------------------------------------------------------------- | --------------- |
| [Magic link](https://www.better-auth.com/docs/plugins/magic-link) | 🔜 Coming soon |
| Phone number (bring your own SMS provider) | 🔜 Coming soon |
| MFA support | 🔜 Coming soon |
| [Admin](/docs/auth/guides/plugins/admin) plugin customization | 🔜 Coming soon |
Expand Down
2 changes: 2 additions & 0 deletions content/docs/navigation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,8 @@
slug: auth/guides/plugins/email-otp
- title: JWT
slug: auth/guides/plugins/jwt
- title: Magic Link
slug: auth/guides/plugins/magic-link
- title: OpenAPI
slug: auth/guides/plugins/openapi
- title: Organization
Expand Down
Binary file added public/docs/auth/neon_auth_plugins_magic_link.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.