Compare commits

..

30 Commits

Author SHA1 Message Date
Balázs Orbán
623a6e97ca chore: rename docs-nextra to docs 2023-03-27 13:09:50 +02:00
Balázs Orbán
f7d964d0b9 trigger 2023-03-27 05:12:18 +02:00
Balázs Orbán
dd4170cf08 trigger 2023-03-27 05:07:35 +02:00
Balázs Orbán
b85b5aa5f6 fix code 2023-03-20 23:38:23 +01:00
Balázs Orbán
d77b97c9fc add/fix CI build 2023-03-20 23:34:47 +01:00
Balázs Orbán
e3cfef3c8f Merge branch 'main' into ndom91/docs-rewrite-links 2023-03-20 23:10:15 +01:00
ndom91
50118f305a feat: add codesandbox embed playground 2023-02-21 19:01:36 +01:00
ndom91
e250dceb38 fix: small page full width 2023-02-21 18:13:01 +01:00
ndom91
0a1b6eb63b feat: add 404/500 pages and skipToContent a11y link 2023-02-21 17:58:16 +01:00
ndom91
0c76c5a5a6 fix: add react/react-dom dependencies back 2023-02-21 17:41:59 +01:00
ndom91
26e2f795d8 fix: refactor all to Callout 2023-02-18 18:44:41 +01:00
ndom91
d5eade5ef4 fix: add _meta.json for all sidebar categories and start adding Callouts 2023-02-18 18:12:56 +01:00
Balázs Orbán
2fab7d4cd1 update typedoc-plugin-markdown 2023-01-30 01:57:10 +01:00
Balázs Orbán
177fa44a49 fix formatting, and relative links in nextra 2023-01-19 11:21:03 +01:00
Balázs Orbán
92657bade3 use patched typedoc markdown plugin 2023-01-18 14:42:12 +01:00
Balázs Orbán
581d37b50f Merge branch 'main' into ndom91/docs-rewrite-links 2023-01-18 11:39:38 +01:00
Balázs Orbán
1a928949ce bump nextra 2023-01-18 11:34:55 +01:00
Balázs Orbán
8f222b2e56 fixes 2023-01-16 15:46:07 +01:00
ndom91
01a78e1282 chore: WIP duration hack 2023-01-16 01:20:21 +01:00
ndom91
be481e6fe6 feat: marquee animations 2023-01-16 00:40:52 +01:00
ndom91
e1f5fe3e6d feat: new blob svgs 2023-01-15 23:59:40 +01:00
ndom91
40f754fbca feat: blob design + marquee 2023-01-15 23:46:58 +01:00
ndom91
cf236c6bf3 feat: style landing page 2023-01-15 22:36:13 +01:00
ndom91
b7a38ec7b1 chore: add blob assets 2023-01-15 21:43:45 +01:00
ndom91
db26a4c18d feat: add tailwind and homepage starter 2023-01-15 17:56:50 +01:00
Balázs Orbán
42b55f44f4 remove ignored file 2023-01-13 17:09:37 +01:00
Balázs Orbán
1d3761356a fixes 2023-01-13 17:08:08 +01:00
ndom91
79147f1cf5 fix: typedoc markdown plugin 2023-01-12 22:48:43 +01:00
ndom91
caf63f0eb4 chore(docs): copy over docs 2023-01-12 22:42:43 +01:00
ndom91
7dec2eec71 chore(docs): test nextra docs 2023-01-12 22:13:26 +01:00
809 changed files with 22460 additions and 37873 deletions

74
.eslintignore Normal file
View File

@@ -0,0 +1,74 @@
.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/pages/reference/core
docs/pages/reference/sveltekit
docs/pages/reference/adapter
static
# TODO: Enable
docs-nextra
# --------------- 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
View 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,
}

View File

@@ -11,7 +11,7 @@ body:
### Important :exclamation:
_Providing incorrect/insufficient information or skipping steps to reproduce the issue will result in closing the issue and/or converting to a discussion without further explanation._
_Providing incorrect/insufficient information or skipping steps to reproduce the issue may result in closing the issue or converting to a discussion without further explanation._
If you have a generic question specific to your project, it is best asked in Discussions under the [Questions category](https://github.com/nextauthjs/next-auth/discussions/new?category=Questions)
# Let's wait with this until adoption in other frameworks.

View File

@@ -31,17 +31,13 @@ body:
- "Authentik"
- "Azure Active Directory"
- "Azure Active Directory B2C"
- "Azure DevOps"
- "Battlenet"
- "Beyond Identity"
- "Box"
- "Bungie"
- "ClickUp"
- "Cognito"
- "Coinbase"
- "Descope"
- "Discord"
- "Dribbble"
- "Dropbox"
- "EVE Online"
- "Facebook"
@@ -60,7 +56,6 @@ body:
- "LinkedIn"
- "Mailchimp"
- "Mail.ru"
- "Mastodon"
- "Medium"
- "Naver"
- "Netlify"
@@ -76,7 +71,6 @@ body:
- "Slack"
- "Spotify"
- "Strava"
- "Tiktok"
- "Todoist"
- "Trakt"
- "Twitch"

View File

@@ -21,22 +21,20 @@ body:
multiple: true
options:
- "Custom adapter"
- "@auth/dgraph-adapter"
- "@auth/drizzle-adapter"
- "@auth/dynamodb-adapter"
- "@auth/fauna-adapter"
- "@auth/firebase-adapter"
- "@auth/kysely-adapter"
- "@auth/mikro-orm-adapter"
- "@auth/mongodb-adapter"
- "@auth/neo4j-adapter"
- "@auth/pouchdb-adapter"
- "@auth/prisma-adapter"
- "@auth/sequelize-adapter"
- "@auth/supabase-adapter"
- "@auth/typeorm-adapter"
- "@auth/upstash-redis-adapter"
- "@auth/xata-adapter"
- "@next-auth/dgraph-adapter"
- "@next-auth/dynamodb-adapter"
- "@next-auth/fauna-adapter"
- "@next-auth/firebase-adapter"
- "@next-auth/mikro-orm-adapter"
- "@next-auth/mongodb-adapter"
- "@next-auth/neo4j-adapter"
- "@next-auth/pouchdb-adapter"
- "@next-auth/prisma-adapter"
- "@next-auth/sequelize-adapter"
- "@next-auth/supabase-adapter"
- "@next-auth/typeorm-legacy-adapter"
- "@next-auth/upstash-redis-adapter"
- "@next-auth/xata-adapter"
validations:
required: true
- type: textarea

View File

@@ -1,49 +1,43 @@
# https://github.com/github/issue-labeler#basic-examples
dgraph:
- "@auth/dgraph-adapter"
drizzle:
- "@auth/drizzle-adapter"
- "@next-auth/dgraph-adapter"
dynamodb:
- "@auth/dynamodb-adapter"
- "@next-auth/dynamodb-adapter"
fauna:
- "@auth/fauna-adapter"
- "@next-auth/fauna-adapter"
firebase:
- "@auth/firebase-adapter"
kysely:
- "@auth/kysely-adapter"
- "@next-auth/firebase-adapter"
mikro-orm:
- "@auth/mikro-orm-adapter"
- "@next-auth/mikro-orm-adapter"
mongodb:
- "@auth/mongodb-adapter"
- "@next-auth/mongodb-adapter"
neo4j:
- "@auth/neo4j-adapter"
- "@next-auth/neo4j-adapter"
pouchdb:
- "@auth/pouchdb-adapter"
- "@next-auth/pouchdb-adapter"
prisma:
- "@auth/prisma-adapter"
- "@next-auth/prisma-adapter"
sequelize:
- "@auth/sequelize-adapter"
- "@next-auth/sequelize-adapter"
supabase:
- "@auth/supabase-adapter"
- "@next-auth/supabase-adapter"
typeorm:
- "@auth/typeorm-adapter"
typeorm-legacy:
- "@next-auth/typeorm-legacy-adapter"
upstash-redis:
- "@auth/upstash-redis-adapter"
- "@next-auth/upstash-redis-adapter"
xata:
- "@auth/xata-adapter"
- "@next-auth/xata-adapter"

View File

@@ -2,7 +2,7 @@
adapters: ["packages/core/src/adapters.ts", "packages/adapter-*/**/*"]
core: ["packages/core/src/**/*"]
dgraph: ["packages/adapter-dgraph/**/*"]
documentation: ["packages/docs/docs/**/*"]
documentation: ["packages/docs/pages/**/*"]
dynamodb: ["packages/adapter-dynamodb/**/*"]
examples: ["apps/examples/**/*"]
fauna: ["packages/adapter-fauna/**/*"]
@@ -15,13 +15,12 @@ neo4j: ["packages/adapter-neo4j/**/*"]
playgrounds: ["apps/playgrounds/**/*"]
pouchdb: ["packages/adapter-pouchdb/**/*"]
prisma: ["packages/adapter-prisma/**/*"]
kysely: ["packages/adapter-kysely/**/*"]
providers: ["packages/core/src/providers/**/*"]
sequelize: ["packages/adapter-sequelize/**/*"]
solidjs: ["packages/frameworks-solid-start/**/*"]
supabase: ["packages/adapter-supabase/**/*"]
svelte: ["packages/frameworks-sveltekit/**/*"]
test: ["**test**/*"]
typeorm: ["packages/adapter-typeorm/**/*"]
typeorm-legacy: ["packages/adapter-typeorm-legacy/**/*"]
upstash-redis: ["packages/adapter-upstash-redis/**/*"]
xata: ["packages/adapter-xata/**/*"]

9
.github/sync.yml vendored
View File

@@ -1,3 +1,5 @@
# Note that nextauthjs/next-auth-example syncs from the v4 branch
nextauthjs/sveltekit-auth-example:
- source: apps/examples/sveltekit
dest: .
@@ -18,10 +20,3 @@ nextauthjs/next-auth-gatsby-example:
deleteOrphaned: true
- .github/FUNDING.yml
- LICENSE
nextauthjs/next-auth-example:
- source: apps/examples/nextjs
dest: .
deleteOrphaned: true
- .github/FUNDING.yml
- LICENSE

View File

@@ -5,15 +5,14 @@ const core = require("@actions/core")
try {
const packageJSONPath = path.join(
process.cwd(),
`packages/${process.env.PACKAGE_PATH || "next-auth"}/package.json`
"packages/next-auth/package.json"
)
const packageJSON = JSON.parse(fs.readFileSync(packageJSONPath, "utf8"))
const sha8 = process.env.GITHUB_SHA.substring(0, 8)
const prefix = "0.0.0-"
const pr = process.env.PR_NUMBER
const source = pr ? `pr.${pr}` : "manual"
const packageVersion = `${prefix}${source}.${sha8}`
const prNumber = process.env.PR_NUMBER
const packageVersion = `0.0.0-pr.${prNumber}.${sha8}`
packageJSON.version = packageVersion
core.setOutput("version", packageVersion)
fs.writeFileSync(packageJSONPath, JSON.stringify(packageJSON))

View File

@@ -8,57 +8,6 @@ on:
- next
- 3.x
pull_request:
# TODO: Support latest releases
workflow_dispatch:
inputs:
name:
type: choice
description: Package name (npm)
options:
- "@auth/core"
- "@auth/dgraph-adapter"
- "@auth/drizzle-adapter"
- "@auth/dynamodb-adapter"
- "@auth/fauna-adapter"
- "@auth/firebase-adapter"
- "@auth/mikro-orm-adapter"
- "@auth/mongodb-adapter"
- "@auth/neo4j-adapter"
- "@auth/pouchdb-adapter"
- "@auth/prisma-adapter"
- "@auth/sequelize-adapter"
- "@auth/supabase-adapter"
- "@auth/typeorm-adapter"
- "@auth/upstash-redis-adapter"
- "@auth/xata-adapter"
- "next-auth"
# TODO: Infer from package name
path:
type: choice
description: Directory name (packages/*)
options:
- "core"
- "frameworks-nextjs"
- "adapter-dgraph"
- "adapter-drizzle"
- "adapter-dynamodb"
- "adapter-fauna"
- "adapter-firebase"
- "adapter-mikro-orm"
- "adapter-mongodb"
- "adapter-neo4j"
- "adapter-pouchdb"
- "adapter-prisma"
- "adapter-sequelize"
- "adapter-supabase"
- "adapter-typeorm"
- "adapter-upstash-redis"
- "adapter-xata"
- "next-auth"
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
FORCE_COLOR: true
jobs:
test:
@@ -75,17 +24,16 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: 18
cache: "pnpm"
- name: Install dependencies
run: pnpm install
- name: Build
run: pnpm build
- name: Run tests
run: pnpm test
timeout-minutes: 15
env:
UPSTASH_REDIS_URL: ${{ secrets.UPSTASH_REDIS_URL }}
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
@@ -94,7 +42,7 @@ jobs:
# AUTH0_USERNAME: ${{ secrets.AUTH0_USERNAME }}
# AUTH0_PASSWORD: ${{ secrets.AUTH0_PASSWORD }}
# TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
# TURBO_TEAM: ${{ vars.TURBO_TEAM }}
# TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
# - name: Upload E2E artifacts
# if: github.repository == 'nextauthjs/next-auth'
# uses: actions/upload-artifact@v3
@@ -125,7 +73,6 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: 18
cache: "pnpm"
- name: Install dependencies
run: pnpm install
- name: Publish to npm and GitHub
@@ -150,7 +97,6 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: 18
cache: "pnpm"
- name: Install dependencies
run: pnpm install
- name: Determine version
@@ -176,36 +122,3 @@ jobs:
env:
VERSION: ${{ steps.determine-version.outputs.version }}
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
release-manual:
name: Publish manually
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' }}
steps:
- name: Init
uses: actions/checkout@v3
- name: Install pnpm
uses: pnpm/action-setup@v2.2.4
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 18
cache: "pnpm"
- name: Install dependencies
run: pnpm install
- name: Determine version
uses: ./.github/version-pr
id: determine-version
env:
PACKAGE_PATH: ${{ github.event.inputs.path }}
- name: Publish to npm
run: |
pnpm build
cd packages/$PACKAGE_PATH
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> .npmrc
pnpm publish --no-git-checks --access public --tag experimental
echo "🎉 Experimental release published 📦️ on npm: https://npmjs.com/package/${{ github.event.inputs.name }}/v/${{ env.VERSION }}"
echo "Install via: pnpm add ${{ github.event.inputs.name }}@${{ env.VERSION }}"
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
PACKAGE_PATH: ${{ github.event.inputs.path }}
VERSION: ${{ steps.determine-version.outputs.version }}

13
.gitignore vendored
View File

@@ -6,8 +6,6 @@
.env.development.local
.env.test.local
.env.production.local
packages/*/.npmrc
npm-debug.log*
yarn-debug.log*
@@ -40,13 +38,11 @@ packages/next-auth/next
packages/*/*.js
packages/*/*.d.ts
packages/*/*.d.ts.map
packages/*/lib
# Development app
apps/dev/src/css
apps/dev/prisma/migrations
apps/dev/typeorm
apps/dev/nextjs-2
# VS
/.vs/slnx.sqlite-journal
@@ -67,7 +63,6 @@ packages/adapter-prisma/prisma/dev.db
packages/adapter-prisma/prisma/migrations
db.sqlite
packages/adapter-supabase/supabase/.branches
packages/adapter-drizzle/.drizzle
# Tests
coverage
@@ -87,8 +82,7 @@ packages/core/src/providers/oauth-types.ts
packages/core/lib
packages/core/providers
packages/core/src/lib/pages/styles.ts
docs/docs/reference/core
docs/docs/reference/sveltekit
docs/pages/reference/core
# SvelteKit
@@ -98,9 +92,8 @@ packages/frameworks-sveltekit/.svelte-kit
packages/frameworks-sveltekit/package
packages/frameworks-sveltekit/vite.config.js.timestamp-*
packages/frameworks-sveltekit/vite.config.ts.timestamp-*
docs/pages/reference/sveltekit
# Adapters
docs/docs/reference/adapter
## Drizzle migration folder
.drizzle
docs/pages/reference/adapter

View File

@@ -13,16 +13,14 @@ pnpm-lock.yaml
*.d.ts.map
.svelte-kit
.next
.nuxt
# --------------- Docs ---------------
.docusaurus
build
docs/docs/reference/core
docs/docs/reference/sveltekit
static
.next
docs/pages/reference/core
docs/pages/reference/sveltekit
docs/pages/reference/adapter
docs/providers.json
# --------------- Packages ---------------

22
.prettierrc.js Normal file
View 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",
},
},
],
}

