Compare commits
22 Commits
@auth/core
...
ndom91/san
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e2ff3ee72 | ||
|
|
9b6bcfe2ce | ||
|
|
26e2f795d8 | ||
|
|
d5eade5ef4 | ||
|
|
2fab7d4cd1 | ||
|
|
177fa44a49 | ||
|
|
92657bade3 | ||
|
|
581d37b50f | ||
|
|
1a928949ce | ||
|
|
8f222b2e56 | ||
|
|
01a78e1282 | ||
|
|
be481e6fe6 | ||
|
|
e1f5fe3e6d | ||
|
|
40f754fbca | ||
|
|
cf236c6bf3 | ||
|
|
b7a38ec7b1 | ||
|
|
db26a4c18d | ||
|
|
42b55f44f4 | ||
|
|
1d3761356a | ||
|
|
79147f1cf5 | ||
|
|
caf63f0eb4 | ||
|
|
7dec2eec71 |
70
.eslintignore
Normal file
@@ -0,0 +1,70 @@
|
||||
.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/03-core
|
||||
docs/docs/reference/04-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
Normal file
@@ -0,0 +1,75 @@
|
||||
// @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,
|
||||
}
|
||||
2
.github/ISSUE_TEMPLATE/1_bug_framework.yml
vendored
@@ -30,7 +30,7 @@ body:
|
||||
Run this command in your project's root folder and paste the result:
|
||||
|
||||
```sh
|
||||
npx envinfo --system --binaries --browsers --npmPackages "next,react,next-auth,@auth/*"
|
||||
npx envinfo --system --binaries --browsers --npmPackages "next,react,next-auth"
|
||||
```
|
||||
Alternatively, you can manually gather the version information from your package.json for these packages: "next", "react" and "next-auth". Please also mention your OS and Node.js version, as well as the browser you are using.
|
||||
validations:
|
||||
|
||||
5
.github/ISSUE_TEMPLATE/2_bug_provider.yml
vendored
@@ -25,14 +25,12 @@ body:
|
||||
- "Custom provider"
|
||||
- "42 School"
|
||||
- "Apple"
|
||||
- "Asgardeo"
|
||||
- "Atlassian"
|
||||
- "Auth0"
|
||||
- "Authentik"
|
||||
- "Azure Active Directory"
|
||||
- "Azure Active Directory B2C"
|
||||
- "Battlenet"
|
||||
- "Beyond Identity"
|
||||
- "Box"
|
||||
- "Bungie"
|
||||
- "Cognito"
|
||||
@@ -59,7 +57,6 @@ body:
|
||||
- "Medium"
|
||||
- "Naver"
|
||||
- "Netlify"
|
||||
- "Notion"
|
||||
- "Okta"
|
||||
- "OneLogin"
|
||||
- "Osso"
|
||||
@@ -90,7 +87,7 @@ body:
|
||||
Run this command in your project's root folder and paste the result:
|
||||
|
||||
```sh
|
||||
npx envinfo --system --binaries --browsers --npmPackages "next,react,next-auth,@auth/*"
|
||||
npx envinfo --system --binaries --browsers --npmPackages "next,react,next-auth"
|
||||
```
|
||||
Alternatively, you can manually gather the version information from your package.json for these packages: "next", "react" and "next-auth". Please also mention your OS and Node.js version, as well as the browser you are using.
|
||||
validations:
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/3_bug_adapter.yml
vendored
@@ -44,7 +44,7 @@ body:
|
||||
Run this command in your project's root folder and paste the result:
|
||||
|
||||
```sh
|
||||
npx envinfo --system --binaries --browsers --npmPackages "next,react,next-auth,@auth/*" && npx envinfo --npmPackages "@next-auth/*"
|
||||
npx envinfo --system --binaries --browsers --npmPackages "next,react,next-auth" && npx envinfo --npmPackages "@next-auth/*"
|
||||
```
|
||||
Alternatively, if the above command did not work, we need the version of the following packages from your package.json: "next", "react", "next-auth" and your adapter. Please also mention your OS and Node.js version, as well as the browser you are using.
|
||||
validations:
|
||||
|
||||
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 closed after 7 days.
|
||||
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.
|
||||
|
||||
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.)
|
||||
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.
|
||||
|
||||
### **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
@@ -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 }}
|
||||
|
||||
27
.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,6 +24,7 @@ jobs:
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: "pnpm"
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
- name: Run tests
|
||||
@@ -34,22 +35,6 @@ jobs:
|
||||
UPSTASH_REDIS_KEY: ${{ secrets.UPSTASH_REDIS_KEY }}
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||
# - name: Run E2E tests
|
||||
# if: github.repository == 'nextauthjs/next-auth'
|
||||
# run: pnpm e2e
|
||||
# timeout-minutes: 15
|
||||
# env:
|
||||
# AUTH0_USERNAME: ${{ secrets.AUTH0_USERNAME }}
|
||||
# AUTH0_PASSWORD: ${{ secrets.AUTH0_PASSWORD }}
|
||||
# TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
# TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||
# - name: Upload E2E artifacts
|
||||
# if: github.repository == 'nextauthjs/next-auth'
|
||||
# uses: actions/upload-artifact@v3
|
||||
# with:
|
||||
# name: playwright-report
|
||||
# path: apps/dev/nextjs/playwright-report/
|
||||
# retention-days: 30
|
||||
# - name: Coverage
|
||||
# uses: codecov/codecov-action@v1
|
||||
# with:
|
||||
@@ -73,6 +58,7 @@ jobs:
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: "pnpm"
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
- name: Publish to npm and GitHub
|
||||
@@ -97,6 +83,7 @@ jobs:
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: "pnpm"
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
- name: Determine version
|
||||
|
||||
27
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
# Misc
|
||||
.DS_Store
|
||||
.npmrc
|
||||
.eslintcache
|
||||
.env
|
||||
.env.local
|
||||
@@ -11,9 +12,8 @@ npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
firebase-debug.log
|
||||
ui-debug.log
|
||||
.pnpm-debug.log
|
||||
.husky
|
||||
|
||||
|
||||
# Dependencies
|
||||
node_modules
|
||||
@@ -34,16 +34,18 @@ packages/next-auth/utils
|
||||
packages/next-auth/core
|
||||
packages/next-auth/jwt
|
||||
packages/next-auth/react
|
||||
packages/next-auth/adapters.d.ts
|
||||
packages/next-auth/adapters.js
|
||||
packages/next-auth/index.d.ts
|
||||
packages/next-auth/index.js
|
||||
packages/next-auth/next
|
||||
packages/*/*.js
|
||||
packages/*/*.d.ts
|
||||
packages/*/*.d.ts.map
|
||||
packages/next-auth/middleware.d.ts
|
||||
packages/next-auth/middleware.js
|
||||
|
||||
# Development app
|
||||
apps/dev/src/css
|
||||
apps/dev/prisma/migrations
|
||||
apps/dev/typeorm
|
||||
apps/dev/nextjs-2
|
||||
|
||||
# VS
|
||||
/.vs/slnx.sqlite-journal
|
||||
@@ -79,12 +81,15 @@ docs/.docusaurus
|
||||
docs/providers.json
|
||||
|
||||
# Core
|
||||
packages/core/src/providers/oauth-types.ts
|
||||
packages/core/*.js
|
||||
packages/core/*.d.ts
|
||||
packages/core/*.d.ts.map
|
||||
packages/core/lib
|
||||
packages/core/providers
|
||||
packages/core/src/lib/pages/styles.ts
|
||||
docs/docs/reference/core
|
||||
docs/docs/reference/sveltekit
|
||||
docs/docs/reference/03-core
|
||||
docs/docs/reference/04-sveltekit
|
||||
docs-nextra/pages/reference/core
|
||||
|
||||
|
||||
# SvelteKit
|
||||
@@ -94,7 +99,3 @@ packages/frameworks-sveltekit/.svelte-kit
|
||||
packages/frameworks-sveltekit/package
|
||||
packages/frameworks-sveltekit/vite.config.js.timestamp-*
|
||||
packages/frameworks-sveltekit/vite.config.ts.timestamp-*
|
||||
|
||||
# Adapters
|
||||
|
||||
docs/docs/reference/adapter
|
||||
@@ -20,8 +20,8 @@ pnpm-lock.yaml
|
||||
|
||||
.docusaurus
|
||||
build
|
||||
docs/docs/reference/core
|
||||
docs/docs/reference/sveltekit
|
||||
docs/docs/reference/03-core
|
||||
docs/docs/reference/04-sveltekit
|
||||
static
|
||||
docs/providers.json
|
||||
|
||||
|
||||
22
.prettierrc.js
Normal file
@@ -0,0 +1,22 @@
|
||||
// @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",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
# 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
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"typescript.tsdk": "../../node_modules/.pnpm/typescript@4.8.4/node_modules/typescript/lib",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
# 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)
|
||||
@@ -1,14 +0,0 @@
|
||||
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 }
|
||||
@@ -1,12 +0,0 @@
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<html>
|
||||
<head></head>
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
import { getServerSession } from "next-auth/next"
|
||||
|
||||
export default async function Page() {
|
||||
const session = await getServerSession()
|
||||
return <pre>{JSON.stringify(session, null, 2)}</pre>
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
.footer {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.navItems {
|
||||
margin-bottom: 1rem;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.navItem {
|
||||
display: inline-block;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/* 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;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import Header from 'components/header'
|
||||
import Footer from 'components/footer'
|
||||
|
||||
export default function Layout ({ children }) {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<main>
|
||||
{children}
|
||||
</main>
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
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
@@ -1,6 +0,0 @@
|
||||
/// <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.
|
||||
@@ -1,9 +0,0 @@
|
||||
/** @type {import("next").NextConfig} */
|
||||
module.exports = {
|
||||
webpack(config) {
|
||||
config.experiments = { ...config.experiments, topLevelAwait: true }
|
||||
return config
|
||||
},
|
||||
experimental: { appDir: true },
|
||||
typescript: { ignoreBuildErrors: true },
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { SessionProvider } from "next-auth/react"
|
||||
import "./styles.css"
|
||||
|
||||
export default function App({ Component, pageProps }) {
|
||||
return (
|
||||
<SessionProvider session={pageProps.session}>
|
||||
<Component {...pageProps} />
|
||||
</SessionProvider>
|
||||
)
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
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)
|
||||
@@ -1,7 +0,0 @@
|
||||
// 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))
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
// 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.",
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
// 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)
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
// 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))
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
// 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>
|
||||
)
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
// 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>
|
||||
)
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import Layout from "components/layout"
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<Layout>
|
||||
<h1>Page protected by Middleware</h1>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
// 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,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
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),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
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);
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
// 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,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
-- 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");
|
||||
@@ -1,3 +0,0 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (i.e. Git)
|
||||
provider = "sqlite"
|
||||
@@ -1,57 +0,0 @@
|
||||
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])
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
{
|
||||
"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
@@ -1,20 +0,0 @@
|
||||
// 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
|
||||
}
|
||||
}
|
||||
@@ -9,39 +9,20 @@ NEXTAUTH_URL=http://localhost:3000
|
||||
# and/or verification tokens.
|
||||
NEXTAUTH_SECRET=secret
|
||||
|
||||
ASGARDEO_CLIENT_ID=
|
||||
ASGARDEO_CLIENT_SECRET=
|
||||
ASGARDEO_ISSUER=
|
||||
|
||||
AUTH0_ID=
|
||||
AUTH0_SECRET=
|
||||
AUTH0_ISSUER=
|
||||
|
||||
# Beyond Identity Provider
|
||||
BEYOND_IDENTITY_CLIENT_ID=
|
||||
BEYOND_IDENTITY_CLIENT_SECRET=
|
||||
BEYOND_IDENTITY_ISSUER=
|
||||
|
||||
GITHUB_ID=
|
||||
GITHUB_SECRET=
|
||||
|
||||
NOTION_ID=
|
||||
NOTION_SECRET=
|
||||
NOTION_REDIRECT_URI=
|
||||
|
||||
IDS4_ID=
|
||||
IDS4_SECRET=
|
||||
IDS4_ISSUER=
|
||||
|
||||
KEYCLOAK_ID=
|
||||
KEYCLOAK_SECRET=
|
||||
KEYCLOAK_ISSUER=
|
||||
|
||||
LINE_ID=
|
||||
LINE_SECRET=
|
||||
IDS4_ID=
|
||||
IDS4_SECRET=
|
||||
IDS4_ISSUER=
|
||||
|
||||
TRAKT_ID=
|
||||
TRAKT_SECRET=
|
||||
GITHUB_ID=
|
||||
GITHUB_SECRET=
|
||||
|
||||
TWITCH_ID=
|
||||
TWITCH_SECRET=
|
||||
@@ -49,12 +30,11 @@ TWITCH_SECRET=
|
||||
TWITTER_ID=
|
||||
TWITTER_SECRET=
|
||||
|
||||
WIKIMEDIA_ID=
|
||||
WIKIMEDIA_SECRET=
|
||||
LINE_ID=
|
||||
LINE_SECRET=
|
||||
|
||||
# Yandex OAuth. new app -> https://oauth.yandex.com/client/new/id
|
||||
YANDEX_ID=
|
||||
YANDEX_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
|
||||
@@ -67,9 +47,12 @@ 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
|
||||
4
apps/dev/nextjs/.gitignore
vendored
@@ -1,4 +0,0 @@
|
||||
node_modules/
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/playwright/.cache/
|
||||
1
apps/dev/nextjs/next-env.d.ts
vendored
@@ -1,6 +1,5 @@
|
||||
/// <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,12 +9,10 @@
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"email": "fake-smtp-server",
|
||||
"start:email": "pnpm email",
|
||||
"e2e": "pnpm dlx playwright test"
|
||||
"start:email": "pnpm email"
|
||||
},
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@auth/core": "workspace:*",
|
||||
"@next-auth/fauna-adapter": "workspace:*",
|
||||
"@next-auth/prisma-adapter": "workspace:*",
|
||||
"@next-auth/supabase-adapter": "workspace:*",
|
||||
@@ -22,18 +20,17 @@
|
||||
"@prisma/client": "^3",
|
||||
"@supabase/supabase-js": "^2.0.5",
|
||||
"faunadb": "^4",
|
||||
"next": "13.3.0",
|
||||
"next": "13.1.1",
|
||||
"next-auth": "workspace:*",
|
||||
"@auth/core": "workspace:*",
|
||||
"nodemailer": "^6",
|
||||
"react": "^18",
|
||||
"react-dom": "^18"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "1.29.2",
|
||||
"@types/jsonwebtoken": "^8.5.5",
|
||||
"@types/react": "^18.0.15",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"dotenv": "^16.0.3",
|
||||
"fake-smtp-server": "^0.8.0",
|
||||
"pg": "^8.7.3",
|
||||
"prisma": "^3",
|
||||
|
||||
@@ -2,11 +2,9 @@ import { Auth, type AuthConfig } from "@auth/core"
|
||||
|
||||
// Providers
|
||||
import Apple from "@auth/core/providers/apple"
|
||||
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"
|
||||
@@ -25,7 +23,6 @@ import Instagram from "@auth/core/providers/instagram"
|
||||
import Line from "@auth/core/providers/line"
|
||||
import LinkedIn from "@auth/core/providers/linkedin"
|
||||
import Mailchimp from "@auth/core/providers/mailchimp"
|
||||
import Notion from "@auth/core/providers/notion"
|
||||
// import Okta from "@auth/core/providers/okta"
|
||||
import Osu from "@auth/core/providers/osu"
|
||||
import Patreon from "@auth/core/providers/patreon"
|
||||
@@ -34,7 +31,6 @@ 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"
|
||||
@@ -72,7 +68,7 @@ import WorkOS from "@auth/core/providers/workos"
|
||||
|
||||
export const authConfig: AuthConfig = {
|
||||
// adapter,
|
||||
debug: process.env.NODE_ENV !== "production",
|
||||
// debug: process.env.NODE_ENV !== "production",
|
||||
theme: {
|
||||
logo: "https://next-auth.js.org/img/logo/logo-sm.png",
|
||||
brandColor: "#1786fb",
|
||||
@@ -86,7 +82,6 @@ export const authConfig: AuthConfig = {
|
||||
},
|
||||
}),
|
||||
Apple({ clientId: process.env.APPLE_ID, clientSecret: process.env.APPLE_SECRET }),
|
||||
Asgardeo({ clientId: process.env.ASGARDEO_CLIENT_ID, clientSecret: process.env.ASGARDEO_CLIENT_SECRET, issuer: process.env.ASGARDEO_ISSUER }),
|
||||
Auth0({ clientId: process.env.AUTH0_ID, clientSecret: process.env.AUTH0_SECRET, issuer: process.env.AUTH0_ISSUER }),
|
||||
AzureAD({
|
||||
clientId: process.env.AZURE_AD_CLIENT_ID,
|
||||
@@ -94,7 +89,6 @@ 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 }),
|
||||
@@ -111,7 +105,6 @@ export const authConfig: AuthConfig = {
|
||||
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 }),
|
||||
Notion({ clientId: process.env.NOTION_ID, clientSecret: process.env.NOTION_SECRET, redirectUri: process.env.NOTION_REDIRECT_URI }),
|
||||
// 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 }),
|
||||
@@ -121,7 +114,6 @@ 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,107 +0,0 @@
|
||||
import type { PlaywrightTestConfig } from '@playwright/test';
|
||||
import { devices } from '@playwright/test';
|
||||
|
||||
/**
|
||||
* Read environment variables from file.
|
||||
* https://github.com/motdotla/dotenv
|
||||
*/
|
||||
require('dotenv').config();
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
const config: PlaywrightTestConfig = {
|
||||
testDir: './tests',
|
||||
/* Maximum time one test can run for. */
|
||||
timeout: 30 * 1000,
|
||||
expect: {
|
||||
/**
|
||||
* Maximum time expect() should wait for the condition to be met.
|
||||
* For example in `await expect(locator).toHaveText();`
|
||||
*/
|
||||
timeout: 5000
|
||||
},
|
||||
/* Run tests in files in parallel */
|
||||
fullyParallel: true,
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
forbidOnly: !!process.env.CI,
|
||||
/* Retry on CI only */
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
/* Opt out of parallel tests on CI. */
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: 'html',
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
|
||||
actionTimeout: 0,
|
||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||
// baseURL: 'http://localhost:3000',
|
||||
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: 'on-first-retry',
|
||||
},
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'firefox',
|
||||
use: {
|
||||
...devices['Desktop Firefox'],
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'webkit',
|
||||
use: {
|
||||
...devices['Desktop Safari'],
|
||||
},
|
||||
},
|
||||
|
||||
/* Test against mobile viewports. */
|
||||
// {
|
||||
// name: 'Mobile Chrome',
|
||||
// use: {
|
||||
// ...devices['Pixel 5'],
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: 'Mobile Safari',
|
||||
// use: {
|
||||
// ...devices['iPhone 12'],
|
||||
// },
|
||||
// },
|
||||
|
||||
/* Test against branded browsers. */
|
||||
// {
|
||||
// name: 'Microsoft Edge',
|
||||
// use: {
|
||||
// channel: 'msedge',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: 'Google Chrome',
|
||||
// use: {
|
||||
// channel: 'chrome',
|
||||
// },
|
||||
// },
|
||||
],
|
||||
|
||||
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
|
||||
// outputDir: 'test-results/',
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
// webServer: {
|
||||
// command: 'npm run start',
|
||||
// port: 3000,
|
||||
// },
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -1,39 +0,0 @@
|
||||
import { test, expect } from "@playwright/test"
|
||||
|
||||
test("Sign in with Auth0", async ({ page }) => {
|
||||
// Go to NextAuth example app
|
||||
await page.goto("https://next-auth-example.vercel.app")
|
||||
|
||||
// Click 'Sign In'
|
||||
await page.click("#__next > header > div > p > a")
|
||||
|
||||
// Auth0 Login Provider
|
||||
await page.click('body > div > div form[action*="auth0"] > button')
|
||||
|
||||
// Enter Credentials (Username/Password Login) on Auth0 Widget
|
||||
await page.type("#username", process.env.AUTH0_USERNAME!)
|
||||
await page.type("#password", process.env.AUTH0_PASSWORD!)
|
||||
|
||||
// Snap a screenshot
|
||||
// await page.screenshot({ path: "1-auth0-login.png", fullPage: true })
|
||||
|
||||
// Press submit on Auth0 form
|
||||
await page.click('body > div > main > section > div button[type="submit"]')
|
||||
|
||||
// Wait for next-auth example page login status header to appear
|
||||
await page.waitForTimeout(2000)
|
||||
|
||||
// Snap a screenshot
|
||||
// await page.screenshot({
|
||||
// path: "2-next-auth-redirect-result.png",
|
||||
// fullPage: false,
|
||||
// })
|
||||
|
||||
// Check session object after successful login
|
||||
const response = await page.goto(
|
||||
"https://next-auth-example.vercel.app/api/auth/session"
|
||||
)
|
||||
const session = await response?.json()
|
||||
expect(session?.user?.email).toBe(process.env.AUTH0_USERNAME)
|
||||
// TODO: Check whole object with .toEqual()
|
||||
})
|
||||
@@ -1,11 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
@@ -23,17 +19,8 @@
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"strictNullChecks": true
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"jest.config.js"
|
||||
]
|
||||
}
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules", "jest.config.js"]
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
"vite": "4.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@auth/core": "workspace:*",
|
||||
"@auth/sveltekit": "workspace:*"
|
||||
"@auth/core": "0.2.5",
|
||||
"@auth/sveltekit": "0.1.12"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getServerSession } from "next-auth/next"
|
||||
import { unstable_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 getServerSession(
|
||||
session: await unstable_getServerSession(
|
||||
context.req,
|
||||
context.res,
|
||||
authOptions
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Protected } from "~/components";
|
||||
export const { routeData, Page } = Protected((session) => {
|
||||
return (
|
||||
<main class="flex flex-col gap-2 items-center">
|
||||
<h1>This is a protected route</h1>
|
||||
<h1>This is a proteced route</h1>
|
||||
</main>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
"clean": "gatsby clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"dotenv": "16.0.0",
|
||||
"gatsby": "5.8.0-next.3",
|
||||
"dotenv": "^16.0.0",
|
||||
"gatsby": "next",
|
||||
"next-auth": "workspace:*",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
"react": "^18",
|
||||
"react-dom": "^18"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vercel": "23.1.2"
|
||||
"vercel": "^23.1.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
"name": "playground-nuxt",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "nuxt prepare && nuxt build",
|
||||
"dev": "nuxt prepare && export NODE_OPTIONS='--no-experimental-fetch' && nuxt dev",
|
||||
"build": "nuxt build",
|
||||
"dev": "export NODE_OPTIONS='--no-experimental-fetch' && nuxt dev",
|
||||
"generate": "nuxt generate",
|
||||
"preview": "nuxt preview"
|
||||
"preview": "nuxt preview",
|
||||
"postinstall": "nuxt prepare"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxt/eslint-config": "^0.1.1",
|
||||
|
||||
19
docs-nextra/assets/img/blobs/blob1.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<!-- <svg width="1077" height="990" viewBox="0 0 1077 990" fill="none" xmlns="http://www.w3.org/2000/svg"> -->
|
||||
<!-- <path d="M909.079 176.687C486.468 1429.32 94.8519 797.145 -245.666 989.191L-273.518 122.311C-98.0847 109.104 255.784 81.3405 267.79 75.9479C282.798 69.2072 1153.31 -485.661 1071.51 -140.889C1022.28 -26.4819 1001.45 -97.0987 909.079 176.687Z" fill="url(#paint0_linear_228_76)" fill-opacity="1" /> -->
|
||||
<!-- <defs> -->
|
||||
<!-- <linearGradient id="paint0_linear_228_76" x1="588.47" y1="791.265" x2="238.137" y2="497.91" gradientUnits="userSpaceOnUse"> -->
|
||||
<!-- <stop stop-color="#FF4400"/> -->
|
||||
<!-- <stop offset="1" stop-color="#111" /> -->
|
||||
<!-- </linearGradient> -->
|
||||
<!-- </defs> -->
|
||||
<!-- </svg> -->
|
||||
<svg width="1056" height="855" viewBox="0 0 1056 855" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M888.078 42.3027C465.467 1294.94 73.851 662.761 -266.667 854.807L-294.519 -12.0732C-119.086 -25.2807 234.783 -53.0438 246.789 -58.4364C261.797 -65.1771 1132.31 -620.045 1050.51 -275.273C1001.28 -160.866 980.447 -231.483 888.078 42.3027Z" fill="url(#paint0_radial_228_76)"/>
|
||||
<defs>
|
||||
<radialGradient id="paint0_radial_228_76" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(666.999 571.116) rotate(-129.513) scale(513.947 862.777)">
|
||||
<stop stop-color="#FF4400"/>
|
||||
<stop offset="1" stop-color="#111111"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
20
docs-nextra/assets/img/blobs/blob2.svg
Normal file
@@ -0,0 +1,20 @@
|
||||
<!-- <svg width="1405" height="1025" viewBox="0 0 1405 1025" fill="none" xmlns="http://www.w3.org/2000/svg"> -->
|
||||
<!-- <path d="M1180.05 977.4C483.802 708.04 103.247 143.506 0 -105.091L1102.61 -196C1418.53 307.367 1876.29 1246.76 1180.05 977.4Z" fill="url(#paint0_linear_228_83)" fill-opacity="0.44"/> -->
|
||||
<!-- <defs> -->
|
||||
<!-- <linearGradient id="paint0_linear_228_83" x1="185.171" y1="460.565" x2="641.405" y2="-62.9701" gradientUnits="userSpaceOnUse"> -->
|
||||
<!-- <stop stop-color="#BB44CC"/> -->
|
||||
<!-- <stop offset="1" stop-color="#BB44CC" stop-opacity="0"/> -->
|
||||
<!-- </linearGradient> -->
|
||||
<!-- </defs> -->
|
||||
<!-- </svg> -->
|
||||
|
||||
<svg width="1405" height="1025" viewBox="0 0 1405 1025" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1180.05 977.4C483.802 708.04 103.247 143.506 0 -105.091L1102.61 -196C1418.53 307.367 1876.29 1246.76 1180.05 977.4Z" fill="url(#paint0_radial_228_83)"/>
|
||||
<defs>
|
||||
<radialGradient id="paint0_radial_228_83" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(498 619) rotate(-46.5881) scale(548.572 1194.34)">
|
||||
<stop stop-color="#BB44CC"/>
|
||||
<stop offset="1" stop-color="#111111"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
20
docs-nextra/assets/img/blobs/blob3.svg
Normal file
@@ -0,0 +1,20 @@
|
||||
<!-- <svg width="2402" height="275" viewBox="0 0 2402 275" fill="none" xmlns="http://www.w3.org/2000/svg"> -->
|
||||
<!-- <path d="M305.668 155.376C1150.52 -387.169 3270.85 708.01 2011.14 262.274L1847.48 804.627C1613.84 721.267 1142.31 553.804 1125.29 550.832C1104 547.117 -168.361 431.431 18.8067 264.119C109.949 220.108 121.011 273.958 305.668 155.376Z" fill="url(#paint0_linear_228_78)" fill-opacity="0.66"/> -->
|
||||
<!-- <defs> -->
|
||||
<!-- <linearGradient id="paint0_linear_228_78" x1="869.047" y1="-51.7999" x2="921.583" y2="353.448" gradientUnits="userSpaceOnUse"> -->
|
||||
<!-- <stop stop-color="#44BBCC"/> -->
|
||||
<!-- <stop offset="1" stop-color="#44BBCC" stop-opacity="0.12"/> -->
|
||||
<!-- </linearGradient> -->
|
||||
<!-- </defs> -->
|
||||
<!-- </svg> -->
|
||||
|
||||
<svg width="2402" height="275" viewBox="0 0 2402 275" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M305.668 155.376C1150.52 -387.169 3270.85 708.01 2011.14 262.274L1847.48 804.627C1613.84 721.267 1142.31 553.804 1125.29 550.832C1104 547.117 -168.361 431.431 18.8067 264.119C109.949 220.108 121.011 273.958 305.668 155.376Z" fill="url(#paint0_radial_228_78)"/>
|
||||
<defs>
|
||||
<radialGradient id="paint0_radial_228_78" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(883.5 -66) rotate(93.0267) scale(757.557 879.417)">
|
||||
<stop stop-color="#44BBCC"/>
|
||||
<stop offset="1" stop-color="#111111"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
66
docs-nextra/components/marquee.jsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import { useState, useEffect } from "react"
|
||||
import { motion } from "framer-motion"
|
||||
|
||||
const clamp = (num, min, max) => Math.min(Math.max(num, min), max)
|
||||
|
||||
export default function Marquee({ files }) {
|
||||
const [{ width, height }, setSize] = useState({ height: 0, width: 0 })
|
||||
useEffect(() => {
|
||||
setSize({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
})
|
||||
}, [])
|
||||
|
||||
if (width === 0) return
|
||||
|
||||
return (
|
||||
<div className="absolute w-full h-full top-0 left-0 dark:opacity-5 opacity-20 saturate-0 brightness-75 dark:brightness-[1000] -z-20 overflow-hidden flex flex-col marquee-wrapper">
|
||||
{files.map((name) => (
|
||||
<MarqueeItem key={name} name={name} width={width} height={height} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function MarqueeItem({ name, width, height }) {
|
||||
const [y] = useState(height * Math.random())
|
||||
const [reset, setReset] = useState(false)
|
||||
const [duration] = useState(clamp(Math.random() * 25, 20, 25))
|
||||
useEffect(() => {
|
||||
const timeout =
|
||||
setTimeout(() => {
|
||||
setReset(true)
|
||||
}, (1000 * (width - offset)) / duration) - 2000
|
||||
return () => clearTimeout(timeout)
|
||||
}, [duration, width])
|
||||
const offset = Math.random() * width
|
||||
return (
|
||||
<motion.span
|
||||
key={name + reset}
|
||||
className="absolute flex items-center justify-center"
|
||||
initial={{ x: reset ? 0 : offset, y }}
|
||||
animate={{ x: width }}
|
||||
transition={{
|
||||
repeat: Infinity,
|
||||
duration: reset ? duration : (width - offset) / duration,
|
||||
ease: "linear",
|
||||
}}
|
||||
>
|
||||
<motion.img
|
||||
src={`/img/providers/${name}`}
|
||||
alt={name}
|
||||
className="relative w-12 drop-shadow-xl"
|
||||
initial={{ opacity: 0, scale: 0.5 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{
|
||||
delay: reset ? 0 : Math.random() * 5,
|
||||
duration: 1,
|
||||
type: "spring",
|
||||
|
||||
stiffness: 150,
|
||||
}}
|
||||
/>
|
||||
</motion.span>
|
||||
)
|
||||
}
|
||||
61
docs-nextra/components/sandpack.jsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import { Sandpack } from "@codesandbox/sandpack-react"
|
||||
import { freeCodeCampDark } from "@codesandbox/sandpack-themes"
|
||||
|
||||
export default function AuthSandbox() {
|
||||
return (
|
||||
<div style={{ width: "100%" }}>
|
||||
<Sandpack
|
||||
template="nextjs"
|
||||
files={{
|
||||
"/pages/api/auth/[...nextauth].js": `import NextAuth from "next-auth"
|
||||
import GithubProvider from "next-auth/providers/github"
|
||||
export const authOptions = {
|
||||
// Configure one or more authentication providers
|
||||
providers: [
|
||||
GithubProvider({
|
||||
clientId: process.env.GITHUB_ID,
|
||||
clientSecret: process.env.GITHUB_SECRET,
|
||||
}),
|
||||
],
|
||||
}
|
||||
export default NextAuth(authOptions)
|
||||
`,
|
||||
"pages/_app.js": `import { SessionProvider } from "next-auth/react"
|
||||
export default function App({
|
||||
Component,
|
||||
pageProps: { session, ...pageProps },
|
||||
}) {
|
||||
return (
|
||||
<SessionProvider session={session}>
|
||||
<Component {...pageProps} />
|
||||
</SessionProvider>
|
||||
)
|
||||
}`,
|
||||
".env": `GITHUB_ID=123abc
|
||||
GITHUB_SECRET=abc123
|
||||
NEXTAUTH_SECRET=abcbaerjwiofjlakshfu9fnajs
|
||||
`,
|
||||
"/pages/index.js": `export default function App() {
|
||||
return <div><h1>Hello NextAuth User</h1><a href="/api/auth/signin">Signin</a></div>
|
||||
}`,
|
||||
}}
|
||||
theme={freeCodeCampDark}
|
||||
customSetup={{
|
||||
dependencies: {
|
||||
"next-auth": "latest",
|
||||
},
|
||||
}}
|
||||
options={{
|
||||
showInlineErrors: true,
|
||||
showLineNumbers: true,
|
||||
showTabs: true,
|
||||
closableTabs: true,
|
||||
showNavigator: true,
|
||||
showConsole: true,
|
||||
showConsoleButton: true,
|
||||
editorHeight: "70vh",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
85
docs-nextra/components/triangle.jsx
Normal file
@@ -0,0 +1,85 @@
|
||||
export default function Triangle({ className }) {
|
||||
return (
|
||||
<svg
|
||||
id="triangle"
|
||||
viewBox="0 0 445 379"
|
||||
className={className}
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<style jsx>
|
||||
{`
|
||||
@media (prefers-color-scheme: light) {
|
||||
#triangle-0 {
|
||||
fill: #000;
|
||||
}
|
||||
#triangle-1 {
|
||||
fill: #ff4400;
|
||||
}
|
||||
#triangle-2 {
|
||||
fill: #bb44cc;
|
||||
}
|
||||
#triangle-3 {
|
||||
fill: #44bbcc;
|
||||
}
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
<path
|
||||
id="triangle-0"
|
||||
d="M334.033 238.33L222.692 46.7201L111.352 238.33H334.033Z"
|
||||
fill="#fff"
|
||||
/>
|
||||
<path
|
||||
id="triangle-1"
|
||||
d="M198.174 1.12812L4.62255 51.2473C1.37638 52.0878 -0.550786 55.4258 0.344333 58.6574L53.7157 251.338C55.1362 256.466 62.0336 257.344 64.6942 252.736L204.874 9.93656C207.535 5.32834 203.325 -0.205751 198.174 1.12812Z"
|
||||
fill="url(#triangle-1-gradient)"
|
||||
/>
|
||||
<path
|
||||
id="triangle-2"
|
||||
d="M246.294 1.12825L439.846 51.2473C443.092 52.0879 445.019 55.4259 444.124 58.6574L390.753 251.338C389.332 256.466 382.435 257.344 379.774 252.736L239.594 9.93665C236.933 5.32844 241.143 -0.205634 246.294 1.12825Z"
|
||||
fill="url(#triangle-2-gradient)"
|
||||
/>
|
||||
<path
|
||||
id="triangle-3"
|
||||
d="M361.388 286.577L225.585 377.959C223.559 379.322 220.91 379.322 218.885 377.959L83.0814 286.577C78.1672 283.27 80.5079 275.599 86.4311 275.599H358.039C363.962 275.599 366.303 283.27 361.388 286.577Z"
|
||||
fill="url(#triangle-3-gradient)"
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="triangle-1-gradient"
|
||||
x1="-1.28386"
|
||||
y1="52.7769"
|
||||
x2="134.785"
|
||||
y2="131.336"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#992900" />
|
||||
<stop offset="1" stopColor="#FF4400" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="triangle-2-gradient"
|
||||
x1="445.753"
|
||||
y1="52.7769"
|
||||
x2="309.684"
|
||||
y2="131.336"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#6D2178" />
|
||||
<stop offset="1" stopColor="#BB44CC" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="triangle-3-gradient"
|
||||
x1="222.236"
|
||||
y1="380.213"
|
||||
x2="222.236"
|
||||
y2="275.599"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#1B5B64" />
|
||||
<stop offset="1" stopColor="#44BBCC" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
26
docs-nextra/next.config.mjs
Normal file
@@ -0,0 +1,26 @@
|
||||
import withNextra from "nextra"
|
||||
import RemarkLinkRewrite from "remark-link-rewrite"
|
||||
|
||||
export default withNextra({
|
||||
theme: "nextra-theme-docs",
|
||||
themeConfig: "./theme.config.jsx",
|
||||
mdxOptions: {
|
||||
format: "detect",
|
||||
remarkPlugins: [
|
||||
[
|
||||
RemarkLinkRewrite,
|
||||
{
|
||||
replacer(url) {
|
||||
if (url.includes(".md")) {
|
||||
url = url.replace(/\.md$/, "").replace(".md#", "#")
|
||||
}
|
||||
if (url.startsWith("https://authjs.dev")) {
|
||||
url = url.replace("https://authjs.dev", "")
|
||||
}
|
||||
return url
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
})()
|
||||
31
docs-nextra/package.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "docs-nextra",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"typedoc": "typedoc",
|
||||
"dev": "TYPEDOC_WATCH=1 pnpm typedoc & next dev",
|
||||
"start": "next start",
|
||||
"build": "next build"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "ndom91 <yo@ndo.dev> (https://ndo.dev/)",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@codesandbox/sandpack-react": "^2.0.6",
|
||||
"@codesandbox/sandpack-themes": "^2.0.1",
|
||||
"framer-motion": "^8.4.3",
|
||||
"next": "13.1.1",
|
||||
"nextra": "^2.2.5",
|
||||
"nextra-theme-docs": "^2.2.5",
|
||||
"remark-link-rewrite": "^1.0.6",
|
||||
"typedoc": "^0.23.24",
|
||||
"typedoc-plugin-markdown": "next"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^10.4.13",
|
||||
"postcss": "^8.4.21",
|
||||
"tailwindcss": "^3.2.4"
|
||||
}
|
||||
}
|
||||
5
docs-nextra/pages/_app.jsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import "../styles/index.css"
|
||||
|
||||
export default function NextraApp({ Component, pageProps }) {
|
||||
return ( <Component {...pageProps} />)
|
||||
}
|
||||
32
docs-nextra/pages/_meta.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"docs": {
|
||||
"title": "Docs",
|
||||
"type": "page",
|
||||
"href": "/getting-started/introduction"
|
||||
},
|
||||
"frameworks": {
|
||||
"title": "Frameworks",
|
||||
"type": "page",
|
||||
"href": "/reference"
|
||||
},
|
||||
"index": {
|
||||
"title": "Home",
|
||||
"theme": {
|
||||
"sidebar": false,
|
||||
"layout": "raw"
|
||||
}
|
||||
},
|
||||
"getting-started": "Getting Started",
|
||||
"guides": "Guides",
|
||||
"reference": "Reference",
|
||||
"concepts": "Concepts",
|
||||
"--": {
|
||||
"type": "separator"
|
||||
},
|
||||
"contributors": "Contributors",
|
||||
"github-link": {
|
||||
"title": "Auth.js Repo",
|
||||
"href": "https://github.com/nextauthjs/next-auth",
|
||||
"newWindow": true
|
||||
}
|
||||
}
|
||||
4
docs-nextra/pages/concepts/_meta.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"faq": "FAQ",
|
||||
"oauth": "OAuth"
|
||||
}
|
||||
359
docs-nextra/pages/concepts/faq.md
Normal file
@@ -0,0 +1,359 @@
|
||||
---
|
||||
id: faq
|
||||
title: Frequently Asked Questions
|
||||
---
|
||||
|
||||
## About Auth.js
|
||||
|
||||
### Is Auth.js commercial software?
|
||||
|
||||
Auth.js is an open source project built by individual contributors.
|
||||
|
||||
It is not commercial software and is not associated with a commercial organization.
|
||||
|
||||
---
|
||||
|
||||
## Compatibility
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>What databases does Auth.js support?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
You can use Auth.js with MySQL, MariaDB, Postgres, MongoDB and SQLite or without a database. (See our [using a database adapter guide](/guides/adapters/using-a-database-adapter)).
|
||||
|
||||
You can use also Auth.js with any database using a custom database adapter, or by using a custom credentials authentication provider - e.g. to support signing in with a username and password stored in an existing database.
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>What authentication services does Auth.js support?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
<p>Auth.js includes built-in support for signing in with
|
||||
(See also: <a href="/reference/providers/oauth-builtin">Providers</a>)
|
||||
</p>
|
||||
|
||||
Auth.js also supports email for passwordless sign in, which is useful for account recovery or for people who are not able to use an account with the configured OAuth services (e.g. due to service outage, account suspension or otherwise becoming locked out of an account).
|
||||
|
||||
You can also use a custom based provider to support signing in with a username and password stored in an external database and/or using two factor authentication.
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>Does Auth.js support signing in with a username and password?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
Auth.js is designed to avoid the need to store passwords for user accounts.
|
||||
|
||||
If you have an existing database of usernames and passwords, you can use a custom credentials provider to allow signing in with a username and password stored in an existing database.
|
||||
|
||||
_If you use a custom credentials provider user accounts will not be persisted in a database by Auth.js (even if one is configured). The option to use JSON Web Tokens for session tokens (which allow sign in without using a session database) must be enabled to use a custom credentials provider._
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>Can I use Auth.js with a website that does not use Next.js?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
Auth.js is designed for use with Next.js and Serverless.
|
||||
|
||||
If you are using a different framework for your website, you can create a website that handles sign in with Next.js and then access those sessions on a website that does not use Next.js as long as the websites are on the same domain.
|
||||
|
||||
If you use Auth.js on a website with a different subdomain then the rest of your website (e.g. `auth.example.com` vs `www.example.com`) you will need to set a custom cookie domain policy for the Session Token cookie. (See also: [Cookies](/reference/configuration/auth-config#cookies))
|
||||
|
||||
Auth.js does not currently support automatically signing into sites on different top level domains (e.g. `www.example.com` vs `www.example.org`) using a single session.
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>Can I use Auth.js with React Native?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
Auth.js is designed as a secure, confidential client and implements a server side authentication flow.
|
||||
|
||||
It is not intended to be used in native applications on desktop or mobile applications, which typically implement public clients (e.g. with client / secrets embedded in the application).
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>Is Auth.js supporting TypeScript?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
Yes! Check out the [TypeScript docs](/getting-started/typescript)
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>Is Auth.js compatible with Next.js 12 Middleware?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
[Next.js Middleware](https://nextjs.org/docs/middleware) is supported. Head over to the [this page](/reference/nextjs/#middleware)
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## Databases
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>What databases are supported by Auth.js?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
Auth.js can be used with MySQL, Postgres, MongoDB, SQLite and compatible databases (e.g. MariaDB, Amazon Aurora, Amazon DocumentDB…) or with no database.
|
||||
|
||||
It also provides an Adapter API which allows you to connect it to any database.
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>What does Auth.js use databases for?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
Databases in Auth.js are used for persisting users, OAuth accounts, email sign in tokens and sessions.
|
||||
|
||||
Specifying a database is optional if you don't need to persist user data or support email sign in. If you don't specify a database then JSON Web Tokens will be enabled for session storage and used to store session data.
|
||||
|
||||
If you are using a database with Auth.js, you can still explicitly enable JSON Web Tokens for sessions (instead of using database sessions).
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>Should I use a database?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
- Using Auth.js without a database works well for internal tools - where you need to control who is able to sign in, but when you do not need to create user accounts for them in your application.
|
||||
|
||||
- Using Auth.js with a database is usually a better approach for a consumer facing application where you need to persist accounts (e.g. for billing, to contact customers, etc).
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>What database should I use?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
Managed database solutions for MySQL, Postgres and MongoDB (and compatible databases) are well supported from cloud providers such as Amazon, Google, Microsoft and Atlas.
|
||||
|
||||
If you are deploying directly to a particular cloud platform you may also want to consider serverless database offerings they have (e.g. [Amazon Aurora Serverless on AWS](https://aws.amazon.com/rds/aurora/serverless/)).
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
Parts of this section has been moved to its [own page](/getting-started/security).
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>How do I get Refresh Tokens and Access Tokens for an OAuth account?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
Auth.js provides a solution for authentication, session management and user account creation.
|
||||
|
||||
Auth.js records Refresh Tokens and Access Tokens on sign in (if supplied by the provider) and it will pass them, along with the User ID, Provider and Provider Account ID, to either:
|
||||
|
||||
1. A database - if a database connection string is provided
|
||||
2. The JSON Web Token callback - if JWT sessions are enabled (e.g. if no database specified)
|
||||
|
||||
You can then look them up from the database or persist them to the JSON Web Token.
|
||||
|
||||
Note: Auth.js does not currently handle Access Token rotation for OAuth providers for you, however you can check out [this tutorial](/guides/basics/refresh-token-rotation) if you want to implement it.
|
||||
|
||||
We also have an [example repository](https://github.com/nextauthjs/next-auth-refresh-token-example) / project based upon Auth.js v4 where we demonstrate how to use a refresh token to refresh the provided access token.
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>When I sign in with another account with the same email address, why are accounts not linked automatically?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
Automatic account linking on sign in is not secure between arbitrary providers - with the exception of allowing users to sign in via an email addresses as a fallback (as they must verify their email address as part of the flow).
|
||||
|
||||
When an email address is associated with an OAuth account it does not necessarily mean that it has been verified as belonging to account holder — how email address verification is handled is not part of the OAuth specification and varies between providers (e.g. some do not verify first, some do verify first, others return metadata indicating the verification status).
|
||||
|
||||
With automatic account linking on sign in, this can be exploited by bad actors to hijack accounts by creating an OAuth account associated with the email address of another user.
|
||||
|
||||
For this reason it is not secure to automatically link accounts between arbitrary providers on sign in, which is why this feature is generally not provided by authentication service and is not provided by Auth.js.
|
||||
|
||||
Automatic account linking is seen on some sites, sometimes insecurely. It can be technically possible to do automatic account linking securely if you trust all the providers involved to ensure they have securely verified the email address associated with the account, but requires placing trust (and transferring the risk) to those providers to handle the process securely.
|
||||
|
||||
Examples of scenarios where this is secure include with an OAuth provider you control (e.g. that only authorizes users internal to your organization) or with a provider you explicitly trust to have verified the users email address.
|
||||
|
||||
Automatic account linking is not a planned feature of Auth.js, however there is scope to improve the user experience of account linking and of handling this flow, in a secure way. Typically this involves providing a fallback option to sign in via email, which is already possible (and recommended), but the current implementation of this flow could be improved on.
|
||||
|
||||
Providing support for secure account linking and unlinking of additional providers - which can only be done if a user is already signed in already - was originally a feature in v1.x but has not been present since v2.0, is planned to return in a future release.
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## Feature Requests
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>Why doesn't Auth.js support [a particular feature]?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
Auth.js is an open source project built by individual contributors who are volunteers writing code and providing support in their spare time.
|
||||
|
||||
If you would like Auth.js to support a particular feature, the best way to help make it happen is to raise a feature request describing the feature and offer to work with other contributors to develop and test it.
|
||||
|
||||
If you are not able to develop a feature yourself, you can offer to sponsor someone to work on it.
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>I disagree with a design decision, how can I change your mind?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
Product design decisions on Auth.js are made by core team members.
|
||||
|
||||
You can raise suggestions as feature requests / requests for enhancement.
|
||||
|
||||
Requests that provide the detail requested in the template and follow the format requested may be more likely to be supported, as additional detail prompted in the templates often provides important context.
|
||||
|
||||
Ultimately if your request is not accepted or is not actively in development, you are always free to fork the project under the terms of the ISC License.
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## JSON Web Tokens
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3>Does Auth.js use JSON Web Tokens?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
Auth.js by default uses JSON Web Tokens for saving the user's session. However, if you use a [database adapter](/guides/adapters/using-a-database-adapter), the database will be used to persist the user's session. You can force the usage of JWT when using a database [through the configuration options](/reference/configuration/auth-config#session). Since v4 all our JWT tokens are now encrypted by default with A256GCM.
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3>What are the advantages of JSON Web Tokens?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
JSON Web Tokens can be used for session tokens, but are also used for lots of other things, such as sending signed objects between services in authentication flows.
|
||||
|
||||
- Advantages of using a JWT as a session token include that they do not require a database to store sessions, this can be faster and cheaper to run and easier to scale.
|
||||
|
||||
- JSON Web Tokens in Auth.js are secured using cryptographic encryption (JWE) to store the included information directly in a JWT session token. You may then use the token to pass information between services and APIs on the same domain without having to contact a database to verify the included information.
|
||||
|
||||
- You can use JWT to securely store information you do not mind the client knowing even without encryption, as the JWT is stored in a server-readable-only cookie so data in the JWT is not accessible to third party JavaScript running on your site.
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3>What are the disadvantages of JSON Web Tokens?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
- You cannot as easily expire a JSON Web Token - doing so requires maintaining a server side blocklist of invalid tokens (at least until they expire) and checking every token against the list every time a token is presented.
|
||||
|
||||
Shorter session expiry times are used when using JSON Web Tokens as session tokens to allow sessions to be invalidated sooner and simplify this problem.
|
||||
|
||||
Auth.js client includes advanced features to mitigate the downsides of using shorter session expiry times on the user experience, including automatic session token rotation, optionally sending keep alive messages to prevent short lived sessions from expiring if there is an window or tab open, background re-validation, and automatic tab/window syncing that keeps sessions in sync across windows any time session state changes or a window or tab gains or loses focus.
|
||||
|
||||
- As with database session tokens, JSON Web Tokens are limited in the amount of data you can store in them. There is typically a limit of around 4096 bytes per cookie, though the exact limit varies between browsers, proxies and hosting services. If you want to support most browsers, then do not exceed 4096 bytes per cookie. If you want to save more data, you will need to persist your sessions in a database (Source: [browsercookielimits.iain.guru](http://browsercookielimits.iain.guru/))
|
||||
|
||||
The more data you try to store in a token and the more other cookies you set, the closer you will come to this limit. Since v4 we have implemented cookie chunking so that cookies over the 4kb limit get split and reassembled upon parsing. However since this data needs to be transmitted on every request, if you wish to store more than ~4 KB of data you're probably at the point where you want to store a unique ID in the token and persist the data elsewhere (e.g. in a server-side key/value store).
|
||||
|
||||
- Data stored in an encrypted JSON Web Token (JWE) may be compromised at some point.
|
||||
|
||||
Even if appropriately configured, information stored in an encrypted JWT should not be assumed to be impossible to decrypt at some point - e.g. due to the discovery of a defect or advances in technology.
|
||||
|
||||
Avoid storing any data in a token that might be problematic if it were to be decrypted in the future.
|
||||
|
||||
- If you do not explicitly specify a secret for for Auth.js, existing sessions will be invalidated any time your Auth.js configuration changes, as Auth.js will default to an auto-generated secret. Since v4 this only impacts development and generating a secret is required in production.
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3>Are JSON Web Tokens secure?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
By default tokens are not signed (JWS) but are encrypted (JWE). Since v4 we have implemented cookie chunking so that cookies over the 4kb limit get split and reassembled upon parsing.
|
||||
|
||||
You can specify other valid algorithms - [as specified in RFC 7518](https://tools.ietf.org/html/rfc7517) - with either a secret (for symmetric encryption) or a public/private key pair (for asymmetric encryption).
|
||||
|
||||
Auth.js will generate keys for you, but this will generate a warning at start up.
|
||||
|
||||
Using explicit public/private keys for signing is strongly recommended.
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3>What signing and encryption standards does Auth.js support?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
Auth.js includes a largely complete implementation of JSON Object Signing and Encryption (JOSE):
|
||||
|
||||
- [RFC 7515 - JSON Web Signature (JWS)](https://tools.ietf.org/html/rfc7515)
|
||||
- [RFC 7516 - JSON Web Encryption (JWE)](https://tools.ietf.org/html/rfc7516)
|
||||
- [RFC 7517 - JSON Web Key (JWK)](https://tools.ietf.org/html/rfc7517)
|
||||
- [RFC 7518 - JSON Web Algorithms (JWA)](https://tools.ietf.org/html/rfc7518)
|
||||
- [RFC 7519 - JSON Web Token (JWT)](https://tools.ietf.org/html/rfc7519)
|
||||
|
||||
This incorporates support for:
|
||||
|
||||
- [RFC 7638 - JSON Web Key Thumbprint](https://tools.ietf.org/html/rfc7638)
|
||||
- [RFC 7787 - JSON JWS Unencoded Payload Option](https://tools.ietf.org/html/rfc7797)
|
||||
- [RFC 8037 - CFRG Elliptic Curve ECDH and Signatures](https://tools.ietf.org/html/rfc8037)
|
||||
|
||||
</p>
|
||||
</details>
|
||||
53
docs-nextra/pages/concepts/oauth.mdx
Normal file
@@ -0,0 +1,53 @@
|
||||
---
|
||||
title: How OAuth works
|
||||
---
|
||||
|
||||
import { Callout } from 'nextra-theme-docs'
|
||||
|
||||
Authentication Providers in **Auth.js** are OAuth definitions that allow your users to sign in with their favorite preexisting logins. You can use any of our many predefined providers, or write your own custom OAuth configuration.
|
||||
|
||||
- [Using a built-in OAuth Provider](#built-in-providers) (e.g Github, Twitter, Google, etc...)
|
||||
- [Using a custom OAuth Provider](#using-a-custom-provider)
|
||||
|
||||
<Callout>
|
||||
Auth.js is designed to work with any OAuth service, it supports **OAuth 1.0**, **1.0A**, **2.0** and **OpenID Connect** and has built-in support for most popular sign-in services.
|
||||
</Callout>
|
||||
|
||||
Without going into too much detail, the OAuth flow generally has 6 parts:
|
||||
|
||||
1. The application requests authorization to access service resources from the user
|
||||
2. If the user authorized the request, the application receives an authorization grant
|
||||
3. The application requests an access token from the authorization server (API) by presenting authentication of its own identity, and the authorization grant
|
||||
4. If the application identity is authenticated and the authorization grant is valid, the authorization server (API) issues an access token to the application. Authorization is complete.
|
||||
5. The application requests the resource from the resource server (API) and presents the access token for authentication
|
||||
6. If the access token is valid, the resource server (API) serves the resource to the application
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Browser
|
||||
participant App Server
|
||||
participant Auth Server (Github)
|
||||
Note left of Browser: User clicks on "Sign in"
|
||||
Browser->>App Server: GET<br/>"api/auth/signin"
|
||||
App Server->>App Server: Computes the available<br/>sign in providers<br/>from the "providers" option
|
||||
App Server->>Browser: Redirects to Sign in page
|
||||
Note left of Browser: Sign in options<br/>are shown the user<br/>(Github, Twitter, etc...)
|
||||
Note left of Browser: User clicks on<br/>"Sign in with Github"
|
||||
Browser->>App Server: POST<br/>"api/auth/signin/github"
|
||||
App Server->>App Server: Computes sign in<br/>options for Github<br/>(scopes, callback URL, etc...)
|
||||
App Server->>Auth Server (Github): GET<br/>"github.com/login/oauth/authorize"
|
||||
Note left of Auth Server (Github): Sign in options<br> are supplied as<br/>query params<br/>(clientId, <br/>scope, etc...)
|
||||
Auth Server (Github)->>Browser: Shows sign in page<br/>in Github.com<br/>to the user
|
||||
Note left of Browser: User inserts their<br/>credentials in Github
|
||||
Browser->>Auth Server (Github): Github validates the inserted credentials
|
||||
Auth Server (Github)->>Auth Server (Github): Generates one time access code<br/>and calls callback<br>URL defined in<br/>App settings
|
||||
Auth Server (Github)->>App Server: GET<br/>"api/auth/github/callback?code=123"
|
||||
App Server->>App Server: Grabs code<br/>to exchange it for<br/>access token
|
||||
App Server->>Auth Server (Github): POST<br/>"github.com/login/oauth/access_token"<br/>{code: 123}
|
||||
Auth Server (Github)->>Auth Server (Github): Verifies code is<br/>valid and generates<br/>access token
|
||||
Auth Server (Github)->>App Server: { access_token: 16C7x... }
|
||||
App Server->>App Server: Generates session token<br/>and stores session
|
||||
App Server->>Browser: You're now logged in!
|
||||
```
|
||||
|
||||
For more details, check out Aaron Parecki's blog post [OAuth2 Simplified](https://aaronparecki.com/oauth-2-simplified/) or Postman's blog post [OAuth 2.0: Implicit Flow is Dead, Try PKCE Instead](https://blog.postman.com/pkce-oauth-how-to/).
|
||||
41
docs-nextra/pages/contributors.md
Normal file
@@ -0,0 +1,41 @@
|
||||
## Core team
|
||||
|
||||
Without these people, the project could not have become one of the most used authentication libraries in its category.
|
||||
|
||||
- [Balázs Orbán](https://github.com/balazsorban44) - **Lead Maintainer**
|
||||
- [Thang Vu](https://github.com/ThangHuuVu) - Maintainer (Core)
|
||||
- [Nico Domino](https://github.com/ndom91) - Maintainer (Core, Documentation)
|
||||
- [Lluis Agusti](https://github.com/lluia) - Maintainer (Documentation, Testing, TypeScript)
|
||||
|
||||
## Special thanks
|
||||
|
||||
Special thanks to Lori Karikari for creating most of the original provider configurations, to Fredrik Pettersen for creating the original Prisma Adapter, to Gerald Nolan for adding support for Sign in with Apple, and to Jefferson Bledsoe for working on the original testing automation.
|
||||
|
||||
- [Lori Karikari](https://github.com/LoriKarikari)
|
||||
- [Fredrik Pettersen](https://github.com/Fumler)
|
||||
- [Gerald Nolan](https://github.com/geraldnolan)
|
||||
- [Jefferson Bledsoe](https://github.com/JeffersonBledsoe)
|
||||
|
||||
## Other contributors
|
||||
|
||||
Auth.js as it exists today has been possible thanks to the work of many individual contributors.
|
||||
|
||||
Thank you to the [dozens of individual contributors](https://github.com/nextauthjs/next-auth/graphs/contributors) who have helped shape Auth.js.
|
||||
|
||||
## Open Collective
|
||||
|
||||
You can find Auth.js on Open Collective. We are very thankful for all of our existing contributors and would be delighted if you or your company would decide to join them.
|
||||
|
||||
More information can be found at: https://opencollective.com/nextauth
|
||||
|
||||
## History
|
||||
|
||||
- NextAuth.js was originally developed by <a href="https://github.com/iaincollins">Iain Collins</a> in 2016 for Next.js.
|
||||
|
||||
- In 2020, NextAuth.js was rebuilt from the ground up to support Serverless, with support for MySQL, Postgres and MongoDB, JSON Web Tokens and built-in support for over a dozen authentication providers.
|
||||
|
||||
- In 2021, efforts have started to move NextAuth.js to other frameworks and to support as many databases and providers as possible.
|
||||
|
||||
- In 2022, NextAuth.js was rebranded to Auth.js and became framework/runtime agnostic. The core library `@auth/core` was written by <a href="https://twitter.com/balazsorban44">Balázs Orbán</a>
|
||||
|
||||
- In 2023 // TODO: 👀🤫
|
||||
12
docs-nextra/pages/getting-started/_meta.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"credentials-tutorial": "Credentials Authentication",
|
||||
"databases": "Databases",
|
||||
"email-tutorial": "Email Authentication",
|
||||
"introduction": "Introduction",
|
||||
"oauth-tutorial": "OAuth Authentication",
|
||||
"typescript": "Typescript",
|
||||
"security": "Security",
|
||||
"img": {
|
||||
"display": "hidden"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
---
|
||||
title: Credentials authentication
|
||||
title: Credentials Authentication
|
||||
---
|
||||
|
||||
import { Callout } from 'nextra-theme-docs'
|
||||
|
||||
Auth.js is built in a way that is flexible to integrate it with any authentication back-end you or your company may already have.
|
||||
|
||||
This library has been designed to handle the user session client-wise, to support multiple authentication methods (OAuth, Email, etc...) so that you're not forced to run your own authentication service.
|
||||
@@ -10,9 +12,9 @@ In case you already have an authentication service, you can use the Credentials
|
||||
|
||||
For this tutorial, we're going to use [Auth.js example app](https://github.com/nextauthjs/next-auth-example) as a base.
|
||||
|
||||
:::warning
|
||||
<Callout type="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.
|
||||
:::
|
||||
</Callout>
|
||||
|
||||
Integrating the Credentials Provider is as simple as initializing it in the Auth.js configuration file:
|
||||
|
||||
@@ -45,12 +47,12 @@ export default NextAuth({
|
||||
})
|
||||
```
|
||||
|
||||
:::note
|
||||
Check the [Credentials Provider options](/reference/core/providers_credentials) for further customization
|
||||
:::
|
||||
<Callout type="info">
|
||||
Check the [Credentials Provider options](/reference/providers/credentials) for further customization
|
||||
</Callout>
|
||||
|
||||
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.
|
||||
|
||||
:::info
|
||||
<Callout type="info">
|
||||
If you're using TypeScript, you can [augment the `User` interface](/getting-started/typescript#module-augmentation) to match the response of your `authorize` callback, so whenever you read the user in other callbacks (like the `jwt`) the type will match correctly.
|
||||
:::
|
||||
</Callout>
|
||||
22
docs-nextra/pages/getting-started/databases.mdx
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
title: Databasess
|
||||
---
|
||||
|
||||
import { Callout } from 'nextra-theme-docs'
|
||||
|
||||
Auth.js offers multiple database adapters. Check our guides on:
|
||||
|
||||
- [Using an existing database adapter](/guides/adapters/using-a-database-adapter)
|
||||
- [Creating your own](/guides/adapters/creating-a-database-adapter)
|
||||
|
||||
<Callout type="info">
|
||||
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.
|
||||
</Callout>
|
||||
|
||||
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).
|
||||
@@ -2,6 +2,9 @@
|
||||
title: Email authentication
|
||||
---
|
||||
|
||||
import { Callout } from "nextra-theme-docs"
|
||||
import Image from "next/image"
|
||||
|
||||
import smtpConfig from "./img/dashboard-smtp.png"
|
||||
import startPageImg from "./img/email-tutorial-start.png"
|
||||
import checkPageImg from "./img/email-tutorial-check.png"
|
||||
@@ -18,9 +21,12 @@ The Email provider can be used in conjunction with (or instead of) one or more O
|
||||
|
||||
On initial sign in, a **Verification Token** is sent to the email address provided. By default this token **is valid for 24 hours**. If the Verification Token is used within that time (i.e. by clicking on the link in the email) an account is created for the user and they are signed in.
|
||||
|
||||
:::tip
|
||||
The Email Provider can be used with both JSON Web Tokens and database sessions, but you [must configure a database adapter](/guides/adapters/using-a-database-adapter) to use it. It is not possible to enable email sign in without using a database.
|
||||
:::
|
||||
<Callout>
|
||||
The Email Provider can be used with both JSON Web Tokens and database
|
||||
sessions, but you [must configure a database
|
||||
adapter](/guides/adapters/using-a-database-adapter) to use it. It is not
|
||||
possible to enable email sign in without using a database.
|
||||
</Callout>
|
||||
|
||||
## 1. Installing `nodemailer`
|
||||
|
||||
@@ -36,13 +42,14 @@ npm install -D nodemailer
|
||||
|
||||
Next we need a [SMTP service](https://sendgrid.com/blog/what-is-an-smtp-server/) which will be in charge of sending emails from our application. There's a number of services available for this, however [here are the ones](http://nodemailer.com/smtp/well-known/) known to work with `nodemailer`.
|
||||
|
||||
:::info
|
||||
For this tutorial, we're going to be using [Sendgrid](https://sendgrid.com/), but any of the services linked above should work the same
|
||||
:::
|
||||
<Callout>
|
||||
For this tutorial, we're going to be using [Sendgrid](https://sendgrid.com/),
|
||||
but any of the services linked above should work the same
|
||||
</Callout>
|
||||
|
||||
First create an account in and then login to the dashboard, then navigate to "Settings → API Keys" and create an API key:
|
||||
|
||||
<img src={smtpConfig} />
|
||||
<Image src={smtpConfig} />
|
||||
|
||||
Next paste the API in your terminal as so, and run the command:
|
||||
|
||||
@@ -66,17 +73,17 @@ 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 Email from "next-auth/providers/email"
|
||||
import EmailProvider from "next-auth/providers/email"
|
||||
|
||||
export default NextAuth({
|
||||
providers: [
|
||||
Email({
|
||||
server: {
|
||||
host: process.env.SMTP_HOST,
|
||||
port: Number(process.env.SMTP_PORT),
|
||||
host: process.env.EMAIL_SERVER_HOST,
|
||||
port: Number(process.env.EMAIL_SERVER_PORT),
|
||||
auth: {
|
||||
user: process.env.SMTP_USER,
|
||||
pass: process.env.SMTP_PASSWORD,
|
||||
user: process.env.EMAIL_SERVER_USER,
|
||||
pass: process.env.EMAIL_SERVER_PASSWORD,
|
||||
},
|
||||
},
|
||||
from: process.env.EMAIL_FROM,
|
||||
@@ -147,8 +154,8 @@ import EmailProvider from "next-auth/providers/email"
|
||||
|
||||
export default NextAuth({
|
||||
secret: process.env.NEXTAUTH_SECRET,
|
||||
+ adapter: MongoDBAdapter(clientPromise),
|
||||
providers: [
|
||||
+ adapter: MongoDBAdapter(clientPromise),
|
||||
EmailProvider({
|
||||
server: {
|
||||
host: process.env.EMAIL_SERVER_HOST,
|
||||
@@ -172,30 +179,34 @@ Let's start by running a Next.js application with NextAuth, making sure the **Em
|
||||
|
||||
For this tutorial we're going to be using NextAuth example app. Launch the app and click on "Sign in", we're redirected to the Sign In page:
|
||||
|
||||
<img src={startPageImg} alt="Screenshot of sign in page" />
|
||||
<Image src={startPageImg} alt="Screenshot of sign in page" />
|
||||
|
||||
:::info
|
||||
You can customize the look and feel of your Sign in page pretty easily with NextAuth. Refer to our [pages guide](/guides/basics/pages) for that!
|
||||
:::
|
||||
<Callout>
|
||||
You can customize the look and feel of your Sign in page pretty easily with
|
||||
NextAuth. Refer to our [pages guide](/guides/basics/pages) for that!
|
||||
</Callout>
|
||||
|
||||
Then we insert the email address we want to log-in with in the Email credentials section and click on "Sign in with Email".
|
||||
|
||||
NextAuth will then display another page hinting the user to check their email:
|
||||
|
||||
<img src={checkPageImg} alt="Screenshot of check email page" />
|
||||
<Image src={checkPageImg} alt="Screenshot of check email page" />
|
||||
|
||||
Let's now check our email, and look for one sent from NextAuth (check your spam folder just in case):
|
||||
|
||||
<img src={mailboxImg} alt="Screenshot of mailbox" />
|
||||
<Image src={mailboxImg} alt="Screenshot of mailbox" />
|
||||
|
||||
Nice! We got one, coming from the sender specified in the `EMAIL_FROM` environment variable from our configuration above and that's is the sender we verified in Sendgrid.
|
||||
Nice! We got one, coming from the sender specified in the `EMAIL_FROM` environment variable from our configuration above and that's is the sender we verified in Sengrid.
|
||||
|
||||
Click on "Sign in" and a new browser tab will open, you should then land on your application as authenticated!
|
||||
|
||||
<img src={loggedInImg} alt="Screenshot of logged in" />
|
||||
<Image src={loggedInImg} alt="Screenshot of logged in" />
|
||||
|
||||
Easy right? We had to configure Sendgrid and install a database adapter so the user sessions can be saved somewhere, but once done NextAuth will deal with all the user session management for us in a secure way!
|
||||
|
||||
:::info
|
||||
A user account (i.e. an entry in the Users table) will not be created for the user until the first time they verify their email address. If an email address is already associated with an account, the user will be signed in to that account when they use the link in the email.
|
||||
:::
|
||||
<Callout>
|
||||
A user account (i.e. an entry in the Users table) will not be created for the
|
||||
user until the first time they verify their email address. If an email address
|
||||
is already associated with an account, the user will be signed in to that
|
||||
account when they use the link in the email.
|
||||
</Callout>
|
||||
BIN
docs-nextra/pages/getting-started/img/dashboard-smtp.png
Normal file
|
After Width: | Height: | Size: 172 KiB |
BIN
docs-nextra/pages/getting-started/img/email-tutorial-check.png
Normal file
|
After Width: | Height: | Size: 132 KiB |
BIN
docs-nextra/pages/getting-started/img/email-tutorial-logged.png
Normal file
|
After Width: | Height: | Size: 175 KiB |
BIN
docs-nextra/pages/getting-started/img/email-tutorial-mailbox.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
docs-nextra/pages/getting-started/img/email-tutorial-start.png
Normal file
|
After Width: | Height: | Size: 127 KiB |
|
After Width: | Height: | Size: 157 KiB |
|
After Width: | Height: | Size: 183 KiB |
|
After Width: | Height: | Size: 204 KiB |
|
After Width: | Height: | Size: 172 KiB |
|
After Width: | Height: | Size: 193 KiB |
|
After Width: | Height: | Size: 202 KiB |
56
docs-nextra/pages/getting-started/introduction.md
Normal file
@@ -0,0 +1,56 @@
|
||||
---
|
||||
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.
|
||||
302
docs-nextra/pages/getting-started/oauth-tutorial.mdx
Normal file
@@ -0,0 +1,302 @@
|
||||
---
|
||||
title: OAuth authentication
|
||||
---
|
||||
|
||||
import { Callout } from "nextra-theme-docs"
|
||||
import Image from "next/image"
|
||||
|
||||
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. Is 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**.
|
||||
|
||||
<Callout type="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.
|
||||
</Callout>
|
||||
|
||||
## 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.
|
||||
|
||||
<Callout type="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).
|
||||
</Callout>
|
||||
|
||||
### 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.ts"
|
||||
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. 💪🏽
|
||||
|
||||
<Callout>
|
||||
Check our [client docs](/reference/react/) to learn all the available options for handling sessions on the browser.
|
||||
</Callout>
|
||||
|
||||
### 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.
|
||||
|
||||
<Callout type="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.
|
||||
</Callout>
|
||||
|
||||
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.
|
||||
|
||||
<Image 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:
|
||||
|
||||
<Image 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
|
||||
```
|
||||
|
||||
<Callout type="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`.
|
||||
</Callout>
|
||||
|
||||
Next you'll be presented with the following screen which presents all the configuration for your new OAuth app. For now, let's we need two things from it: the **Client ID** and **Client Secret** for our new OAuth app:
|
||||
|
||||
<Image 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).
|
||||
|
||||
<Callout type="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 tem a malicious actor could hijack your application and cause you and your user serious problems!
|
||||
</Callout>
|
||||
|
||||
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!
|
||||
|
||||
<Callout type="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
|
||||
</Callout>
|
||||
|
||||
## 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:
|
||||
|
||||
<Image 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:
|
||||
|
||||
<Image 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:
|
||||
|
||||
<Image src={nextAuthUserLoggedIn} />
|
||||
|
||||
Great! We have completed the whole E2E authentication flow setup so that users can login in our application through Github!
|
||||
|
||||
<Callout type="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.
|
||||
</Callout>
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
<Callout type="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).
|
||||
</Callout>
|
||||
|
||||
For more information please check out our [deployment page](/guides/basics/deployment).
|
||||
30
docs-nextra/pages/getting-started/security.mdx
Normal file
@@ -0,0 +1,30 @@
|
||||
---
|
||||
title: Security
|
||||
---
|
||||
|
||||
import { Callout } from 'nextra-theme-docs'
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Auth.js practices responsible disclosure.
|
||||
|
||||
We request that you contact us directly to report serious issues that might impact the security of sites using Auth.js.
|
||||
|
||||
If you contact us regarding a serious issue:
|
||||
|
||||
- We will endeavor to get back to you within 72 hours.
|
||||
- We will aim to publish a fix within 30 days.
|
||||
- We will disclose the issue (and credit you, with your consent) once a fix to resolve the issue has been released.
|
||||
- If 90 days has elapsed and we still don't have a fix, we will disclose the issue publicly.
|
||||
|
||||
The best way to report an issue is by contacting us via email at info@balazsorban.com or me@iaincollins.com and yo@ndo.dev, or raise a public issue requesting someone get in touch with you via whatever means you prefer for more details. (Please do not disclose sensitive details publicly at this stage.)
|
||||
|
||||
<Callout>
|
||||
For less serious issues (e.g. RFC compliance for unsupported flows or potential issues that may cause a problem in the future) it is appropriate to make these public as bug reports or feature requests or to raise a question to open a discussion around them.
|
||||
</Callout>
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Security updates are only released for the current version.
|
||||
|
||||
Old releases are not maintained and do not receive updates.
|
||||
164
docs-nextra/pages/getting-started/typescript.mdx
Normal file
@@ -0,0 +1,164 @@
|
||||
---
|
||||
title: TypeScript
|
||||
---
|
||||
|
||||
import { Callout } from 'nextra-theme-docs'
|
||||
|
||||
Auth.js has its own type definitions to use in your TypeScript projects safely. Even if you don't use TypeScript, IDEs like VSCode will pick this up to provide you with a better developer experience. While you are typing, you will get suggestions about what certain objects/functions look like, and sometimes links to documentation, examples, and other valuable resources.
|
||||
|
||||
Check out the example repository showcasing how to use `next-auth` on a Next.js application with TypeScript:
|
||||
https://github.com/nextauthjs/next-auth-typescript-example
|
||||
|
||||
---
|
||||
|
||||
## Adapters
|
||||
|
||||
If you're writing your own custom Adapter, you can take advantage of the types to make sure your implementation conforms to what's expected:
|
||||
|
||||
```ts
|
||||
import type { Adapter } from "next-auth/adapters"
|
||||
|
||||
function MyAdapter(): Adapter {
|
||||
return {
|
||||
// your adapter methods here
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When writing your own custom Adapter in plain JavaScript, note that you can use **JSDoc** to get helpful editor hints and auto-completion like so:
|
||||
|
||||
```js
|
||||
/** @return { import("next-auth/adapters").Adapter } */
|
||||
function MyAdapter() {
|
||||
return {
|
||||
// your adapter methods here
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<Callout>
|
||||
This will work in code editors with a strong TypeScript integration like VSCode or WebStorm. It might not work if you're using more lightweight editors like VIM or Atom.
|
||||
</Callout>
|
||||
|
||||
## Module Augmentation
|
||||
|
||||
`next-auth` comes with certain types/interfaces that are shared across submodules. Good examples are `Session` and `JWT`. Ideally, you should only need to create these types at a single place, and TS should pick them up in every location where they are referenced. Luckily, Module Augmentation is exactly that, which can do this for us. Define your shared interfaces in a single place, and get type-safety across your application when using `next-auth` (or one of its submodules).
|
||||
|
||||
### Main module
|
||||
|
||||
Let's look at `Session`:
|
||||
|
||||
```ts title="pages/api/auth/[...nextauth].ts"
|
||||
import NextAuth from "next-auth"
|
||||
|
||||
export default NextAuth({
|
||||
callbacks: {
|
||||
session({ session, token, user }) {
|
||||
return session // The return type will match the one returned in `useSession()`
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
```ts title="pages/index.ts"
|
||||
import { useSession } from "next-auth/react"
|
||||
|
||||
export default function IndexPage() {
|
||||
// `session` will match the returned value of `callbacks.session()` from `NextAuth()`
|
||||
const { data: session } = useSession()
|
||||
|
||||
return (
|
||||
// Your component
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
To extend/augment this type, create a `types/next-auth.d.ts` file in your project:
|
||||
|
||||
```ts title="types/next-auth.d.ts"
|
||||
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 {
|
||||
user: {
|
||||
/** The user's postal address. */
|
||||
address: string
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Extend default interface properties
|
||||
|
||||
By default, TypeScript will merge new interface properties and overwrite existing ones. In this case, the default session user properties will be overwritten, with the new one defined above.
|
||||
|
||||
If you want to keep the default session user properties, you need to add them back into the newly declared interface:
|
||||
|
||||
```ts title="types/next-auth.d.ts"
|
||||
import NextAuth, { DefaultSession } from "next-auth"
|
||||
|
||||
declare module "next-auth" {
|
||||
/**
|
||||
* Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
|
||||
*/
|
||||
interface Session {
|
||||
user: {
|
||||
/** The user's postal address. */
|
||||
address: string
|
||||
} & DefaultSession["user"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Popular interfaces to augment
|
||||
|
||||
Although you can augment almost anything, here are some of the more common interfaces that you might want to override in the `next-auth` module:
|
||||
|
||||
```ts
|
||||
/**
|
||||
* The shape of the user object returned in the OAuth providers' `profile` callback,
|
||||
* or the second parameter of the `session` callback, when using a database.
|
||||
*/
|
||||
interface User {}
|
||||
/**
|
||||
* Usually contains information about the provider being used
|
||||
* and also extends `TokenSet`, which is different tokens returned by OAuth Providers.
|
||||
*/
|
||||
interface Account {}
|
||||
/** The OAuth profile returned from your provider */
|
||||
interface Profile {}
|
||||
```
|
||||
|
||||
Make sure that the `types` folder is added to [`typeRoots`](https://www.typescriptlang.org/tsconfig/#typeRoots) in your project's `tsconfig.json` file.
|
||||
|
||||
### Submodules
|
||||
|
||||
The `JWT` interface can be found in the `next-auth/jwt` submodule:
|
||||
|
||||
```ts title="types/next-auth.d.ts"
|
||||
import { JWT } from "next-auth/jwt"
|
||||
|
||||
declare module "next-auth/jwt" {
|
||||
/** Returned by the `jwt` callback and `getToken`, when using JWT sessions */
|
||||
interface JWT {
|
||||
/** OpenID ID Token */
|
||||
idToken?: string
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Useful links
|
||||
|
||||
1. [TypeScript documentation: Module Augmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation)
|
||||
2. [Digital Ocean: Module Augmentation in TypeScript](https://www.digitalocean.com/community/tutorials/typescript-module-augmentation)
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions of any kind are always welcome, especially for TypeScript. Please keep in mind that we are a small team working on this project in our free time. We will try our best to give support, but if you think you have a solution for a problem, please open a PR!
|
||||
|
||||
<Callout>
|
||||
When contributing to TypeScript, if the actual JavaScript user API does not change in a breaking manner, we reserve the right to push any TypeScript change in a minor release. This ensures that we can keep on a faster release cycle.
|
||||
</Callout>
|
||||
13
docs-nextra/pages/guides/_meta.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"index": "Introduction",
|
||||
"basics": "Basics",
|
||||
"adapters": "Adapters",
|
||||
"providers": "Providers",
|
||||
"testing": "Testing",
|
||||
"corporate-proxies": "Proxies",
|
||||
"other": "Other",
|
||||
"resources": "Community Resources",
|
||||
"img": {
|
||||
"display": "hidden"
|
||||
}
|
||||
}
|
||||
4
docs-nextra/pages/guides/adapters/_meta.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"creating-a-database-adapter": "Create a Database Adapter",
|
||||
"using-a-database-adapter": "Using a Database Adapter"
|
||||
}
|
||||