mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
Compare commits
113 Commits
feat/vipps
...
@next-auth
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25388de027 | ||
|
|
ad77e1c2b7 | ||
|
|
cd654c3001 | ||
|
|
6f9ca4143d | ||
|
|
e97b27414a | ||
|
|
9018939ee7 | ||
|
|
c2fc41b44d | ||
|
|
01d7eb4feb | ||
|
|
2388c20cc6 | ||
|
|
9a1bef9e72 | ||
|
|
35a72d2273 | ||
|
|
5f1b75a7a2 | ||
|
|
fa58065951 | ||
|
|
b31f2af66c | ||
|
|
71bb6f2590 | ||
|
|
6c07331cc5 | ||
|
|
c8ef94b2be | ||
|
|
75a59fbd92 | ||
|
|
3dd47b0735 | ||
|
|
4dc1d421f8 | ||
|
|
99ca67f1cf | ||
|
|
a087df8494 | ||
|
|
1aa4994de6 | ||
|
|
88023f69b9 | ||
|
|
b02057a72d | ||
|
|
400da8c766 | ||
|
|
b48104801b | ||
|
|
ccbbc800d2 | ||
|
|
d7888263ca | ||
|
|
47d3151410 | ||
|
|
7d264860ab | ||
|
|
6184b936f5 | ||
|
|
1954258a0a | ||
|
|
c580f0db22 | ||
|
|
d1cf701ed9 | ||
|
|
69398e2d3a | ||
|
|
856b5c50fc | ||
|
|
2830b7de5b | ||
|
|
40a0faa586 | ||
|
|
a6b4d958ac | ||
|
|
cc13df9d51 | ||
|
|
06b8d4772c | ||
|
|
d644d1fcbf | ||
|
|
380f2de961 | ||
|
|
dc5f3e1873 | ||
|
|
93f3fcd1b7 | ||
|
|
1d9b9ba47c | ||
|
|
8f6108f230 | ||
|
|
15943d6696 | ||
|
|
81589bf738 | ||
|
|
09402bf2fc | ||
|
|
e39a968a7b | ||
|
|
b03378be7f | ||
|
|
af415e9438 | ||
|
|
90eeeeab2f | ||
|
|
f0b475fc72 | ||
|
|
f4f8c4a0b3 | ||
|
|
6f2cb460c9 | ||
|
|
46f285f6f0 | ||
|
|
6bdb8af78d | ||
|
|
04e0637fd8 | ||
|
|
b5712448a1 | ||
|
|
605d15c3cc | ||
|
|
d1479125cb | ||
|
|
2e09bc0d19 | ||
|
|
843fc6ff8f | ||
|
|
6695ff8503 | ||
|
|
80c1f375b8 | ||
|
|
5a13288d47 | ||
|
|
26201e6271 | ||
|
|
d0d7b90ba1 | ||
|
|
874624dfbe | ||
|
|
4b5cd08800 | ||
|
|
1c104afef9 | ||
|
|
ff5b8ba8e2 | ||
|
|
42d5899efd | ||
|
|
b278975c3f | ||
|
|
997e595b5b | ||
|
|
527c25b128 | ||
|
|
0e2bbda537 | ||
|
|
7f3b35593f | ||
|
|
bce6b00c43 | ||
|
|
1bbd5d51d1 | ||
|
|
b24b02fe71 | ||
|
|
2c5c4d18c4 | ||
|
|
e3f9b398f0 | ||
|
|
ab13930020 | ||
|
|
f6bb16b264 | ||
|
|
a220245d03 | ||
|
|
7462e797de | ||
|
|
36286b1fae | ||
|
|
2e8e90a9be | ||
|
|
d06a552bf6 | ||
|
|
5cb8dd5f37 | ||
|
|
7c1a3b547e | ||
|
|
2534ae8801 | ||
|
|
14a120277b | ||
|
|
c49f484743 | ||
|
|
676b39d5b1 | ||
|
|
e27dbcab2f | ||
|
|
63805c7d75 | ||
|
|
5d1c35e8aa | ||
|
|
a77f557cbf | ||
|
|
657492d921 | ||
|
|
d1d63bddba | ||
|
|
99ac4899b5 | ||
|
|
2130765a57 | ||
|
|
4079274aaf | ||
|
|
3d7985fd6d | ||
|
|
331e0ce51e | ||
|
|
8af666a4cc | ||
|
|
fed0a67917 | ||
|
|
0332d86942 |
@@ -1,70 +0,0 @@
|
||||
.eslintrc.js
|
||||
.cache-loader
|
||||
.DS_Store
|
||||
.pnpm-debug.log
|
||||
.turbo
|
||||
.vscode/generated*
|
||||
/_work
|
||||
/actions-runner
|
||||
node_modules
|
||||
patches
|
||||
pnpm-lock.yaml
|
||||
.github/actions/issue-validator/index.mjs
|
||||
*.cjs
|
||||
*.js
|
||||
*.d.ts
|
||||
*.d.ts.map
|
||||
|
||||
.svelte-kit
|
||||
.next
|
||||
.nuxt
|
||||
|
||||
# --------------- Docs ---------------
|
||||
|
||||
.docusaurus
|
||||
build
|
||||
docs/docs/reference/core
|
||||
docs/docs/reference/sveltekit
|
||||
static
|
||||
|
||||
# --------------- Packages ---------------
|
||||
|
||||
coverage
|
||||
dist
|
||||
|
||||
# @auth/core
|
||||
packages/core/src/providers/oauth-types.ts
|
||||
packages/core/src/lib/pages/styles.ts
|
||||
|
||||
# @auth/sveltekit
|
||||
packages/frameworks-sveltekit/package
|
||||
packages/frameworks-sveltekit/vite.config.{js,ts}.timestamp-*
|
||||
|
||||
# next-auth
|
||||
packages/next-auth/src/providers/oauth-types.ts
|
||||
packages/next-auth/css/index.css
|
||||
|
||||
|
||||
# Adapters
|
||||
.branches
|
||||
db.sqlite
|
||||
dev.db
|
||||
dynamodblocal-bin
|
||||
firebase-debug.log
|
||||
firestore-debug.log
|
||||
migrations
|
||||
test.schema.gql
|
||||
|
||||
# --------------- Apps ---------------
|
||||
|
||||
|
||||
# Examples should have their own Prettier config since they are templates too
|
||||
apps/example-sveltekit
|
||||
|
||||
# Development app
|
||||
apps
|
||||
|
||||
|
||||
# --------------- Tests ---------------
|
||||
# TODO: these should be linted
|
||||
packages/**/*test*
|
||||
75
.eslintrc.js
75
.eslintrc.js
@@ -1,75 +0,0 @@
|
||||
// @ts-check
|
||||
|
||||
/** @type {import("eslint").ESLint.ConfigData} */
|
||||
module.exports = {
|
||||
env: { browser: true, es2022: true, node: true },
|
||||
extends: ["eslint:recommended", "prettier"],
|
||||
overrides: [
|
||||
{
|
||||
files: ["*.ts", "*.tsx"],
|
||||
parser: "@typescript-eslint/parser",
|
||||
parserOptions: {
|
||||
project: ["./packages/**/tsconfig.json", "./apps/**/tsconfig.json"],
|
||||
},
|
||||
settings: { react: { version: "18" } },
|
||||
extends: [
|
||||
"plugin:react/recommended",
|
||||
"plugin:react/jsx-runtime",
|
||||
"standard-with-typescript",
|
||||
"prettier",
|
||||
],
|
||||
rules: {
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/method-signature-style": "off",
|
||||
"@typescript-eslint/naming-convention": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/restrict-template-expressions": "off",
|
||||
"@typescript-eslint/strict-boolean-expressions": "off",
|
||||
"react/prop-types": "off",
|
||||
"react/no-unescaped-entities": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["*.test.ts", "*.test.js"],
|
||||
extends: ["plugin:jest/recommended"],
|
||||
env: { jest: true },
|
||||
},
|
||||
{
|
||||
files: ["docs/**"],
|
||||
plugins: ["@docusaurus"],
|
||||
extends: ["plugin:@docusaurus/recommended"],
|
||||
},
|
||||
{
|
||||
// TODO: Expand to all packages
|
||||
files: ["packages/{core,sveltekit}/*.ts"],
|
||||
plugins: ["jsdoc"],
|
||||
extends: ["plugin:jsdoc/recommended"],
|
||||
rules: {
|
||||
"jsdoc/require-param": "off",
|
||||
"jsdoc/require-returns": "off",
|
||||
"jsdoc/require-jsdoc": [
|
||||
"warn",
|
||||
{ publicOnly: true, enableFixer: false },
|
||||
],
|
||||
"jsdoc/no-multi-asterisks": ["warn", { allowWhitespace: true }],
|
||||
"jsdoc/tag-lines": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["packages/frameworks-sveltekit"],
|
||||
plugins: ["svelte3"],
|
||||
overrides: [{ files: ["*.svelte"], processor: "svelte3/svelte3" }],
|
||||
settings: {
|
||||
"svelte3/typescript": () => require("typescript"),
|
||||
},
|
||||
parserOptions: { sourceType: "module", ecmaVersion: 2020 },
|
||||
env: { browser: true, es2017: true, node: true },
|
||||
},
|
||||
],
|
||||
parserOptions: {
|
||||
sourceType: "module",
|
||||
ecmaVersion: "latest",
|
||||
ecmaFeatures: { jsx: true },
|
||||
},
|
||||
root: true,
|
||||
}
|
||||
1
.github/ISSUE_TEMPLATE/2_bug_provider.yml
vendored
1
.github/ISSUE_TEMPLATE/2_bug_provider.yml
vendored
@@ -32,6 +32,7 @@ body:
|
||||
- "Azure Active Directory"
|
||||
- "Azure Active Directory B2C"
|
||||
- "Battlenet"
|
||||
- "Beyond Identity"
|
||||
- "Box"
|
||||
- "Bungie"
|
||||
- "Cognito"
|
||||
|
||||
4
.github/actions/issue-validator/repro.md
vendored
4
.github/actions/issue-validator/repro.md
vendored
@@ -14,9 +14,9 @@ Ensure the link is pointing to a codebase that is accessible (e.g. not a private
|
||||
|
||||
### **What happens if I don't provide a sufficient minimal reproduction?**
|
||||
|
||||
Issues with the `incomplete` label that receives no meaningful activity (e.g. new comments with a reproduction link) are automatically closed and locked after 30 days.
|
||||
Issues with the `incomplete` label that receives no meaningful activity (e.g. new comments with a reproduction link) are closed after 7 days.
|
||||
|
||||
If your issue has _not_ been resolved in that time and it has been closed/locked, please open a new issue with the required reproduction.
|
||||
If your issue has _not_ been resolved in that time and it has been closed/locked, please open a new issue with the required reproduction. (It's less likely that we check back on already closed issues.)
|
||||
|
||||
### **I did not open this issue, but it is relevant to me, what can I do to help?**
|
||||
|
||||
|
||||
2
.github/workflows/issue-validator.yml
vendored
2
.github/workflows/issue-validator.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- name: "Run issue validator"
|
||||
- name: Run issue validator
|
||||
run: node /home/runner/work/next-auth/next-auth/.github/actions/issue-validator/index.mjs
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
11
.github/workflows/release.yml
vendored
11
.github/workflows/release.yml
vendored
@@ -3,10 +3,10 @@ name: Release
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
- "beta"
|
||||
- "next"
|
||||
- "3.x"
|
||||
- main
|
||||
- beta
|
||||
- next
|
||||
- 3.x
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
@@ -24,7 +24,6 @@ jobs:
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: "pnpm"
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
- name: Run tests
|
||||
@@ -74,7 +73,6 @@ jobs:
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: "pnpm"
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
- name: Publish to npm and GitHub
|
||||
@@ -99,7 +97,6 @@ jobs:
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: "pnpm"
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
- name: Determine version
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,6 +1,5 @@
|
||||
# Misc
|
||||
.DS_Store
|
||||
.npmrc
|
||||
.eslintcache
|
||||
.env
|
||||
.env.local
|
||||
@@ -14,7 +13,7 @@ yarn-error.log*
|
||||
firebase-debug.log
|
||||
ui-debug.log
|
||||
.pnpm-debug.log
|
||||
|
||||
.husky
|
||||
|
||||
# Dependencies
|
||||
node_modules
|
||||
@@ -44,6 +43,7 @@ packages/*/*.d.ts.map
|
||||
apps/dev/src/css
|
||||
apps/dev/prisma/migrations
|
||||
apps/dev/typeorm
|
||||
apps/dev/nextjs-2
|
||||
|
||||
# VS
|
||||
/.vs/slnx.sqlite-journal
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
// @ts-check
|
||||
|
||||
/** @type {import("prettier").Config} */
|
||||
module.exports = {
|
||||
semi: false,
|
||||
singleQuote: false,
|
||||
overrides: [
|
||||
{
|
||||
files: [
|
||||
"apps/dev/nextjs/pages/api/auth/[...nextauth].ts",
|
||||
"docs/{sidebars,docusaurus.config}.js",
|
||||
],
|
||||
options: { printWidth: 150 },
|
||||
},
|
||||
{
|
||||
files: ["**/*package.json"],
|
||||
options: {
|
||||
trailingComma: "none",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
58
apps/dev/nextjs-v4/.env.local.example
Normal file
58
apps/dev/nextjs-v4/.env.local.example
Normal file
@@ -0,0 +1,58 @@
|
||||
# Rename file to .env.local (or .env) and populate values
|
||||
# to be able to run the dev app
|
||||
|
||||
NEXTAUTH_URL=http://localhost:3000
|
||||
|
||||
# You can use `openssl rand -hex 32` or
|
||||
# https://generate-secret.vercel.app/32 to generate a secret.
|
||||
# Note: Changing a secret may invalidate existing sessions
|
||||
# and/or verification tokens.
|
||||
NEXTAUTH_SECRET=secret
|
||||
|
||||
AUTH0_ID=
|
||||
AUTH0_SECRET=
|
||||
AUTH0_ISSUER=
|
||||
|
||||
KEYCLOAK_ID=
|
||||
KEYCLOAK_SECRET=
|
||||
KEYCLOAK_ISSUER=
|
||||
|
||||
IDS4_ID=
|
||||
IDS4_SECRET=
|
||||
IDS4_ISSUER=
|
||||
|
||||
GITHUB_ID=
|
||||
GITHUB_SECRET=
|
||||
|
||||
TWITCH_ID=
|
||||
TWITCH_SECRET=
|
||||
|
||||
TWITTER_ID=
|
||||
TWITTER_SECRET=
|
||||
|
||||
LINE_ID=
|
||||
LINE_SECRET=
|
||||
|
||||
TRAKT_ID=
|
||||
TRAKT_SECRET=
|
||||
|
||||
# Example configuration for a Gmail account (will need SMTP enabled)
|
||||
EMAIL_SERVER=smtps://user@gmail.com:password@smtp.gmail.com:465
|
||||
EMAIL_FROM=user@gmail.com
|
||||
|
||||
# Note: If using with Prisma adapter, you need to use a `.env`
|
||||
# file rather than a `.env.local` file to configure env vars.
|
||||
# Postgres: DATABASE_URL=postgres://nextauth:password@127.0.0.1:5432/nextauth?synchronize=true
|
||||
# MySQL: DATABASE_URL=mysql://nextauth:password@127.0.0.1:3306/nextauth?synchronize=true
|
||||
# MongoDB: DATABASE_URL=mongodb://nextauth:password@127.0.0.1:27017/nextauth?synchronize=true
|
||||
DATABASE_URL=
|
||||
|
||||
WIKIMEDIA_ID=
|
||||
WIKIMEDIA_SECRET=
|
||||
|
||||
# Supabase Example Configuration
|
||||
# Supabase Example Configuration
|
||||
# NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321
|
||||
# SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSJ9.vI9obAHOGyVVKa3pD--kJlyxp-Z2zV9UUMAhKpNLAcU
|
||||
# SUPABASE_JWT_SECRET=super-secret-jwt-token-with-at-least-32-characters-long
|
||||
# NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24ifQ.625_WdcF3KHqz5amU0x2X5WWHP-OEs_4qj0ssLNHzTs
|
||||
4
apps/dev/nextjs-v4/.vscode/settings.json
vendored
Normal file
4
apps/dev/nextjs-v4/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"typescript.tsdk": "../../node_modules/.pnpm/typescript@4.8.4/node_modules/typescript/lib",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true
|
||||
}
|
||||
6
apps/dev/nextjs-v4/README.md
Normal file
6
apps/dev/nextjs-v4/README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# NextAuth.js Development App
|
||||
|
||||
This folder contains a Next.js app using NextAuth.js for local development. See the following section on how to start:
|
||||
|
||||
[Setting up local environment
|
||||
](https://github.com/nextauthjs/next-auth/blob/main/CONTRIBUTING.md#setting-up-local-environment)
|
||||
14
apps/dev/nextjs-v4/app/api/auth/[...nextauth]/route.ts
Normal file
14
apps/dev/nextjs-v4/app/api/auth/[...nextauth]/route.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import NextAuth, { type NextAuthOptions } from "next-auth"
|
||||
import GitHub from "next-auth/providers/github"
|
||||
|
||||
export const authOptions: NextAuthOptions = {
|
||||
providers: [
|
||||
GitHub({
|
||||
clientId: process.env.GITHUB_ID,
|
||||
clientSecret: process.env.GITHUB_SECRET,
|
||||
}),
|
||||
],
|
||||
}
|
||||
|
||||
const handler = NextAuth(authOptions)
|
||||
export { handler as GET, handler as POST }
|
||||
12
apps/dev/nextjs-v4/app/layout.tsx
Normal file
12
apps/dev/nextjs-v4/app/layout.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<html>
|
||||
<head></head>
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
6
apps/dev/nextjs-v4/app/server-component/page.tsx
Normal file
6
apps/dev/nextjs-v4/app/server-component/page.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
import { getServerSession } from "next-auth/next"
|
||||
|
||||
export default async function Page() {
|
||||
const session = await getServerSession()
|
||||
return <pre>{JSON.stringify(session, null, 2)}</pre>
|
||||
}
|
||||
20
apps/dev/nextjs-v4/components/access-denied.js
Normal file
20
apps/dev/nextjs-v4/components/access-denied.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import { signIn } from "next-auth/react"
|
||||
|
||||
export default function AccessDenied() {
|
||||
return (
|
||||
<>
|
||||
<h1>Access Denied</h1>
|
||||
<p>
|
||||
<a
|
||||
href="/api/auth/signin"
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
signIn()
|
||||
}}
|
||||
>
|
||||
You must be signed in to view this page
|
||||
</a>
|
||||
</p>
|
||||
</>
|
||||
)
|
||||
}
|
||||
28
apps/dev/nextjs-v4/components/footer.js
Normal file
28
apps/dev/nextjs-v4/components/footer.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import Link from "next/link"
|
||||
import styles from "./footer.module.css"
|
||||
import packageJSON from "package.json"
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
<footer className={styles.footer}>
|
||||
<hr />
|
||||
<ul className={styles.navItems}>
|
||||
<li className={styles.navItem}>
|
||||
<a href="https://next-auth.js.org">Documentation</a>
|
||||
</li>
|
||||
<li className={styles.navItem}>
|
||||
<a href="https://www.npmjs.com/package/next-auth">NPM</a>
|
||||
</li>
|
||||
<li className={styles.navItem}>
|
||||
<a href="https://github.com/nextauthjs/next-auth-example">GitHub</a>
|
||||
</li>
|
||||
<li className={styles.navItem}>
|
||||
<Link href="/policy">Policy</Link>
|
||||
</li>
|
||||
<li className={styles.navItem}>
|
||||
<em>{packageJSON.version}</em>
|
||||
</li>
|
||||
</ul>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
14
apps/dev/nextjs-v4/components/footer.module.css
Normal file
14
apps/dev/nextjs-v4/components/footer.module.css
Normal file
@@ -0,0 +1,14 @@
|
||||
.footer {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.navItems {
|
||||
margin-bottom: 1rem;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.navItem {
|
||||
display: inline-block;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
103
apps/dev/nextjs-v4/components/header.js
Normal file
103
apps/dev/nextjs-v4/components/header.js
Normal file
@@ -0,0 +1,103 @@
|
||||
import Link from "next/link"
|
||||
import { signIn, signOut, useSession } from "next-auth/react"
|
||||
import styles from "./header.module.css"
|
||||
|
||||
// The approach used in this component shows how to built a sign in and sign out
|
||||
// component that works on pages which support both client and server side
|
||||
// rendering, and avoids any flash incorrect content on initial page load.
|
||||
export default function Header() {
|
||||
const { data: session, status } = useSession()
|
||||
|
||||
return (
|
||||
<header>
|
||||
<noscript>
|
||||
<style>{".nojs-show { opacity: 1; top: 0; }"}</style>
|
||||
</noscript>
|
||||
<div className={styles.signedInStatus}>
|
||||
<p
|
||||
className={`nojs-show ${
|
||||
!session && status === "loading" ? styles.loading : styles.loaded
|
||||
}`}
|
||||
>
|
||||
{!session && (
|
||||
<>
|
||||
<span className={styles.notSignedInText}>
|
||||
You are not signed in
|
||||
</span>
|
||||
<a
|
||||
href="/api/auth/signin"
|
||||
className={styles.buttonPrimary}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
signIn()
|
||||
}}
|
||||
>
|
||||
Sign in
|
||||
</a>
|
||||
</>
|
||||
)}
|
||||
{session && (
|
||||
<>
|
||||
{session.user.image && (
|
||||
<img src={session.user.image} className={styles.avatar} />
|
||||
)}
|
||||
<span className={styles.signedInText}>
|
||||
<small>Signed in as</small>
|
||||
<br />
|
||||
<strong>{session.user.email} </strong>
|
||||
{session.user.name ? `(${session.user.name})` : null}
|
||||
</span>
|
||||
<a
|
||||
href="/api/auth/signout"
|
||||
className={styles.button}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
signOut()
|
||||
}}
|
||||
>
|
||||
Sign out
|
||||
</a>
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<nav>
|
||||
<ul className={styles.navItems}>
|
||||
<li className={styles.navItem}>
|
||||
<Link href="/">Home</Link>
|
||||
</li>
|
||||
<li className={styles.navItem}>
|
||||
<Link href="/client">Client</Link>
|
||||
</li>
|
||||
<li className={styles.navItem}>
|
||||
<Link href="/server">Server</Link>
|
||||
</li>
|
||||
<li className={styles.navItem}>
|
||||
<Link href="/protected">Protected</Link>
|
||||
</li>
|
||||
<li className={styles.navItem}>
|
||||
<Link href="/protected-ssr">Protected(SSR)</Link>
|
||||
</li>
|
||||
<li className={styles.navItem}>
|
||||
<Link href="/api-example">API</Link>
|
||||
</li>
|
||||
<li className={styles.navItem}>
|
||||
<Link href="/credentials">Credentials</Link>
|
||||
</li>
|
||||
<li className={styles.navItem}>
|
||||
<Link href="/email">Email</Link>
|
||||
</li>
|
||||
<li className={styles.navItem}>
|
||||
<Link href="/middleware-protected">Middleware protected</Link>
|
||||
</li>
|
||||
<li className={styles.navItem}>
|
||||
<Link href="/supabase-client-rls">Supabase RLS</Link>
|
||||
</li>
|
||||
<li className={styles.navItem}>
|
||||
<Link href="/supabase-ssr">Supabase RLS(SSR)</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
92
apps/dev/nextjs-v4/components/header.module.css
Normal file
92
apps/dev/nextjs-v4/components/header.module.css
Normal file
@@ -0,0 +1,92 @@
|
||||
/* Set min-height to avoid page reflow while session loading */
|
||||
.signedInStatus {
|
||||
display: block;
|
||||
min-height: 4rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.loading,
|
||||
.loaded {
|
||||
position: relative;
|
||||
top: 0;
|
||||
opacity: 1;
|
||||
overflow: hidden;
|
||||
border-radius: 0 0 .6rem .6rem;
|
||||
padding: .6rem 1rem;
|
||||
margin: 0;
|
||||
background-color: rgba(0,0,0,.05);
|
||||
transition: all 0.2s ease-in;
|
||||
}
|
||||
|
||||
.loading {
|
||||
top: -2rem;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.signedInText,
|
||||
.notSignedInText {
|
||||
position: absolute;
|
||||
padding-top: .8rem;
|
||||
left: 1rem;
|
||||
right: 6.5rem;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
display: inherit;
|
||||
z-index: 1;
|
||||
line-height: 1.3rem;
|
||||
}
|
||||
|
||||
.signedInText {
|
||||
padding-top: 0rem;
|
||||
left: 4.6rem;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
border-radius: 2rem;
|
||||
float: left;
|
||||
height: 2.8rem;
|
||||
width: 2.8rem;
|
||||
background-color: white;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.button,
|
||||
.buttonPrimary {
|
||||
float: right;
|
||||
margin-right: -.4rem;
|
||||
font-weight: 500;
|
||||
border-radius: .3rem;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
line-height: 1.4rem;
|
||||
padding: .7rem .8rem;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
background-color: transparent;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.buttonPrimary {
|
||||
background-color: #346df1;
|
||||
border-color: #346df1;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
padding: .7rem 1.4rem;
|
||||
}
|
||||
|
||||
.buttonPrimary:hover {
|
||||
box-shadow: inset 0 0 5rem rgba(0,0,0,0.2)
|
||||
}
|
||||
|
||||
.navItems {
|
||||
margin-bottom: 2rem;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.navItem {
|
||||
display: inline-block;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
14
apps/dev/nextjs-v4/components/layout.js
Normal file
14
apps/dev/nextjs-v4/components/layout.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import Header from 'components/header'
|
||||
import Footer from 'components/footer'
|
||||
|
||||
export default function Layout ({ children }) {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<main>
|
||||
{children}
|
||||
</main>
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
45
apps/dev/nextjs-v4/middleware.ts
Normal file
45
apps/dev/nextjs-v4/middleware.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
export { default } from "next-auth/middleware"
|
||||
|
||||
export const config = { matcher: ["/middleware-protected"] }
|
||||
|
||||
// Other ways to use this middleware
|
||||
|
||||
// import withAuth from "next-auth/middleware"
|
||||
// import { withAuth } from "next-auth/middleware"
|
||||
|
||||
// export function middleware(req, ev) {
|
||||
// return withAuth(req)
|
||||
// }
|
||||
|
||||
// export function middleware(req, ev) {
|
||||
// return withAuth(req, ev)
|
||||
// }
|
||||
|
||||
// export function middleware(req, ev) {
|
||||
// return withAuth(req, {
|
||||
// callbacks: {
|
||||
// authorized: ({ token }) => !!token,
|
||||
// },
|
||||
// })
|
||||
// }
|
||||
|
||||
// export default withAuth(function middleware(req, ev) {
|
||||
// console.log(req.nextauth.token)
|
||||
// })
|
||||
|
||||
// export default withAuth(
|
||||
// function middleware(req, ev) {
|
||||
// console.log(req, ev)
|
||||
// },
|
||||
// {
|
||||
// callbacks: {
|
||||
// authorized: ({ token }) => token.name === "Balázs Orbán",
|
||||
// },
|
||||
// }
|
||||
// )
|
||||
|
||||
// export default withAuth({
|
||||
// callbacks: {
|
||||
// authorized: ({ token }) => !!token,
|
||||
// },
|
||||
// })
|
||||
6
apps/dev/nextjs-v4/next-env.d.ts
vendored
Normal file
6
apps/dev/nextjs-v4/next-env.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
/// <reference types="next/navigation-types/compat/navigation" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
9
apps/dev/nextjs-v4/next.config.js
Normal file
9
apps/dev/nextjs-v4/next.config.js
Normal file
@@ -0,0 +1,9 @@
|
||||
/** @type {import("next").NextConfig} */
|
||||
module.exports = {
|
||||
webpack(config) {
|
||||
config.experiments = { ...config.experiments, topLevelAwait: true }
|
||||
return config
|
||||
},
|
||||
experimental: { appDir: true },
|
||||
typescript: { ignoreBuildErrors: true },
|
||||
}
|
||||
40
apps/dev/nextjs-v4/package.json
Normal file
40
apps/dev/nextjs-v4/package.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "next-auth-app-v4",
|
||||
"version": "1.0.0",
|
||||
"description": "NextAuth.js Developer app",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"clean": "rm -rf .next",
|
||||
"dev": "next dev",
|
||||
"lint": "next lint",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"email": "fake-smtp-server",
|
||||
"start:email": "pnpm email"
|
||||
},
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@next-auth/fauna-adapter": "workspace:*",
|
||||
"@next-auth/prisma-adapter": "workspace:*",
|
||||
"@next-auth/supabase-adapter": "workspace:*",
|
||||
"@next-auth/typeorm-legacy-adapter": "workspace:*",
|
||||
"@prisma/client": "^3",
|
||||
"@supabase/supabase-js": "^2.0.5",
|
||||
"faunadb": "^4",
|
||||
"next": "13.3.0",
|
||||
"next-auth": "workspace:*",
|
||||
"nodemailer": "^6",
|
||||
"react": "^18",
|
||||
"react-dom": "^18"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jsonwebtoken": "^8.5.5",
|
||||
"@types/react": "^18.0.15",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"fake-smtp-server": "^0.8.0",
|
||||
"pg": "^8.7.3",
|
||||
"prisma": "^3",
|
||||
"sqlite3": "^5.0.8",
|
||||
"typeorm": "0.3.7"
|
||||
}
|
||||
}
|
||||
10
apps/dev/nextjs-v4/pages/_app.js
Normal file
10
apps/dev/nextjs-v4/pages/_app.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import { SessionProvider } from "next-auth/react"
|
||||
import "./styles.css"
|
||||
|
||||
export default function App({ Component, pageProps }) {
|
||||
return (
|
||||
<SessionProvider session={pageProps.session}>
|
||||
<Component {...pageProps} />
|
||||
</SessionProvider>
|
||||
)
|
||||
}
|
||||
17
apps/dev/nextjs-v4/pages/api-example.js
Normal file
17
apps/dev/nextjs-v4/pages/api-example.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import Layout from '../components/layout'
|
||||
|
||||
export default function Page () {
|
||||
return (
|
||||
<Layout>
|
||||
<h1>API Example</h1>
|
||||
<p>The examples below show responses from the example API endpoints.</p>
|
||||
<p><em>You must be signed in to see responses.</em></p>
|
||||
<h2>Session</h2>
|
||||
<p>/api/examples/session</p>
|
||||
<iframe src='/api/examples/session' />
|
||||
<h2>JSON Web Token</h2>
|
||||
<p>/api/examples/jwt</p>
|
||||
<iframe src='/api/examples/jwt' />
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
132
apps/dev/nextjs-v4/pages/api/auth-old/[...nextauth].ts
Normal file
132
apps/dev/nextjs-v4/pages/api/auth-old/[...nextauth].ts
Normal file
@@ -0,0 +1,132 @@
|
||||
import NextAuth, { NextAuthOptions } from "next-auth"
|
||||
|
||||
// Providers
|
||||
import Apple from "next-auth/providers/apple"
|
||||
import Auth0 from "next-auth/providers/auth0"
|
||||
import AzureAD from "next-auth/providers/azure-ad"
|
||||
import AzureB2C from "next-auth/providers/azure-ad-b2c"
|
||||
import BoxyHQSAML from "next-auth/providers/boxyhq-saml"
|
||||
// import Cognito from "next-auth/providers/cognito"
|
||||
import Credentials from "next-auth/providers/credentials"
|
||||
import Discord from "next-auth/providers/discord"
|
||||
import DuendeIDS6 from "next-auth/providers/duende-identity-server6"
|
||||
// import Email from "next-auth/providers/email"
|
||||
import Facebook from "next-auth/providers/facebook"
|
||||
import Foursquare from "next-auth/providers/foursquare"
|
||||
import Freshbooks from "next-auth/providers/freshbooks"
|
||||
import GitHub from "next-auth/providers/github"
|
||||
import Gitlab from "next-auth/providers/gitlab"
|
||||
import Google from "next-auth/providers/google"
|
||||
// import IDS4 from "next-auth/providers/identity-server4"
|
||||
import Instagram from "next-auth/providers/instagram"
|
||||
// import Keycloak from "next-auth/providers/keycloak"
|
||||
import Line from "next-auth/providers/line"
|
||||
import LinkedIn from "next-auth/providers/linkedin"
|
||||
import Mailchimp from "next-auth/providers/mailchimp"
|
||||
// import Okta from "next-auth/providers/okta"
|
||||
import Osu from "next-auth/providers/osu"
|
||||
import Patreon from "next-auth/providers/patreon"
|
||||
import Slack from "next-auth/providers/slack"
|
||||
import Spotify from "next-auth/providers/spotify"
|
||||
import Trakt from "next-auth/providers/trakt"
|
||||
import Twitch from "next-auth/providers/twitch"
|
||||
import Twitter from "next-auth/providers/twitter"
|
||||
import Vk from "next-auth/providers/vk"
|
||||
import Wikimedia from "next-auth/providers/wikimedia"
|
||||
import WorkOS from "next-auth/providers/workos"
|
||||
|
||||
// // Prisma
|
||||
// import { PrismaClient } from "@prisma/client"
|
||||
// import { PrismaAdapter } from "@next-auth/prisma-adapter"
|
||||
// const client = globalThis.prisma || new PrismaClient()
|
||||
// if (process.env.NODE_ENV !== "production") globalThis.prisma = client
|
||||
// const adapter = PrismaAdapter(client)
|
||||
|
||||
// // Fauna
|
||||
// import { Client as FaunaClient } from "faunadb"
|
||||
// import { FaunaAdapter } from "@next-auth/fauna-adapter"
|
||||
// const opts = { secret: process.env.FAUNA_SECRET, domain: process.env.FAUNA_DOMAIN }
|
||||
// const client = globalThis.fauna || new FaunaClient(opts)
|
||||
// if (process.env.NODE_ENV !== "production") globalThis.fauna = client
|
||||
// const adapter = FaunaAdapter(client)
|
||||
|
||||
// // TypeORM
|
||||
// import { TypeORMLegacyAdapter } from "@next-auth/typeorm-legacy-adapter"
|
||||
// const adapter = TypeORMLegacyAdapter({
|
||||
// type: "sqlite",
|
||||
// name: "next-auth-test-memory",
|
||||
// database: "./typeorm/dev.db",
|
||||
// synchronize: true,
|
||||
// })
|
||||
|
||||
// // Supabase
|
||||
// import { SupabaseAdapter } from "@next-auth/supabase-adapter"
|
||||
// const adapter = SupabaseAdapter({
|
||||
// url: process.env.NEXT_PUBLIC_SUPABASE_URL,
|
||||
// secret: process.env.SUPABASE_SERVICE_ROLE_KEY,
|
||||
// })
|
||||
|
||||
export const authOptions: NextAuthOptions = {
|
||||
// adapter,
|
||||
// debug: process.env.NODE_ENV !== "production",
|
||||
theme: {
|
||||
logo: "https://next-auth.js.org/img/logo/logo-sm.png",
|
||||
brandColor: "#1786fb",
|
||||
},
|
||||
providers: [
|
||||
Credentials({
|
||||
credentials: { password: { label: "Password", type: "password" } },
|
||||
async authorize(credentials) {
|
||||
if (credentials.password !== "pw") return null
|
||||
return { name: "Fill Murray", email: "bill@fillmurray.com", image: "https://www.fillmurray.com/64/64", id: "1", foo: "" }
|
||||
},
|
||||
}),
|
||||
Apple({ clientId: process.env.APPLE_ID, clientSecret: process.env.APPLE_SECRET }),
|
||||
Auth0({ clientId: process.env.AUTH0_ID, clientSecret: process.env.AUTH0_SECRET, issuer: process.env.AUTH0_ISSUER }),
|
||||
AzureAD({
|
||||
clientId: process.env.AZURE_AD_CLIENT_ID,
|
||||
clientSecret: process.env.AZURE_AD_CLIENT_SECRET,
|
||||
tenantId: process.env.AZURE_AD_TENANT_ID,
|
||||
}),
|
||||
AzureB2C({ clientId: process.env.AZURE_B2C_ID, clientSecret: process.env.AZURE_B2C_SECRET, issuer: process.env.AZURE_B2C_ISSUER }),
|
||||
BoxyHQSAML({ issuer: "https://jackson-demo.boxyhq.com", clientId: "tenant=boxyhq.com&product=saml-demo.boxyhq.com", clientSecret: "dummy" }),
|
||||
// Cognito({ clientId: process.env.COGNITO_ID, clientSecret: process.env.COGNITO_SECRET, issuer: process.env.COGNITO_ISSUER }),
|
||||
Discord({ clientId: process.env.DISCORD_ID, clientSecret: process.env.DISCORD_SECRET }),
|
||||
DuendeIDS6({ clientId: "interactive.confidential", clientSecret: "secret", issuer: "https://demo.duendesoftware.com" }),
|
||||
Facebook({ clientId: process.env.FACEBOOK_ID, clientSecret: process.env.FACEBOOK_SECRET }),
|
||||
Foursquare({ clientId: process.env.FOURSQUARE_ID, clientSecret: process.env.FOURSQUARE_SECRET }),
|
||||
Freshbooks({ clientId: process.env.FRESHBOOKS_ID, clientSecret: process.env.FRESHBOOKS_SECRET }),
|
||||
GitHub({ clientId: process.env.GITHUB_ID, clientSecret: process.env.GITHUB_SECRET }),
|
||||
Gitlab({ clientId: process.env.GITLAB_ID, clientSecret: process.env.GITLAB_SECRET }),
|
||||
Google({ clientId: process.env.GOOGLE_ID, clientSecret: process.env.GOOGLE_SECRET }),
|
||||
// IDS4({ clientId: process.env.IDS4_ID, clientSecret: process.env.IDS4_SECRET, issuer: process.env.IDS4_ISSUER }),
|
||||
Instagram({ clientId: process.env.INSTAGRAM_ID, clientSecret: process.env.INSTAGRAM_SECRET }),
|
||||
// Keycloak({ clientId: process.env.KEYCLOAK_ID, clientSecret: process.env.KEYCLOAK_SECRET, issuer: process.env.KEYCLOAK_ISSUER }),
|
||||
Line({ clientId: process.env.LINE_ID, clientSecret: process.env.LINE_SECRET }),
|
||||
LinkedIn({ clientId: process.env.LINKEDIN_ID, clientSecret: process.env.LINKEDIN_SECRET }),
|
||||
Mailchimp({ clientId: process.env.MAILCHIMP_ID, clientSecret: process.env.MAILCHIMP_SECRET }),
|
||||
// Okta({ clientId: process.env.OKTA_ID, clientSecret: process.env.OKTA_SECRET, issuer: process.env.OKTA_ISSUER }),
|
||||
Osu({ clientId: process.env.OSU_CLIENT_ID, clientSecret: process.env.OSU_CLIENT_SECRET }),
|
||||
Patreon({ clientId: process.env.PATREON_ID, clientSecret: process.env.PATREON_SECRET }),
|
||||
Slack({ clientId: process.env.SLACK_ID, clientSecret: process.env.SLACK_SECRET }),
|
||||
Spotify({ clientId: process.env.SPOTIFY_ID, clientSecret: process.env.SPOTIFY_SECRET }),
|
||||
Trakt({ clientId: process.env.TRAKT_ID, clientSecret: process.env.TRAKT_SECRET }),
|
||||
Twitch({ clientId: process.env.TWITCH_ID, clientSecret: process.env.TWITCH_SECRET }),
|
||||
Twitter({ clientId: process.env.TWITTER_ID, clientSecret: process.env.TWITTER_SECRET }),
|
||||
// TwitterLegacy({ clientId: process.env.TWITTER_LEGACY_ID, clientSecret: process.env.TWITTER_LEGACY_SECRET }),
|
||||
Vk({ clientId: process.env.VK_ID, clientSecret: process.env.VK_SECRET }),
|
||||
Wikimedia({ clientId: process.env.WIKIMEDIA_ID, clientSecret: process.env.WIKIMEDIA_SECRET }),
|
||||
WorkOS({ clientId: process.env.WORKOS_ID, clientSecret: process.env.WORKOS_SECRET }),
|
||||
],
|
||||
}
|
||||
|
||||
if (authOptions.adapter) {
|
||||
// TODO:
|
||||
// authOptions.providers.unshift(
|
||||
// // NOTE: You can start a fake e-mail server with `pnpm email`
|
||||
// // and then go to `http://localhost:1080` in the browser
|
||||
// Email({ server: "smtp://127.0.0.1:1025?tls.rejectUnauthorized=false" })
|
||||
// )
|
||||
}
|
||||
|
||||
export default NextAuth(authOptions)
|
||||
7
apps/dev/nextjs-v4/pages/api/examples/jwt.js
Normal file
7
apps/dev/nextjs-v4/pages/api/examples/jwt.js
Normal file
@@ -0,0 +1,7 @@
|
||||
// This is an example of how to read a JSON Web Token from an API route
|
||||
import { getToken } from "next-auth/jwt"
|
||||
|
||||
export default async (req, res) => {
|
||||
const token = await getToken({ req })
|
||||
res.send(JSON.stringify(token, null, 2))
|
||||
}
|
||||
19
apps/dev/nextjs-v4/pages/api/examples/protected.js
Normal file
19
apps/dev/nextjs-v4/pages/api/examples/protected.js
Normal file
@@ -0,0 +1,19 @@
|
||||
// This is an example of to protect an API route
|
||||
import { getServerSession } from "next-auth/next"
|
||||
import { authOptions } from "../auth/[...nextauth]"
|
||||
|
||||
export default async (req, res) => {
|
||||
const session = await getServerSession(req, res, authOptions)
|
||||
|
||||
if (session) {
|
||||
res.send({
|
||||
content:
|
||||
"This is protected content. You can access this content because you are signed in.",
|
||||
session,
|
||||
})
|
||||
} else {
|
||||
res.send({
|
||||
error: "You must be sign in to view the protected content on this page.",
|
||||
})
|
||||
}
|
||||
}
|
||||
8
apps/dev/nextjs-v4/pages/api/examples/session.js
Normal file
8
apps/dev/nextjs-v4/pages/api/examples/session.js
Normal file
@@ -0,0 +1,8 @@
|
||||
// This is an example of how to access a session from an API route
|
||||
import { getServerSession } from "next-auth/next"
|
||||
import { authOptions } from "../auth/[...nextauth]"
|
||||
|
||||
export default async (req, res) => {
|
||||
const session = await getServerSession(req, res, authOptions)
|
||||
res.json(session)
|
||||
}
|
||||
30
apps/dev/nextjs-v4/pages/api/examples/supabase-rls.js
Normal file
30
apps/dev/nextjs-v4/pages/api/examples/supabase-rls.js
Normal file
@@ -0,0 +1,30 @@
|
||||
// This is an example of how to query data from Supabase with RLS.
|
||||
// Learn more about Row Levele Security (RLS): https://supabase.com/docs/guides/auth/row-level-security
|
||||
import { getServerSession } from "next-auth/next"
|
||||
import { authOptions } from "../auth/[...nextauth]"
|
||||
import { createClient } from "@supabase/supabase-js"
|
||||
|
||||
export default async (req, res) => {
|
||||
const session = await getServerSession(req, res, authOptions)
|
||||
|
||||
if (!session)
|
||||
return res.send(JSON.stringify({ error: "No session!" }, null, 2))
|
||||
|
||||
const { supabaseAccessToken } = session
|
||||
|
||||
const supabase = createClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
|
||||
{
|
||||
global: {
|
||||
headers: {
|
||||
Authorization: `Bearer ${supabaseAccessToken}`,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
// Now you can query with RLS enabled.
|
||||
const { data, error } = await supabase.from("users").select("*")
|
||||
|
||||
res.send(JSON.stringify({ supabaseAccessToken, data, error }, null, 2))
|
||||
}
|
||||
22
apps/dev/nextjs-v4/pages/client.js
Normal file
22
apps/dev/nextjs-v4/pages/client.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import Layout from '../components/layout'
|
||||
|
||||
export default function Page () {
|
||||
return (
|
||||
<Layout>
|
||||
<h1>Client Side Rendering</h1>
|
||||
<p>
|
||||
This page uses the <strong>useSession()</strong> React Hook in the <strong></Header></strong> component.
|
||||
</p>
|
||||
<p>
|
||||
The <strong>useSession()</strong> React Hook easy to use and allows pages to render very quickly.
|
||||
</p>
|
||||
<p>
|
||||
The advantage of this approach is that session state is shared between pages by using the <strong>Provider</strong> in <strong>_app.js</strong> so
|
||||
that navigation between pages using <strong>useSession()</strong> is very fast.
|
||||
</p>
|
||||
<p>
|
||||
The disadvantage of <strong>useSession()</strong> is that it requires client side JavaScript.
|
||||
</p>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
67
apps/dev/nextjs-v4/pages/credentials.js
Normal file
67
apps/dev/nextjs-v4/pages/credentials.js
Normal file
@@ -0,0 +1,67 @@
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
import * as React from "react"
|
||||
import { signIn, signOut, useSession } from "next-auth/react"
|
||||
import Layout from "components/layout"
|
||||
|
||||
export default function Page() {
|
||||
const [response, setResponse] = React.useState(null)
|
||||
const handleLogin = (options) => async () => {
|
||||
if (options.redirect) {
|
||||
return signIn("credentials", options)
|
||||
}
|
||||
const response = await signIn("credentials", options)
|
||||
setResponse(response)
|
||||
}
|
||||
|
||||
const handleLogout = (options) => async () => {
|
||||
if (options.redirect) {
|
||||
return signOut(options)
|
||||
}
|
||||
const response = await signOut(options)
|
||||
setResponse(response)
|
||||
}
|
||||
|
||||
const { data: session } = useSession()
|
||||
|
||||
if (session) {
|
||||
return (
|
||||
<Layout>
|
||||
<h1>Test different flows for Credentials logout</h1>
|
||||
<span className="spacing">Default:</span>
|
||||
<button onClick={handleLogout({ redirect: true })}>Logout</button>
|
||||
<br />
|
||||
<span className="spacing">No redirect:</span>
|
||||
<button onClick={handleLogout({ redirect: false })}>Logout</button>
|
||||
<br />
|
||||
<p>Response:</p>
|
||||
<pre style={{ background: "#eee", padding: 16 }}>
|
||||
{JSON.stringify(response, null, 2)}
|
||||
</pre>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<h1>Test different flows for Credentials login</h1>
|
||||
<span className="spacing">Default:</span>
|
||||
<button onClick={handleLogin({ redirect: true, password: "password" })}>
|
||||
Login
|
||||
</button>
|
||||
<br />
|
||||
<span className="spacing">No redirect:</span>
|
||||
<button onClick={handleLogin({ redirect: false, password: "password" })}>
|
||||
Login
|
||||
</button>
|
||||
<br />
|
||||
<span className="spacing">No redirect, wrong password:</span>
|
||||
<button onClick={handleLogin({ redirect: false, password: "" })}>
|
||||
Login
|
||||
</button>
|
||||
<p>Response:</p>
|
||||
<pre style={{ background: "#eee", padding: 16 }}>
|
||||
{JSON.stringify(response, null, 2)}
|
||||
</pre>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
80
apps/dev/nextjs-v4/pages/email.js
Normal file
80
apps/dev/nextjs-v4/pages/email.js
Normal file
@@ -0,0 +1,80 @@
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
import * as React from "react"
|
||||
import { signIn, signOut, useSession } from "next-auth/react"
|
||||
import Layout from "components/layout"
|
||||
|
||||
export default function Page() {
|
||||
const [response, setResponse] = React.useState(null)
|
||||
const [email, setEmail] = React.useState("")
|
||||
|
||||
const handleChange = (event) => {
|
||||
setEmail(event.target.value)
|
||||
}
|
||||
|
||||
const handleLogin = (options) => async (event) => {
|
||||
event.preventDefault()
|
||||
|
||||
if (options.redirect) {
|
||||
return signIn("email", options)
|
||||
}
|
||||
const response = await signIn("email", options)
|
||||
setResponse(response)
|
||||
}
|
||||
|
||||
const handleLogout = (options) => async (event) => {
|
||||
if (options.redirect) {
|
||||
return signOut(options)
|
||||
}
|
||||
const response = await signOut(options)
|
||||
setResponse(response)
|
||||
}
|
||||
|
||||
const { data: session } = useSession()
|
||||
|
||||
if (session) {
|
||||
return (
|
||||
<Layout>
|
||||
<h1>Test different flows for Email logout</h1>
|
||||
<span className="spacing">Default:</span>
|
||||
<button onClick={handleLogout({ redirect: true })}>Logout</button>
|
||||
<br />
|
||||
<span className="spacing">No redirect:</span>
|
||||
<button onClick={handleLogout({ redirect: false })}>Logout</button>
|
||||
<br />
|
||||
<p>Response:</p>
|
||||
<pre style={{ background: "#eee", padding: 16 }}>
|
||||
{JSON.stringify(response, null, 2)}
|
||||
</pre>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<h1>Test different flows for Email login</h1>
|
||||
<label className="spacing">
|
||||
Email address:{" "}
|
||||
<input
|
||||
type="text"
|
||||
id="email"
|
||||
name="email"
|
||||
value={email}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</label>
|
||||
<br />
|
||||
<form onSubmit={handleLogin({ redirect: true, email })}>
|
||||
<span className="spacing">Default:</span>
|
||||
<button type="submit">Sign in with Email</button>
|
||||
</form>
|
||||
<form onSubmit={handleLogin({ redirect: false, email })}>
|
||||
<span className="spacing">No redirect:</span>
|
||||
<button type="submit">Sign in with Email</button>
|
||||
</form>
|
||||
<p>Response:</p>
|
||||
<pre style={{ background: "#eee", padding: 16 }}>
|
||||
{JSON.stringify(response, null, 2)}
|
||||
</pre>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
12
apps/dev/nextjs-v4/pages/index.js
Normal file
12
apps/dev/nextjs-v4/pages/index.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import Layout from 'components/layout'
|
||||
|
||||
export default function Page () {
|
||||
return (
|
||||
<Layout>
|
||||
<h1>NextAuth.js Example</h1>
|
||||
<p>
|
||||
This is an example site to demonstrate how to use <a href='https://next-auth.js.org'>NextAuth.js</a> for authentication.
|
||||
</p>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
9
apps/dev/nextjs-v4/pages/middleware-protected/index.js
Normal file
9
apps/dev/nextjs-v4/pages/middleware-protected/index.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import Layout from "components/layout"
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<Layout>
|
||||
<h1>Page protected by Middleware</h1>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
30
apps/dev/nextjs-v4/pages/policy.js
Normal file
30
apps/dev/nextjs-v4/pages/policy.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import Layout from '../components/layout'
|
||||
|
||||
export default function Page () {
|
||||
return (
|
||||
<Layout>
|
||||
<p>
|
||||
This is an example site to demonstrate how to use <a href='https://next-auth.js.org'>NextAuth.js</a> for authentication.
|
||||
</p>
|
||||
<h2>Terms of Service</h2>
|
||||
<p>
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
</p>
|
||||
<h2>Privacy Policy</h2>
|
||||
<p>
|
||||
This site uses JSON Web Tokens and an in-memory database which resets every ~2 hours.
|
||||
</p>
|
||||
<p>
|
||||
Data provided to this site is exclusively used to support signing in
|
||||
and is not passed to any third party services, other than via SMTP or OAuth for the
|
||||
purposes of authentication.
|
||||
</p>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
48
apps/dev/nextjs-v4/pages/protected-ssr.js
Normal file
48
apps/dev/nextjs-v4/pages/protected-ssr.js
Normal file
@@ -0,0 +1,48 @@
|
||||
// This is an example of how to protect content using server rendering
|
||||
import { getServerSession } from "next-auth/next"
|
||||
import { authOptions } from "./api/auth/[...nextauth]"
|
||||
import Layout from "../components/layout"
|
||||
import AccessDenied from "../components/access-denied"
|
||||
|
||||
export default function Page({ content, session }) {
|
||||
// If no session exists, display access denied message
|
||||
if (!session) {
|
||||
return (
|
||||
<Layout>
|
||||
<AccessDenied />
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
// If session exists, display content
|
||||
return (
|
||||
<Layout>
|
||||
<h1>Protected Page</h1>
|
||||
<p>
|
||||
<strong>{content}</strong>
|
||||
</p>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getServerSideProps(context) {
|
||||
const session = await getServerSession(context.req, context.res, authOptions)
|
||||
let content = null
|
||||
|
||||
if (session) {
|
||||
const hostname = process.env.NEXTAUTH_URL || "http://localhost:3000"
|
||||
const options = { headers: { cookie: context.req.headers.cookie } }
|
||||
const res = await fetch(`${hostname}/api/examples/protected`, options)
|
||||
const json = await res.json()
|
||||
if (json.content) {
|
||||
content = json.content
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
props: {
|
||||
session,
|
||||
content,
|
||||
},
|
||||
}
|
||||
}
|
||||
35
apps/dev/nextjs-v4/pages/protected.js
Normal file
35
apps/dev/nextjs-v4/pages/protected.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import { useState, useEffect } from "react"
|
||||
import { useSession } from "next-auth/react"
|
||||
import Layout from "../components/layout"
|
||||
|
||||
export default function Page() {
|
||||
const { status } = useSession({
|
||||
required: true,
|
||||
})
|
||||
const [content, setContent] = useState()
|
||||
|
||||
// Fetch content from protected route
|
||||
useEffect(() => {
|
||||
if (status === "loading") return
|
||||
const fetchData = async () => {
|
||||
const res = await fetch("/api/examples/protected")
|
||||
const json = await res.json()
|
||||
if (json.content) {
|
||||
setContent(json.content)
|
||||
}
|
||||
}
|
||||
fetchData()
|
||||
}, [status])
|
||||
|
||||
if (status === "loading") return <Layout>Loading...</Layout>
|
||||
|
||||
// If session exists, display content
|
||||
return (
|
||||
<Layout>
|
||||
<h1>Protected Page</h1>
|
||||
<p>
|
||||
<strong>{content}</strong>
|
||||
</p>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
46
apps/dev/nextjs-v4/pages/server.js
Normal file
46
apps/dev/nextjs-v4/pages/server.js
Normal file
@@ -0,0 +1,46 @@
|
||||
import { getServerSession } from "next-auth/next"
|
||||
import Layout from "../components/layout"
|
||||
import { authOptions } from "./api/auth/[...nextauth]"
|
||||
|
||||
export default function Page() {
|
||||
// As this page uses Server Side Rendering, the `session` will be already
|
||||
// populated on render without needing to go through a loading stage.
|
||||
// This is possible because of the shared context configured in `_app.js` that
|
||||
// is used by `useSession()`.
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<h1>Server Side Rendering</h1>
|
||||
<p>
|
||||
This page uses the <strong>getServerSession()</strong> method in{" "}
|
||||
<strong>getServerSideProps()</strong>.
|
||||
</p>
|
||||
<p>
|
||||
Using <strong>getServerSession()</strong> in{" "}
|
||||
<strong>getServerSideProps()</strong> is currently the recommended
|
||||
approach, although the API may still change, if you need to support
|
||||
Server Side Rendering with authentication.
|
||||
</p>
|
||||
<p>
|
||||
Using <strong>getSession()</strong> is still recommended on the client.
|
||||
</p>
|
||||
<p>
|
||||
The advantage of Server Side Rendering is this page does not require
|
||||
client side JavaScript.
|
||||
</p>
|
||||
<p>
|
||||
The disadvantage of Server Side Rendering is that this page is slower to
|
||||
render.
|
||||
</p>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
// Export the `session` prop to use sessions with Server Side Rendering
|
||||
export async function getServerSideProps(context) {
|
||||
return {
|
||||
props: {
|
||||
session: await getServerSession(context.req, context.res, authOptions),
|
||||
},
|
||||
}
|
||||
}
|
||||
32
apps/dev/nextjs-v4/pages/styles.css
Normal file
32
apps/dev/nextjs-v4/pages/styles.css
Normal file
@@ -0,0 +1,32 @@
|
||||
body {
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
|
||||
"Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif,
|
||||
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
padding: 0 1rem 1rem 1rem;
|
||||
max-width: 680px;
|
||||
margin: 0 auto;
|
||||
background: #fff;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
li,
|
||||
p {
|
||||
line-height: 1.5rem;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
iframe {
|
||||
background: #ccc;
|
||||
border: 1px solid #ccc;
|
||||
height: 10rem;
|
||||
width: 100%;
|
||||
border-radius: .5rem;
|
||||
filter: invert(1);
|
||||
}
|
||||
49
apps/dev/nextjs-v4/pages/supabase-client-rls.js
Normal file
49
apps/dev/nextjs-v4/pages/supabase-client-rls.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import Layout from "../components/layout"
|
||||
import { useState, useEffect } from "react"
|
||||
import { useSession } from "next-auth/react"
|
||||
import { createClient } from "@supabase/supabase-js"
|
||||
|
||||
export default function Page() {
|
||||
const { data: session } = useSession()
|
||||
const [data, setData] = useState(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (session) {
|
||||
console.log(session)
|
||||
// User is logged in, let's fetch their data.
|
||||
const { supabaseAccessToken } = session
|
||||
const supabase = createClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
|
||||
{
|
||||
global: {
|
||||
headers: { Authorization: `Bearer ${supabaseAccessToken}` },
|
||||
},
|
||||
}
|
||||
)
|
||||
// Fetch data with RLS enabled.
|
||||
supabase
|
||||
.from("users")
|
||||
.select("*")
|
||||
.then(({ data }) => setData(data))
|
||||
}
|
||||
}, [session])
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<h1>Fetch Data from Supabase with RLS</h1>
|
||||
<h2>Client-side data fetching with RLS:</h2>
|
||||
<pre>{JSON.stringify(data, null, 2)}</pre>
|
||||
<h2>API Example</h2>
|
||||
<p>
|
||||
You can also use Supabase in API routes. See the code in the
|
||||
`/pages/api/examples/supabase-rls.js` file.
|
||||
</p>
|
||||
<p>
|
||||
<em>You must be signed in to see responses.</em>
|
||||
</p>
|
||||
<p>/api/examples/supabase-rls</p>
|
||||
<iframe src="/api/examples/supabase-rls" />
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
64
apps/dev/nextjs-v4/pages/supabase-ssr.js
Normal file
64
apps/dev/nextjs-v4/pages/supabase-ssr.js
Normal file
@@ -0,0 +1,64 @@
|
||||
// This is an example of how to protect content using server rendering
|
||||
// and fetching data from Supabase with RLS enabled.
|
||||
import { getServerSession } from "next-auth/next"
|
||||
import { authOptions } from "./api/auth/[...nextauth]"
|
||||
import { createClient } from "@supabase/supabase-js"
|
||||
import Layout from "../components/layout"
|
||||
import AccessDenied from "../components/access-denied"
|
||||
|
||||
export default function Page({ data, session }) {
|
||||
// If no session exists, display access denied message
|
||||
if (!session) {
|
||||
return (
|
||||
<Layout>
|
||||
<AccessDenied />
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
// If session exists, display content
|
||||
return (
|
||||
<Layout>
|
||||
<h1>Protected Page</h1>
|
||||
<p>Data fetched during SSR from Supabase with RSL enabled:</p>
|
||||
<pre>{JSON.stringify(data, null, 2)}</pre>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getServerSideProps(context) {
|
||||
const session = await getServerSession(context.req, context.res, authOptions)
|
||||
|
||||
if (!session)
|
||||
return {
|
||||
props: {
|
||||
session,
|
||||
data: null,
|
||||
error: "No session",
|
||||
},
|
||||
}
|
||||
|
||||
const { supabaseAccessToken } = session
|
||||
|
||||
const supabase = createClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
|
||||
{
|
||||
global: {
|
||||
headers: {
|
||||
Authorization: `Bearer ${supabaseAccessToken}`,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
// Now you can query with RLS enabled.
|
||||
const { data, error } = await supabase.from("users").select("*")
|
||||
|
||||
return {
|
||||
props: {
|
||||
session,
|
||||
data,
|
||||
error,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "Account" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"userId" TEXT NOT NULL,
|
||||
"type" TEXT NOT NULL,
|
||||
"provider" TEXT NOT NULL,
|
||||
"providerAccountId" TEXT NOT NULL,
|
||||
"refresh_token" TEXT,
|
||||
"access_token" TEXT,
|
||||
"expires_at" INTEGER,
|
||||
"token_type" TEXT,
|
||||
"scope" TEXT,
|
||||
"id_token" TEXT,
|
||||
"session_state" TEXT,
|
||||
"oauth_token_secret" TEXT,
|
||||
"oauth_token" TEXT,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL,
|
||||
CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Session" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"sessionToken" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"expires" DATETIME NOT NULL,
|
||||
CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "User" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"name" TEXT,
|
||||
"email" TEXT,
|
||||
"emailVerified" DATETIME,
|
||||
"image" TEXT
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "VerificationToken" (
|
||||
"identifier" TEXT NOT NULL,
|
||||
"token" TEXT NOT NULL,
|
||||
"expires" DATETIME NOT NULL
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Account_provider_providerAccountId_key" ON "Account"("provider", "providerAccountId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Session_sessionToken_key" ON "Session"("sessionToken");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "VerificationToken_token_key" ON "VerificationToken"("token");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "VerificationToken_identifier_token_key" ON "VerificationToken"("identifier", "token");
|
||||
3
apps/dev/nextjs-v4/prisma/migrations/migration_lock.toml
Normal file
3
apps/dev/nextjs-v4/prisma/migrations/migration_lock.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (i.e. Git)
|
||||
provider = "sqlite"
|
||||
57
apps/dev/nextjs-v4/prisma/schema.prisma
Normal file
57
apps/dev/nextjs-v4/prisma/schema.prisma
Normal file
@@ -0,0 +1,57 @@
|
||||
datasource db {
|
||||
provider = "sqlite"
|
||||
url = "file:./dev.db"
|
||||
}
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
model Account {
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
type String
|
||||
provider String
|
||||
providerAccountId String
|
||||
refresh_token String?
|
||||
access_token String?
|
||||
expires_at Int?
|
||||
token_type String?
|
||||
scope String?
|
||||
id_token String?
|
||||
session_state String?
|
||||
oauth_token_secret String?
|
||||
oauth_token String?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
|
||||
@@unique([provider, providerAccountId])
|
||||
}
|
||||
|
||||
model Session {
|
||||
id String @id @default(cuid())
|
||||
sessionToken String @unique
|
||||
userId String
|
||||
expires DateTime
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
name String?
|
||||
email String? @unique
|
||||
emailVerified DateTime?
|
||||
image String?
|
||||
accounts Account[]
|
||||
sessions Session[]
|
||||
}
|
||||
|
||||
model VerificationToken {
|
||||
identifier String
|
||||
token String @unique
|
||||
expires DateTime
|
||||
|
||||
@@unique([identifier, token])
|
||||
}
|
||||
39
apps/dev/nextjs-v4/tsconfig.json
Normal file
39
apps/dev/nextjs-v4/tsconfig.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"incremental": true,
|
||||
"jsx": "preserve",
|
||||
"baseUrl": ".",
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"strictNullChecks": true
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"jest.config.js"
|
||||
]
|
||||
}
|
||||
20
apps/dev/nextjs-v4/types/nextauth.d.ts
vendored
Normal file
20
apps/dev/nextjs-v4/types/nextauth.d.ts
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import NextAuth from "next-auth"
|
||||
|
||||
declare module "next-auth" {
|
||||
/**
|
||||
* Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
|
||||
*/
|
||||
interface Session {
|
||||
// A JWT which can be used as Authorization header with supabase-js for RLS.
|
||||
supabaseAccessToken?: string
|
||||
user: {
|
||||
/** The user's postal address. */
|
||||
address: string
|
||||
} & User
|
||||
}
|
||||
|
||||
interface User {
|
||||
foo: string
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,13 @@ AUTH0_ID=
|
||||
AUTH0_SECRET=
|
||||
AUTH0_ISSUER=
|
||||
|
||||
KEYCLOAK_ID=
|
||||
KEYCLOAK_SECRET=
|
||||
KEYCLOAK_ISSUER=
|
||||
# Beyond Identity Provider
|
||||
BEYOND_IDENTITY_CLIENT_ID=
|
||||
BEYOND_IDENTITY_CLIENT_SECRET=
|
||||
BEYOND_IDENTITY_ISSUER=
|
||||
|
||||
GITHUB_ID=
|
||||
GITHUB_SECRET=
|
||||
|
||||
NOTION_ID=
|
||||
NOTION_SECRET=
|
||||
@@ -29,8 +33,15 @@ IDS4_ID=
|
||||
IDS4_SECRET=
|
||||
IDS4_ISSUER=
|
||||
|
||||
GITHUB_ID=
|
||||
GITHUB_SECRET=
|
||||
KEYCLOAK_ID=
|
||||
KEYCLOAK_SECRET=
|
||||
KEYCLOAK_ISSUER=
|
||||
|
||||
LINE_ID=
|
||||
LINE_SECRET=
|
||||
|
||||
TRAKT_ID=
|
||||
TRAKT_SECRET=
|
||||
|
||||
TWITCH_ID=
|
||||
TWITCH_SECRET=
|
||||
@@ -38,11 +49,12 @@ TWITCH_SECRET=
|
||||
TWITTER_ID=
|
||||
TWITTER_SECRET=
|
||||
|
||||
LINE_ID=
|
||||
LINE_SECRET=
|
||||
WIKIMEDIA_ID=
|
||||
WIKIMEDIA_SECRET=
|
||||
|
||||
TRAKT_ID=
|
||||
TRAKT_SECRET=
|
||||
# Yandex OAuth. new app -> https://oauth.yandex.com/client/new/id
|
||||
YANDEX_ID=
|
||||
YANDEX_SECRET=
|
||||
|
||||
# Example configuration for a Gmail account (will need SMTP enabled)
|
||||
EMAIL_SERVER=smtps://user@gmail.com:password@smtp.gmail.com:465
|
||||
@@ -55,12 +67,9 @@ EMAIL_FROM=user@gmail.com
|
||||
# MongoDB: DATABASE_URL=mongodb://nextauth:password@127.0.0.1:27017/nextauth?synchronize=true
|
||||
DATABASE_URL=
|
||||
|
||||
WIKIMEDIA_ID=
|
||||
WIKIMEDIA_SECRET=
|
||||
|
||||
# Supabase Example Configuration
|
||||
# Supabase Example Configuration
|
||||
# NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321
|
||||
# SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSJ9.vI9obAHOGyVVKa3pD--kJlyxp-Z2zV9UUMAhKpNLAcU
|
||||
# SUPABASE_JWT_SECRET=super-secret-jwt-token-with-at-least-32-characters-long
|
||||
# NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24ifQ.625_WdcF3KHqz5amU0x2X5WWHP-OEs_4qj0ssLNHzTs
|
||||
# NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24ifQ.625_WdcF3KHqz5amU0x2X5WWHP-OEs_4qj0ssLNHzTs
|
||||
|
||||
@@ -6,6 +6,7 @@ import Asgardeo from "@auth/core/providers/asgardeo"
|
||||
import Auth0 from "@auth/core/providers/auth0"
|
||||
import AzureAD from "@auth/core/providers/azure-ad"
|
||||
import AzureB2C from "@auth/core/providers/azure-ad-b2c"
|
||||
import BeyondIdentity from "@auth/core/providers/beyondidentity"
|
||||
import BoxyHQSAML from "@auth/core/providers/boxyhq-saml"
|
||||
// import Cognito from "@auth/core/providers/cognito"
|
||||
import Credentials from "@auth/core/providers/credentials"
|
||||
@@ -33,6 +34,7 @@ import Spotify from "@auth/core/providers/spotify"
|
||||
import Trakt from "@auth/core/providers/trakt"
|
||||
import Twitch from "@auth/core/providers/twitch"
|
||||
import Twitter from "@auth/core/providers/twitter"
|
||||
import Yandex from "@auth/core/providers/yandex"
|
||||
import Vk from "@auth/core/providers/vk"
|
||||
import Wikimedia from "@auth/core/providers/wikimedia"
|
||||
import WorkOS from "@auth/core/providers/workos"
|
||||
@@ -92,6 +94,7 @@ export const authConfig: AuthConfig = {
|
||||
tenantId: process.env.AZURE_AD_TENANT_ID,
|
||||
}),
|
||||
AzureB2C({ clientId: process.env.AZURE_B2C_ID, clientSecret: process.env.AZURE_B2C_SECRET, issuer: process.env.AZURE_B2C_ISSUER }),
|
||||
BeyondIdentity({ clientId: process.env.BEYOND_IDENTITY_CLIENT_ID, clientSecret: process.env.BEYOND_IDENTITY_CLIENT_SECRET, issuer: process.env.BEYOND_IDENTITY_ISSUER }),
|
||||
BoxyHQSAML({ issuer: "https://jackson-demo.boxyhq.com", clientId: "tenant=boxyhq.com&product=saml-demo.boxyhq.com", clientSecret: "dummy" }),
|
||||
// Cognito({ clientId: process.env.COGNITO_ID, clientSecret: process.env.COGNITO_SECRET, issuer: process.env.COGNITO_ISSUER }),
|
||||
Discord({ clientId: process.env.DISCORD_ID, clientSecret: process.env.DISCORD_SECRET }),
|
||||
@@ -118,6 +121,7 @@ export const authConfig: AuthConfig = {
|
||||
Twitch({ clientId: process.env.TWITCH_ID, clientSecret: process.env.TWITCH_SECRET }),
|
||||
Twitter({ clientId: process.env.TWITTER_ID, clientSecret: process.env.TWITTER_SECRET }),
|
||||
// TwitterLegacy({ clientId: process.env.TWITTER_LEGACY_ID, clientSecret: process.env.TWITTER_LEGACY_SECRET }),
|
||||
Yandex({ clientId: process.env.YANDEX_ID, clientSecret: process.env.YANDEX_SECRET }),
|
||||
Vk({ clientId: process.env.VK_ID, clientSecret: process.env.VK_SECRET }),
|
||||
Wikimedia({ clientId: process.env.WIKIMEDIA_ID, clientSecret: process.env.WIKIMEDIA_SECRET }),
|
||||
WorkOS({ clientId: process.env.WORKOS_ID, clientSecret: process.env.WORKOS_SECRET }),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { unstable_getServerSession } from "next-auth/next"
|
||||
import { getServerSession } from "next-auth/next"
|
||||
import { authOptions } from "./api/auth/[...nextauth]"
|
||||
import Layout from "../components/layout"
|
||||
|
||||
@@ -38,7 +38,7 @@ export default function ServerSidePage() {
|
||||
export async function getServerSideProps(context: GetServerSidePropsContext) {
|
||||
return {
|
||||
props: {
|
||||
session: await unstable_getServerSession(
|
||||
session: await getServerSession(
|
||||
context.req,
|
||||
context.res,
|
||||
authOptions
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
"clean": "gatsby clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"dotenv": "^16.0.0",
|
||||
"gatsby": "next",
|
||||
"dotenv": "16.0.0",
|
||||
"gatsby": "5.8.0-next.3",
|
||||
"next-auth": "workspace:*",
|
||||
"react": "^18",
|
||||
"react-dom": "^18"
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vercel": "^23.1.2"
|
||||
"vercel": "23.1.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ If you are deploying directly to a particular cloud platform you may also want t
|
||||
|
||||
## Security
|
||||
|
||||
Parts of this section has been moved to its [own page](/getting-started/security).
|
||||
Parts of this section has been moved to its [own page](/security).
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
---
|
||||
title: Introduction
|
||||
sidebar_position: 0
|
||||
---
|
||||
|
||||
## About Auth.js
|
||||
|
||||
Auth.js is a complete open-source authentication solution for [Next.js](http://nextjs.org/) applications.
|
||||
|
||||
It is designed from the ground up to support Next.js and Serverless.
|
||||
|
||||
Check our tutorials to see how easy it is to use Auth.js for authentication:
|
||||
|
||||
- [Setup with OAuth](/getting-started/oauth-tutorial)
|
||||
- [Setup with magic links](/getting-started/email-tutorial)
|
||||
- [Integrating with external auth](/getting-started/credentials-tutorial)
|
||||
|
||||
### Flexible and easy to use
|
||||
|
||||
- Designed to work with any OAuth service, it supports OAuth 1.0, 1.0A, 2.0 and OpenID Connect
|
||||
- Built-in support for [many popular sign-in services](/reference/providers/oauth-builtin)
|
||||
- Supports [email / passwordless authentication](/getting-started/email-tutorial)
|
||||
- Supports stateless authentication with [any backend](/getting-started/credentials-tutorial) (Active Directory, LDAP, etc)
|
||||
- Supports both JSON Web Tokens and database sessions
|
||||
- Designed for Serverless but runs anywhere (AWS Lambda, Docker, Heroku, etc…)
|
||||
|
||||
### Own your own data
|
||||
|
||||
Auth.js can be used with or without a database.
|
||||
|
||||
- An open-source solution that allows you to keep control of your data
|
||||
- Supports Bring Your Own Database (BYOD) and can be used with any database
|
||||
- Built-in support for [MySQL, MariaDB, Postgres, SQL Server, MongoDB and SQLite](/getting-started/databases)
|
||||
- Works great with databases from popular hosting providers
|
||||
- Can also be used _without a database_ (e.g. OAuth + JWT)
|
||||
|
||||
_Note: Email sign-in requires a database to be configured to store single-use verification tokens._
|
||||
|
||||
### Secure by default
|
||||
|
||||
- Promotes the use of passwordless sign-in mechanisms
|
||||
- Designed to be secure by default and encourage best practices for safeguarding user data
|
||||
- Uses Cross-Site Request Forgery Tokens on POST routes (sign in, sign out)
|
||||
- Default cookie policy aims for the most restrictive policy appropriate for each cookie
|
||||
- When JSON Web Tokens are enabled, they are encrypted by default (JWE) with A256GCM
|
||||
- Auto-generates symmetric signing and encryption keys for developer convenience
|
||||
- Features tab/window syncing and keepalive messages to support short-lived sessions
|
||||
- Attempts to implement the latest guidance published by [Open Web Application Security Project](https://owasp.org/)
|
||||
|
||||
Advanced options allow you to define your own routines to handle controlling what accounts are allowed to sign in, for encoding and decoding JSON Web Tokens and to set custom cookie security policies and session properties, so you can control who can sign in and how often sessions have to be re-validated.
|
||||
|
||||
## Credits
|
||||
|
||||
Auth.js is an open-source project that is only possible [thanks to contributors](/contributors).
|
||||
|
||||
If you would like to financially support the development of Auth.js, you can find more information on our [OpenCollective](https://opencollective.com/nextauth) page.
|
||||
@@ -1,301 +0,0 @@
|
||||
---
|
||||
title: OAuth authentication
|
||||
---
|
||||
|
||||
import creatingOauthAppImg from "./img/getting-started-creating-oauth-app.png"
|
||||
import addingCallbackUrlImg from "./img/getting-started-oauth-callback-url.png"
|
||||
import gettingClientIdSecretImg from "./img/getting-started-oauth-clientid-secret.png"
|
||||
import startAppAndSignInImg from "./img/getting-started-app-start.png"
|
||||
import githubAuthCredentials from "./img/getting-started-github-auth.png"
|
||||
import nextAuthUserLoggedIn from "./img/getting-started-nextauth-success.png"
|
||||
|
||||
We know, authentication is hard. It's a rabbit hole and it's easy to get lost on it. The goal of making Auth.js is that you can add authentication easily to your project with just a few lines of code.
|
||||
|
||||
The easiest way is to setup Auth.js with an [OAuth](https://en.wikipedia.org/wiki/OAuth) provider. In this tutorial we'll be setting Auth.js in a **Next.js app** to be able to login with **Github**.
|
||||
|
||||
:::info
|
||||
Auth.js comes with a long list of [built-in providers](/reference/providers/oauth-builtin) (Google, Facebook, Twitter, etc...) you can also integrate it with your own OAuth service easily by [building a custom provider](/guides/providers/custom-provider). Auth.js can integrate as well with other frameworks like SvelteKit, SolidStart and Gatsby.
|
||||
:::
|
||||
|
||||
## 1. Configuring Auth.js
|
||||
|
||||
### Creating the server config
|
||||
|
||||
To add Auth.js to a [**Next.js**](https://nextjs.org/) project, create the following [API route](https://nextjs.org/docs/api-routes/introduction):
|
||||
|
||||
```
|
||||
pages/api/auth/[...nextauth].ts
|
||||
```
|
||||
|
||||
This route will contain the **dynamic route handler** for Auth.js which describes your global auth configuration:
|
||||
|
||||
```ts title="pages/api/auth/[...nextauth].js"
|
||||
import NextAuth from "next-auth"
|
||||
import GithubProvider from "next-auth/providers/github"
|
||||
|
||||
export default NextAuth({
|
||||
providers: [
|
||||
GithubProvider({
|
||||
clientId: process.env.GITHUB_ID,
|
||||
clientSecret: process.env.GITHUB_SECRET,
|
||||
}),
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
Behind the scenes this creates all the relevant OAuth API routes within `/api/auth/*` so that auth API requests to:
|
||||
|
||||
- `/api/auth/callback`
|
||||
- `/api/auth/signIn`
|
||||
- `/api/auth/signOut`
|
||||
- etc...
|
||||
|
||||
can be handled by Auth.js. In this way, Auth.js stays in charge of handling the whole authentication request/response flow of your application for you.
|
||||
|
||||
You may notice there are some environment variables in the code example above. `GITHUB_ID` and `GITHUB_SECRET` are provided by the OAuth provider (in this case **Github**) see ["Configuring OAuth Provider"](/getting-started/oauth-tutorial#2-configuring-oauth-provider) section on how to get those.
|
||||
|
||||
`NEXTAUTH_SECRET` is a random string used by the library to encrypt tokens and email verification hashes, and **it's mandatory to keep things secure**! 🔥 🔐 . You can use:
|
||||
|
||||
```
|
||||
$ openssl rand -base64 32
|
||||
```
|
||||
|
||||
or https://generate-secret.vercel.app/32 to generate a random value for it.
|
||||
|
||||
:::info
|
||||
Auth.js is extremely customizable, [our guides section](/guides/overview) will teach you how you can set it up to handle auth in different ways. All the possible configuration options are [listed here](/reference/configuration/auth-config).
|
||||
:::
|
||||
|
||||
### Exposing the session via provider
|
||||
|
||||
To be able to use `useSession` first you'll need to expose the session context, [`<SessionProvider />`](/reference/react/#sessionprovider), at the top level of your application:
|
||||
|
||||
```ts title="pages/_app.tsx"
|
||||
import { SessionProvider } from "next-auth/react"
|
||||
|
||||
export default function App({
|
||||
Component,
|
||||
pageProps: { session, ...pageProps },
|
||||
}) {
|
||||
return (
|
||||
<SessionProvider session={session}>
|
||||
<Component {...pageProps} />
|
||||
</SessionProvider>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
Instances of `useSession` (more on it in the next section) will then have access to the session data and status. The `<SessionProvider />` also takes care of keeping the session updated and synced between browser tabs and windows. 💪🏽
|
||||
|
||||
:::tip
|
||||
Check our [client docs](/reference/react/) to learn all the available options for handling sessions on the browser.
|
||||
:::
|
||||
|
||||
### Consuming the session via hooks
|
||||
|
||||
Auth.js exposes a [`useSession()`](/reference/react/#usesession) React Hook so that you can easily check if someone is signed in:
|
||||
|
||||
```ts title="pages/overview.tsx"
|
||||
import { useSession, signIn, signOut } from "next-auth/react"
|
||||
|
||||
export default function CamperVanPage() {
|
||||
const { data: session, status } = useSession()
|
||||
const userEmail = session?.user.email;
|
||||
|
||||
if (status === "loading") {
|
||||
return <p>Hang on there...</p>
|
||||
}
|
||||
|
||||
if (status === "authenticated") {
|
||||
return (
|
||||
<>
|
||||
<p>Signed in as {userEmail}</p>
|
||||
<button onClick={() => signOut()}>Sign out</button>
|
||||
<img src="https://cdn.pixabay.com/photo/2017/08/11/19/36/vw-2632486_1280.png" />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>Not signed in.</p>
|
||||
<button onClick={() => signIn()}>Sign in</button>
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
You can use the `useSession` hook from anywhere in your application (e.g. in a header component). Behind the scenes, the hook will connect to the `<SessionProvider />` to read the current user session.
|
||||
|
||||
### Protecting API Routes
|
||||
|
||||
Protecting your custom API Routes (.i.e not allowing a resource to be accessed in case the user is not logged in) is easy! You can use [`getSession()`](/reference/utilities/#getsession) to know whether a session exists or not:
|
||||
|
||||
```ts title="pages/api/movies/list.ts"
|
||||
import { getSession } from "next-auth/react"
|
||||
|
||||
export default async function listMovies(req, res) {
|
||||
const session = await getSession({ req })
|
||||
|
||||
if (session) {
|
||||
res.send({
|
||||
movies: [
|
||||
{ title: "Alien vs Predator", id: 1 },
|
||||
{ title: "Reservoir Dogs", id: 2 },
|
||||
],
|
||||
})
|
||||
} else {
|
||||
res.send({
|
||||
error: "You must sign in to view movies.",
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 2. Configuring OAuth Provider
|
||||
|
||||
Ok, we have our Next.js app setup with NextAuth, however, if you run the app right now, it won't work as we haven't configured our OAuth provider (**Github**) yet.
|
||||
|
||||
:::info
|
||||
When using OAuth you're asking for a third-party service (in this case Github, although it could be Google, Twitter, etc...) to handle user authentication for your app.
|
||||
:::
|
||||
|
||||
We need to register our new Next.js app in Github, so that when Auth.js forwards the authorization requests to it, Github can recognize your application and prompt the user to sign in.
|
||||
|
||||
<img src={creatingOauthAppImg} />
|
||||
|
||||
Log in into **Github**, go to `Settings / Developers / OAuth Apps` and click on "New OAuth App".
|
||||
|
||||
Next you'll be presented with a screen to add details about your new application. Fill in the required fields, but pay extra attention to the **Authorization Callback URL** one:
|
||||
|
||||
<img src={addingCallbackUrlImg} />
|
||||
|
||||
The callback URL we insert should have the following pattern:
|
||||
|
||||
```
|
||||
[origin]/api/auth/callback/[provider]
|
||||
```
|
||||
|
||||
In this case, given we want to try our authentication working locally on our machine and we're using **Github** as our OAuth provider, it'll be:
|
||||
|
||||
```
|
||||
http://localhost:3000/api/auth/callback/github
|
||||
```
|
||||
|
||||
:::info
|
||||
Auth.js will already magically create this API endpoint for you when we start the application later. Note that because we're using Next.js, locally it starts our server on the port `3000`, hence the origin is `http://localhost:3000`.
|
||||
:::
|
||||
|
||||
Next you'll be presented with the following screen which presents all the configuration for your new OAuth app. For now, we need two things from it: the **Client ID** and **Client Secret** for our new OAuth app:
|
||||
|
||||
<img src={gettingClientIdSecretImg} />
|
||||
|
||||
The Client ID is always there, a public identifier of your OAuth application within Github. Click on the **Generate a new client Secret** button and should be presented with a new string (which is just a randomized string).
|
||||
|
||||
:::warning
|
||||
🔥 Keep both your Client ID and Client Secret secure and never expose them to the public or shared with people outside your organization. With them, a malicious actor could hijack your application and cause you and your user serious problems!
|
||||
:::
|
||||
|
||||
Now let's copy both the Client ID and Client Secret and paste them in an environment file in the root of your project like so:
|
||||
|
||||
```title=".env.local"
|
||||
GITHUB_ID=12345
|
||||
GITHUB_SECRET=67890
|
||||
```
|
||||
|
||||
Cool! We have finished the configuring our OAuth provider, now let's wire all together so we can finally see authentication working in our app!
|
||||
|
||||
:::info
|
||||
As noted previously, Auth.js has built-in support for multiple OAuth providers, <a href="">here the full list</a>. You can also easily build your own in case the provider you need is not on the list.
|
||||
|
||||
Note that, for each provider, the configuration process will be similar to what we just did:
|
||||
|
||||
1. Log in to the provider
|
||||
2. Create create your OAuth application within it
|
||||
3. Set the callback URL
|
||||
4. Get the Client ID and Generate a Client Secret
|
||||
:::
|
||||
|
||||
## 3. Wiring all together
|
||||
|
||||
Finally, we just need to reference our **Client ID** and **Client Secret** we just generated in the previous in our Auth.js config. In this way the library will be able to use them when forwarding users to Github, and Github will be able to recognize the request as generated from our application:
|
||||
|
||||
```ts title="pages/api/auth/[...nextauth].js"
|
||||
import NextAuth from "next-auth"
|
||||
import GithubProvider from "next-auth/providers/github"
|
||||
|
||||
export default NextAuth({
|
||||
providers: [
|
||||
GithubProvider({
|
||||
clientId: process.env.GITHUB_ID,
|
||||
clientSecret: process.env.GITHUB_SECRET,
|
||||
}),
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
Great! We're now ready to run our application locally. Start the Next.js app by running on your terminal the following command and navigating to [`http://localhost:3000`](http://localhost:3000):
|
||||
|
||||
```
|
||||
$ npm run next dev
|
||||
```
|
||||
|
||||
You should see the following page:
|
||||
|
||||
<img src={startAppAndSignInImg} />
|
||||
|
||||
Click on "Sign in" and then on "Sign in with Github": Auth.js will redirect you to Github, and Github will recognize our app [that we just registered](#2-configuring-oauth-provider) and ask the user (in this case you) to enter its credentials to proceed:
|
||||
|
||||
<img src={githubAuthCredentials} />
|
||||
|
||||
Once inserted and correct, Github will redirect the user to our app and Auth.js will take care of any further calls with Github to get access to the user profile and start a user sessions safely in the background:
|
||||
|
||||
<img src={nextAuthUserLoggedIn} />
|
||||
|
||||
Great! We have completed the whole E2E authentication flow setup so that users can login in our application through Github!
|
||||
:::
|
||||
|
||||
:::info
|
||||
You can create your own Sign In page instead of using the default one from Auth.js. You can learn how to do so in our [dedicated guide for it](/guides/basics/pages).
|
||||
:::
|
||||
|
||||
|
||||
## 4. Deploying to production
|
||||
|
||||
### Configuring different environments
|
||||
|
||||
It's normal to test your application under different environments. Usually you'll have a development environment (when you run the application locally in your machine), a staging environment (for teams members to try the application) and a production environment.
|
||||
|
||||
For each environment, you're going to need to create an OAuth application in your provider respectively, as [we did previously](#2-configuring-oauth-provider), and point the **callback URL** to it.
|
||||
|
||||
For instance in the previous section, we pointed the callback URL to:
|
||||
|
||||
```
|
||||
http://localhost:3000/api/auth/callback/github
|
||||
```
|
||||
|
||||
as we wanted to test our application in the development environment.
|
||||
|
||||
If we were to deploy our app to production, we would need to create again a new **OAuth App** in Github (calling it something like "Van life – prod") and point the **callback URL** to our production domain:
|
||||
|
||||
```
|
||||
https://example.com/api/auth/callback/github
|
||||
```
|
||||
|
||||
Finally, we would need just to point the environment variables we set ( `GITHUB_ID` and `GITHUB_SECRET` ) to the credentials of the OAuth app we want our application to run against.
|
||||
|
||||
### Setting up `NEXTAUTH_URL`
|
||||
|
||||
When deploying your site, **you need to set** the `NEXTAUTH_URL` environment variable to the canonical URL of your website:
|
||||
|
||||
```
|
||||
NEXTAUTH_URL=https://example.com
|
||||
```
|
||||
|
||||
:::warning
|
||||
In production, this needs to be set as an environment variable on the service you use to deploy your app.
|
||||
|
||||
To set environment variables on Vercel, you can use the [dashboard](https://vercel.com/dashboard) or the `vercel env pull` [command](https://vercel.com/docs/build-step#development-environment-variables).
|
||||
:::
|
||||
|
||||
For more information please check out our [deployment page](/guides/basics/deployment).
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
title: Databases
|
||||
---
|
||||
|
||||
Auth.js offers multiple database adapters. Check our guides on:
|
||||
|
||||
- [using a database adapter](/guides/adapters/using-a-database-adapter)
|
||||
- [creating your own](/guides/adapters/creating-a-database-adapter)
|
||||
|
||||
> As of **v4** Auth.js no longer ships with an adapter included by default. If you would like to persist any information, you need to install one of the many available adapters yourself. See the individual adapter documentation pages for more details.
|
||||
|
||||
To learn more about databases in Auth.js and how they are used, check out [databases in the FAQ](/concepts/faq#databases).
|
||||
|
||||
---
|
||||
|
||||
## How to use a database
|
||||
|
||||
See the [documentation for adapters](/reference/adapters/overview) for more information on advanced configuration, including how to use Auth.js with other databases using a [custom adapter](/guides/adapters/creating-a-database-adapter).
|
||||
@@ -46,7 +46,7 @@ export default NextAuth({
|
||||
```
|
||||
|
||||
:::note
|
||||
Check the [Credentials Provider options](/reference/providers/credentials) for further customization
|
||||
Check the [Credentials Provider options](/reference/core/providers_credentials) for further customization
|
||||
:::
|
||||
|
||||
Note that we only need to define an `authorize` method that is in charge of receiving the credentials inserted by the user and call the authorization service.
|
||||
14
docs/docs/getting-started/databases.md
Normal file
14
docs/docs/getting-started/databases.md
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: Databases
|
||||
---
|
||||
|
||||
Auth.js offers multiple database adapters. Check our guides on:
|
||||
|
||||
- [Using a database adapter](/guides/adapters/using-a-database-adapter)
|
||||
- [Creating your own](/guides/adapters/creating-a-database-adapter)
|
||||
|
||||
To learn more about databases in Auth.js and how they are used, check out [databases in the FAQ](/concepts/faq#databases).
|
||||
|
||||
## How to use a database
|
||||
|
||||
See the [documentation for adapters](/reference/adapters) for more information on advanced configuration, including how to use Auth.js with other databases using a [custom adapter](/guides/adapters/creating-a-database-adapter).
|
||||
@@ -66,7 +66,7 @@ Nice! We're getting there. Now we need to read supply this values as the configu
|
||||
|
||||
```ts title="pages/api/auth/[...nextauth].ts"
|
||||
import NextAuth from "next-auth"
|
||||
import EmailProvider from "next-auth/providers/email"
|
||||
import Email from "next-auth/providers/email"
|
||||
|
||||
export default NextAuth({
|
||||
providers: [
|
||||
44
docs/docs/getting-started/introduction.md
Normal file
44
docs/docs/getting-started/introduction.md
Normal file
@@ -0,0 +1,44 @@
|
||||
---
|
||||
title: Introduction
|
||||
sidebar_position: 0
|
||||
---
|
||||
|
||||
## About Auth.js
|
||||
|
||||
Auth.js is a complete open-source authentication solution for web applications. Check out the live demos of Auth.js in action:
|
||||
|
||||
- [Next.js](https://next-auth-example.vercel.app/)
|
||||
- [SvelteKit](https://sveltekit-auth-example.vercel.app/)
|
||||
- [SolidStart](https://auth-solid.vercel.app/)
|
||||
|
||||
Continue to our tutorials to see how to use Auth.js for authentication:
|
||||
|
||||
- [Setup with OAuth](/getting-started/oauth-tutorial)
|
||||
- [Setup with magic links](/getting-started/email-tutorial)
|
||||
- [Integrating with external auth](/getting-started/credentials-tutorial)
|
||||
|
||||
### Battery included
|
||||
|
||||
- Built in support for 60+ popular services (Google, Facebook, Auth0, Apple…)
|
||||
- Built-in email/password-less/magic link
|
||||
- Use with any OAuth 2 or OpenID Connect provider
|
||||
- Use with any username/password store
|
||||
|
||||
### Flexible
|
||||
- Runtime agnostic - run anywhere! Vercel Edge Functions, Node.js, Serverless, etc.
|
||||
- Use with any modern framework! Next.js, SolidStart, SvelteKit, etc.
|
||||
- [Bring Your Own Database](/getting-started/databases) - or none! MySQL, Postgres, MSSQL, MongoDB, etc. Choose database sessions or JWT.
|
||||
|
||||
_Note: Email sign-in requires a database to store single-use verification tokens._
|
||||
|
||||
### Secure by default
|
||||
- Signed, prefixed, server-only cookies
|
||||
- Built-in CSRF protection
|
||||
- Doesn't rely on client-side JavaScript
|
||||
- JWT with JWS / JWE / JWK.
|
||||
|
||||
## Credits
|
||||
|
||||
Auth.js is an open-source project that is only possible [thanks to contributors](/contributors).
|
||||
|
||||
To financially support the development of Auth.js, you can check our [OpenCollective](https://opencollective.com/nextauth) page. We appreciate your support 💚.
|
||||
369
docs/docs/getting-started/oauth-tutorial.mdx
Normal file
369
docs/docs/getting-started/oauth-tutorial.mdx
Normal file
@@ -0,0 +1,369 @@
|
||||
---
|
||||
title: OAuth authentication
|
||||
---
|
||||
|
||||
import creatingOauthAppImg from "./img/getting-started-creating-oauth-app.png"
|
||||
import addingCallbackUrlImg from "./img/getting-started-oauth-callback-url.png"
|
||||
import gettingClientIdSecretImg from "./img/getting-started-oauth-clientid-secret.png"
|
||||
import startAppAndSignInImg from "./img/getting-started-app-start.png"
|
||||
import githubAuthCredentials from "./img/getting-started-github-auth.png"
|
||||
import nextAuthUserLoggedIn from "./img/getting-started-nextauth-success.png"
|
||||
import Tabs from "@theme/Tabs"
|
||||
import TabItem from "@theme/TabItem"
|
||||
|
||||
The goal of Auth.js is that you can add authentication easily to your project with just a few lines of code.
|
||||
|
||||
The fastest way to set up Auth.js is with an [OAuth](/concepts/oauth) provider. In this tutorial, we'll be setting Auth.js in a web application to be able to log in with **GitHub**.
|
||||
|
||||
:::info
|
||||
Auth.js comes with a list of [built-in providers](/reference/providers/oauth-builtin) (Google, Facebook, Twitter, etc.). You can also integrate it with your OAuth service by [building a custom provider](/guides/providers/custom-provider).
|
||||
:::
|
||||
|
||||
## 1. Configuring Auth.js
|
||||
|
||||
To add Auth.js to your project:
|
||||
|
||||
<Tabs groupId="frameworks" queryString>
|
||||
<TabItem value="next" label="Next.js" default>
|
||||
|
||||
### Prerequisites
|
||||
|
||||
This tutorial assumes you have a Next.js application set up. If you don't, you can follow the [Next.js tutorial](https://nextjs.org/learn/basics/create-nextjs-app) to get started.
|
||||
|
||||
### Installing NextAuth.js
|
||||
|
||||
```bash npm2yarn
|
||||
npm install next-auth
|
||||
```
|
||||
|
||||
:::info
|
||||
We are working on a new `@auth/nextjs` package that will make it easier to set up Auth.js with Next.js. Stay tuned! For now, you can use the `next-auth` package.
|
||||
:::
|
||||
|
||||
### Creating the server config
|
||||
|
||||
Create the following [API route](https://nextjs.org/docs/api-routes/dynamic-api-routes#catch-all-api-routes) file. This route contains the necessary configuration for NextAuth.js, as well as the dynamic route handler:
|
||||
|
||||
```ts title="pages/api/auth/[...nextauth].ts"
|
||||
import NextAuth from "next-auth"
|
||||
import GithubProvider from "next-auth/providers/github"
|
||||
|
||||
export default NextAuth({
|
||||
providers: [
|
||||
GithubProvider({
|
||||
clientId: process.env.GITHUB_ID,
|
||||
clientSecret: process.env.GITHUB_SECRET,
|
||||
}),
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
:::info
|
||||
|
||||
Behind the scenes, this creates all the relevant OAuth API routes within `/api/auth/*` so that auth API requests to:
|
||||
|
||||
- [GET `/api/auth/signin`](https://authjs.dev/reference/rest-api#get--apiauthsignin)
|
||||
- [POST `/api/auth/signin/:provider`](https://authjs.dev/reference/rest-api#post--apiauthsigninprovider)
|
||||
- [GET/POST `/api/auth/callback/:provider`](https://authjs.dev/reference/rest-api#get--post--apiauthcallbackprovider)
|
||||
- [GET `/api/auth/signout`](https://authjs.dev/reference/rest-api#get--apiauthsignout)
|
||||
- [POST `/api/auth/signout`](https://authjs.dev/reference/rest-api#post--apiauthsignout)
|
||||
- [GET `/api/auth/session`](https://authjs.dev/reference/rest-api#get--apiauthsession)
|
||||
- [GET `/api/auth/csrf`](https://authjs.dev/reference/rest-api#get--apiauthcsrf)
|
||||
- [GET `/api/auth/providers`](https://authjs.dev/reference/rest-api#get--apiauthproviders)
|
||||
|
||||
can be handled by NextAuth.js. In this way, NextAuth.js stays in charge of the whole application's authentication request/response flow.
|
||||
|
||||
NextAuth.js is fully customizable - [our guides section](/guides/overview) teaches you how to set it up to handle auth in different ways. All the possible configuration options are [listed here](/reference/configuration/auth-config).
|
||||
:::
|
||||
|
||||
### Adding environment variables
|
||||
|
||||
You may notice we are using environment variables in the code example above. We take the value of `GITHUB_ID` and `GITHUB_SECRET` from the GitHub Developer OAuth Portal. See [Configuring OAuth Provider](/getting-started/oauth-tutorial#2-configuring-oauth-provider) section on how to get those.
|
||||
|
||||
In your project root, create a `.env.local` file and add the `NEXTAUTH_SECRET` environment variable:
|
||||
|
||||
```title=".env.local"
|
||||
NEXTAUTH_SECRET="This is an example"
|
||||
```
|
||||
|
||||
`NEXTAUTH_SECRET` is a random string used by the library to encrypt tokens and email verification hashes, and **it's mandatory to keep things secure**! 🔥 🔐 . You can use:
|
||||
|
||||
```
|
||||
$ openssl rand -base64 32
|
||||
```
|
||||
|
||||
or https://generate-secret.vercel.app/32 to generate a random value for it.
|
||||
|
||||
### Exposing the session via `SessionProvider`:
|
||||
|
||||
NextAuth.js provides [`useSession()`](/reference/react/#usesession) - a [React Hooks](https://reactjs.org/docs/hooks-intro.html) to access the session data and status. To use it first you'll need to expose the session context - [`<SessionProvider />`](/reference/react/#sessionprovider) - at the top level of your application:
|
||||
|
||||
```ts title="pages/_app.tsx"
|
||||
import { SessionProvider } from "next-auth/react"
|
||||
|
||||
export default function App({
|
||||
Component,
|
||||
pageProps: { session, ...pageProps },
|
||||
}) {
|
||||
return (
|
||||
<SessionProvider session={session}>
|
||||
<Component {...pageProps} />
|
||||
</SessionProvider>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
Instances of `useSession` (more on it in the next section) will have access to the session data and status. The `<SessionProvider />` also keep the session updated and synced between browser tabs and windows. 💪🏽
|
||||
|
||||
:::tip
|
||||
Check our [client docs](/reference/react/) to learn all the available options for handling sessions on the browser.
|
||||
:::
|
||||
|
||||
### Consuming the session via hooks
|
||||
|
||||
You can use the `useSession` hook from anywhere in your application (E.g. in a header component). Behind the scenes, the hook will connect to the `<SessionProvider />` to read the current user session. Learn more about React Context in the [React docs](https://reactjs.org/docs/context.html).
|
||||
|
||||
```ts title="pages/overview.tsx"
|
||||
import { useSession, signIn, signOut } from "next-auth/react"
|
||||
|
||||
export default function CamperVanPage() {
|
||||
const { data: session, status } = useSession()
|
||||
const userEmail = session?.user?.email
|
||||
|
||||
if (status === "loading") {
|
||||
return <p>Hang on there...</p>
|
||||
}
|
||||
|
||||
if (status === "authenticated") {
|
||||
return (
|
||||
<>
|
||||
<p>Signed in as {userEmail}</p>
|
||||
<button onClick={() => signOut()}>Sign out</button>
|
||||
<img src="https://cdn.pixabay.com/photo/2017/08/11/19/36/vw-2632486_1280.png" />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>Not signed in.</p>
|
||||
<button onClick={() => signIn("github")}>Sign in</button>
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Protecting API Routes
|
||||
|
||||
To protect your API Routes (blocking unauthorized access to resources), you can use [`getServerSession()`](/reference/nextjs#getserversession) to know whether a session exists or not:
|
||||
|
||||
```ts title="pages/api/movies/list.ts"
|
||||
import { getServerSession } from "next-auth/next"
|
||||
import { authOptions } from "../auth/[...nextauth]"
|
||||
|
||||
export default async function listMovies(req, res) {
|
||||
const session = await getServerSession(req, res, authOptions)
|
||||
|
||||
if (session) {
|
||||
res.send({
|
||||
movies: [
|
||||
{ title: "Alien vs Predator", id: 1 },
|
||||
{ title: "Reservoir Dogs", id: 2 },
|
||||
],
|
||||
})
|
||||
} else {
|
||||
res.send({
|
||||
error: "You must sign in to view movies.",
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="sveltekit" label="SvelteKit">
|
||||
TODO: SvelteKit
|
||||
</TabItem>
|
||||
<TabItem value="solidstart" label="SolidStart">
|
||||
TODO: SolidStart
|
||||
</TabItem>
|
||||
<TabItem value="core" label="Vanilla (No Framework)">
|
||||
TODO Core
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## 2. Configuring OAuth Provider
|
||||
|
||||
Ok, we have our app set up with NextAuth.js, however, if you run the app right now, it won't work as we haven't configured our OAuth provider (**GitHub**) yet.
|
||||
|
||||
:::info
|
||||
When using OAuth you're asking for a third-party service (in this case GitHub, although it could be Google, Twitter, etc...) to handle user authentication for your app.
|
||||
:::
|
||||
|
||||
We need to register our new app in GitHub, so that when NextAuth.js forwards the authorization requests to it, GitHub can recognize your application and prompt the user to sign in.
|
||||
|
||||
<img src={creatingOauthAppImg} />
|
||||
|
||||
Log in to **GitHub**, go to [`Settings / Developers / OAuth Apps`](https://github.com/settings/developers) and click "New OAuth App"
|
||||
|
||||
Next, you'll be presented with a screen to add details about your new application. Fill in the required fields, but pay extra attention to the **Authorization Callback URL** one:
|
||||
|
||||
<img src={addingCallbackUrlImg} />
|
||||
|
||||
The callback URL we insert should have the following pattern:
|
||||
|
||||
```
|
||||
[origin]/api/auth/callback/[provider]
|
||||
```
|
||||
|
||||
In this case, given we want to try our authentication working locally on our machine and we're using **GitHub** as our OAuth provider, it'll be:
|
||||
|
||||
<Tabs groupId="frameworks">
|
||||
<TabItem value="next" label="Next.js" default>
|
||||
|
||||
```
|
||||
http://localhost:3000/api/auth/callback/github
|
||||
```
|
||||
|
||||
:::info
|
||||
NextAuth.js will already create this API endpoint for you when we start the application later. Note that because we're using Next.js, locally it starts our server on port `3000` by default. Hence, the origin is `http://localhost:3000`.
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="sveltekit" label="SvelteKit">
|
||||
|
||||
```
|
||||
http://localhost:5173/auth/callback/github
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="solidstart" label="SolidStart">
|
||||
TODO SolidStart
|
||||
</TabItem>
|
||||
<TabItem value="core" label="Vanilla (No Framework)">
|
||||
TODO Core
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
:::info
|
||||
The last part of the URL, `[provider]`, is the ID of the provider you're using. In this case, we're using GitHub, so it's `github`. If you're using Google, it'll be `google`, etc... We keep track of the provider IDs internally.
|
||||
The same id is used in the `signIn()` method we saw earlier.
|
||||
:::
|
||||
To register, tap on "Register application" button.
|
||||
|
||||
The next screen shows all the configurations for your newly created OAuth app. For now, we need two things from it - the **Client ID** and **Client Secret**:
|
||||
|
||||
<img src={gettingClientIdSecretImg} />
|
||||
|
||||
The Client ID is always there, a public identifier of your OAuth application within GitHub. Click on the **Generate a new client Secret** button and should be presented with a new string (which is just a randomized string).
|
||||
|
||||
:::warning
|
||||
Keep both your Client ID and Client Secret secure and never expose them to the public or share them with people outside your organization. With them, a malicious actor could hijack your application and cause you and your user serious problems!
|
||||
:::
|
||||
|
||||
Cool! We have finished configuring our OAuth provider, now let's wire all together so we can finally see authentication working in our app!
|
||||
|
||||
:::info
|
||||
As noted previously, NextAuth.js has built-in support for multiple OAuth providers, <a href="">here is the full list</a>. You can also easily build your own in case the provider you need is not on the list.
|
||||
|
||||
Note that, for each provider, the configuration process will be similar to what we just did:
|
||||
|
||||
1. Log in to the provider
|
||||
2. Create create your OAuth application within it
|
||||
3. Set the callback URL
|
||||
4. Get the Client ID and Generate a Client Secret
|
||||
:::
|
||||
|
||||
## 3. Wiring all together
|
||||
|
||||
Finally, we just need to reference our **Client ID** and **Client Secret** we just generated in the previous in our Auth.js config. In this way, the library will be able to use them when forwarding users to GitHub, and GitHub will be able to recognize the request as generated from our application.
|
||||
|
||||
Now let's copy both the Client ID and Client Secret and paste them into an environment file in the root of your project like so:
|
||||
|
||||
```title=".env.local"
|
||||
GITHUB_ID=12345
|
||||
GITHUB_SECRET=67890
|
||||
```
|
||||
|
||||
Here is our server configuration file again:
|
||||
|
||||
<Tabs groupId="frameworks">
|
||||
<TabItem value="next" label="Next.js" default>
|
||||
|
||||
```ts title="pages/api/auth/[...nextauth].ts"
|
||||
import NextAuth from "next-auth"
|
||||
import GithubProvider from "next-auth/providers/github"
|
||||
|
||||
export default NextAuth({
|
||||
providers: [
|
||||
GithubProvider({
|
||||
clientId: process.env.GITHUB_ID,
|
||||
clientSecret: process.env.GITHUB_SECRET,
|
||||
}),
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
Great! We're now ready to run our application locally. Start the Next.js app by running on your terminal the following command and navigating to [`http://localhost:3000`](http://localhost:3000):
|
||||
|
||||
```
|
||||
$ npm run next dev
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="sveltekit" label="SvelteKit">
|
||||
TODO SvelteKit
|
||||
</TabItem>
|
||||
<TabItem value="solidstart" label="SolidStart">
|
||||
TODO SolidStart
|
||||
</TabItem>
|
||||
<TabItem value="core" label="Vanilla (No Framework)">
|
||||
TODO Core
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
You should see the following page:
|
||||
|
||||
<img src={startAppAndSignInImg} />
|
||||
|
||||
Click on "Sign in" and then on "Sign in with GitHub": Auth.js will redirect you to GitHub, and GitHub will recognize our app [that we just registered](#2-configuring-oauth-provider) and ask the user (in this case you) to enter its credentials to proceed:
|
||||
|
||||
<img src={githubAuthCredentials} />
|
||||
|
||||
Once inserted and correct, GitHub will redirect the user to our app and NextAuth.js will take care of any further calls with GitHub to get access to the user profile and start a user session safely in the background:
|
||||
|
||||
<img src={nextAuthUserLoggedIn} />
|
||||
|
||||
Great! We have completed the whole E2E authentication flow setup so that users can log in to our application through GitHub!
|
||||
|
||||
## 4. Deploying to production
|
||||
|
||||
### Configuring different environments
|
||||
|
||||
It's normal to test your application in different environments. Usually, you'll have a development environment (when you run the application locally on your machine), a staging environment (for team members to try the application), and a production environment.
|
||||
|
||||
For each environment, you need to create an OAuth application in your provider respectively, as [we did previously](#2-configuring-oauth-provider), and point the **callback URL** to it.
|
||||
|
||||
For instance, in the previous section, we pointed the callback URL to `http://localhost:3000/api/auth/callback/github` as we wanted to test our application in the development environment.
|
||||
|
||||
If we were to deploy our app to production, we would need to create a new **OAuth App** in GitHub (calling it something like "Van life – prod") and point the **callback URL** to our production domain: `https://example.com/api/auth/callback/github`
|
||||
|
||||
Finally, we would need to point the environment variables we set ( `GITHUB_ID` and `GITHUB_SECRET` ) to the credentials of the OAuth app we want our application to run with.
|
||||
|
||||
### Setting up `NEXTAUTH_URL`
|
||||
|
||||
:::tip
|
||||
Skip this section if you are deploying to Vercel.
|
||||
:::
|
||||
|
||||
When deploying your site, **you need to set** the `NEXTAUTH_URL` environment variable to the canonical URL of your website:
|
||||
|
||||
```
|
||||
NEXTAUTH_URL=https://example.com
|
||||
```
|
||||
|
||||
:::warning
|
||||
In production, this needs to be set as an environment variable on the service you use to deploy your app.
|
||||
|
||||
To set environment variables on Vercel, you can use the [dashboard](https://vercel.com/dashboard) or the `vercel env pull` [command](https://vercel.com/docs/build-step#development-environment-variables).
|
||||
:::
|
||||
|
||||
For more information please check out our [deployment page](/guides/basics/deployment).
|
||||
@@ -239,7 +239,7 @@ Introduced in https://github.com/nextauthjs/next-auth/releases/tag/v4.0.0-next.1
|
||||
|
||||
## `nodemailer`
|
||||
|
||||
Like `typeorm` and `prisma`, [`nodemailer`](https://npmjs.com/package/nodemailer) is no longer included as a dependency by default. If you are using the Email provider you must install it in your project manually, or use any other Email library in the [`sendVerificationRequest`](/reference/providers/email) callback. This reduces bundle size for those not actually using the Email provider. Remember, when using the Email provider, it is mandatory to also use a database adapter due to the fact that verification tokens need to be persisted longer term for the magic link functionality to work.
|
||||
Like `typeorm` and `prisma`, [`nodemailer`](https://npmjs.com/package/nodemailer) is no longer included as a dependency by default. If you are using the Email provider you must install it in your project manually, or use any other Email library in the [`sendVerificationRequest`](/guides/providers/email) callback. This reduces bundle size for those not actually using the Email provider. Remember, when using the Email provider, it is mandatory to also use a database adapter due to the fact that verification tokens need to be persisted longer term for the magic link functionality to work.
|
||||
|
||||
Introduced in https://github.com/nextauthjs/next-auth/releases/tag/v4.0.0-next.2
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
---
|
||||
title: Using a custom Provider
|
||||
sidebar_label: Creating a Provider
|
||||
---
|
||||
|
||||
You can use an OAuth provider that isn't built-in by using a custom object.
|
||||
|
||||
As an example of what this looks like, this is the provider object returned for the Google provider:
|
||||
|
||||
```js
|
||||
{
|
||||
id: "google",
|
||||
name: "Google",
|
||||
type: "oauth",
|
||||
wellKnown: "https://accounts.google.com/.well-known/openid-configuration",
|
||||
authorization: { params: { scope: "openid email profile" } },
|
||||
idToken: true,
|
||||
checks: ["pkce", "state"],
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.sub,
|
||||
name: profile.name,
|
||||
email: profile.email,
|
||||
image: profile.picture,
|
||||
}
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
As you can see, if your provider supports OpenID Connect and the `/.well-known/openid-configuration` endpoint contains support for the `grant_type`: `authorization_code`, you only need to pass the URL to that configuration file and define some basic fields like `name` and `type`.
|
||||
|
||||
Otherwise, you can pass a more full set of URLs for each OAuth2.0 flow step, for example:
|
||||
|
||||
```js
|
||||
{
|
||||
id: "kakao",
|
||||
name: "Kakao",
|
||||
type: "oauth",
|
||||
authorization: "https://kauth.kakao.com/oauth/authorize",
|
||||
token: "https://kauth.kakao.com/oauth/token",
|
||||
userinfo: "https://kapi.kakao.com/v2/user/me",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.id,
|
||||
name: profile.kakao_account?.profile.nickname,
|
||||
email: profile.kakao_account?.email,
|
||||
image: profile.kakao_account?.profile.profile_image_url,
|
||||
}
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Replace all the options in this JSON object with the ones from your custom provider - be sure to give it a unique ID and specify the required URLs, and finally add it to the providers array when initializing the library:
|
||||
|
||||
```js title="pages/api/auth/[...nextauth].js"
|
||||
import TwitterProvider from "next-auth/providers/twitter"
|
||||
...
|
||||
providers: [
|
||||
TwitterProvider({
|
||||
clientId: process.env.TWITTER_ID,
|
||||
clientSecret: process.env.TWITTER_SECRET,
|
||||
}),
|
||||
{
|
||||
id: 'customProvider',
|
||||
name: 'CustomProvider',
|
||||
type: 'oauth',
|
||||
scope: '' // Make sure to request the users email address
|
||||
...
|
||||
}
|
||||
]
|
||||
...
|
||||
```
|
||||
|
||||
### Override default options
|
||||
|
||||
For built-in providers, in most cases you will only need to specify the `clientId` and `clientSecret`. If you need to override any of the defaults, add your own [options](#options).
|
||||
|
||||
Even if you are using a built-in provider, you can override any of these options to tweak the default configuration.
|
||||
|
||||
:::note
|
||||
The user provided options are deeply merged with the default options. That means you only have to override part of the options that you need to be different. For example if you want different scopes, overriding `authorization.params.scope` is enough, instead of the whole `authorization` option.
|
||||
:::
|
||||
|
||||
```js title=/api/auth/[...nextauth].js
|
||||
import Auth0Provider from "next-auth/providers/auth0"
|
||||
|
||||
Auth0Provider({
|
||||
clientId: process.env.CLIENT_ID,
|
||||
clientSecret: process.env.CLIENT_SECRET,
|
||||
issuer: process.env.ISSUER,
|
||||
authorization: { params: { scope: "openid your_custom_scope" } },
|
||||
})
|
||||
```
|
||||
|
||||
Another example, the `profile` callback will return `id`, `name`, `email` and `picture` by default, but you might need more information from the provider. After setting the correct scopes, you can then do something like this:
|
||||
|
||||
```js title=/api/auth/[...nextauth].js
|
||||
import GoogleProvider from "next-auth/providers/google"
|
||||
|
||||
GoogleProvider({
|
||||
clientId: process.env.GOOGLE_CLIENT_ID,
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
||||
profile(profile) {
|
||||
return {
|
||||
// Return all the profile information you need.
|
||||
// The only truly required field is `id`
|
||||
// to be able identify the account when added to a database
|
||||
}
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
An example of how to enable automatic account linking:
|
||||
|
||||
```js title=/api/auth/[...nextauth].js
|
||||
import GoogleProvider from "next-auth/providers/google"
|
||||
GoogleProvider({
|
||||
clientId: process.env.GOOGLE_CLIENT_ID,
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
||||
allowDangerousEmailAccountLinking: true,
|
||||
})
|
||||
```
|
||||
|
||||
### Adding a new built-in provider
|
||||
|
||||
If you think your custom provider might be useful to others, we encourage you to open a PR and add it to the built-in list so others can discover it much more easily!
|
||||
|
||||
You only need to add three changes:
|
||||
|
||||
1. Add your config: [`src/providers/{provider}.ts`](https://github.com/nextauthjs/next-auth/tree/main/packages/next-auth/src/providers)<br />
|
||||
- Make sure you use a named default export, like this: `export default function YourProvider`
|
||||
- Add two SVG's of the provider logo, like `google-dark.svg` (dark mode) and `google.svg` (light mode), to the `/packages/next-auth/provider-logos/` directory as well as the styling config to the provider config object. See existing provider for example
|
||||
2. Add provider documentation: [`docs/docs/reference/05-oauth-providers/{provider}.md`](https://github.com/nextauthjs/next-auth/tree/main/docs/docs/reference/05-oauth-providers)
|
||||
3. Add the new provider name to the `Provider type` dropdown options in [`the provider issue template`](https://github.com/nextauthjs/next-auth/edit/main/.github/ISSUE_TEMPLATE/2_bug_provider.yml)
|
||||
|
||||
That's it! 🎉 Others will be able to discover and use this provider much more easily now!
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"label": "Other",
|
||||
"collapsible": true,
|
||||
"collapsed": true
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
---
|
||||
title: LDAP Authentication
|
||||
---
|
||||
|
||||
Auth.js provides the ability to setup a [custom Credential provider](/guides/providers/credentials) which we can take advantage of to authenticate users against an existing LDAP server.
|
||||
|
||||
You will need an additional dependency, `ldapjs`, which you can install by running
|
||||
|
||||
```bash npm2yarn2pnpm
|
||||
npm install ldapjs
|
||||
```
|
||||
|
||||
Then you must setup the `CredentialsProvider()` provider key like so:
|
||||
|
||||
```js title="[...nextauth].js"
|
||||
const ldap = require("ldapjs")
|
||||
import NextAuth from "next-auth"
|
||||
import CredentialsProvider from "next-auth/providers/credentials"
|
||||
|
||||
export default NextAuth({
|
||||
providers: [
|
||||
CredentialsProvider({
|
||||
name: "LDAP",
|
||||
credentials: {
|
||||
username: { label: "DN", type: "text", placeholder: "" },
|
||||
password: { label: "Password", type: "password" },
|
||||
},
|
||||
async authorize(credentials, req) {
|
||||
// You might want to pull this call out so we're not making a new LDAP client on every login attempt
|
||||
const client = ldap.createClient({
|
||||
url: process.env.LDAP_URI,
|
||||
})
|
||||
|
||||
// Essentially promisify the LDAPJS client.bind function
|
||||
return new Promise((resolve, reject) => {
|
||||
client.bind(credentials.username, credentials.password, (error) => {
|
||||
if (error) {
|
||||
console.error("Failed")
|
||||
reject()
|
||||
} else {
|
||||
console.log("Logged in")
|
||||
resolve({
|
||||
username: credentials.username,
|
||||
password: credentials.password,
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
}),
|
||||
],
|
||||
callbacks: {
|
||||
async jwt({ token, user }) {
|
||||
const isSignIn = user ? true : false
|
||||
if (isSignIn) {
|
||||
token.username = user.username
|
||||
token.password = user.password
|
||||
}
|
||||
return token
|
||||
},
|
||||
async session({ session, token }) {
|
||||
return { ...session, user: { username: token.username } }
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
The idea is that once one is authenticated with the LDAP server, one can pass through both the username/DN and password to the JWT stored in the browser.
|
||||
|
||||
This is then passed back to any API routes and retrieved as such:
|
||||
|
||||
```js title="/pages/api/doLDAPWork.js"
|
||||
token = await jwt.getToken({
|
||||
req,
|
||||
})
|
||||
const { username, password } = token
|
||||
```
|
||||
|
||||
> Thanks to [Winwardo](https://github.com/Winwardo) for the code example
|
||||
@@ -1,60 +0,0 @@
|
||||
---
|
||||
title: Usage with class components
|
||||
---
|
||||
|
||||
If you want to use the [`useSession()`](/reference/react/#usesession) hook in your class components you can do so with the help of a higher order component or with a render prop.
|
||||
|
||||
## Higher Order Component
|
||||
|
||||
```js
|
||||
import { useSession } from "next-auth/react"
|
||||
|
||||
const withSession = (Component) => (props) => {
|
||||
const session = useSession()
|
||||
|
||||
// if the component has a render property, we are good
|
||||
if (Component.prototype.render) {
|
||||
return <Component session={session} {...props} />
|
||||
}
|
||||
|
||||
// if the passed component is a function component, there is no need for this wrapper
|
||||
throw new Error(
|
||||
[
|
||||
"You passed a function component, `withSession` is not needed.",
|
||||
"You can `useSession` directly in your component.",
|
||||
].join("\n")
|
||||
)
|
||||
}
|
||||
|
||||
// Usage
|
||||
class ClassComponent extends React.Component {
|
||||
render() {
|
||||
const { data: session, status } = this.props.session
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const ClassComponentWithSession = withSession(ClassComponent)
|
||||
```
|
||||
|
||||
## Render Prop
|
||||
|
||||
```js
|
||||
import { useSession } from "next-auth/react"
|
||||
|
||||
const UseSession = ({ children }) => {
|
||||
const session = useSession()
|
||||
return children(session)
|
||||
}
|
||||
|
||||
// Usage
|
||||
class ClassComponent extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<UseSession>
|
||||
{(session) => <pre>{JSON.stringify(session, null, 2)}</pre>}
|
||||
</UseSession>
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -10,4 +10,4 @@ When using a database, you can still use JWT for session handling for fast acces
|
||||
|
||||
We have a list of official adapters that are distributed as their own packages under the `@next-auth/{name}-adapter` namespace. Their source code is available in their various adapters package directories at [`nextauthjs/next-auth`](https://github.com/nextauthjs/next-auth/tree/main/packages):
|
||||
|
||||
- [All available adapters](/reference/adapters/overview)
|
||||
- [All available adapters](/reference/adapters)
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"label": "Basics",
|
||||
"collapsible": true,
|
||||
"collapsed": true
|
||||
"collapsed": false
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
title: Role-based authentication
|
||||
title: Role-based access control
|
||||
---
|
||||
|
||||
There are two ways to add role-based authentication (RBAC) to your application, based on the [session strategy](/concepts/session-strategies) you choose. Let's see an example for each of these.
|
||||
There are two ways to add role-based access control (RBAC) to your application, based on the [session strategy](/concepts/session-strategies) you choose. Let's see an example for each of these.
|
||||
|
||||
## Getting the role
|
||||
|
||||
@@ -150,4 +150,4 @@ When using Next.js and JWT, you can alternatively also use [Middleware](https://
|
||||
- [Next.js: Middleware](https://next-auth.js.org/configuration/nextjs#wrap-middleware)
|
||||
- [Adapters: User model](/reference/adapters/models#user)
|
||||
- [Adapters: Prisma adapter](/reference/adapters/prisma)
|
||||
- [TypeScript](/getting-started/typescript)
|
||||
- [TypeScript](/getting-started/typescript)
|
||||
@@ -1,9 +1,12 @@
|
||||
---
|
||||
title: Overview
|
||||
sidebar_label: Guides
|
||||
sidebar_position: 0
|
||||
---
|
||||
|
||||
We're creating internal guides to help understand how to use Auth.js and all the possible configurations and uses cases it supports.
|
||||
This section contains guides for common use cases.
|
||||
|
||||
If you can't find what you're looking for, [raise an issue](https://github.com/nextauthjs/next-auth/issues/new/choose) then take a look at our third-party [community resources](/guides/resources).
|
||||
If you can't find what you're looking for, [raise an issue](https://github.com/nextauthjs/next-auth/issues/new?assignees=&labels=triage%2Cdocumentation&template=4_documentation.yml).
|
||||
|
||||
:::warning Warning
|
||||
Guides are being migrated from the [old documentation page](https://next-auth.js.org), so there are going to be references to `next-auth` still. We are continuously working on updating the naming/references.
|
||||
:::
|
||||
@@ -3,21 +3,21 @@ id: credentials
|
||||
title: Credentials Provider
|
||||
---
|
||||
|
||||
The Credentials provider allows you to handle signing in with arbitrary credentials, such as a username and password, domain, or two factor authentication or hardware device (e.g. YubiKey U2F / FIDO).
|
||||
The Credentials provider allows you to handle signing in with arbitrary credentials, such as a username and password, domain, or two-factor authentication or hardware device (e.g. YubiKey U2F / FIDO).
|
||||
|
||||
It is intended to support use cases where you have an existing system you need to authenticate users against.
|
||||
|
||||
It comes with the constraint that users authenticated in this manner are not persisted in the database, and consequently that the Credentials provider can only be used if JSON Web Tokens are enabled for sessions.
|
||||
|
||||
:::warning
|
||||
The functionality provided for credentials based authentication is intentionally limited to discourage use of passwords due to the inherent security risks associated with them and the additional complexity associated with supporting usernames and passwords.
|
||||
The functionality provided for credentials-based authentication is intentionally limited to discourage the use of passwords due to the inherent security risks associated with them and the additional complexity associated with supporting usernames and passwords.
|
||||
:::
|
||||
|
||||
## Options
|
||||
|
||||
The **Credentials Provider** comes with a set of default options:
|
||||
|
||||
- [Credentials Provider options](/reference/providers/credentials)
|
||||
- [Credentials Provider options](/reference/core/providers_credentials)
|
||||
|
||||
You can override any of the options to suit your own use case.
|
||||
|
||||
@@ -33,7 +33,7 @@ If you return an object it will be persisted to the JSON Web Token and the user
|
||||
|
||||
3. If you throw an Error, the user will be sent to the error page with the error message as a query parameter.
|
||||
|
||||
The Credentials provider's `authorize()` method also provides the request object as the second parameter (see example below).
|
||||
The Credentials provider's `authorize()` method also provides the request object as the second parameter (see the example below).
|
||||
|
||||
```js title="pages/api/auth/[...nextauth].js"
|
||||
import CredentialsProvider from "next-auth/providers/credentials";
|
||||
@@ -89,7 +89,7 @@ You can specify more than one credentials provider by specifying a unique `id` f
|
||||
|
||||
You can also use them in conjunction with other provider options.
|
||||
|
||||
As with all providers, the order you specify them is the order they are displayed on the sign in page.
|
||||
As with all providers, the order you specify is the order they are displayed on the sign-in page.
|
||||
|
||||
```js
|
||||
providers: [
|
||||
112
docs/docs/guides/providers/custom-provider.md
Normal file
112
docs/docs/guides/providers/custom-provider.md
Normal file
@@ -0,0 +1,112 @@
|
||||
---
|
||||
title: OAuth Provider
|
||||
---
|
||||
|
||||
Auth.js comes with a set of built-in OAuth providers that you can import from `@auth/core/providers/*`. Every provider has their separate documentation page under the [core package's API Reference](/reference/core)
|
||||
|
||||
|
||||
## Use your own provider
|
||||
|
||||
However, you can use _any_ provider as long as they are compliant with the OAuth/OIDC specifications.
|
||||
|
||||
Auth.js uses the [`oauth4webapi`](https://github.com/panva/oauth4webapi/blob/main/docs/README.md) package under the hood.
|
||||
|
||||
To use a custom OAuth provider with Auth.js, pass an object to the [`providers` list](/reference/core#providers).
|
||||
|
||||
It can implement either the [`OAuth2Config`](/reference/core/providers#oauth2configprofile) or the [`OIDCConfig`](/reference/core/providers#oidcconfigprofile) interface, depending on if your provider is OAuth 2 or OpenID Connect compliant.
|
||||
|
||||
For example, if you have a fully OIDC-compliant provider, this is all you need:
|
||||
|
||||
```ts
|
||||
import type { OIDCConfig } from "@auth/core/providers"
|
||||
|
||||
...
|
||||
providers: [
|
||||
{
|
||||
id: "my-oidc-provider",
|
||||
name: "My Provider",
|
||||
type: "oidc",
|
||||
issuer: "https://my.oidc-provider.com",
|
||||
clientId: process.env.CLIENT_ID,
|
||||
clientSecret: process.env.CLIENT_SECRET
|
||||
} satisfies OIDCConfig
|
||||
]
|
||||
...
|
||||
```
|
||||
|
||||
Then, you can set the [Redirect URI](https://www.ietf.org/archive/id/draft-ietf-oauth-v2-1-07.html#name-client-redirection-endpoint) in your provider's dashboard to something like `https://app-url.com/{path-to-auth-handler}/callback/my-oidc-provider`.
|
||||
|
||||
`{path-to-auth-handler}` is _usually_ `auth` or `api/auth`, depending on your framework of your choice.
|
||||
`my-oidc-provider` matches the `id` you set in the [`providers` list](/reference/core#providers).
|
||||
|
||||
|
||||
## Override default provider config
|
||||
|
||||
For built-in providers, in most cases you will only need to specify the `clientId` and `clientSecret`, and in case of OIDC providers, the `issuer` property. If you need to override any of the defaults, you can add them in the provider's function call and they will be deep-merged with the default configuration options.
|
||||
|
||||
:::note
|
||||
The user provided options are deeply merged with the default options. That means you only have to override part of the options that you need to be different. For example if you want different scopes, overriding `authorization.params.scope` is enough, instead of the whole `authorization` option.
|
||||
:::
|
||||
|
||||
|
||||
For example, to override a provider's default scopes, you can do the following:
|
||||
|
||||
```ts
|
||||
import Auth0Provider from "@auth/core/providers/auth0"
|
||||
|
||||
Auth0Provider({
|
||||
clientId: process.env.CLIENT_ID,
|
||||
clientSecret: process.env.CLIENT_SECRET,
|
||||
issuer: process.env.ISSUER,
|
||||
authorization: { params: { scope: "openid your_custom_scope" } },
|
||||
})
|
||||
```
|
||||
|
||||
Another example, the `profile` callback will return `id`, `name`, `email` and `picture` by default, but you might want to return more information from the provider. After setting the correct scopes, you can then do something like this:
|
||||
|
||||
```ts
|
||||
import GoogleProvider from "@auth/core/providers/google"
|
||||
|
||||
GoogleProvider({
|
||||
clientId: process.env.GOOGLE_CLIENT_ID,
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
||||
profile(profile) {
|
||||
return {
|
||||
// Return all the profile information you need.
|
||||
// The only truly required field is `id`
|
||||
// to be able identify the account when added to a database
|
||||
}
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
An example of how to enable automatic account linking:
|
||||
|
||||
```ts
|
||||
import GoogleProvider from "@auth/core/providers/google"
|
||||
|
||||
GoogleProvider({
|
||||
clientId: process.env.GOOGLE_CLIENT_ID,
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
||||
allowDangerousEmailAccountLinking: true,
|
||||
})
|
||||
```
|
||||
|
||||
## Adding a new built-in provider
|
||||
|
||||
If you think your custom provider might be useful to others, we encourage you to open a PR and add it to the built-in list.
|
||||
|
||||
:::note
|
||||
We are only accepting new providers to `@auth/core`, and not `next-auth`. Follow the steps below to make sure your PR is merged!
|
||||
:::
|
||||
|
||||
1. Create a new `{provider}.ts` (for it to get merged, you must use TypeScript) file under the [`packages/core/src/providers`](https://github.com/nextauthjs/next-auth/tree/main/packages/core/src/providers) directory.
|
||||
2. Make sure that you are following other providers, ie.:
|
||||
- Use a named default export: `export default function YourProvider`
|
||||
- Export the TypeScript `interface` that defines the provider's available user info properties
|
||||
- Add the necessary JSDoc comments/documentation (Study the built-in providers to get an understanding what's needed. For example, the [Auth0 provider](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/auth0.ts) is a good example for OIDC and the [GitHub Provider](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/github.ts) is an OAuth provider.)
|
||||
- Add links to the provider's API reference/documentation so others can understand how to use the provider
|
||||
3. Add the new provider name to the `Provider type` dropdown options in [`the provider issue template`](https://github.com/nextauthjs/next-auth/edit/main/.github/ISSUE_TEMPLATE/2_bug_provider.yml)
|
||||
4. (Optional): Add a logo `{provider}.svg` to the [`docs/static/img/providers`](https://github.com/nextauthjs/next-auth/tree/main/docs/static/img/providers) directory.
|
||||
|
||||
That's it! 🎉 Others will be able to discover and use this provider!
|
||||
109
docs/docs/guides/providers/email-http-api.md
Normal file
109
docs/docs/guides/providers/email-http-api.md
Normal file
@@ -0,0 +1,109 @@
|
||||
---
|
||||
id: email-http
|
||||
title: HTTP-based Email Provider
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
:::note
|
||||
The following guide is written for `next-auth` (NextAuth.js), but it should work for any of the Auth.js framework libraries (`@auth/*`) as well.
|
||||
:::
|
||||
|
||||
|
||||
There is a built-in Email provider with which you could connect to the SMTP server of your choice to send "magic link" emails for sign-in purposes. However, the Email provider can also be used with HTTP-based email services, like AWS SES, Postmark, Sendgrid, etc. In this guide, we are going to explain how to use our Email magic link provider with any of the more modern HTTP-based Email APIs.
|
||||
|
||||
For this example, we will be using [SendGrid](https://sendgrid.com), but any email service providing an HTTP API or JS client library will work.
|
||||
We will also refer to the [Prisma Adapter](/reference/adapter/prisma). A [database adapter](/adapters/overview) is a requirement for the Email provider.
|
||||
|
||||
## Setup
|
||||
|
||||
First, if you do not have a project using Auth.js, clone and set up a basic Auth.js project like the one [provided in](https://github.com/nextauthjs/next-auth-example.git) our example repo](https://github.com/nextauthjs/next-auth-example.git).
|
||||
|
||||
- Install the [Prisma Adapter](/reference/adapter/prisma)
|
||||
- Generate an API key from your cloud Email provider of choice and add it to your `.env.*` file. For example, mine is going to be called `SENDGRID_API`
|
||||
- Add the following configuration to your configuration file:
|
||||
|
||||
```js title="pages/api/auth/[...nextauth].ts"
|
||||
import NextAuth, { NextAuthOptions } from "next-auth"
|
||||
import { PrismaAdapter } from "@next-auth/prisma-adapter"
|
||||
import { PrismaClient } from "@prisma/client"
|
||||
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
export const authOptions: NextAuthOptions = {
|
||||
adapter: PrismaAdapter(prisma),
|
||||
providers: [
|
||||
{
|
||||
id: 'sendgrid',
|
||||
type: 'email',
|
||||
async sendVerificationRequest({identifier: email, url}) {
|
||||
}
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
export default NextAuth(authOptions)
|
||||
```
|
||||
|
||||
Next, all that's left to do is call the HTTP endpoint from our cloud email provider and pass it the required metadata like the `to` address, the email `body`, and any other fields we may need to include.
|
||||
|
||||
As mentioned earlier, we're going to be using SendGrid in this example, so the appropriate endpoint is `https://api.sendgrid.com/v3/mail/send` ([more info](https://docs.sendgrid.com/for-developers/sending-email/api-getting-started)). Therefore, we're going to pull out some of the important information from the `params` argument and use it in a `fetch()` call to the previously mentioned SendGrid API.
|
||||
|
||||
```js title="pages/api/auth/[...nextauth].ts"
|
||||
import NextAuth, { NextAuthOptions } from "next-auth"
|
||||
import { PrismaAdapter } from "@next-auth/prisma-adapter"
|
||||
import { PrismaClient } from "@prisma/client"
|
||||
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
export const authOptions: NextAuthOptions = {
|
||||
adapter: PrismaAdapter(prisma),
|
||||
providers: [
|
||||
{
|
||||
id: 'sendgrid',
|
||||
type: 'email',
|
||||
async sendVerificationRequest({identifier: email, url}) {
|
||||
// highlight-start
|
||||
// Call the cloud Email provider API for sending emails
|
||||
// See https://docs.sendgrid.com/api-reference/mail-send/mail-send
|
||||
const response = await fetch("https://api.sendgrid.com/v3/mail/send", {
|
||||
// The body format will vary depending on provider, please see their documentation
|
||||
// for further details.
|
||||
body: JSON.stringify({
|
||||
personalizations: [{ to: [{ email }] }],
|
||||
from: { email: "noreply@company.com" },
|
||||
subject: "Sign in to Your page",
|
||||
content: [
|
||||
{
|
||||
type: "text/plain",
|
||||
value: `Please click here to authenticate - ${url}`,
|
||||
},
|
||||
],
|
||||
}),
|
||||
headers: {
|
||||
// Authentication will also vary from provider to provider, please see their docs.
|
||||
Authorization: `Bearer ${process.env.SENDGRID_API}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
method: "POST",
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const { errors } = await response.json()
|
||||
throw new Error(JSON.stringify(errors))
|
||||
}
|
||||
// highlight-end
|
||||
},
|
||||
}
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
And that's all we need to do to send Emails via an HTTP API! Note here that the example is only using `text/plain` as the body type. You'll probably want to change that to `text/html` and pass in a nice-looking HTML email. See, for example, our `html` function in [the Auth.js docs](/providers/email#customizing-emails).
|
||||
|
||||
To sign in via this custom provider, you would refer to it by the `id` in when you are calling the sign-in method, for example: `signIn('sendgrid', { email: 'user@company.com' })`.
|
||||
|
||||
## References
|
||||
|
||||
- [Email provider documentation with HTML generation and more](/reference/core/modules/providers_email)
|
||||
- [SendGrid JSON Body documentation](https://docs.sendgrid.com/api-reference/mail-send/mail-send#body)
|
||||
@@ -23,7 +23,7 @@ The Email Provider can be used with both JSON Web Tokens and database sessions,
|
||||
|
||||
The **Email Provider** comes with a set of default options:
|
||||
|
||||
- [Email Provider options](/reference/providers/email)
|
||||
- [Email Provider options](/guides/providers/email)
|
||||
|
||||
You can override any of the options to suit your own use case.
|
||||
|
||||
@@ -1,447 +0,0 @@
|
||||
---
|
||||
title: Initialization
|
||||
---
|
||||
|
||||
## Options
|
||||
|
||||
Options are passed to Auth.js when initializing it in a server environment like a Next.js API Route.
|
||||
|
||||
### providers
|
||||
|
||||
- **Default value**: `[]`
|
||||
- **Required**: _Yes_
|
||||
|
||||
#### Description
|
||||
|
||||
An array of authentication providers for signing in (e.g. Google, Facebook, Twitter, GitHub, Email, etc) in any order. This can be one of the built-in providers or an object with a custom provider.
|
||||
|
||||
Refer to the list of [all available Oauth providers](/reference/providers/oauth-builtin) and the [Oauth tutorial](/getting-started/oauth-tutorial) on how to use them.
|
||||
|
||||
---
|
||||
|
||||
### secret
|
||||
|
||||
- **Default value**: `string` (_SHA hash of the "options" object_) in development, no default in production.
|
||||
- **Required**: _Yes, in production!_
|
||||
|
||||
#### Description
|
||||
|
||||
A random string is used to hash tokens, sign/encrypt cookies and generate cryptographic keys.
|
||||
|
||||
If you set [`NEXTAUTH_SECRET`](#nextauth_secret) as an environment variable, you don't have to define this option.
|
||||
|
||||
If no value specified specified in development (and there is no `NEXTAUTH_SECRET` variable either), it uses a hash for all configuration options, including OAuth Client ID / Secrets for entropy.
|
||||
|
||||
:::warning
|
||||
Not providing any `secret` or `NEXTAUTH_SECRET` will throw [an error](/reference/errors#no_secret) in production.
|
||||
:::
|
||||
|
||||
You can quickly create a good value on the command line via this `openssl` command.
|
||||
|
||||
```bash
|
||||
$ openssl rand -base64 32
|
||||
```
|
||||
|
||||
:::tip
|
||||
If you rely on the default secret generation in development, you might notice JWT decryption errors, since the secret changes whenever you change your configuration. Defining an explicit secret will make this problem go away. We will likely make this option mandatory, even in development, in the future.
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
### session
|
||||
|
||||
- **Default value**: `object`
|
||||
- **Required**: _No_
|
||||
|
||||
#### Description
|
||||
|
||||
The `session` object and all properties on it are optional.
|
||||
|
||||
Default values for this option are shown below:
|
||||
|
||||
```js
|
||||
session: {
|
||||
// Choose how you want to save the user session.
|
||||
// The default is `"jwt"`, an encrypted JWT (JWE) stored in the session cookie.
|
||||
// If you use an `adapter` however, we default it to `"database"` instead.
|
||||
// You can still force a JWT session by explicitly defining `"jwt"`.
|
||||
// When using `"database"`, the session cookie will only contain a `sessionToken` value,
|
||||
// which is used to look up the session in the database.
|
||||
strategy: "database",
|
||||
|
||||
// Seconds - How long until an idle session expires and is no longer valid.
|
||||
maxAge: 30 * 24 * 60 * 60, // 30 days
|
||||
|
||||
// Seconds - Throttle how frequently to write to database to extend a session.
|
||||
// Use it to limit write operations. Set to 0 to always update the database.
|
||||
// Note: This option is ignored if using JSON Web Tokens
|
||||
updateAge: 24 * 60 * 60, // 24 hours
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### jwt
|
||||
|
||||
- **Default value**: `object`
|
||||
- **Required**: _No_
|
||||
|
||||
#### Description
|
||||
|
||||
JSON Web Tokens can be used for session tokens if enabled with `session: { strategy: "jwt" }` option. JSON Web Tokens are enabled by default if you have not specified an adapter. JSON Web Tokens are encrypted (JWE) by default. We recommend you keep this behaviour. See the [Override JWT `encode` and `decode` methods](#override-jwt-encode-and-decode-methods) advanced option.
|
||||
|
||||
#### JSON Web Token Options
|
||||
|
||||
```js
|
||||
jwt: {
|
||||
// The maximum age of the Auth.js issued JWT in seconds.
|
||||
// Defaults to `session.maxAge`.
|
||||
maxAge: 60 * 60 * 24 * 30,
|
||||
// You can define your own encode/decode functions for signing and encryption
|
||||
async encode() {},
|
||||
async decode() {},
|
||||
}
|
||||
```
|
||||
|
||||
An example JSON Web Token contains a payload like this:
|
||||
|
||||
```js
|
||||
{
|
||||
name: 'Iain Collins',
|
||||
email: 'me@iaincollins.com',
|
||||
picture: 'https://example.com/image.jpg',
|
||||
iat: 1594601838,
|
||||
exp: 1597193838
|
||||
}
|
||||
```
|
||||
|
||||
#### JWT Helper
|
||||
|
||||
You can use the built-in `getToken()` helper method to verify and decrypt the token, like this:
|
||||
|
||||
```js
|
||||
import { getToken } from "next-auth/jwt"
|
||||
|
||||
const secret = process.env.NEXTAUTH_SECRET
|
||||
|
||||
export default async function handler(req, res) {
|
||||
// if using `NEXTAUTH_SECRET` env variable, we detect it, and you won't actually need to `secret`
|
||||
// const token = await getToken({ req })
|
||||
const token = await getToken({ req, secret })
|
||||
console.log("JSON Web Token", token)
|
||||
res.end()
|
||||
}
|
||||
```
|
||||
|
||||
_For convenience, this helper function is also able to read and decode tokens passed from the `Authorization: 'Bearer token'` HTTP header._
|
||||
|
||||
**Required**
|
||||
|
||||
The getToken() helper requires the following options:
|
||||
|
||||
- `req` - (object) Request object
|
||||
- `secret` - (string) JWT Secret. Use `NEXTAUTH_SECRET` instead.
|
||||
|
||||
You must also pass _any options configured on the `jwt` option_ to the helper.
|
||||
|
||||
e.g. Including custom session `maxAge` and custom signing and/or encryption keys or options
|
||||
|
||||
**Optional**
|
||||
|
||||
It also supports the following options:
|
||||
|
||||
- `secureCookie` - (boolean) Use secure prefixed cookie name
|
||||
|
||||
By default, the helper function will attempt to determine if it should use the secure prefixed cookie (e.g. `true` in production and `false` in development, unless NEXTAUTH_URL contains an HTTPS URL).
|
||||
|
||||
- `cookieName` - (string) Session token cookie name
|
||||
|
||||
The `secureCookie` option is ignored if `cookieName` is explicitly specified.
|
||||
|
||||
- `raw` - (boolean) Get raw token (not decoded)
|
||||
|
||||
If set to `true` returns the raw token without decrypting or verifying it.
|
||||
|
||||
:::note
|
||||
The JWT is stored in the Session Token cookie, the same cookie used for tokens with database sessions.
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
### pages
|
||||
|
||||
- **Default value**: `{}`
|
||||
- **Required**: _No_
|
||||
|
||||
#### Description
|
||||
|
||||
Specify URLs to be used if you want to create custom sign in, sign out and error pages.
|
||||
|
||||
Pages specified will override the corresponding built-in page.
|
||||
|
||||
_For example:_
|
||||
|
||||
```js
|
||||
pages: {
|
||||
signIn: '/auth/signin',
|
||||
signOut: '/auth/signout',
|
||||
error: '/auth/error', // Error code passed in query string as ?error=
|
||||
verifyRequest: '/auth/verify-request', // (used for check email message)
|
||||
newUser: '/auth/new-user' // New users will be directed here on first sign in (leave the property out if not of interest)
|
||||
}
|
||||
```
|
||||
|
||||
:::note
|
||||
When using this configuration, ensure that these pages actually exist. For example `error: '/auth/error'` refers to a page file at `pages/auth/error.js`.
|
||||
:::
|
||||
|
||||
See the documentation for the [creating custom pages guide](/guides/basics/pages) for more information.
|
||||
|
||||
---
|
||||
|
||||
### callbacks
|
||||
|
||||
- **Default value**: `object`
|
||||
- **Required**: _No_
|
||||
|
||||
#### Description
|
||||
|
||||
Callbacks are asynchronous functions you can use to control what happens when an action is performed.
|
||||
|
||||
Callbacks are extremely powerful, especially in scenarios involving JSON Web Tokens as they allow you to implement access controls without a database and to integrate with external databases or APIs.
|
||||
|
||||
You can specify a handler for any of the callbacks below.
|
||||
|
||||
```js
|
||||
callbacks: {
|
||||
async signIn({ user, account, profile, email, credentials }) {
|
||||
return true
|
||||
},
|
||||
async redirect({ url, baseUrl }) {
|
||||
return baseUrl
|
||||
},
|
||||
async session({ session, token, user }) {
|
||||
return session
|
||||
},
|
||||
async jwt({ token, user, account, profile, isNewUser }) {
|
||||
return token
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
See [our callbacks guide](/guides/basics/callbacks) for more information on how to use the callback functions.
|
||||
|
||||
---
|
||||
|
||||
### events
|
||||
|
||||
- **Default value**: `object`
|
||||
- **Required**: _No_
|
||||
|
||||
#### Description
|
||||
|
||||
Events are asynchronous functions that do not return a response, they are useful for audit logging.
|
||||
|
||||
You can specify a handler for any of these events below - e.g. for debugging or to create an audit log.
|
||||
|
||||
The content of the message object varies depending on the flow (e.g. OAuth or Email authentication flow, JWT or database sessions, etc). See the [events guide](/guides/basics/events) for more information on the form of each message object and how to use the events functions.
|
||||
|
||||
```js
|
||||
events: {
|
||||
async signIn(message) { /* on successful sign in */ },
|
||||
async signOut(message) { /* on signout */ },
|
||||
async createUser(message) { /* user created */ },
|
||||
async updateUser(message) { /* user updated - e.g. their email was verified */ },
|
||||
async linkAccount(message) { /* account (e.g. Twitter) linked to a user */ },
|
||||
async session(message) { /* session is active */ },
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### adapter
|
||||
|
||||
- **Default value**: none
|
||||
- **Required**: _No_
|
||||
|
||||
#### Description
|
||||
|
||||
By default Auth.js does not include an adapter any longer. If you would like to persist user / account data, please install one of the many available adapters. More information can be found in the [adapter documentation](/reference/adapters/overview).
|
||||
|
||||
---
|
||||
|
||||
### debug
|
||||
|
||||
- **Default value**: `false`
|
||||
- **Required**: _No_
|
||||
|
||||
#### Description
|
||||
|
||||
Set debug to `true` to enable debug messages for authentication and database operations.
|
||||
|
||||
---
|
||||
|
||||
### logger
|
||||
|
||||
- **Default value**: `console`
|
||||
- **Required**: _No_
|
||||
|
||||
#### Description
|
||||
|
||||
Override any of the logger levels (`undefined` levels will use the built-in logger), and intercept logs in NextAuth. You can use this to send NextAuth logs to a third-party logging service.
|
||||
|
||||
The `code` parameter for `error` and `warn` are explained in the [Warnings](/reference/warnings) and [Errors](/reference/errors) pages respectively.
|
||||
|
||||
Example:
|
||||
|
||||
```js title="/pages/api/auth/[...nextauth].js"
|
||||
import log from "logging-service"
|
||||
|
||||
export default NextAuth({
|
||||
...
|
||||
logger: {
|
||||
error(code, metadata) {
|
||||
log.error(code, metadata)
|
||||
},
|
||||
warn(code) {
|
||||
log.warn(code)
|
||||
},
|
||||
debug(code, metadata) {
|
||||
log.debug(code, metadata)
|
||||
}
|
||||
}
|
||||
...
|
||||
})
|
||||
```
|
||||
|
||||
:::note
|
||||
If the `debug` level is defined by the user, it will be called regardless of the `debug: false` [option](#debug).
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
### theme
|
||||
|
||||
- **Default value**: `object`
|
||||
- **Required**: _No_
|
||||
|
||||
#### Description
|
||||
|
||||
Changes the color scheme theme of [pages](/reference/configuration/auth-config#pages) as well as allows some minor customization. Set `theme.colorScheme` to `"light"`, if you want to force pages to always be light. Set to `"dark"`, if you want to force pages to always be dark. Set to `"auto"`, (or leave this option out) if you want the pages to follow the preferred system theme. (Uses the [prefers-color-scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme) media query.)
|
||||
|
||||
In addition, you can define a logo URL in `theme.logo` which will be rendered above the main card in the default signin/signout/error/verify-request pages, as well as a `theme.brandColor` which will affect the accent color of these pages.
|
||||
|
||||
```js
|
||||
theme: {
|
||||
colorScheme: "auto", // "auto" | "dark" | "light"
|
||||
brandColor: "", // Hex color code
|
||||
logo: "" // Absolute URL to image
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Advanced Options
|
||||
|
||||
Advanced options are passed the same way as basic options, but may have complex implications or side effects. You should try to avoid using advanced options unless you are very comfortable using them.
|
||||
|
||||
---
|
||||
|
||||
### useSecureCookies
|
||||
|
||||
- **Default value**: `true` for HTTPS sites / `false` for HTTP sites
|
||||
- **Required**: _No_
|
||||
|
||||
#### Description
|
||||
|
||||
When set to `true` (the default for all site URLs that start with `https://`) then all cookies set by Auth.js will only be accessible from HTTPS URLs.
|
||||
|
||||
This option defaults to `false` on URLs that start with `http://` (e.g. `http://localhost:3000`) for developer convenience.
|
||||
|
||||
:::note
|
||||
Properties on any custom `cookies` that are specified override this option.
|
||||
:::
|
||||
|
||||
:::warning
|
||||
Setting this option to _false_ in production is a security risk and may allow sessions to be hijacked if used in production. It is intended to support development and testing. Using this option is not recommended.
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
### cookies
|
||||
|
||||
- **Default value**: `{}`
|
||||
- **Required**: _No_
|
||||
|
||||
#### Description
|
||||
|
||||
Cookies in Auth.js are chunked by default, meaning that once they reach the 4kb limit, we will create a new cookie with the `.{number}` suffix and reassemble the cookies in the correct order when parsing / reading them. This was introduced to avoid size constraints which can occur when users want to store additional data in their sessionToken, for example.
|
||||
|
||||
You can override the default cookie names and options for any of the cookies used by Auth.js.
|
||||
|
||||
This is an advanced option and using it is not recommended as you may break authentication or introduce security flaws into your application.
|
||||
|
||||
You can specify one or more cookies with custom properties, but if you specify custom options for a cookie you must provide all the options for that cookie.
|
||||
|
||||
If you use this feature, you will likely want to create conditional behaviour to support setting different cookies policies in development and production builds, as you will be opting out of the built-in dynamic policy.
|
||||
|
||||
:::tip
|
||||
An example of a use case for this option is to support sharing session tokens across subdomains.
|
||||
:::
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
cookies: {
|
||||
sessionToken: {
|
||||
name: `__Secure-next-auth.session-token`,
|
||||
options: {
|
||||
httpOnly: true,
|
||||
sameSite: 'lax',
|
||||
path: '/',
|
||||
secure: true
|
||||
}
|
||||
},
|
||||
callbackUrl: {
|
||||
name: `__Secure-next-auth.callback-url`,
|
||||
options: {
|
||||
sameSite: 'lax',
|
||||
path: '/',
|
||||
secure: true
|
||||
}
|
||||
},
|
||||
csrfToken: {
|
||||
name: `__Host-next-auth.csrf-token`,
|
||||
options: {
|
||||
httpOnly: true,
|
||||
sameSite: 'lax',
|
||||
path: '/',
|
||||
secure: true
|
||||
}
|
||||
},
|
||||
pkceCodeVerifier: {
|
||||
name: `${cookiePrefix}next-auth.pkce.code_verifier`,
|
||||
options: {
|
||||
httpOnly: true,
|
||||
sameSite: 'lax',
|
||||
path: '/',
|
||||
secure: useSecureCookies,
|
||||
maxAge: 900
|
||||
}
|
||||
},
|
||||
state: {
|
||||
name: `${cookiePrefix}next-auth.state`,
|
||||
options: {
|
||||
httpOnly: true,
|
||||
sameSite: "lax",
|
||||
path: "/",
|
||||
secure: useSecureCookies,
|
||||
maxAge: 900
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
:::warning
|
||||
Using a custom cookie policy may introduce security flaws into your application and is intended as an option for advanced users who understand the implications. Using this option is not recommended.
|
||||
:::
|
||||
@@ -1,38 +0,0 @@
|
||||
---
|
||||
title: Environment variables
|
||||
sidebar_label: Environment Variables
|
||||
---
|
||||
|
||||
## NEXTAUTH_URL
|
||||
|
||||
When deploying to production, set the `NEXTAUTH_URL` environment variable to the canonical URL of your site.
|
||||
|
||||
```
|
||||
NEXTAUTH_URL=https://example.com
|
||||
```
|
||||
|
||||
If your Next.js application uses a custom base path, specify the route to the API endpoint in full.
|
||||
|
||||
_e.g. `NEXTAUTH_URL=https://example.com/custom-route/api/auth`_
|
||||
|
||||
:::note
|
||||
Using [System Environment Variables](https://vercel.com/docs/concepts/projects/environment-variables#system-environment-variables) we automatically detect when you deploy to [Vercel](https://vercel.com) so you don't have to define this variable. Make sure **Automatically expose System Environment Variables** is checked in your Project Settings.
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## NEXTAUTH_SECRET
|
||||
|
||||
Used to encrypt the Auth.js JWT, and to hash [email verification tokens](/reference/adapters/models#verification-token). This is the default value for the [`secret`](/reference/configuration/auth-config#secret) option. The `secret` option might be removed in the future in favor of this.
|
||||
|
||||
If you are using [Middleware](/reference/nextjs/#prerequisites) this environment variable must be set.
|
||||
|
||||
---
|
||||
|
||||
## NEXTAUTH_URL_INTERNAL
|
||||
|
||||
If provided, server-side calls will use this instead of `NEXTAUTH_URL`. Useful in environments when the server doesn't have access to the canonical URL of your site. Defaults to `NEXTAUTH_URL`.
|
||||
|
||||
```
|
||||
NEXTAUTH_URL_INTERNAL=http://10.240.8.16
|
||||
```
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"label": "Configuration",
|
||||
"collapsible": true,
|
||||
"collapsed": true
|
||||
}
|
||||
@@ -1,271 +0,0 @@
|
||||
---
|
||||
id: client
|
||||
title: Utilities
|
||||
---
|
||||
|
||||
The Auth.js client library makes it easy to interact with sessions from React applications.
|
||||
|
||||
#### Example Session Object
|
||||
|
||||
```ts
|
||||
{
|
||||
user: {
|
||||
name: string
|
||||
email: string
|
||||
image: string
|
||||
},
|
||||
expires: Date // This is the expiry of the session, not any of the tokens within the session
|
||||
}
|
||||
```
|
||||
|
||||
:::tip
|
||||
The session data returned to the client does not contain sensitive information such as the Session Token or OAuth tokens. It contains a minimal payload that includes enough data needed to display information on a page about the user who is signed in for presentation purposes (e.g name, email, image).
|
||||
|
||||
You can use the [session callback](/reference/configuration/auth-config#callbacks) to customize the session object returned to the client if you need to return additional data in the session object.
|
||||
:::
|
||||
|
||||
:::note
|
||||
The `expires` value is rotated, meaning whenever the session is retrieved from the [REST API](/reference/rest-api), this value will be updated as well, to avoid session expiry.
|
||||
:::
|
||||
|
||||
## getSession()
|
||||
|
||||
- Client Side: **Yes**
|
||||
- Server Side: **No** (See: [`unstable_getServerSession()`](/reference/nextjs/#unstable_getserversession)
|
||||
|
||||
Auth.js provides a `getSession()` helper which should be called **client side only** to return the current active session.
|
||||
|
||||
On the server side, **this is still available to use**, however, we recommend using `unstable_getServerSession` going forward. The idea behind this is to avoid an additional unnecessary `fetch` call on the server side. For more information, please check out [this issue](https://github.com/nextauthjs/next-auth/issues/1535).
|
||||
|
||||
:::note
|
||||
The `unstable_getServerSession` only has the prefix `unstable_` at the moment, because the API may change in the future. There are no known bugs at the moment and it is safe to use. If you discover any issues, please do report them as a [GitHub Issue](https://github.com/nextauthjs/next-auth/issues) and we will patch them as soon as possible.
|
||||
:::
|
||||
|
||||
This helper is helpful in case you want to read the session outside of the context of React.
|
||||
|
||||
When called, `getSession()` will send a request to `/api/auth/session` and returns a promise with a [session object](https://github.com/nextauthjs/next-auth/blob/main/packages/next-auth/src/core/types.ts#L407-L425), or `null` if no session exists.
|
||||
|
||||
```js
|
||||
async function myFunction() {
|
||||
const session = await getSession()
|
||||
/* ... */
|
||||
}
|
||||
```
|
||||
|
||||
Read the tutorial [securing pages and API routes](/guides/basics/securing-pages-and-api-routes) to know how to fetch the session in server side calls using `unstable_getServerSession()`.
|
||||
|
||||
---
|
||||
|
||||
## getCsrfToken()
|
||||
|
||||
- Client Side: **Yes**
|
||||
- Server Side: **Yes**
|
||||
|
||||
The `getCsrfToken()` method returns the current Cross Site Request Forgery Token (CSRF Token) required to make POST requests (e.g. for signing in and signing out).
|
||||
|
||||
You likely only need to use this if you are not using the built-in `signIn()` and `signOut()` methods.
|
||||
|
||||
#### Client Side Example
|
||||
|
||||
```js
|
||||
async function myFunction() {
|
||||
const csrfToken = await getCsrfToken()
|
||||
/* ... */
|
||||
}
|
||||
```
|
||||
|
||||
#### Server Side Example
|
||||
|
||||
```js
|
||||
import { getCsrfToken } from "next-auth/react"
|
||||
|
||||
export default async (req, res) => {
|
||||
const csrfToken = await getCsrfToken({ req })
|
||||
/* ... */
|
||||
res.end()
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## getProviders()
|
||||
|
||||
- Client Side: **Yes**
|
||||
- Server Side: **Yes**
|
||||
|
||||
The `getProviders()` method returns the list of providers currently configured for sign in.
|
||||
|
||||
It calls `/api/auth/providers` and returns a list of the currently configured authentication providers.
|
||||
|
||||
It can be useful if you are creating a dynamic custom sign in page.
|
||||
|
||||
---
|
||||
|
||||
#### API Route
|
||||
|
||||
```jsx title="pages/api/example.js"
|
||||
import { getProviders } from "next-auth/react"
|
||||
|
||||
export default async (req, res) => {
|
||||
const providers = await getProviders()
|
||||
console.log("Providers", providers)
|
||||
res.end()
|
||||
}
|
||||
```
|
||||
|
||||
:::note
|
||||
Unlike and `getCsrfToken()`, when calling `getProviders()` server side, you don't need to pass anything, just as calling it client side.
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## signIn()
|
||||
|
||||
- Client Side: **Yes**
|
||||
- Server Side: No
|
||||
|
||||
Using the `signIn()` method ensures the user ends back on the page they started on after completing a sign in flow. It will also handle CSRF Tokens for you automatically when signing in with email.
|
||||
|
||||
The `signIn()` method can be called from the client in different ways, as shown below.
|
||||
|
||||
### Redirects to sign in page when clicked
|
||||
|
||||
```js
|
||||
import { signIn } from "next-auth/react"
|
||||
|
||||
export default () => <button onClick={() => signIn()}>Sign in</button>
|
||||
```
|
||||
|
||||
### Starts OAuth sign-in flow when clicked
|
||||
|
||||
By default, when calling the `signIn()` method with no arguments, you will be redirected to the Auth.js sign-in page. If you want to skip that and get redirected to your provider's page immediately, call the `signIn()` method with the provider's `id`.
|
||||
|
||||
For example to sign in with Google:
|
||||
|
||||
```js
|
||||
import { signIn } from "next-auth/react"
|
||||
|
||||
export default () => (
|
||||
<button onClick={() => signIn("google")}>Sign in with Google</button>
|
||||
)
|
||||
```
|
||||
|
||||
### Starts Email sign-in flow when clicked
|
||||
|
||||
When using it with the email flow, pass the target `email` as an option.
|
||||
|
||||
```js
|
||||
import { signIn } from "next-auth/react"
|
||||
|
||||
export default ({ email }) => (
|
||||
<button onClick={() => signIn("email", { email })}>Sign in with Email</button>
|
||||
)
|
||||
```
|
||||
|
||||
### Specifying a `callbackUrl`
|
||||
|
||||
The `callbackUrl` specifies to which URL the user will be redirected after signing in. Defaults to the page URL the sign-in is initiated from.
|
||||
|
||||
You can specify a different `callbackUrl` by specifying it as the second argument of `signIn()`. This works for all providers.
|
||||
|
||||
e.g.
|
||||
|
||||
- `signIn(undefined, { callbackUrl: '/foo' })`
|
||||
- `signIn('google', { callbackUrl: 'http://localhost:3000/bar' })`
|
||||
- `signIn('email', { email, callbackUrl: 'http://localhost:3000/foo' })`
|
||||
|
||||
The URL must be considered valid by the [redirect callback handler](/reference/configuration/auth-config#callbacks). By default it requires the URL to be an absolute URL at the same host name, or a relative url starting with a slash. If it does not match it will redirect to the homepage. You can define your own [redirect callback](/guides/basics/callbacks#redirect-callback) to allow other URLs.
|
||||
|
||||
### Using the `redirect: false` option
|
||||
|
||||
:::note
|
||||
The redirect option is only available for `credentials` and `email` providers.
|
||||
:::
|
||||
|
||||
In some cases, you might want to deal with the sign in response on the same page and disable the default redirection. For example, if an error occurs (like wrong credentials given by the user), you might want to handle the error on the same page. For that, you can pass `redirect: false` in the second parameter object.
|
||||
|
||||
e.g.
|
||||
|
||||
- `signIn('credentials', { redirect: false, password: 'password' })`
|
||||
- `signIn('email', { redirect: false, email: 'bill@fillmurray.com' })`
|
||||
|
||||
`signIn` will then return a Promise, that resolves to the following:
|
||||
|
||||
```ts
|
||||
{
|
||||
/**
|
||||
* Will be different error codes,
|
||||
* depending on the type of error.
|
||||
*/
|
||||
error: string | undefined
|
||||
/**
|
||||
* HTTP status code,
|
||||
* hints the kind of error that happened.
|
||||
*/
|
||||
status: number
|
||||
/**
|
||||
* `true` if the signin was successful
|
||||
*/
|
||||
ok: boolean
|
||||
/**
|
||||
* `null` if there was an error,
|
||||
* otherwise the url the user
|
||||
* should have been redirected to.
|
||||
*/
|
||||
url: string | null
|
||||
}
|
||||
```
|
||||
|
||||
### Additional parameters
|
||||
|
||||
It is also possible to pass additional parameters to the `/authorize` endpoint through the third argument of `signIn()`.
|
||||
|
||||
See the [Authorization Request OIDC spec](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest) for some ideas. (These are not the only possible ones, all parameters will be forwarded)
|
||||
|
||||
e.g.
|
||||
|
||||
- `signIn("identity-server4", null, { prompt: "login" })` _always ask the user to re-authenticate_
|
||||
- `signIn("auth0", null, { login_hint: "info@example.com" })` _hints the e-mail address to the provider_
|
||||
|
||||
:::note
|
||||
You can also set these parameters through [`provider.authorizationParams`](/reference/providers/oauth).
|
||||
:::
|
||||
|
||||
:::note
|
||||
The following parameters are always overridden server-side: `redirect_uri`, `state`
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## signOut()
|
||||
|
||||
- Client Side: **Yes**
|
||||
- Server Side: No
|
||||
|
||||
In order to logout, use the `signOut()` method to ensure the user ends back on the page they started on after completing the sign out flow. It also handles CSRF tokens for you automatically.
|
||||
|
||||
It reloads the page in the browser when complete.
|
||||
|
||||
```js
|
||||
import { signOut } from "next-auth/react"
|
||||
|
||||
export default () => <button onClick={() => signOut()}>Sign out</button>
|
||||
```
|
||||
|
||||
### Specifying a `callbackUrl`
|
||||
|
||||
As with the `signIn()` function, you can specify a `callbackUrl` parameter by passing it as an option.
|
||||
|
||||
e.g. `signOut({ callbackUrl: 'http://localhost:3000/foo' })`
|
||||
|
||||
The URL must be considered valid by the [redirect callback handler](/guides/basics/callbacks#redirect-callback). By default, it requires the URL to be an absolute URL at the same host name, or you can also supply a relative URL starting with a slash. If it does not match it will redirect to the homepage. You can define your own [redirect callback](/guides/basics/callbacks#redirect-callback) to allow other URLs.
|
||||
|
||||
### Using the `redirect: false` option
|
||||
|
||||
If you pass `redirect: false` to `signOut`, the page will not reload. The session will be deleted, and the `useSession` hook is notified, so any indication about the user will be shown as logged out automatically. It can give a very nice experience for the user.
|
||||
|
||||
:::tip
|
||||
If you need to redirect to another page but you want to avoid a page reload, you can try:
|
||||
`const data = await signOut({redirect: false, callbackUrl: "/foo"})`
|
||||
where `data.url` is the validated URL you can redirect the user to without any flicker by using Next.js's `useRouter().push(data.url)`
|
||||
:::
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
title: Available OAuth providers
|
||||
sidebar_label: OAuth providers
|
||||
---
|
||||
|
||||
Authentication Providers in **Auth.js** are services that can be used to sign a user in.
|
||||
|
||||
Auth.js comes with a set of built-in providers. You can find them [here](https://github.com/nextauthjs/next-auth/tree/main/packages/core/src/providers). Each built-in provider has its own documentation page:
|
||||
|
||||
:::note
|
||||
Auth.js supports any **2.x** and **OpenID Connect (OIDC)** compliant providers and has built-in support for the most popular services.
|
||||
:::
|
||||
|
||||
<ul>
|
||||
{Object.entries(require("../../../providers.json"))
|
||||
.filter(([key]) => !["email", "credentials"].includes(key))
|
||||
.sort(([, a], [, b]) => a.localeCompare(b))
|
||||
.map(([key, name]) => (
|
||||
<li key={key}>
|
||||
<a href={`/reference/oauth-providers/${key}`}>{name}</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
@@ -1,193 +0,0 @@
|
||||
---
|
||||
title: OAuth Provider Options
|
||||
sidebar_label: OAuth options
|
||||
---
|
||||
|
||||
## Provider Options
|
||||
|
||||
Whenever you configure a custom or a built-in OAuth provider, you have the following options available:
|
||||
|
||||
```ts
|
||||
interface OAuthConfig {
|
||||
/**
|
||||
* OpenID Connect (OIDC) compliant providers can configure
|
||||
* this instead of `authorize`/`token`/`userinfo` options
|
||||
* without further configuration needed in most cases.
|
||||
* You can still use the `authorize`/`token`/`userinfo`
|
||||
* options for advanced control.
|
||||
*
|
||||
* [Authorization Server Metadata](https://datatracker.ietf.org/doc/html/rfc8414#section-3)
|
||||
*/
|
||||
wellKnown?: string
|
||||
/**
|
||||
* The login process will be initiated by sending the user to this URL.
|
||||
*
|
||||
* [Authorization endpoint](https://datatracker.ietf.org/doc/html/rfc6749#section-3.1)
|
||||
*/
|
||||
authorization: EndpointHandler<AuthorizationParameters>
|
||||
/**
|
||||
* Endpoint that returns OAuth 2/OIDC tokens and information about them.
|
||||
* This includes `access_token`, `id_token`, `refresh_token`, etc.
|
||||
*
|
||||
* [Token endpoint](https://datatracker.ietf.org/doc/html/rfc6749#section-3.2)
|
||||
*/
|
||||
token: EndpointHandler<
|
||||
UrlParams,
|
||||
{
|
||||
/**
|
||||
* Parameters extracted from the request to the `/api/auth/callback/:providerId` endpoint.
|
||||
* Contains params like `state`.
|
||||
*/
|
||||
params: CallbackParamsType
|
||||
/**
|
||||
* When using this custom flow, make sure to do all the necessary security checks.
|
||||
* This object contains parameters you have to match against the request to make sure it is valid.
|
||||
*/
|
||||
checks: OAuthChecks
|
||||
},
|
||||
{ tokens: TokenSet }
|
||||
>
|
||||
/**
|
||||
* When using an OAuth 2 provider, the user information must be requested
|
||||
* through an additional request from the userinfo endpoint.
|
||||
*
|
||||
* [Userinfo endpoint](https://www.oauth.com/oauth2-servers/signing-in-with-google/verifying-the-user-info)
|
||||
*/
|
||||
userinfo?: EndpointHandler<UrlParams, { tokens: TokenSet }, Profile>
|
||||
type: "oauth"
|
||||
/**
|
||||
* Used in URLs to refer to a certain provider.
|
||||
* @example /api/auth/callback/twitter // where the `id` is "twitter"
|
||||
*/
|
||||
id: string
|
||||
version: string
|
||||
profile(profile: P, tokens: TokenSet): Awaitable<User & { id: string }>
|
||||
checks?: ChecksType | ChecksType[]
|
||||
clientId: string
|
||||
clientSecret: string
|
||||
/**
|
||||
* If set to `true`, the user information will be extracted
|
||||
* from the `id_token` claims, instead of
|
||||
* making a request to the `userinfo` endpoint.
|
||||
*
|
||||
* `id_token` is usually present in OpenID Connect (OIDC) compliant providers.
|
||||
*
|
||||
* [`id_token` explanation](https://www.oauth.com/oauth2-servers/openid-connect/id-tokens)
|
||||
*/
|
||||
idToken?: boolean
|
||||
region?: string
|
||||
issuer?: string
|
||||
client?: Partial<ClientMetadata>
|
||||
allowDangerousEmailAccountLinking?: boolean
|
||||
/**
|
||||
* Object containing the settings for the styling of the providers sign-in buttons
|
||||
*/
|
||||
style: ProviderStyleType
|
||||
}
|
||||
```
|
||||
|
||||
### `authorization` option
|
||||
|
||||
Configure how to construct the request to the [_Authorization endpoint_](https://datatracker.ietf.org/doc/html/rfc6749#section-3.1).
|
||||
|
||||
There are two ways to use this option:
|
||||
|
||||
1. You can either set `authorization` to be a full URL, like `"https://example.com/oauth/authorization?scope=email"`.
|
||||
2. Use an object with `url` and `params` like so
|
||||
```js
|
||||
authorization: {
|
||||
url: "https://example.com/oauth/authorization",
|
||||
params: { scope: "email" }
|
||||
}
|
||||
```
|
||||
|
||||
:::tip
|
||||
If your Provider is OpenID Connect (OIDC) compliant, we recommend using the `wellKnown` option instead.
|
||||
:::
|
||||
|
||||
### `token` option
|
||||
|
||||
Configure how to construct the request to the [_Token endpoint_](https://datatracker.ietf.org/doc/html/rfc6749#section-3.2).
|
||||
|
||||
There are three ways to use this option:
|
||||
|
||||
1. You can either set `token` to be a full URL, like `"https://example.com/oauth/token?some=param"`.
|
||||
2. Use an object with `url` and `params` like so
|
||||
```js
|
||||
token: {
|
||||
url: "https://example.com/oauth/token",
|
||||
params: { some: "param" }
|
||||
}
|
||||
```
|
||||
3. Completely take control of the request:
|
||||
```js
|
||||
token: {
|
||||
url: "https://example.com/oauth/token",
|
||||
async request(context) {
|
||||
// context contains useful properties to help you make the request.
|
||||
const tokens = await makeTokenRequest(context)
|
||||
return { tokens }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::warning
|
||||
Option 3. should not be necessary in most cases, but if your provider does not follow the spec, or you have some very unique constraints it can be useful. Try to avoid it, if possible.
|
||||
:::
|
||||
|
||||
:::tip
|
||||
If your Provider is OpenID Connect (OIDC) compliant, we recommend using the `wellKnown` option instead.
|
||||
:::
|
||||
|
||||
### `userinfo` option
|
||||
|
||||
A `userinfo` endpoint returns information about the logged-in user. It is not part of the OAuth specification, but usually available for most providers.
|
||||
|
||||
There are three ways to use this option:
|
||||
|
||||
1. You can either set `userinfo` to be a full URL, like `"https://example.com/oauth/userinfo?some=param"`.
|
||||
2. Use an object with `url` and `params` like so
|
||||
```js
|
||||
userinfo: {
|
||||
url: "https://example.com/oauth/userinfo",
|
||||
params: { some: "param" }
|
||||
}
|
||||
```
|
||||
3. Completely take control of the request:
|
||||
```js
|
||||
userinfo: {
|
||||
url: "https://example.com/oauth/userinfo",
|
||||
// The result of this method will be the input to the `profile` callback.
|
||||
async request(context) {
|
||||
// context contains useful properties to help you make the request.
|
||||
return await makeUserinfoRequest(context)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::warning
|
||||
Option 3. should not be necessary in most cases, but if your provider does not follow the spec, or you have some very unique constraints it can be useful. Try to avoid it, if possible.
|
||||
:::
|
||||
|
||||
:::tip
|
||||
In the rare case you don't care about what this endpoint returns, or your provider does not have one, you could create a noop function:
|
||||
|
||||
```js
|
||||
userinfo: {
|
||||
request: () => {}
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::tip
|
||||
If your Provider is OpenID Connect (OIDC) compliant, we recommend using the `wellKnown` option instead. OIDC usually returns an `id_token` from the `token` endpoint. `next-auth` can decode the `id_token` to get the user information, instead of making an additional request to the `userinfo` endpoint. Just set `idToken: true` at the top-level of your provider configuration. If not set, `next-auth` will still try to contact this endpoint.
|
||||
:::
|
||||
|
||||
### `client` option
|
||||
|
||||
An advanced option, hopefully you won't need it in most cases. `next-auth` uses `openid-client` under the hood, see the docs on this option [here](https://github.com/panva/node-openid-client/blob/main/docs/README.md#new-clientmetadata-jwks-options).
|
||||
|
||||
### `allowDangerousEmailAccountLinking` option
|
||||
|
||||
Normally, when you sign in with an OAuth provider and another account with the same email address already exists, the accounts are not linked automatically. Automatic account linking on sign in is not secure between arbitrary providers and is disabled by default (see our [Security FAQ](https://authjs.dev/reference/faq#security)). However, it may be desirable to allow automatic account linking if you trust that the provider involved has securely verified the email address associated with the account. Just set `allowDangerousEmailAccountLinking: true` in your provider configuration to enable automatic account linking.
|
||||
@@ -1,19 +0,0 @@
|
||||
---
|
||||
title: Email Provider options
|
||||
sidebar_label: Email options
|
||||
---
|
||||
|
||||
| Name | Description | Type | Required |
|
||||
| :---------------------: | :---------------------------------------------------------------------------------: | :------------------------------: | :------: |
|
||||
| id | Unique ID for the provider | `string` | Yes |
|
||||
| name | Descriptive name for the provider | `string` | Yes |
|
||||
| type | Type of provider, in this case `email` | `"email"` | Yes |
|
||||
| server | Path or object pointing to the email server | `string` or `Object` | Yes |
|
||||
| sendVerificationRequest | Callback to execute when a verification request is sent | `(params) => Promise<undefined>` | Yes |
|
||||
| from | The email address from which emails are sent, default: "<no-reply@example.com>" | `string` | No |
|
||||
| maxAge | How long until the e-mail can be used to log the user in seconds. Defaults to 1 day | `number` | No |
|
||||
|
||||
See our guides on magic links authentication for further tips on how to customize this provider:
|
||||
|
||||
- [Tutorial](/getting-started/email-tutorial)
|
||||
- [Guide deep-dive](/guides/providers/email)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user