153
README.md
View File

@@ -1,153 +0,0 @@
<p align="center">
<br/>
<a href="https://authjs.dev" target="_blank"><img width="96px" src="https://authjs.dev/img/logo/logo-sm.png" /></a>
<h3 align="center">Auth.js</h3>
<p align="center">Authentication for the Web.</p>
<p align="center">Open Source. Full Stack. Own Your Data.</p>
<p align="center" style="align: center;">
<a href="https://npm.im/@auth/prisma-adapter">
<img src="https://img.shields.io/badge/TypeScript-blue?style=flat-square" alt="TypeScript" />
</a>
<a href="https://www.npmtrends.com/next-auth">
<img src="https://img.shields.io/npm/dm/next-auth?style=flat-square" alt="Downloads" />
</a>
<a href="https://github.com/nextauthjs/next-auth/stargazers">
<img src="https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square" alt="Github Stars" />
</a>
<a href="https://www.npmjs.com/package/next-auth">
<img src="https://img.shields.io/github/v/release/nextauthjs/next-auth?label=latest&style=flat-square" alt="Github Stable Release" />
</a>
</p>
</p>
Auth.js is a set of open-source packages that are built on Web Standard APIs for authentication in modern applications with any framework on any platform in any JS runtime.
See [authjs.dev](https://authjs.dev) for our framework-specific libraries, or check out [next-auth.js.org](https://next-auth.js.org) for `next-auth` (Next.js).
## Features
### Flexible and easy to use
- Designed to work with any OAuth service, it supports 2.0+, OIDC
- Built-in support for [many popular sign-in services](https://github.com/nextauthjs/next-auth/tree/main/packages/core/src/providers)
- Email/Passwordless authentication
- Bring Your Database - or none! - stateless authentication with any backend (Active Directory, LDAP, etc.)
- Runtime-agnostic, runs anywhere! (Vercel Edge Functions, Node.js, Serverless, etc.)
### Own your data
Auth.js can be used with or without a database.
- An open-source solution that allows you to keep control of your data
- Built-in support for [MySQL, MariaDB, Postgres, Microsoft SQL Server, MongoDB, SQLite, etc.](https://adapters.authjs.dev)
- Works great with databases from popular hosting providers
### 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 (CSRF) 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 used, they are encrypted by default (JWE) with A256GCM
- Features tab/window syncing and session polling to support short-lived sessions
- Attempts to implement the latest guidance published by [Open Web Application Security Project](https://owasp.org)
Advanced configuration allows you to define your 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.
### TypeScript
Auth.js libraries are written with type safety in mind. [Check out the docs](https://authjs.dev/getting-started/typescript) for more information.
## Security
If you think you have found a vulnerability (or are not sure) in Auth.js or any of the related packages (i.e. Adapters), we ask you to read our [Security Policy](https://authjs.dev/security) to reach out responsibly. Please do not open Pull Requests/Issues/Discussions before consulting with us.
## Acknowledgments
[Auth.js is made possible thanks to all of its contributors.](https://authjs.dev/contributors)
<a href="https://github.com/nextauthjs/next-auth/graphs/contributors">
<img width="500px" src="https://contrib.rocks/image?repo=nextauthjs/next-auth" />
</a>
<div>
<a href="https://vercel.com?utm_source=nextauthjs&utm_campaign=oss"></a>
</div>
### Support
We have an [OpenCollective](https://opencollective.com/nextauth) for individuals and companies looking to contribute financially to the project!
<!--sponsors start-->
<table>
<tbody>
<tr>
<td align="center" valign="top">
<a href="https://vercel.com" target="_blank">
<img width="128px" src="https://avatars.githubusercontent.com/u/14985020?v=4" alt="Vercel Logo" />
</a><br />
<div>Vercel</div><br />
<sub>🥉 Bronze Financial Sponsor <br /> ☁️ Infrastructure Support</sub>
</td>
<td align="center" valign="top">
<a href="https://prisma.io" target="_blank">
<img width="128px" src="https://avatars.githubusercontent.com/u/17219288?v=4" alt="Prisma Logo" />
</a><br />
<div>Prisma</div><br />
<sub>🥉 Bronze Financial Sponsor</sub>
</td>
<td align="center" valign="top">
<a href="https://clerk.com" target="_blank">
<img width="128px" src="https://avatars.githubusercontent.com/u/49538330?s=200&v=4" alt="Clerk Logo" />
</a><br />
<div>Clerk</div><br />
<sub>🥉 Bronze Financial Sponsor</sub>
</td>
<td align="center" valign="top">
<a href="https://lowdefy.com" target="_blank">
<img width="128px" src="https://avatars.githubusercontent.com/u/47087496?s=200&v=4" alt="Lowdefy Logo" />
</a><br />
<div>Lowdefy</div><br />
<sub>🥉 Bronze Financial Sponsor</sub>
</td>
<td align="center" valign="top">
<a href="https://workos.com" target="_blank">
<img width="128px" src="https://avatars.githubusercontent.com/u/47638084?s=200&v=4" alt="WorkOS Logo" />
</a><br />
<div>WorkOS</div><br />
<sub>🥉 Bronze Financial Sponsor</sub>
</td>
<td align="center" valign="top">
<a href="https://www.descope.com" target="_blank">
<img width="128px" src="https://avatars.githubusercontent.com/u/97479186?v=4" alt="Descope Logo" />
</a><br />
<div>Descope</div><br />
<sub>🥉 Bronze Financial Sponsor</sub>
</td>
<td align="center" valign="top">
<a href="https://checklyhq.com" target="_blank">
<img width="128px" src="https://avatars.githubusercontent.com/u/25982255?v=4" alt="Checkly Logo" />
</a><br />
<div>Checkly</div><br />
<sub>☁️ Infrastructure Support</sub>
</td>
<td align="center" valign="top">
<a href="https://superblog.ai/" target="_blank">
<img width="128px" src="https://d33wubrfki0l68.cloudfront.net/cdc4a3833bd878933fcc131655878dbf226ac1c5/10cd6/images/logo_bolt_small.png" alt="superblog Logo" />
</a><br />
<div>superblog</div><br />
<sub>☁️ Infrastructure Support</sub>
</td>
</tr><tr></tr>
</tbody>
</table>
<br />
<!--sponsors end-->
## Contributing
We're open to all community contributions! If you'd like to contribute in any way, please first read
our [Contributing Guide](https://github.com/nextauthjs/.github/blob/main/CONTRIBUTING.md).
## License
ISC

1
README.md Symbolic link
View File

@@ -0,0 +1 @@
packages/next-auth/README.md

View File

@@ -1,61 +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=
DESCOPE_ID=
DESCOPE_SECRET=
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

View File

@@ -1,4 +0,0 @@
{
"typescript.tsdk": "../../node_modules/.pnpm/typescript@4.8.4/node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
}

View File

@@ -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/.github/blob/main/CONTRIBUTING.md#setting-up-local-environment)

View File

@@ -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 }

View File

@@ -1,12 +0,0 @@
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html>
<head></head>
<body>{children}</body>
</html>
)
}

View File

@@ -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>
}

View File

@@ -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>
</>
)
}

View File

@@ -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>
)
}

View File

@@ -1,14 +0,0 @@
.footer {
margin-top: 2rem;
}
.navItems {
margin-bottom: 1rem;
padding: 0;
list-style: none;
}
.navItem {
display: inline-block;
margin-right: 1rem;
}

View File

@@ -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>
)
}

View File

@@ -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;
}

View File

@@ -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 />
</>
)
}

View File

@@ -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,
// },
// })

View File

@@ -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 },
}

View File

@@ -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": {
"@auth/fauna-adapter": "workspace:*",
"@auth/prisma-adapter": "workspace:*",
"@auth/supabase-adapter": "workspace:*",
"@auth/typeorm-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.37",
"@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"
}
}

View File

@@ -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>
)
}

View File

@@ -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>
)
}

View File

@@ -1,217 +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 "@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 "@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 { TypeORMAdapter } from "@auth/typeorm-adapter"
// const adapter = TypeORMAdapter({
// type: "sqlite",
// name: "next-auth-test-memory",
// database: "./typeorm/dev.db",
// synchronize: true,
// })
// // Supabase
// import { SupabaseAdapter } from "@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)

View File

@@ -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))
}

View File

@@ -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.",
})
}
}

View File

@@ -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)
}

View File

@@ -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))
}

View File

@@ -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>&lt;/Header&gt;</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>
)
}

View File

@@ -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>
)
}

View File

@@ -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>
)
}

View File

@@ -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>
)
}

View File

@@ -1,9 +0,0 @@
import Layout from "components/layout"
export default function Page() {
return (
<Layout>
<h1>Page protected by Middleware</h1>
</Layout>
)
}

View File

@@ -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>
)
}

View File

@@ -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,
},
}
}

View File

@@ -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>
)
}

View File

@@ -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),
},
}
}

View File

@@ -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);
}

View File

@@ -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>
)
}

View File

@@ -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,
},
}
}

View File

@@ -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");

View File

@@ -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"

View File

@@ -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])
}

View File

@@ -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
}
}

View File

@@ -22,9 +22,6 @@ BEYOND_IDENTITY_CLIENT_ID=
BEYOND_IDENTITY_CLIENT_SECRET=
BEYOND_IDENTITY_ISSUER=
DESCOPE_ID=
DESCOPE_SECRET=
GITHUB_ID=
GITHUB_SECRET=
@@ -55,14 +52,6 @@ TWITTER_SECRET=
WIKIMEDIA_ID=
WIKIMEDIA_SECRET=
# Yandex OAuth. new app -> https://oauth.yandex.com/client/new/id
YANDEX_ID=
YANDEX_SECRET=
# ClickUp OAuth. https://clickup.com/api/
CLICK_UP_ID=
CLICK_UP_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

View File

@@ -2,4 +2,3 @@ node_modules/
/test-results/
/playwright-report/
/playwright/.cache/
dbschema/edgeql-js

View File

@@ -1,71 +0,0 @@
module default {
type User {
property name -> str;
required property email -> str {
constraint exclusive;
}
property emailVerified -> datetime;
property image -> str;
multi link accounts := .<user[is Account];
multi link sessions := .<user[is Session];
property createdAt -> datetime {
default := datetime_current();
};
}
type Account {
required property userId := .user.id;
required property type -> str;
required property provider -> str;
required property providerAccountId -> str {
constraint exclusive;
};
property refresh_token -> str;
property access_token -> str;
property expires_at -> int64;
property token_type -> str;
property scope -> str;
property id_token -> str;
property session_state -> str;
required link user -> User {
on target delete delete source;
};
property createdAt -> datetime {
default := datetime_current();
};
constraint exclusive on ((.provider, .providerAccountId))
}
type Session {
required property sessionToken -> str {
constraint exclusive;
}
required property userId := .user.id;
required property expires -> datetime;
required link user -> User {
on target delete delete source;
};
property createdAt -> datetime {
default := datetime_current();
};
}
type VerificationToken {
required property identifier -> str;
required property token -> str {
constraint exclusive;
}
required property expires -> datetime;
property createdAt -> datetime {
default := datetime_current();
};
constraint exclusive on ((.identifier, .token))
}
}
# Disable the application of access policies within access policies
# themselves. This behavior will become the default in EdgeDB 3.0.
# See: https://www.edgedb.com/docs/reference/ddl/access_policies#nonrecursive
using future nonrecursive_access_policies;

View File

@@ -1,2 +0,0 @@
[edgedb]
server-version = "2.6"

View File

@@ -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.

View File

@@ -15,26 +15,23 @@
"license": "ISC",
"dependencies": {
"@auth/core": "workspace:*",
"@auth/edgedb-adapter": "workspace:*",
"@auth/fauna-adapter": "workspace:*",
"@auth/prisma-adapter": "workspace:*",
"@auth/supabase-adapter": "workspace:*",
"@auth/typeorm-adapter": "workspace:*",
"@next-auth/fauna-adapter": "workspace:*",
"@next-auth/prisma-adapter": "workspace:*",
"@next-auth/supabase-adapter": "workspace:*",
"@next-auth/typeorm-legacy-adapter": "workspace:*",
"@prisma/client": "^3",
"edgedb": "^1.0.1",
"@supabase/supabase-js": "^2.0.5",
"faunadb": "^4",
"next": "13.4.0",
"next": "13.1.1",
"next-auth": "workspace:*",
"nodemailer": "^6",
"react": "^18",
"react-dom": "^18"
},
"devDependencies": {
"@edgedb/generate": "^0.0.4",
"@playwright/test": "1.29.2",
"@types/jsonwebtoken": "^8.5.5",
"@types/react": "18.0.37",
"@types/react": "^18.0.15",
"@types/react-dom": "^18.0.6",
"dotenv": "^16.0.3",
"fake-smtp-server": "^0.8.0",

View File

@@ -2,15 +2,14 @@ import { Auth, type AuthConfig } from "@auth/core"
// Providers
import Apple from "@auth/core/providers/apple"
// import Asgardeo from "@auth/core/providers/asgardeo"
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 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"
import Descope from "@auth/core/providers/descope"
import Discord from "@auth/core/providers/discord"
import DuendeIDS6 from "@auth/core/providers/duende-identity-server6"
// import Email from "@auth/core/providers/email"
@@ -35,29 +34,28 @@ 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"
import ClickUp from '@auth/core/providers/click-up'
// // Prisma
// import { PrismaClient } from "@prisma/client"
// import { PrismaAdapter } from "@auth/prisma-adapter"
// 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 "@auth/fauna-adapter"
// 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 { TypeORMAdapter } from "@auth/typeorm-adapter"
// const adapter = TypeORMAdapter({
// import { TypeORMLegacyAdapter } from "@next-auth/typeorm-legacy-adapter"
// const adapter = TypeORMLegacyAdapter({
// type: "sqlite",
// name: "next-auth-test-memory",
// database: "./typeorm/dev.db",
@@ -65,18 +63,12 @@ import ClickUp from '@auth/core/providers/click-up'
// })
// // Supabase
// import { SupabaseAdapter } from "@auth/supabase-adapter"
// import { SupabaseAdapter } from "@next-auth/supabase-adapter"
// const adapter = SupabaseAdapter({
// url: process.env.NEXT_PUBLIC_SUPABASE_URL,
// secret: process.env.SUPABASE_SERVICE_ROLE_KEY,
// })
// // EdgeDB
// import { EdgeDBAdapter } from "@auth/edgedb-adapter"
// import { createHttpClient } from "edgedb"
// const client = createHttpClient()
// const adapter = EdgeDBAdapter(client)
export const authConfig: AuthConfig = {
// adapter,
debug: process.env.NODE_ENV !== "production",
@@ -92,8 +84,8 @@ export const authConfig: AuthConfig = {
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 as string }),
// Asgardeo({ clientId: process.env.ASGARDEO_CLIENT_ID, clientSecret: process.env.ASGARDEO_CLIENT_SECRET, issuer: process.env.ASGARDEO_ISSUER }),
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,
@@ -101,20 +93,15 @@ 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,
// }),
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 }),
Descope({ clientId: process.env.DESCOPE_ID, clientSecret: process.env.DESCOPE_SECRET }),
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, redirectProxyUrl: process.env.AUTH_REDIRECT_PROXY_URL }),
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 }),
@@ -123,7 +110,7 @@ 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 as string }),
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 }),
@@ -133,11 +120,9 @@ 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 }),
ClickUp({ clientId: process.env.CLICK_UP_ID, clientSecret: process.env.CLICK_UP_SECRET })
],
// debug: process.env.NODE_ENV !== "production",
}
@@ -169,4 +154,4 @@ function AuthHandler(...args: any[]) {
export default AuthHandler(authConfig)
export const config = { runtime: "edge" }
export const config = { runtime: "experimental-edge" }

View File

@@ -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"]
}

View File

@@ -15,8 +15,8 @@
"@sveltejs/kit": "next",
"svelte": "3.55.0",
"svelte-check": "2.10.2",
"typescript": "5.2.2",
"vite": "4.0.5"
"typescript": "4.9.4",
"vite": "4.0.1"
},
"dependencies": {
"@auth/core": "workspace:*",

View File

@@ -1,14 +1,15 @@
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET= # Linux: `openssl rand -hex 32` or go to https://generate-secret.now.sh/32
APPLE_ID=
APPLE_TEAM_ID=
APPLE_PRIVATE_KEY=
APPLE_KEY_ID=
AUTH0_ID=
AUTH0_SECRET=
AUTH0_ISSUER=
DESCOPE_ID=
DESCOPE_SECRET=
FACEBOOK_ID=
FACEBOOK_SECRET=
@@ -20,3 +21,8 @@ GOOGLE_SECRET=
TWITTER_ID=
TWITTER_SECRET=
EMAIL_SERVER=smtp://username:password@smtp.example.com:587
EMAIL_FROM=NextAuth <noreply@example.com>
DATABASE_URL=sqlite://localhost/:memory:?synchronize=true

View File

@@ -1,5 +0,0 @@
import NextAuth from "next-auth/next"
import { config } from "auth"
const handler = NextAuth(config)
export { handler as GET, handler as POST }

View File

@@ -1,294 +0,0 @@
import type { GetServerSidePropsContext, NextApiRequest, NextApiResponse } from "next"
import type { NextAuthOptions as NextAuthConfig } from "next-auth"
import { getServerSession } from "next-auth"
import Apple from "next-auth/providers/apple"
import Atlassian from "next-auth/providers/atlassian"
import Auth0 from "next-auth/providers/auth0"
import Authentik from "next-auth/providers/authentik"
import AzureAD from "next-auth/providers/azure-ad"
import AzureB2C from "next-auth/providers/azure-ad-b2c"
import Battlenet from "next-auth/providers/battlenet"
import Box from "next-auth/providers/box"
import BoxyHQSAML from "next-auth/providers/boxyhq-saml"
import Bungie from "next-auth/providers/bungie"
import Cognito from "next-auth/providers/cognito"
import Coinbase from "next-auth/providers/coinbase"
import Discord from "next-auth/providers/discord"
import Dropbox from "next-auth/providers/dropbox"
import DuendeIDS6 from "next-auth/providers/duende-identity-server6"
import Eveonline from "next-auth/providers/eveonline"
import Facebook from "next-auth/providers/facebook"
import Faceit from "next-auth/providers/faceit"
import FortyTwoSchool from "next-auth/providers/42-school"
import Foursquare from "next-auth/providers/foursquare"
import Freshbooks from "next-auth/providers/freshbooks"
import Fusionauth from "next-auth/providers/fusionauth"
import GitHub from "next-auth/providers/github"
import Gitlab from "next-auth/providers/gitlab"
import Google from "next-auth/providers/google"
import Hubspot from "next-auth/providers/hubspot"
import Instagram from "next-auth/providers/instagram"
import Kakao from "next-auth/providers/kakao"
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 Mailru from "next-auth/providers/mailru"
import Medium from "next-auth/providers/medium"
import Naver from "next-auth/providers/naver"
import Netlify from "next-auth/providers/netlify"
import Okta from "next-auth/providers/okta"
import Onelogin from "next-auth/providers/onelogin"
import Osso from "next-auth/providers/osso"
import Osu from "next-auth/providers/osu"
import Passage from "next-auth/providers/passage"
import Patreon from "next-auth/providers/patreon"
import Pinterest from "next-auth/providers/pinterest"
import Pipedrive from "next-auth/providers/pipedrive"
import Reddit from "next-auth/providers/reddit"
import Salesforce from "next-auth/providers/salesforce"
import Slack from "next-auth/providers/slack"
import Spotify from "next-auth/providers/spotify"
import Strava from "next-auth/providers/strava"
import Todoist from "next-auth/providers/todoist"
import Trakt from "next-auth/providers/trakt"
import Twitch from "next-auth/providers/twitch"
import Twitter from "next-auth/providers/twitter"
import UnitedEffects from "next-auth/providers/united-effects"
import Vk from "next-auth/providers/vk"
import Wikimedia from "next-auth/providers/wikimedia"
import Wordpress from "next-auth/providers/wordpress"
import WorkOS from "next-auth/providers/workos"
import Yandex from "next-auth/providers/yandex"
import Zitadel from "next-auth/providers/zitadel"
import Zoho from "next-auth/providers/zoho"
import Zoom from "next-auth/providers/zoom"
// Read more at: https://next-auth.js.org/getting-started/typescript#module-augmentation
declare module "next-auth/jwt" {
interface JWT {
/** The user's role. */
userRole?: "admin"
}
}
export const config = {
// https://next-auth.js.org/configuration/providers/oauth
providers: [
Apple({ clientId: process.env.AUTH_APPLE_ID, clientSecret: process.env.AUTH_APPLE_SECRET }),
Atlassian({ clientId: process.env.AUTH_ATLASSIAN_ID, clientSecret: process.env.AUTH_ATLASSIAN_SECRET }),
Auth0({ clientId: process.env.AUTH_AUTH0_ID, clientSecret: process.env.AUTH_AUTH0_SECRET, issuer: process.env.AUTH_AUTH0_ISSUER }),
Authentik({ clientId: process.env.AUTH_AUTHENTIK_ID, clientSecret: process.env.AUTH_AUTHENTIK_SECRET }),
AzureAD({ clientId: process.env.AUTH_AZUREAD_ID, clientSecret: process.env.AUTH_AZUREAD_SECRET }),
AzureB2C({ clientId: process.env.AUTH_AZUREB2C_ID, clientSecret: process.env.AUTH_AZUREB2C_SECRET }),
Battlenet({ clientId: process.env.AUTH_BN_ID, clientSecret: process.env.AUTH_BN_SECRET, issuer: process.env.AUTH_BN_ISSUER }),
Box({ clientId: process.env.AUTH_BOX_ID, clientSecret: process.env.AUTH_BOX_SECRET }),
BoxyHQSAML({ clientId: process.env.AUTH_BOXYHQ_ID, clientSecret: process.env.AUTH_BOXYHQ_SECRET, issuer: process.env.AUTH_BOXYHQ_ISSUER }),
Bungie({ clientId: process.env.AUTH_BUNGIE_ID, clientSecret: process.env.AUTH_BUNGIE_SECRET }),
Cognito({ clientId: process.env.AUTH_COGNITO_ID, clientSecret: process.env.AUTH_COGNITO_SECRET }),
Coinbase({ clientId: process.env.AUTH_COINBASE_ID, clientSecret: process.env.AUTH_COINBASE_SECRET }),
Discord({ clientId: process.env.AUTH_DISCORD_ID, clientSecret: process.env.AUTH_DISCORD_SECRET }),
Dropbox({ clientId: process.env.AUTH_DROPBOX_ID, clientSecret: process.env.AUTH_DROPBOX_SECRET }),
DuendeIDS6({ clientId: process.env.AUTH_DUENDEIDS6_ID, clientSecret: process.env.AUTH_DUENDEIDS6_SECRET }),
Eveonline({ clientId: process.env.AUTH_EVEONLINE_ID, clientSecret: process.env.AUTH_EVEONLINE_SECRET }),
Facebook({ clientId: process.env.AUTH_FACEBOOK_ID, clientSecret: process.env.AUTH_FACEBOOK_SECRET }),
Faceit({ clientId: process.env.AUTH_FACEIT_ID, clientSecret: process.env.AUTH_FACEIT_SECRET }),
FortyTwoSchool({ clientId: process.env.AUTH_FORTYTWOSCHOOL_ID, clientSecret: process.env.AUTH_FORTYTWOSCHOOL_SECRET }),
Foursquare({ clientId: process.env.AUTH_FOURSQUARE_ID, clientSecret: process.env.AUTH_FOURSQUARE_SECRET }),
Freshbooks({ clientId: process.env.AUTH_FRESHBOOKS_ID, clientSecret: process.env.AUTH_FRESHBOOKS_SECRET }),
Fusionauth({ clientId: process.env.AUTH_FUSIONAUTH_ID, clientSecret: process.env.AUTH_FUSIONAUTH_SECRET }),
GitHub({ clientId: process.env.AUTH_GITHUB_ID, clientSecret: process.env.AUTH_GITHUB_SECRET }),
Gitlab({ clientId: process.env.AUTH_GITLAB_ID, clientSecret: process.env.AUTH_GITLAB_SECRET }),
Google({ clientId: process.env.AUTH_GOOGLE_ID, clientSecret: process.env.AUTH_GOOGLE_SECRET }),
Hubspot({ clientId: process.env.AUTH_HUBSPOT_ID, clientSecret: process.env.AUTH_HUBSPOT_SECRET }),
Instagram({ clientId: process.env.AUTH_INSTAGRAM_ID, clientSecret: process.env.AUTH_INSTAGRAM_SECRET }),
Kakao({ clientId: process.env.AUTH_KAKAO_ID, clientSecret: process.env.AUTH_KAKAO_SECRET }),
Keycloak({ clientId: process.env.AUTH_KEYCLOAK_ID, clientSecret: process.env.AUTH_KEYCLOAK_SECRET }),
Line({ clientId: process.env.AUTH_LINE_ID, clientSecret: process.env.AUTH_LINE_SECRET }),
LinkedIn({ clientId: process.env.AUTH_LINKEDIN_ID, clientSecret: process.env.AUTH_LINKEDIN_SECRET }),
Mailchimp({ clientId: process.env.AUTH_MAILCHIMP_ID, clientSecret: process.env.AUTH_MAILCHIMP_SECRET }),
Mailru({ clientId: process.env.AUTH_MAILRU_ID, clientSecret: process.env.AUTH_MAILRU_SECRET }),
Medium({ clientId: process.env.AUTH_MEDIUM_ID, clientSecret: process.env.AUTH_MEDIUM_SECRET }),
Naver({ clientId: process.env.AUTH_NAVER_ID, clientSecret: process.env.AUTH_NAVER_SECRET }),
Netlify({ clientId: process.env.AUTH_NETLIFY_ID, clientSecret: process.env.AUTH_NETLIFY_SECRET }),
Okta({ clientId: process.env.AUTH_OKTA_ID, clientSecret: process.env.AUTH_OKTA_SECRET }),
Onelogin({ clientId: process.env.AUTH_ONELOGIN_ID, clientSecret: process.env.AUTH_ONELOGIN_SECRET }),
Osso({ clientId: process.env.AUTH_OSSO_ID, clientSecret: process.env.AUTH_OSSO_SECRET, issuer: process.env.AUTH_OSSO_ISSUER }),
Osu({ clientId: process.env.AUTH_OSU_ID, clientSecret: process.env.AUTH_OSU_SECRET }),
Passage({ clientId: process.env.AUTH_PASSAGE_ID, clientSecret: process.env.AUTH_PASSAGE_SECRET, issuer: process.env.AUTH_PASSAGE_ISSUER }),
Patreon({ clientId: process.env.AUTH_PATREON_ID, clientSecret: process.env.AUTH_PATREON_SECRET }),
Pinterest({ clientId: process.env.AUTH_PINTEREST_ID, clientSecret: process.env.AUTH_PINTEREST_SECRET }),
Pipedrive({ clientId: process.env.AUTH_PIPEDRIVE_ID, clientSecret: process.env.AUTH_PIPEDRIVE_SECRET }),
Reddit({ clientId: process.env.AUTH_REDDIT_ID, clientSecret: process.env.AUTH_REDDIT_SECRET }),
Salesforce({ clientId: process.env.AUTH_SALESFORCE_ID, clientSecret: process.env.AUTH_SALESFORCE_SECRET }),
Slack({ clientId: process.env.AUTH_SLACK_ID, clientSecret: process.env.AUTH_SLACK_SECRET }),
Spotify({ clientId: process.env.AUTH_SPOTIFY_ID, clientSecret: process.env.AUTH_SPOTIFY_SECRET }),
Strava({ clientId: process.env.AUTH_STRAVA_ID, clientSecret: process.env.AUTH_STRAVA_SECRET }),
Todoist({ clientId: process.env.AUTH_TODOIST_ID, clientSecret: process.env.AUTH_TODOIST_SECRET }),
Trakt({ clientId: process.env.AUTH_TRAKT_ID, clientSecret: process.env.AUTH_TRAKT_SECRET }),
Twitch({ clientId: process.env.AUTH_TWITCH_ID, clientSecret: process.env.AUTH_TWITCH_SECRET }),
Twitter({ clientId: process.env.AUTH_TWITTER_ID, clientSecret: process.env.AUTH_TWITTER_SECRET, version: "2.0" }),
UnitedEffects({ clientId: process.env.AUTH_UE_ID, clientSecret: process.env.AUTH_UE_SECRET, issuer: process.env.AUTH_UE_ISSUER }),
Vk({ clientId: process.env.AUTH_VK_ID, clientSecret: process.env.AUTH_VK_SECRET }),
Wikimedia({ clientId: process.env.AUTH_WIKIMEDIA_ID, clientSecret: process.env.AUTH_WIKIMEDIA_SECRET }),
Wordpress({ clientId: process.env.AUTH_WORDPRESS_ID, clientSecret: process.env.AUTH_WORDPRESS_SECRET }),
WorkOS({ clientId: process.env.AUTH_WORKOS_ID, clientSecret: process.env.AUTH_WORKOS_SECRET }),
Yandex({ clientId: process.env.AUTH_YANDEX_ID, clientSecret: process.env.AUTH_YANDEX_SECRET }),
Zitadel({ clientId: process.env.AUTH_ZITADEL_ID, clientSecret: process.env.AUTH_ZITADEL_SECRET }),
Zoho({ clientId: process.env.AUTH_ZOHO_ID, clientSecret: process.env.AUTH_ZOHO_SECRET }),
Zoom({ clientId: process.env.AUTH_ZOOM_ID, clientSecret: process.env.AUTH_ZOOM_SECRET }),
],
callbacks: {
async jwt({ token }) {
token.userRole = "admin"
return token
},
},
} satisfies NextAuthConfig
// Helper function to get session without passing config every time
// https://next-auth.js.org/configuration/nextjs#getserversession
export function auth(...args: [GetServerSidePropsContext["req"], GetServerSidePropsContext["res"]] | [NextApiRequest, NextApiResponse] | []) {
return getServerSession(...args, config)
}
// We recommend doing your own environment variable validation
declare global {
namespace NodeJS {
export interface ProcessEnv {
NEXTAUTH_SECRET: string
AUTH_APPLE_ID: string
AUTH_APPLE_SECRET: string
AUTH_ATLASSIAN_ID: string
AUTH_ATLASSIAN_SECRET: string
AUTH_AUTH0_ID: string
AUTH_AUTH0_ISSUER: string
AUTH_AUTH0_SECRET: string
AUTH_AUTHENTIK_ID: string
AUTH_AUTHENTIK_SECRET: string
AUTH_AZUREAD_ID: string
AUTH_AZUREAD_SECRET: string
AUTH_AZUREB2C_ID: string
AUTH_AZUREB2C_SECRET: string
AUTH_BN_ID: string
AUTH_BN_ISSUER: any
AUTH_BN_SECRET: string
AUTH_BOX_ID: string
AUTH_BOX_SECRET: string
AUTH_BOXYHQ_ID: string
AUTH_BOXYHQ_ISSUER: string
AUTH_BOXYHQ_SECRET: string
AUTH_BUNGIE_ID: string
AUTH_BUNGIE_SECRET: string
AUTH_COGNITO_ID: string
AUTH_COGNITO_SECRET: string
AUTH_COINBASE_ID: string
AUTH_COINBASE_SECRET: string
AUTH_DISCORD_ID: string
AUTH_DISCORD_SECRET: string
AUTH_DROPBOX_ID: string
AUTH_DROPBOX_SECRET: string
AUTH_DUENDEIDS6_ID: string
AUTH_DUENDEIDS6_SECRET: string
AUTH_EVEONLINE_ID: string
AUTH_EVEONLINE_SECRET: string
AUTH_FACEBOOK_ID: string
AUTH_FACEBOOK_SECRET: string
AUTH_FACEIT_ID: string
AUTH_FACEIT_SECRET: string
AUTH_FORTYTWOSCHOOL_ID: string
AUTH_FORTYTWOSCHOOL_SECRET: string
AUTH_FOURSQUARE_ID: string
AUTH_FOURSQUARE_SECRET: string
AUTH_FRESHBOOKS_ID: string
AUTH_FRESHBOOKS_SECRET: string
AUTH_FUSIONAUTH_ID: string
AUTH_FUSIONAUTH_SECRET: string
AUTH_GITHUB_ID: string
AUTH_GITHUB_SECRET: string
AUTH_GITLAB_ID: string
AUTH_GITLAB_SECRET: string
AUTH_GOOGLE_ID: string
AUTH_GOOGLE_SECRET: string
AUTH_HUBSPOT_ID: string
AUTH_HUBSPOT_SECRET: string
AUTH_INSTAGRAM_ID: string
AUTH_INSTAGRAM_SECRET: string
AUTH_KAKAO_ID: string
AUTH_KAKAO_SECRET: string
AUTH_KEYCLOAK_ID: string
AUTH_KEYCLOAK_SECRET: string
AUTH_LINE_ID: string
AUTH_LINE_SECRET: string
AUTH_LINKEDIN_ID: string
AUTH_LINKEDIN_SECRET: string
AUTH_MAILCHIMP_ID: string
AUTH_MAILCHIMP_SECRET: string
AUTH_MAILRU_ID: string
AUTH_MAILRU_SECRET: string
AUTH_MEDIUM_ID: string
AUTH_MEDIUM_SECRET: string
AUTH_NAVER_ID: string
AUTH_NAVER_SECRET: string
AUTH_NETLIFY_ID: string
AUTH_NETLIFY_SECRET: string
AUTH_OKTA_ID: string
AUTH_OKTA_SECRET: string
AUTH_ONELOGIN_ID: string
AUTH_ONELOGIN_SECRET: string
AUTH_OSSO_ID: string
AUTH_OSSO_ISSUER: string
AUTH_OSSO_SECRET: string
AUTH_OSU_ID: string
AUTH_OSU_SECRET: string
AUTH_PASSAGE_ID: string
AUTH_PASSAGE_ISSUER: string
AUTH_PASSAGE_SECRET: string
AUTH_PATREON_ID: string
AUTH_PATREON_SECRET: string
AUTH_PINTEREST_ID: string
AUTH_PINTEREST_SECRET: string
AUTH_PIPEDRIVE_ID: string
AUTH_PIPEDRIVE_SECRET: string
AUTH_REDDIT_ID: string
AUTH_REDDIT_SECRET: string
AUTH_SALESFORCE_ID: string
AUTH_SALESFORCE_SECRET: string
AUTH_SLACK_ID: string
AUTH_SLACK_SECRET: string
AUTH_SPOTIFY_ID: string
AUTH_SPOTIFY_SECRET: string
AUTH_STRAVA_ID: string
AUTH_STRAVA_SECRET: string
AUTH_TODOIST_ID: string
AUTH_TODOIST_SECRET: string
AUTH_TRAKT_ID: string
AUTH_TRAKT_SECRET: string
AUTH_TWITCH_ID: string
AUTH_TWITCH_SECRET: string
AUTH_TWITTER_ID: string
AUTH_TWITTER_SECRET: string
AUTH_UE_ID: string
AUTH_UE_ISSUER: string
AUTH_UE_SECRET: string
AUTH_VK_ID: string
AUTH_VK_SECRET: string
AUTH_WIKIMEDIA_ID: string
AUTH_WIKIMEDIA_SECRET: string
AUTH_WORDPRESS_ID: string
AUTH_WORDPRESS_SECRET: string
AUTH_WORKOS_ID: string
AUTH_WORKOS_SECRET: string
AUTH_YANDEX_ID: string
AUTH_YANDEX_SECRET: string
AUTH_ZITADEL_ID: string
AUTH_ZITADEL_SECRET: string
AUTH_ZOHO_ID: string
AUTH_ZOHO_SECRET: string
AUTH_ZOOM_ID: string
AUTH_ZOOM_SECRET: string
}
}
}

10
apps/examples/nextjs/next-auth.d.ts vendored Normal file
View File

@@ -0,0 +1,10 @@
import "next-auth/jwt"
// Read more at: https://next-auth.js.org/getting-started/typescript#module-augmentation
declare module "next-auth/jwt" {
interface JWT {
/** The user's role. */
userRole?: "admin"
}
}

View File

@@ -1,4 +0,0 @@
/** @type {import("next").NextConfig} */
module.exports = {
reactStrictMode: true,
}

View File

@@ -20,12 +20,13 @@
"dependencies": {
"next": "latest",
"next-auth": "latest",
"nodemailer": "^6",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/node": "^18.16.2",
"@types/react": "^18.2.0",
"typescript": "5.2.2"
"@types/node": "^17",
"@types/react": "^18.0.15",
"typescript": "^4"
}
}

View File

@@ -7,7 +7,7 @@ export default function Page() {
<p>Only admin users can see this page.</p>
<p>
To learn more about the NextAuth middleware see&nbsp;
<a href="https://next-auth.js.org/configuration/nextjs#middleware">
<a href="https://docs-git-misc-docs-nextauthjs.vercel.app/configuration/nextjs#middleware">
the docs
</a>
.

View File

@@ -0,0 +1,65 @@
import NextAuth, { NextAuthOptions } from "next-auth"
import GoogleProvider from "next-auth/providers/google"
import FacebookProvider from "next-auth/providers/facebook"
import GithubProvider from "next-auth/providers/github"
import TwitterProvider from "next-auth/providers/twitter"
import Auth0Provider from "next-auth/providers/auth0"
// import AppleProvider from "next-auth/providers/apple"
// import EmailProvider from "next-auth/providers/email"
// For more information on each option (and a full list of options) go to
// https://next-auth.js.org/configuration/options
export const authOptions: NextAuthOptions = {
// https://next-auth.js.org/configuration/providers/oauth
providers: [
/* EmailProvider({
server: process.env.EMAIL_SERVER,
from: process.env.EMAIL_FROM,
}),
// Temporarily removing the Apple provider from the demo site as the
// callback URL for it needs updating due to Vercel changing domains
Providers.Apple({
clientId: process.env.APPLE_ID,
clientSecret: {
appleId: process.env.APPLE_ID,
teamId: process.env.APPLE_TEAM_ID,
privateKey: process.env.APPLE_PRIVATE_KEY,
keyId: process.env.APPLE_KEY_ID,
},
}),
*/
FacebookProvider({
clientId: process.env.FACEBOOK_ID,
clientSecret: process.env.FACEBOOK_SECRET,
}),
GithubProvider({
clientId: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_SECRET,
}),
GoogleProvider({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
}),
TwitterProvider({
clientId: process.env.TWITTER_ID,
clientSecret: process.env.TWITTER_SECRET,
}),
Auth0Provider({
clientId: process.env.AUTH0_ID,
clientSecret: process.env.AUTH0_SECRET,
issuer: process.env.AUTH0_ISSUER,
}),
],
theme: {
colorScheme: "light",
},
callbacks: {
async jwt({ token }) {
token.userRole = "admin"
return token
},
},
}
export default NextAuth(authOptions)

View File

@@ -1,13 +1,14 @@
// This is an example of to protect an API route
import { unstable_getServerSession } from "next-auth/next"
import { authOptions } from "../auth/[...nextauth]"
import { auth } from "auth"
import type { NextApiRequest, NextApiResponse } from "next"
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const session = await auth(req, res)
const session = await unstable_getServerSession(req, res, authOptions)
if (session) {
return res.send({

View File

@@ -1,12 +1,13 @@
// This is an example of how to access a session from an API route
import { unstable_getServerSession } from "next-auth"
import { authOptions } from "../auth/[...nextauth]"
import { auth } from "auth"
import type { NextApiRequest, NextApiResponse } from "next"
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const session = await auth(req, res)
const session = await unstable_getServerSession(req, res, authOptions)
res.send(JSON.stringify(session, null, 2))
}

View File

@@ -1,4 +1,5 @@
import { auth } from "auth"
import { unstable_getServerSession } from "next-auth/next"
import { authOptions } from "./api/auth/[...nextauth]"
import Layout from "../components/layout"
import type { GetServerSidePropsContext } from "next"
@@ -12,11 +13,11 @@ export default function ServerSidePage() {
<Layout>
<h1>Server Side Rendering</h1>
<p>
This page uses the <strong>getServerSession()</strong> method in{" "}
<strong>getServerSideProps()</strong>.
This page uses the <strong>unstable_getServerSession()</strong> method
in <strong>getServerSideProps()</strong>.
</p>
<p>
Using <strong>getServerSession()</strong> in{" "}
Using <strong>unstable_getServerSession()</strong> in{" "}
<strong>getServerSideProps()</strong> is the recommended approach if you
need to support Server Side Rendering with authentication.
</p>
@@ -35,5 +36,13 @@ export default function ServerSidePage() {
// Export the `session` prop to use sessions with Server Side Rendering
export async function getServerSideProps(context: GetServerSidePropsContext) {
return { props: { session: await auth(context.req, context.res) } }
return {
props: {
session: await unstable_getServerSession(
context.req,
context.res,
authOptions
),
},
}
}

16
apps/examples/nextjs/process.d.ts vendored Normal file
View File

@@ -0,0 +1,16 @@
declare namespace NodeJS {
export interface ProcessEnv {
NEXTAUTH_URL: string
NEXTAUTH_SECRET: string
GITHUB_ID: string
GITHUB_SECRET: string
FACEBOOK_ID: string
FACEBOOK_SECRET: string
TWITTER_ID: string
TWITTER_SECRET: string
GOOGLE_ID: string
GOOGLE_SECRET: string
AUTH0_ID: string
AUTH0_SECRET: string
}
}

View File

@@ -1,11 +1,7 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@@ -17,22 +13,14 @@
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"baseUrl": ".",
"plugins": [
{
"name": "next"
}
]
"incremental": true
},
"include": [
"process.d.ts",
"next-env.d.ts",
"next-auth.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
"**/*.tsx"
],
"exclude": [
"node_modules"
]
"exclude": ["node_modules"]
}

View File

@@ -13,7 +13,7 @@
"solid-start-node": "^0.2.9",
"solid-start-vercel": "^0.2.9",
"tailwindcss": "^3.2.4",
"typescript": "5.2.2",
"typescript": "^4.8.3",
"vite": "^3.1.0"
},
"dependencies": {

View File

@@ -18,7 +18,7 @@
"@sveltejs/kit": "next",
"svelte": "3.55.0",
"svelte-check": "2.10.2",
"typescript": "5.2.2",
"typescript": "4.9.4",
"vite": "4.0.1"
},
"dependencies": {

View File

@@ -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"
}
}

View File

@@ -1,4 +1,4 @@
import { Session } from "@auth/core/types"
import { Session } from "@auth/core"
export default function useSession() {
return useState<Session | null>("session", () => null)

View File

@@ -43,7 +43,7 @@ export async function signIn<
// TODO: Handle custom base path
// TODO: Remove this since Sveltekit offers the CSRF protection via origin check
const { csrfToken } = await $fetch<{ csrfToken: string }>("/api/auth/csrf")
const { csrfToken } = await $fetch("/api/auth/csrf")
console.log(_signInUrl)

View File

@@ -1,14 +1,13 @@
import { AuthConfig, Session } from "@auth/core/types"
import { Auth } from "@auth/core"
import { AuthHandler, AuthOptions, Session } from "@auth/core"
import { fromNodeMiddleware, H3Event } from "h3"
import getURL from "requrl"
import { createMiddleware } from "@hattip/adapter-node"
export function NuxtAuthHandler(options: AuthConfig) {
export function NuxtAuthHandler(options: AuthOptions) {
async function handler(ctx: { request: Request }) {
options.trustHost ??= true
return Auth(ctx.request, options)
return AuthHandler(ctx.request, options)
}
const middleware = createMiddleware(handler)
@@ -18,7 +17,7 @@ export function NuxtAuthHandler(options: AuthConfig) {
export async function getSession(
event: H3Event,
options: AuthConfig
options: AuthOptions
): Promise<Session | null> {
options.trustHost ??= true
@@ -31,7 +30,7 @@ export async function getSession(
nodeHeaders.append(key, headers[key] as any)
})
const response = await Auth(
const response = await AuthHandler(
new Request(url, { headers: nodeHeaders }),
options
)

View File

@@ -1,21 +1,21 @@
{
"name": "next-auth-nuxt",
"name": "playground-nuxt",
"private": true,
"scripts": {
"build": "nuxt build",
"dev": "nuxt prepare && nuxt dev",
"build": "nuxt prepare && nuxt build",
"dev": "nuxt prepare && export NODE_OPTIONS='--no-experimental-fetch' && nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview"
},
"devDependencies": {
"@nuxt/eslint-config": "^0.1.1",
"eslint": "^8.29.0",
"h3": "1.6.6",
"nuxt": "3.5.1"
"h3": "1.0.2",
"nuxt": "3.0.0"
},
"dependencies": {
"@auth/core": "workspace:*",
"@hattip/adapter-node": "^0.0.34",
"@hattip/adapter-node": "^0.0.22",
"requrl": "^3.0.2"
}
}

View File

@@ -1,4 +1,4 @@
import { Session } from "@auth/core/types"
import { Session } from "@auth/core"
export default defineNuxtPlugin(async () => {
const session = useSession()

View File

@@ -1,10 +1,10 @@
import { NuxtAuthHandler } from "@/lib/auth/server"
import GithubProvider from "@auth/core/providers/github"
import type { AuthConfig } from "@auth/core"
import type { AuthOptions } from "@auth/core"
const runtimeConfig = useRuntimeConfig()
export const authOptions = {
export const authOptions: AuthOptions = {
secret: runtimeConfig.secret,
providers: [
GithubProvider({
@@ -12,6 +12,6 @@ export const authOptions = {
clientSecret: runtimeConfig.github.clientSecret,
}),
],
} as AuthConfig
}
export default NuxtAuthHandler(authOptions)

3
docs/.eslintrc Normal file
View File

@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}

View File

@@ -1,75 +0,0 @@
<p align="center">
<br/>
<a href="https://authjs.dev" target="_blank"><img width="150px" src="https://authjs.dev/img/logo/logo-sm.png" /></a>
<h3 align="center">Auth.js</h3>
<p align="center">Authentication for Next.js</p>
<p align="center">
Open Source. Full Stack. Own Your Data.
</p>
<p align="center" style="align: center;">
<a href="https://github.com/nextauthjs/next-auth/actions/workflows/release.yml?query=workflow%3ARelease">
<img src="https://github.com/nextauthjs/next-auth/actions/workflows/release.yml/badge.svg" alt="Release" />
</a>
<a href="https://packagephobia.com/result?p=@auth/core">
<img src="https://packagephobia.com/badge?p=@auth/core" alt="Bundle Size"/>
</a>
<a href="https://www.npmtrends.com/@auth/core">
<img src="https://img.shields.io/npm/dm/@auth/core" alt="Downloads" />
</a>
<a href="https://github.com/nextauthjs/next-auth/stargazers">
<img src="https://img.shields.io/github/stars/nextauthjs/next-auth" alt="Github Stars" />
</a>
<a href="https://www.npmjs.com/package/@auth/core">
<img src="https://img.shields.io/github/v/release/nextauthjs/next-auth?label=latest" alt="Github Stable Release" />
</a>
</p>
</p>
## Overview
This is the repository for the documentation page for Auth.js!
NextAuth.js is a complete open source authentication solution for [Next.js](http://nextjs.org/) applications.
This documentation site is based on the [Docusaurus](https://docusaurus.io) framework.
## Getting Started
To start a local environment of this project, please do the following.
1. Clone the repo:
```sh
git clone git@github.com:nextauthjs/next-auth.git
cd next-auth
```
2. Set up the correct pnpm version, using [Corepack](https://nodejs.org/api/corepack.html). Run the following in the project'a root:
```sh
corepack enable pnpm
```
(Now, if you run `pnpm --version`, it should print the same verion as the `packageManager` property in the [`package.json` file](https://github.com/nextauthjs/next-auth/blob/main/package.json))
3. Install packages. Developing requires Node.js v18:
```sh
pnpm install
```
4. Start the development server
```bash
$ pnpm dev:docs
```
And thats all! Now you should have a local copy of this docs site running at [localhost:3000](http://localhost:3000)!
## Contributing
We're open to all community contributions! If you'd like to contribute in any way, please first read our [Contributing Guide](https://github.com/nextauthjs/.github/blob/main/CONTRIBUTING.md).
## License
ISC

View 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

View 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

View 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

View 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))
const offset = Math.random() * width
useEffect(() => {
const timeout =
setTimeout(() => {
setReset(true)
}, (1000 * (width - offset)) / duration) - 2000
return () => clearTimeout(timeout)
}, [duration, width, offset])
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>
)
}

View 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>
)
}

View File

@@ -1,5 +0,0 @@
{
"label": "Concepts",
"collapsible": true,
"collapsed": true
}

View File

@@ -1,5 +0,0 @@
{
"label": "Getting Started",
"collapsible": true,
"collapsed": true
}

View File

@@ -1,14 +0,0 @@
---
title: Databases
---
Auth.js offers multiple database adapters. Check our guides on:
- [Using a database adapter](/guides/adapters/using-a-database-adapter)
- [Creating your own](/guides/adapters/creating-a-database-adapter)
To learn more about databases in Auth.js and how they are used, check out [databases in the FAQ](/concepts/faq#databases).
## How to use a database
See the [documentation for adapters](/reference/adapters) for more information on advanced configuration, including how to use Auth.js with other databases using a [custom adapter](/guides/adapters/creating-a-database-adapter).

View File

@@ -1,44 +0,0 @@
---
title: Introduction
sidebar_position: 0
---
## About Auth.js
Auth.js is a complete open-source authentication solution for web applications. Check out the live demos of Auth.js in action:
- [Next.js](https://next-auth-example.vercel.app/)
- [SvelteKit](https://sveltekit-auth-example.vercel.app/)
- [SolidStart](https://auth-solid.vercel.app/)
Continue to our tutorials to see how to use Auth.js for authentication:
- [Setup with OAuth](/getting-started/oauth-tutorial)
- [Setup with magic links](/getting-started/email-tutorial)
- [Integrating with external auth](/getting-started/credentials-tutorial)
### Battery included
- Built in support for 60+ popular services (Google, Facebook, Auth0, Apple…)
- Built-in email/password-less/magic link
- Use with any OAuth 2 or OpenID Connect provider
- Use with any username/password store
### Flexible
- Runtime agnostic - run anywhere! Vercel Edge Functions, Node.js, Serverless, etc.
- Use with any modern framework! Next.js, SolidStart, SvelteKit, etc.
- [Bring Your Own Database](/getting-started/databases) - or none! MySQL, Postgres, MSSQL, MongoDB, etc. Choose database sessions or JWT.
_Note: Email sign-in requires a database to store single-use verification tokens._
### Secure by default
- Signed, prefixed, server-only cookies
- Built-in CSRF protection
- Doesn't rely on client-side JavaScript
- JWT with JWS / JWE / JWK.
## Credits
Auth.js is an open-source project that is only possible [thanks to contributors](/contributors).
To financially support the development of Auth.js, you can check our [OpenCollective](https://opencollective.com/nextauth) page. We appreciate your support 💚.

View File

@@ -1,510 +0,0 @@
---
title: OAuth authentication
---
import creatingOauthAppImg from "./img/getting-started-creating-oauth-app.png"
import addingCallbackUrlImg from "./img/getting-started-oauth-callback-url.png"
import gettingClientIdSecretImg from "./img/getting-started-oauth-clientid-secret.png"
import startAppAndSignInImg from "./img/getting-started-app-start.png"
import githubAuthCredentials from "./img/getting-started-github-auth.png"
import nextAuthUserLoggedIn from "./img/getting-started-nextauth-success.png"
import Tabs from "@theme/Tabs"
import TabItem from "@theme/TabItem"
The goal of Auth.js is that you can add authentication easily to your project with just a few lines of code.
The fastest way to set up Auth.js is with an [OAuth](/concepts/oauth) provider. In this tutorial, we'll be setting Auth.js in a web application to be able to log in with **GitHub**.
:::info
Auth.js comes with a list of [built-in providers](/reference/providers/oauth-builtin) (Google, Facebook, Twitter, etc.). You can also integrate it with your OAuth service by [building a custom provider](/guides/providers/custom-provider).
:::
## 1. Configuring Auth.js
To add Auth.js to your project:
<Tabs groupId="frameworks" queryString>
<TabItem value="next" label="Next.js" default>
### Prerequisites
This tutorial assumes you have a Next.js application set up. If you don't, you can follow the [Next.js tutorial](https://nextjs.org/learn/basics/create-nextjs-app) to get started.
### Installing NextAuth.js
```bash npm2yarn
npm install next-auth
```
:::info
We are working on a new release of `next-auth` that will make it easier to set up Auth.js with Next.js. You can follow the development [on this PR](https://github.com/nextauthjs/next-auth/pull/7443)
:::
### Creating the server config
Create the following [API route](https://nextjs.org/docs/api-routes/dynamic-api-routes#catch-all-api-routes) file. This route contains the necessary configuration for NextAuth.js, as well as the dynamic route handler:
```ts title="pages/api/auth/[...nextauth].ts"
import NextAuth from "next-auth"
import GithubProvider from "next-auth/providers/github"
export default NextAuth({
providers: [
GithubProvider({
clientId: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_SECRET,
}),
],
})
```
:::info
Behind the scenes, this creates all the relevant OAuth API routes within `/api/auth/*` so that auth API requests to:
- [GET `/api/auth/signin`](https://authjs.dev/reference/rest-api#get--apiauthsignin)
- [POST `/api/auth/signin/:provider`](https://authjs.dev/reference/rest-api#post--apiauthsigninprovider)
- [GET/POST `/api/auth/callback/:provider`](https://authjs.dev/reference/rest-api#get--post--apiauthcallbackprovider)
- [GET `/api/auth/signout`](https://authjs.dev/reference/rest-api#get--apiauthsignout)
- [POST `/api/auth/signout`](https://authjs.dev/reference/rest-api#post--apiauthsignout)
- [GET `/api/auth/session`](https://authjs.dev/reference/rest-api#get--apiauthsession)
- [GET `/api/auth/csrf`](https://authjs.dev/reference/rest-api#get--apiauthcsrf)
- [GET `/api/auth/providers`](https://authjs.dev/reference/rest-api#get--apiauthproviders)
can be handled by NextAuth.js. In this way, NextAuth.js stays in charge of the whole application's authentication request/response flow.
NextAuth.js is fully customizable - [our guides section](/guides/overview) teaches you how to set it up to handle auth in different ways. All the possible configuration options are [listed here](/reference/configuration/auth-config).
:::
### Adding environment variables
You may notice we are using environment variables in the code example above. We take the value of `GITHUB_ID` and `GITHUB_SECRET` from the GitHub Developer OAuth Portal. See [Configuring OAuth Provider](/getting-started/oauth-tutorial#2-configuring-oauth-provider) section on how to get those.
In your project root, create a `.env.local` file and add the `NEXTAUTH_SECRET` environment variable:
```title=".env.local"
NEXTAUTH_SECRET="This is an example"
```
`NEXTAUTH_SECRET` is a random string used by the library to encrypt tokens and email verification hashes, and **it's mandatory to keep things secure**! 🔥 🔐 . You can use:
```
$ openssl rand -base64 32
```
or https://generate-secret.vercel.app/32 to generate a random value for it.
### Exposing the session via `SessionProvider`:
NextAuth.js provides [`useSession()`](/reference/react/#usesession) - a [React Hooks](https://reactjs.org/docs/hooks-intro.html) to access the session data and status. To use it first you'll need to expose the session context - [`<SessionProvider />`](/reference/react/#sessionprovider) - at the top level of your application:
```ts title="pages/_app.tsx"
import { SessionProvider } from "next-auth/react"
import type { AppProps } from "next/app"
export default function App({
Component,
pageProps: { session, ...pageProps },
}: AppProps) {
return (
<SessionProvider session={session}>
<Component {...pageProps} />
</SessionProvider>
)
}
```
Instances of `useSession` (more on it in the next section) will have access to the session data and status. The `<SessionProvider />` also keep the session updated and synced between browser tabs and windows. 💪🏽
:::tip
Check our [client docs](/reference/react/) to learn all the available options for handling sessions on the browser.
:::
### Consuming the session via hooks
You can use the `useSession` hook from anywhere in your application (E.g. in a header component). Behind the scenes, the hook will connect to the `<SessionProvider />` to read the current user session. Learn more about React Context in the [React docs](https://reactjs.org/docs/context.html).
```ts title="pages/overview.tsx"
import { useSession, signIn, signOut } from "next-auth/react"
export default function CamperVanPage() {
const { data: session, status } = useSession()
const userEmail = session?.user?.email
if (status === "loading") {
return <p>Hang on there...</p>
}
if (status === "authenticated") {
return (
<>
<p>Signed in as {userEmail}</p>
<button onClick={() => signOut()}>Sign out</button>
<img src="https://cdn.pixabay.com/photo/2017/08/11/19/36/vw-2632486_1280.png" />
</>
)
}
return (
<>
<p>Not signed in.</p>
<button onClick={() => signIn("github")}>Sign in</button>
</>
)
}
```
### Protecting API Routes
To protect your API Routes (blocking unauthorized access to resources), you can use [`getServerSession()`](https://next-auth.js.org/configuration/nextjs#getserversession) to know whether a session exists or not:
```ts title="pages/api/movies/list.ts"
import { getServerSession } from "next-auth/next"
import { authOptions } from "../auth/[...nextauth]"
export default async function listMovies(req, res) {
const session = await getServerSession(req, res, authOptions)
if (session) {
res.send({
movies: [
{ title: "Alien vs Predator", id: 1 },
{ title: "Reservoir Dogs", id: 2 },
],
})
} else {
res.send({
error: "You must sign in to view movies.",
})
}
}
```
</TabItem>
<TabItem value="sveltekit" label="SvelteKit">
:::warning
`@auth/sveltekit` is currently experimental. The API _will_ change in the future.
:::
### Prerequisites
This tutorial assumes you have a SvelteKit application set up. If you don't, you can follow the [SvelteKit tutorial](https://kit.svelte.dev/docs/creating-a-project) to get started.
### Installing Auth.js
```bash npm2yarn
npm install @auth/core @auth/sveltekit
```
### Create server hook
Create the following [Server hook](https://kit.svelte.dev/docs/hooks) file. This route contains the necessary configuration for Auth.js, as well as the dynamic route handler:
```ts title="src/hooks.server.ts"
import { SvelteKitAuth } from "@auth/sveltekit"
import GitHub from "@auth/core/providers/github"
import { GITHUB_ID, GITHUB_SECRET } from "$env/static/private"
*
export const handle = SvelteKitAuth({
providers: [GitHub({ clientId: GITHUB_ID, clientSecret: GITHUB_SECRET })],
})
```
:::info
Behind the scenes, this creates all the relevant OAuth API routes within `/api/auth/*` so that auth API requests to:
- [GET `/api/auth/signin`](https://authjs.dev/reference/rest-api#get--apiauthsignin)
- [POST `/api/auth/signin/:provider`](https://authjs.dev/reference/rest-api#post--apiauthsigninprovider)
- [GET/POST `/api/auth/callback/:provider`](https://authjs.dev/reference/rest-api#get--post--apiauthcallbackprovider)
- [GET `/api/auth/signout`](https://authjs.dev/reference/rest-api#get--apiauthsignout)
- [POST `/api/auth/signout`](https://authjs.dev/reference/rest-api#post--apiauthsignout)
- [GET `/api/auth/session`](https://authjs.dev/reference/rest-api#get--apiauthsession)
- [GET `/api/auth/csrf`](https://authjs.dev/reference/rest-api#get--apiauthcsrf)
- [GET `/api/auth/providers`](https://authjs.dev/reference/rest-api#get--apiauthproviders)
can be handled by Auth.js. In this way, Auth.js stays in charge of the whole application's authentication request/response flow.
Auth.js is fully customizable - [our guides section](/guides/overview) teaches you how to set it up to handle auth in different ways. All the possible configuration options are [listed here](/reference/configuration/auth-config).
:::
### Adding environment variables
You may notice we are using environment variables in the code example above. We take the value of `GITHUB_ID` and `GITHUB_SECRET` from the GitHub Developer OAuth Portal. See [Configuring OAuth Provider](/getting-started/oauth-tutorial#2-configuring-oauth-provider) section on how to get those.
In your project root, create a `.env.local` file and add the `AUTH_SECRET` environment variable:
```title=".env.local"
AUTH_SECRET="This is an example"
```
`AUTH_SECRET` is a random string used by the library to encrypt tokens and email verification hashes, and **it's mandatory to keep things secure**! 🔥 🔐 . You can use:
```
$ openssl rand -base64 32
```
or https://generate-secret.vercel.app/32 to generate a random value for it.
### Exposing the session via page store
Auth.js provides us a getSession, function to access the session data and status, to call from the `event.locals` variable. We can now just call it and add it to our `$page` store.
```ts
import type { LayoutServerLoad } from './$types';
*
export const load: LayoutServerLoad = async (event) => {
return {
session: await event.locals.getSession()
};
};
```
### Consuming the session via page store
You can use the `$page.data.session` variable from anywhere on your page. Learn more about SvelteKit's page store in the [SvelteKit docs](https://learn.svelte.dev/tutorial/page-store).
```ts title="route/+page.svelte"
<script>
import { signIn, signOut } from '@auth/sveltekit/client'
import { page } from '$app/stores'
</script>
{#if $page.data.session?.user}
<p>Signed in as {$page.data.session.user.email}</p>
<button on:click={signOut}>Sign out</button>
<img src="https://cdn.pixabay.com/photo/2017/08/11/19/36/vw-2632486_1280.png" />
{:else}
<p>Not signed in.</p>
<button on:click={() => signIn('github')}>Sign in</button>
{/if}
```
### Protecting API Routes
To protect your API Routes (blocking unauthorized access to resources), you can use `locals.getSessions()` just like in the layouts file to know whether a session exists or not:
```ts title="routes/api/movies/+server.ts"
import { json, error } from "@sveltejs/kit";
import type { RequestEvent } from "./$types";
export async function GET({ locals }: RequestEvent) {
const session = await locals.getSession()
if (!session?.user) {
throw error(401, "You must sign in to view movies.");
}
return json({
movies: [
{ title: "Alien vs Predator", id: 1 },
{ title: "Reservoir Dogs", id: 2 },
]
})
}
```
</TabItem>
<TabItem value="solidstart" label="SolidStart">
TODO: SolidStart
</TabItem>
<TabItem value="core" label="Vanilla (No Framework)">
TODO Core
</TabItem>
</Tabs>
## 2. Configuring OAuth Provider
Ok, we have our app set up with NextAuth.js, however, if you run the app right now, it won't work as we haven't configured our OAuth provider (**GitHub**) yet.
:::info
When using OAuth you're asking for a third-party service (in this case GitHub, although it could be Google, Twitter, etc...) to handle user authentication for your app.
:::
We need to register our new app in GitHub, so that when NextAuth.js forwards the authorization requests to it, GitHub can recognize your application and prompt the user to sign in.
<img src={creatingOauthAppImg} />
Log in to **GitHub**, go to [`Settings / Developers / OAuth Apps`](https://github.com/settings/developers) and click "New OAuth App"
Next, you'll be presented with a screen to add details about your new application. Fill in the required fields, but pay extra attention to the **Authorization Callback URL** one:
<img src={addingCallbackUrlImg} />
The callback URL we insert should have the following pattern:
```
[origin]/api/auth/callback/[provider]
```
In this case, given we want to try our authentication working locally on our machine and we're using **GitHub** as our OAuth provider, it'll be:
<Tabs groupId="frameworks" queryString>
<TabItem value="next" label="Next.js" default>
```
http://localhost:3000/api/auth/callback/github
```
:::info
NextAuth.js will already create this API endpoint for you when we start the application later. Note that because we're using Next.js, locally it starts our server on port `3000` by default. Hence, the origin is `http://localhost:3000`.
:::
</TabItem>
<TabItem value="sveltekit" label="SvelteKit">
```
http://localhost:5173/auth/callback/github
```
</TabItem>
<TabItem value="solidstart" label="SolidStart">
TODO SolidStart
</TabItem>
<TabItem value="core" label="Vanilla (No Framework)">
TODO Core
</TabItem>
</Tabs>
:::info
The last part of the URL, `[provider]`, is the ID of the provider you're using. In this case, we're using GitHub, so it's `github`. If you're using Google, it'll be `google`, etc... We keep track of the provider IDs internally.
The same id is used in the `signIn()` method we saw earlier.
:::
To register, tap on "Register application" button.
The next screen shows all the configurations for your newly created OAuth app. For now, we need two things from it - the **Client ID** and **Client Secret**:
<img src={gettingClientIdSecretImg} />
The Client ID is always there, a public identifier of your OAuth application within GitHub. Click on the **Generate a new client Secret** button and should be presented with a new string (which is just a randomized string).
:::warning
Keep both your Client ID and Client Secret secure and never expose them to the public or share them with people outside your organization. With them, a malicious actor could hijack your application and cause you and your user serious problems!
:::
Cool! We have finished configuring our OAuth provider, now let's wire all together so we can finally see authentication working in our app!
:::info
As noted previously, NextAuth.js has built-in support for multiple OAuth providers, <a href="">here is the full list</a>. You can also easily build your own in case the provider you need is not on the list.
Note that, for each provider, the configuration process will be similar to what we just did:
1. Log in to the provider
2. Create create your OAuth application within it
3. Set the callback URL
4. Get the Client ID and Generate a Client Secret
:::
## 3. Wiring all together
Finally, we just need to reference our **Client ID** and **Client Secret** we just generated in the previous in our Auth.js config. In this way, the library will be able to use them when forwarding users to GitHub, and GitHub will be able to recognize the request as generated from our application.
Now let's copy both the Client ID and Client Secret and paste them into an environment file in the root of your project like so:
```title=".env.local"
GITHUB_ID=12345
GITHUB_SECRET=67890
```
Here is our server configuration file again:
<Tabs groupId="frameworks" queryString>
<TabItem value="next" label="Next.js" default>
```ts title="pages/api/auth/[...nextauth].ts"
import NextAuth from "next-auth"
import GithubProvider from "next-auth/providers/github"
export default NextAuth({
providers: [
GithubProvider({
clientId: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_SECRET,
}),
],
})
```
Great! We're now ready to run our application locally. Start the Next.js app by running on your terminal the following command and navigating to [`http://localhost:3000`](http://localhost:3000):
```
$ npm run next dev
```
</TabItem>
<TabItem value="sveltekit" label="SvelteKit">
```ts title="src/hooks.server.ts"
import { SvelteKitAuth } from "@auth/sveltekit"
import GitHub from "@auth/core/providers/github"
import { GITHUB_ID, GITHUB_SECRET } from "$env/static/private"
*
export const handle = SvelteKitAuth({
providers: [GitHub({ clientId: GITHUB_ID, clientSecret: GITHUB_SECRET })],
})
```
Great! We're now ready to run our application locally. Start the Svelte app by running on your terminal the following command and navigating to [`http://localhost:5173`](http://localhost:5173):
```
$ npm run vite dev
```
</TabItem>
<TabItem value="solidstart" label="SolidStart">
TODO SolidStart
</TabItem>
<TabItem value="core" label="Vanilla (No Framework)">
TODO Core
</TabItem>
</Tabs>
You should see the following page:
<img src={startAppAndSignInImg} />
Click on "Sign in" and then on "Sign in with GitHub": Auth.js will redirect you to GitHub, and GitHub will recognize our app [that we just registered](#2-configuring-oauth-provider) and ask the user (in this case you) to enter its credentials to proceed:
<img src={githubAuthCredentials} />
Once inserted and correct, GitHub will redirect the user to our app and NextAuth.js will take care of any further calls with GitHub to get access to the user profile and start a user session safely in the background:
<img src={nextAuthUserLoggedIn} />
Great! We have completed the whole E2E authentication flow setup so that users can log in to our application through GitHub!
## 4. Deploying to production
### Configuring different environments
It's normal to test your application in different environments. Usually, you'll have a development environment (when you run the application locally on your machine), a staging environment (for team members to try the application), and a production environment.
For each environment, you need to create an OAuth application in your provider respectively, as [we did previously](#2-configuring-oauth-provider), and point the **callback URL** to it.
For instance, in the previous section, we pointed the callback URL to `http://localhost:3000/api/auth/callback/github` as we wanted to test our application in the development environment.
If we were to deploy our app to production, we would need to create a new **OAuth App** in GitHub (calling it something like "Van life prod") and point the **callback URL** to our production domain: `https://example.com/api/auth/callback/github`
Finally, we would need to point the environment variables we set ( `GITHUB_ID` and `GITHUB_SECRET` ) to the credentials of the OAuth app we want our application to run with.
### Setting up `NEXTAUTH_URL`
:::tip
Skip this section if you are deploying to Vercel.
:::
When deploying your site, **you need to set** the `NEXTAUTH_URL` environment variable to the canonical URL of your website:
```
NEXTAUTH_URL=https://example.com
```
:::warning
In production, this needs to be set as an environment variable on the service you use to deploy your app.
To set environment variables on Vercel, you can use the [dashboard](https://vercel.com/dashboard) or the `vercel env pull` [command](https://vercel.com/docs/build-step#development-environment-variables).
:::
For more information please check out our [deployment page](/guides/basics/deployment).

View File

@@ -1,612 +0,0 @@
---
title: Upgrade Guide (v4)
---
Auth.js version 4 includes a few breaking changes from the last major version (3.x). So we're here to help you upgrade your applications as smoothly as possible. It should be possible to upgrade from any version of 3.x to the latest 4 release by following the next few migration steps.
:::note
Version 4 has been released to GA 🚨
We encourage users to try it out and report any and all issues they come across.
:::
You can upgrade to the new version by running:
```bash npm2yarn
npm install next-auth
```
## `next-auth/jwt`
We no longer have a default export in `next-auth/jwt`.
To comply with this, change the following:
```diff
- import jwt from "next-auth/jwt"
+ import { getToken } from "next-auth/jwt"
```
## `next-auth/react`
We've renamed the client-side import source to `next-auth/react`. To comply with this change, you will simply have to rename anywhere you were using `next-auth/client`.
For example:
```diff
- import { useSession } from "next-auth/client"
+ import { useSession } from "next-auth/react"
```
We've also made the following changes to the names of the exports:
- `setOptions`: Not exposed anymore, use [`SessionProvider` props](/reference/react/#sessionprovider)
- `options`: Not exposed anymore, [use `SessionProvider` props](/reference/react/#sessionprovider)
- `session`: Renamed to `getSession`
- `providers`: Renamed to `getProviders`
- `csrfToken`: Renamed to `getCsrfToken`
- `signin`: Renamed to `signIn`
- `signout`: Renamed to `signOut`
- `Provider`: Renamed to `SessionProvider`
Introduced in https://github.com/nextauthjs/next-auth/releases/tag/v4.0.0-next.12
## `SessionProvider`
Version 4 makes using the `SessionProvider` mandatory. This means that you will have to wrap any part of your application using `useSession` in this provider, if you were not doing so already. The `SessionProvider` has also undergone a few further changes:
- `Provider` is renamed to `SessionProvider`
- The options prop is now flattened as the props of SessionProvider.
- `keepAlive` has been renamed to `refetchInterval`.
- `clientMaxAge` has been removed in favor of `refetchInterval`, as they overlap in functionality, with the difference that `refetchInterval` will keep re-fetching the session periodically in the background.
The best practice for wrapping your app in Providers is to do so in your `pages/_app.jsx` file.
An example use-case with these new changes:
```jsx
import { SessionProvider } from "next-auth/react"
export default function App({
Component,
pageProps: { session, ...pageProps },
}) {
return (
// `session` comes from `getServerSideProps` or `getInitialProps`.
// Avoids flickering/session loading on first load.
<SessionProvider session={session} refetchInterval={5 * 60}>
<Component {...pageProps} />
</SessionProvider>
)
}
```
Introduced in https://github.com/nextauthjs/next-auth/releases/tag/v4.0.0-next.12
## Providers
Providers now need to be imported individually.
```diff
- import Provider from "next-auth/providers"
- Providers.Auth0({...})
- Providers.Google({...})
+ import Auth0Provider from "next-auth/providers/auth0"
+ import GoogleProvider from "next-auth/providers/google"
+ Auth0Provider({...})
+ GoogleProvider({...})
```
1. The `AzureADB2C` provider has been renamed `AzureAD`.
2. The `Basecamp` provider has been removed, see explanation [here](https://github.com/basecamp/api/blob/master/sections/authentication.md#on-authenticating-users-via-oauth).
3. The GitHub provider by default now will not request full write access to user profiles. If you need this scope, please add `user` to the scope option manually.
The following new options are available when defining your Providers in the configuration:
1. `authorization` (replaces `authorizationUrl`, `authorizationParams`, `scope`)
2. `token` replaces (`accessTokenUrl`, `headers`, `params`)
3. `userinfo` (replaces `profileUrl`)
4. `issuer`(replaces `domain`)
For more details on their usage, please see [options](/reference/providers/oauth) section of the OAuth Provider documentation.
When submitting a new OAuth provider to the repository, the `profile` callback is expected to only return these fields from now on: `id`, `name`, `email`, and `image`. If any of these are missing values, they should be set to `null`.
Also worth noting is that `id` is expected to be returned as a `string` type (For example if your provider returns it as a number, you can cast it by using the `.toString()` method). This makes the returned profile object comply across all providers/accounts/adapters, and hopefully cause less confusion in the future.
Implemented in: https://github.com/nextauthjs/next-auth/pull/2411
Introduced in https://github.com/nextauthjs/next-auth/releases/tag/v4.0.0-next.20
## `useSession` Hook
The `useSession` hook has been updated to return an object. This allows you to test states much more cleanly with the new `status` option.
```diff
- const [ session, loading ] = useSession()
+ const { data: session, status } = useSession()
+ const loading = status === "loading"
```
[Check the docs](/reference/react/#usesession) for the possible values of both `session.status` and `session.data`.
Introduced in https://github.com/nextauthjs/next-auth/releases/tag/v4.0.0-next.18
## Named Parameters
We have changed the arguments to our callbacks to the named parameters pattern. This way you don't have to use dummy `_` placeholders or other tricks.
### Callbacks
The signatures for the callback methods now look like this:
```diff
- signIn(user, account, profileOrEmailOrCredentials)
+ signIn({ user, account, profile, email, credentials })
```
```diff
- redirect(url, baseUrl)
+ redirect({ url, baseUrl })
```
```diff
- session(session, tokenOrUser)
+ session({ session, token, user })
```
```diff
- jwt(token, user, account, OAuthProfile, isNewUser)
+ jwt({ token, user, account, profile, isNewUser })
```
Introduced in https://github.com/nextauthjs/next-auth/releases/tag/v4.0.0-next.17
### Events
Two event signatures have changed to also use the named parameters pattern, `signOut` and `updateUser`.
```diff
// [...nextauth].js
...
events: {
- signOut(tokenOrSession),
+ signOut({ token, session }), // token if using JWT, session if DB persisted sessions.
- updateUser(user)
+ updateUser({ user })
}
```
Introduced in https://github.com/nextauthjs/next-auth/releases/tag/v4.0.0-next.20
## JWT configuration
We have removed some of the [configuration options](/reference/configuration/auth-config) when using JSON Web Tokens, [here's the PR](https://github.com/nextauthjs/next-auth/pull/3039) for more context.
```diff
export default NextAuth({
// ...
jwt: {
secret,
maxAge,
- encryptionKey
- signingKey
- encryptionKey
- verificationOptions
encode({
token
secret
maxAge
- signingKey
- signingOptions
- encryptionKey
- encryptionOptions
- encryption
}) {},
decode({
token
secret
- maxAge
- signingKey
- verificationKey
- verificationOptions
- encryptionKey
- decryptionKey
- decryptionOptions
- encryption
}) {}
}
})
```
## Logger API
The logger API has been simplified to use at most two parameters, where the second is usually an object (`metadata`) containing an `error` object. If you are not using the logger settings you can ignore this change.
```diff
// [...Auth.js]
import log from "some-logger-service"
...
logger: {
- error(code, ...message) {},
+ error(code, metadata) {},
- warn(code, ...message) {},
+ warn(code) {}
- debug(code, ...message) {}
+ debug(code, metadata) {}
}
```
Introduced in https://github.com/nextauthjs/next-auth/releases/tag/v4.0.0-next.19
## `nodemailer`
Like `typeorm` and `prisma`, [`nodemailer`](https://npmjs.com/package/nodemailer) is no longer included as a dependency by default. If you are using the Email provider you must install it in your project manually, or use any other Email library in the [`sendVerificationRequest`](/guides/providers/email) callback. This reduces bundle size for those not actually using the Email provider. Remember, when using the Email provider, it is mandatory to also use a database adapter due to the fact that verification tokens need to be persisted longer term for the magic link functionality to work.
Introduced in https://github.com/nextauthjs/next-auth/releases/tag/v4.0.0-next.2
## Theme
We have added some basic customization options to our built-in pages like `signin`, `signout`, etc.
These can be set under the `theme` configuration key. This used to be a string which only controlled the color scheme option. Now it is an object with the following options:
```js
theme: {
colorScheme: "auto", // "auto" | "dark" | "light"
brandColor: "", // Hex color value
logo: "" // Absolute URL to logo image
}
```
The hope is that with some minimal configuration / customization options, users won't immediately feel the need to replace the built-in pages with their own.
More details and screenshots of the new theme options can be found under [custom pages tutorial](/guides/basics/pages).
Introduced in https://github.com/nextauthjs/next-auth/pull/2788
## Session
The `session.jwt: boolean` option has been renamed to `session.strategy: "jwt" | "database"`. The goal is to make the user's options more intuitive:
1. No adapter, `strategy: "jwt"`: This is the default. The session is saved in a cookie and never persisted anywhere.
2. With Adapter, `strategy: "database"`: If an Adapter is defined, this will be the implicit setting. No user config is needed.
3. With Adapter, `strategy: "jwt"`: The user can explicitly instruct `next-auth` to use JWT even if a database is available. This can result in faster lookups in compromise of lowered security. Read more about: https://authjs.dev/concepts/faq#json-web-tokens
Example:
```diff
session: {
- jwt: true,
+ strategy: "jwt",
}
```
Introduced in https://github.com/nextauthjs/next-auth/pull/3144
## Adapters
Most importantly, the core `next-auth` package no longer ships with `typeorm` or any other database adapter by default. This brings the default bundle size down significantly for those not needing to persist user data to a database.
You can find the official Adapters in the `packages` directory in the primary monorepo ([nextauthjs/next-auth](https://github.com/nextauthjs/next-auth)). Although you can still [create your own](/guides/adapters/creating-a-database-adapter) with a new, [simplified Adapter API](https://github.com/nextauthjs/next-auth/pull/2361).
If you have a database that was created with a `3.x.x` or earlier version of Auth.js, you will need to run a migration to update the schema to the new version 4 database model. See the bottom of this migration guide for database specific migration examples.
1. If you use the built-in TypeORM or Prisma adapters, these have been removed from the core `next-auth` package. Thankfully the migration is easy; you just need to install the external packages for your database and change the import in your `[...nextauth].js`.
The `database` option has been removed, you must now do the following instead:
```diff
// [...nextauth].js
import NextAuth from "next-auth"
+ import { TypeORMLegacyAdapter } from "@next-auth/typeorm-legacy-adapter"
...
export default NextAuth({
- database: "yourconnectionstring",
+ adapter: TypeORMLegacyAdapter("yourconnectionstring")
})
```
2. The `prisma-legacy` adapter has been removed, please use the [`@next-auth/prisma-adapter`](https://npmjs.com/package/@next-auth/prisma-adapter) instead.
3. The `typeorm-legacy` adapter has been upgraded to use the newer adapter API, but has retained the `typeorm-legacy` name. We aim to migrate this to individual lighter weight adapters for each database type in the future, or switch out `typeorm`.
4. MongoDB has been moved to its own adapter under `@next-auth/mongodb-adapter`. See the [MongoDB Adapter docs](/reference/adapters/mongodb).
Introduced in https://github.com/nextauthjs/next-auth/releases/tag/v4.0.0-next.8 and https://github.com/nextauthjs/next-auth/pull/2361
### Adapter API
**This does not require any changes from the user - these are adapter specific changes only**
The Adapter API has been rewritten and significantly simplified in NextAuth v4. The adapters now have less work to do as some functionality has been migrated to the core of NextAuth, like hashing the [verification token](/reference/adapters/models/#verification-token).
If you are an adapter maintainer or are interested in writing your own adapter, you can find more information about this change in https://github.com/nextauthjs/next-auth/pull/2361 and release https://github.com/nextauthjs/next-auth/releases/tag/v4.0.0-next.22.
### Schema changes
The way we save data with adapters have slightly changed. With the new Adapter API, we wanted to make it easier to extend your database with additional fields. For example if your User needs an extra `phone` field, it should be enough to add that to your database's schema, and no changes will be necessary in your adapter.
- `created_at`/`createdAt` and `updated_at`/`updatedAt` fields are removed from all Models.
- `user_id`/`userId` consistently named `userId`.
- `compound_id`/`compoundId` is removed from Account.
- `access_token`/`accessToken` is removed from Session.
- `email_verified`/`emailVerified` on User is consistently named `emailVerified`.
- `provider_id`/`providerId` renamed to `provider` on Account
- `provider_type`/`providerType` renamed to `type` on Account
- `provider_account_id`/`providerAccountId` on Account is consistently named `providerAccountId`
- `access_token_expires`/`accessTokenExpires` on Account renamed to `expires_at`
- New fields on Account: `token_type`, `scope`, `id_token`, `session_state`
- `verification_requests` table has been renamed to `verification_tokens`
<!-- REVIEW: Would something like this below be helpful? -->
<details>
<summary>
See the changes
</summary>
<pre>
```diff
User {
id
name
email
- emailVerified
+ email_verified
image
- created_at
- updated_at
}
Account {
id
- compound_id
- user_id
+ userId
- provider_type
+ type
- provider_id
+ provider
- provider_account_id
+ providerAccountId
refresh_token
access_token
- access_token_expires
+ expires_in
+ expires_at
+ token_type
+ scope
+ id_token
+ session_state
- created_at
- updated_at
}
Session {
id
userId
expires
sessionToken
- access_token
- created_at
- updated_at
}
VerificationToken {
id
token
expires
identifier
- created_at
- updated_at
}
```
</pre>
</details>
For more info, see the [Models page](/reference/adapters/models).
### Database migration
Auth.js v4 has a slightly different database schema compared to v3. If you're using any of our adapters and want to upgrade, you can use on of the below schemas.
They are designed to be run directly against the database itself. So instead of having one in Prisma syntax, one in TypeORM syntax, etc. we've decided to just make one for each underlying database type. i.e. one for Postgres, one for MySQL, one for MongoDB, etc.
#### MySQL
```sql
/* ACCOUNT */
ALTER TABLE accounts
CHANGE "access_token_expires" "expires_at" int
CHANGE "user_id" "userId" varchar(255)
ADD CONSTRAINT fk_user_id FOREIGN KEY (userId) REFERENCES users(id)
RENAME COLUMN "provider_id" "provider"
RENAME COLUMN "provider_account_id" "providerAccountId"
DROP COLUMN "provider_type"
DROP COLUMN "compound_id"
/* The following two timestamp columns have never been necessary for Auth.js to function, but can be kept if you want */
DROP COLUMN "created_at"
DROP COLUMN "updated_at"
ADD COLUMN "token_type" varchar(255) NULL
ADD COLUMN "scope" varchar(255) NULL
ADD COLUMN "id_token" varchar(255) NULL
ADD COLUMN "session_state" varchar(255) NULL
/* Note: These are only needed if you're going to be using the old Twitter OAuth 1.0 provider. */
ADD COLUMN "oauth_token_secret" varchar(255) NULL
ADD COLUMN "oauth_token" varchar(255) NULL
/* USER */
ALTER TABLE users
RENAME COLUMN "email_verified" "emailVerified"
/* The following two timestamp columns have never been necessary for Auth.js to function, but can be kept if you want */
DROP COLUMN "created_at"
DROP COLUMN "updated_at"
/* SESSION */
ALTER TABLE sessions
RENAME COLUMN "session_token" "sessionToken"
CHANGE "user_id" "userId" varchar(255)
ADD CONSTRAINT fk_user_id FOREIGN KEY (userId) REFERENCES users(id)
DROP COLUMN "access_token"
/* The following two timestamp columns have never been necessary for Auth.js to function, but can be kept if you want */
DROP COLUMN "created_at"
DROP COLUMN "updated_at"
/* VERIFICATION REQUESTS */
ALTER TABLE verification_requests RENAME verification_tokens
ALTER TABLE verification_tokens
DROP COLUMN id
/* The following two timestamp columns have never been necessary for Auth.js to function, but can be kept if you want */
DROP COLUMN "created_at"
DROP COLUMN "updated_at"
```
#### Postgres
```sql
/* ACCOUNT */
ALTER TABLE accounts RENAME COLUMN "user_id" TO "userId";
ALTER TABLE accounts RENAME COLUMN "provider_id" TO "provider";
ALTER TABLE accounts RENAME COLUMN "provider_account_id" TO "providerAccountId";
ALTER TABLE accounts RENAME COLUMN "access_token_expires" TO "expires_at";
ALTER TABLE accounts RENAME COLUMN "provider_type" TO "type";
/* Do conversion of TIMESTAMPTZ to BIGINT */
ALTER TABLE accounts ALTER COLUMN "expires_at" TYPE TEXT USING CAST(extract(epoch FROM "expires_at") AS BIGINT)*1000;
/* Keep id as SERIAL with autoincrement when using ORM. Using new v4 uuid format won't work because of incompatibility */
/* ALTER TABLE accounts ALTER COLUMN "id" TYPE TEXT; */
/* ALTER TABLE accounts ALTER COLUMN "userId" TYPE TEXT; */
ALTER TABLE accounts ALTER COLUMN "type" TYPE TEXT;
ALTER TABLE accounts ALTER COLUMN "provider" TYPE TEXT;
ALTER TABLE accounts ALTER COLUMN "providerAccountId" TYPE TEXT;
ALTER TABLE accounts ADD CONSTRAINT fk_user_id FOREIGN KEY ("userId") REFERENCES users(id);
ALTER TABLE accounts
DROP COLUMN IF EXISTS "compound_id";
/* The following two timestamp columns have never been necessary for Auth.js to function, but can be kept if you want */
ALTER TABLE accounts
DROP COLUMN IF EXISTS "created_at",
DROP COLUMN IF EXISTS "updated_at";
ALTER TABLE accounts
ADD COLUMN IF NOT EXISTS "token_type" TEXT NULL,
ADD COLUMN IF NOT EXISTS "scope" TEXT NULL,
ADD COLUMN IF NOT EXISTS "id_token" TEXT NULL,
ADD COLUMN IF NOT EXISTS "session_state" TEXT NULL;
/* Note: These are only needed if you're going to be using the old Twitter OAuth 1.0 provider. */
/* ALTER TABLE accounts
ADD COLUMN IF NOT EXISTS "oauth_token_secret" TEXT NULL,
ADD COLUMN IF NOT EXISTS "oauth_token" TEXT NULL; */
/* USER */
ALTER TABLE users RENAME COLUMN "email_verified" TO "emailVerified";
/* Keep id as SERIAL with autoincrement when using ORM. Using new v4 uuid format won't work because of incompatibility */
/* ALTER TABLE users ALTER COLUMN "id" TYPE TEXT; */
ALTER TABLE users ALTER COLUMN "name" TYPE TEXT;
ALTER TABLE users ALTER COLUMN "email" TYPE TEXT;
ALTER TABLE users ALTER COLUMN "image" TYPE TEXT;
/* Do conversion of TIMESTAMPTZ to BIGINT and then TEXT */
ALTER TABLE users ALTER COLUMN "emailVerified" TYPE TEXT USING CAST(CAST(extract(epoch FROM "emailVerified") AS BIGINT)*1000 AS TEXT);
/* The following two timestamp columns have never been necessary for Auth.js to function, but can be kept if you want */
ALTER TABLE users
DROP COLUMN IF EXISTS "created_at",
DROP COLUMN IF EXISTS "updated_at";
/* SESSION */
ALTER TABLE sessions RENAME COLUMN "session_token" TO "sessionToken";
ALTER TABLE sessions RENAME COLUMN "user_id" TO "userId";
/* Keep id as SERIAL with autoincrement when using ORM. Using new v4 uuid format won't work because of incompatibility */
/* ALTER TABLE sessions ALTER COLUMN "id" TYPE TEXT; */
/* ALTER TABLE sessions ALTER COLUMN "userId" TYPE TEXT; */
ALTER TABLE sessions ALTER COLUMN "sessionToken" TYPE TEXT;
ALTER TABLE sessions ADD CONSTRAINT fk_user_id FOREIGN KEY ("userId") REFERENCES users(id);
/* Do conversion of TIMESTAMPTZ to BIGINT and then TEXT */
ALTER TABLE sessions ALTER COLUMN "expires" TYPE TEXT USING CAST(CAST(extract(epoch FROM "expires") AS BIGINT)*1000 AS TEXT);
ALTER TABLE sessions DROP COLUMN IF EXISTS "access_token";
/* The following two timestamp columns have never been necessary for Auth.js to function, but can be kept if you want */
ALTER TABLE sessions
DROP COLUMN IF EXISTS "created_at",
DROP COLUMN IF EXISTS "updated_at";
/* VERIFICATION REQUESTS */
ALTER TABLE verification_requests RENAME TO verification_tokens;
/* Keep id as ORM needs it */
/* ALTER TABLE verification_tokens DROP COLUMN IF EXISTS id; */
ALTER TABLE verification_tokens ALTER COLUMN "identifier" TYPE TEXT;
ALTER TABLE verification_tokens ALTER COLUMN "token" TYPE TEXT;
/* Do conversion of TIMESTAMPTZ to BIGINT and then TEXT */
ALTER TABLE verification_tokens ALTER COLUMN "expires" TYPE TEXT USING CAST(CAST(extract(epoch FROM "expires") AS BIGINT)*1000 AS TEXT);
/* The following two timestamp columns have never been necessary for Auth.js to function, but can be kept if you want */
ALTER TABLE verification_tokens
DROP COLUMN IF EXISTS "created_at",
DROP COLUMN IF EXISTS "updated_at";
```
#### MongoDB
MongoDB is a document database and as such new fields will be automatically populated. You do, however, need to update the names of existing fields which are going to be reused.
```mongo
db.getCollection('accounts').updateMany({}, {
$rename: {
"provider_id": "provider",
"provider_account_id": "providerAccountId",
"user_id": "userId",
"access_token_expires": "expires_at"
}
})
db.getCollection('users').updateMany({}, {
$rename: {
"email_verified": "emailVerified"
}
})
db.getCollection('sessions').updateMany({}, {
$rename: {
"session_token": "sessionToken",
"user_id": "userId"
}
})
```
## Missing `secret`
Auth.js used to generate a secret for convenience, when the user did not define one. This might have been useful in development, but can be a concern in production. We have always been clear about that in the docs, but from now on, if you forget to define a `secret` property in production, we will show the user an error page. Read more about this option [here](/reference/configuration/auth-config#secret)
You can generate a secret to be placed in the `secret` configuration option via the following command:
```bash
$ openssl rand -base64 32
```
Therefore, your Auth.js config should look something like this:
```javascript title="/pages/api/auth/[...nextauth].js"
...
export default NextAuth({
...
providers: [...],
secret: "LlKq6ZtYbr+hTC073mAmAh9/h2HwMfsFo4hrfCx5mLg=",
...
})
```
Introduced in https://github.com/nextauthjs/next-auth/issues/3143
## Session `strategy`
We have always supported two different session strategies. The first being our most popular and default strategy - the JWT based one. The second is the database adapter persisted session strategy. Both have their advantages/disadvantages, you can learn more about them on the [FAQ](/concepts/faq) page.
Previously, the way you configured this was through the `jwt: boolean` flag in the `session` option. The names `session` and `jwt` might have been a bit overused in the options, and so for a clearer message, we renamed this option to `strategy: "jwt" | "database"`, it is still in the `session` object. This will hopefully better indicate the purpose of this option as well as make very explicit which type of session you are going to use.
See the [`session` option docs](/reference/configuration/auth-config#session) for more details.
Introduced in https://github.com/nextauthjs/next-auth/pull/3144
## Summary
We hope this migration goes smoothly for each and every one of you! If you have any questions or get stuck anywhere, feel free to create [a new issue](https://github.com/nextauthjs/next-auth/issues/new) on GitHub.

View File

@@ -1,5 +0,0 @@
{
"label": "Adapters",
"collapsible": true,
"collapsed": true
}

View File

@@ -1,5 +0,0 @@
{
"label": "Basics",
"collapsible": true,
"collapsed": false
}

View File

@@ -1,75 +0,0 @@
---
title: Deployment
---
Deploying Auth.js only requires a few steps. It can be run anywhere a Next.js application can. Therefore, in a default configuration using only JWT session strategy, i.e. without a database, you will only need these few things in addition to your application:
1. Auth.js environment variables
- `NEXTAUTH_SECRET`
- `NEXTAUTH_URL`
2. Auth.js API Route and its configuration (`/pages/api/auth/[...nextauth].js`).
- OAuth Provider `clientId` / `clientSecret`
Deploying a modern JavaScript application using Auth.js consists of making sure your environment variables are set correctly as well as the configuration in the Auth.js API route is setup, as well as any configuration (like Callback URLs, etc.) are correctly done in your OAuth provider(s) themselves.
See below for more detailed provider settings.
## Vercel
1. Make sure to expose the Vercel [System Environment Variables](https://vercel.com/docs/concepts/projects/environment-variables#system-environment-variables) in your project settings. This way, we can detect the environment. (Setting `NEXTAUTH_URL` environment variable on Vercel is **unnecessary**).
2. Create a `NEXTAUTH_SECRET` environment variable for both Production and Preview environments.
a. You can use `openssl rand -base64 32` or https://generate-secret.vercel.app/32 to generate a random value.
3. Add your provider's client ID and client secret to environment variables. _(Skip this step if not using an [OAuth Provider](/reference/providers/index))_
4. Deploy!
Example repository: https://github.com/nextauthjs/next-auth-example
A few notes about deploying to Vercel. The environment variables are read server-side, so you **should not** prefix them with `NEXT_PUBLIC_` to avoid accidentally bundling a secret in the client-side JavaScript code.
### Securing a preview deployment
Most OAuth providers cannot be configured with multiple callback URLs or using a wildcard.
However, Auth.js **supports Preview deployments**, even **with OAuth providers**:
1. Determine a stable deployment URL. Eg.: A deployment whose URL does not change between builds, for example. `auth.yourdomain.com` (using a subdomain is not a requirement, this can simply be the main site's URL too.),
2. Set `AUTH_REDIRECT_PROXY_URL` to that URL, adding the path up until your `[...nextauth]` route. Eg.: (`https://auth.yourdomain.com/api/auth`)
3. For your OAuth provider, set the callback URL using the stable deployment URL. Eg.: For GitHub `https://auth.yourdomain.com/api/auth/callback/github`)
:::info
To support preview deployments, the `AUTH_SECRET` value needs to be the same for the stable deployment and deployments that will need OAuth support.
:::
:::note
If you are storing users in a [database](reference/adapters), we recommend using a different OAuth app for development/production so that you don't mix your test and production user base.
:::
<details>
<summary>
<b>How does this work?</b>
</summary>
To support preview deployments, Auth.js uses the stable deployment URL as a redirect proxy server.
It will redirect the OAuth callback request to the preview deployment URL, but only when the `AUTH_REDIRECT_PROXY_URL` environment variable is set. The stable deployment can still act as a regular app.
When a user initiates an OAuth sign-in flow on a preview deployment, we save its URL in the `state` query parameter but set the `redirect_uri` to the stable deployment.
Then, the OAuth provider will redirect the user to the stable deployment, which then will verify the `state` parameter and redirect the user to the preview deployment URL if the `state` is valid. This is secured by relying on the same server-side `AUTH_SECRET` for the stable deployment and the preview deployment.
See also:
<ul>
<li><a href="https://www.ietf.org/rfc/rfc6749.html#section-4.1.1">OAuth 2.0 specification: `state` query parameter</a></li>
</ul>
</details>
## Netlify
Netlify is very similar to Vercel in that you can deploy a Next.js project without almost any extra work.
To set up Auth.js correctly here, you will want to make sure you add your `NEXTAUTH_SECRET` environment variable in the project settings. If you are using the [Essential Next.js Build Plugin](https://github.com/netlify/netlify-plugin-nextjs) within your project, you **do not** need to set the `NEXTAUTH_URL` environment variable as it is set automatically as part of the build process.
Netlify also exposes some [system environment variables](https://docs.netlify.com/configure-builds/environment-variables/) from which you can check which `NODE_ENV` you are currently in and much more.
After this, make sure you either have your OAuth provider set up correctly with `clientId` / `clientSecret`'s and callback URLs.

Some files were not shown because too many files have changed in this diff Show More