mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
Compare commits
186 Commits
@auth/core
...
@auth/pris
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a23e41bca | ||
|
|
9dfef763fc | ||
|
|
f53c16a454 | ||
|
|
e999511a2e | ||
|
|
2707f9ebfe | ||
|
|
35977bf132 | ||
|
|
578ff21d7e | ||
|
|
ba238796b8 | ||
|
|
56b27de3be | ||
|
|
0bc9b4fe5a | ||
|
|
327061ce70 | ||
|
|
2ed407d6a5 | ||
|
|
e743340612 | ||
|
|
5140857256 | ||
|
|
9c6f81308c | ||
|
|
66184c55eb | ||
|
|
07599ba41d | ||
|
|
c5fc3e66ad | ||
|
|
5dfec49ee8 | ||
|
|
b20182c5fe | ||
|
|
a6d9459a64 | ||
|
|
99035b98f9 | ||
|
|
fabb0525d1 | ||
|
|
28e4328704 | ||
|
|
7ff4d9d280 | ||
|
|
26815f7621 | ||
|
|
fe2c3dc6bf | ||
|
|
c53435af8a | ||
|
|
f29a1f2778 | ||
|
|
a4c3270307 | ||
|
|
645d003d49 | ||
|
|
3f296615c5 | ||
|
|
a7842077ec | ||
|
|
b2e5b9f6a8 | ||
|
|
0681531627 | ||
|
|
ea81c467e9 | ||
|
|
bf2835d38f | ||
|
|
89d230666b | ||
|
|
f86e56f78a | ||
|
|
fe20b943ae | ||
|
|
4678c4d4fc | ||
|
|
3eb3f8f107 | ||
|
|
7fd03f38e3 | ||
|
|
ae44b72765 | ||
|
|
a996ab57e8 | ||
|
|
ebdeaf740d | ||
|
|
c5c8a81462 | ||
|
|
61d30f3dcd | ||
|
|
a9180a752b | ||
|
|
6c4180146e | ||
|
|
ec6c4ea2be | ||
|
|
3dfc86334e | ||
|
|
01d6019638 | ||
|
|
4730429a9f | ||
|
|
a49236ef62 | ||
|
|
96ade948ef | ||
|
|
550507b2d1 | ||
|
|
1eddcf643c | ||
|
|
17d71a04d6 | ||
|
|
3c65e264af | ||
|
|
28d8d4894d | ||
|
|
c6b98a8f08 | ||
|
|
d042f933c6 | ||
|
|
3a85de2c5f | ||
|
|
d47b56743e | ||
|
|
363440e515 | ||
|
|
60c5037ee1 | ||
|
|
97394baed1 | ||
|
|
f94abb8f70 | ||
|
|
bbfc11e74c | ||
|
|
2a70514df1 | ||
|
|
96d666465f | ||
|
|
ecbf0be22e | ||
|
|
87ec13bd00 | ||
|
|
c0f9af4c56 | ||
|
|
c7b36f45a3 | ||
|
|
68ff69f9eb | ||
|
|
23c0a393da | ||
|
|
f130f62a91 | ||
|
|
c111b436d2 | ||
|
|
ea895b8864 | ||
|
|
cfedc3b1a3 | ||
|
|
287a5fc05a | ||
|
|
f3ad659e91 | ||
|
|
48b9a0203e | ||
|
|
39fbccb783 | ||
|
|
f207e94146 | ||
|
|
b845729cdb | ||
|
|
e459d2d7e2 | ||
|
|
db1fd9007c | ||
|
|
0439fc5fc6 | ||
|
|
d0dd2ababc | ||
|
|
ba58d48dba | ||
|
|
a8d76ed440 | ||
|
|
3d7b8720db | ||
|
|
1e886b97bc | ||
|
|
ecb14ccecd | ||
|
|
8cee24d4ab | ||
|
|
0189a197be | ||
|
|
c44bf75c65 | ||
|
|
cf13b6c7e3 | ||
|
|
dc1a79e547 | ||
|
|
78964c115b | ||
|
|
7fa51e2a61 | ||
|
|
a79774f6e8 | ||
|
|
f779f05906 | ||
|
|
3245c02eac | ||
|
|
a8dfc8ebb1 | ||
|
|
1b80a18dd4 | ||
|
|
50a88bb878 | ||
|
|
a359a562ce | ||
|
|
7edb9cf53f | ||
|
|
018b086c4f | ||
|
|
173000a068 | ||
|
|
8fcd46b0fc | ||
|
|
d5d1313914 | ||
|
|
3285d04241 | ||
|
|
fe442522ef | ||
|
|
6c9dfff45f | ||
|
|
ef50916ec2 | ||
|
|
8e771a2993 | ||
|
|
06a7149b66 | ||
|
|
662e0942cb | ||
|
|
91c71a175b | ||
|
|
3b8c75297b | ||
|
|
5d06fa5852 | ||
|
|
e7a52077c5 | ||
|
|
6e4516a9f8 | ||
|
|
8a0b11fcd6 | ||
|
|
f925e0c2a5 | ||
|
|
de4e20cc04 | ||
|
|
65f4b9c942 | ||
|
|
1d29b0d220 | ||
|
|
cd92aa0c82 | ||
|
|
d414e01181 | ||
|
|
43deda5bfb | ||
|
|
7e79d8c509 | ||
|
|
ab051162a7 | ||
|
|
87298a0150 | ||
|
|
d6abccd9a0 | ||
|
|
2f35daae37 | ||
|
|
a0f3b04c43 | ||
|
|
c7dec376a1 | ||
|
|
925a52e0ec | ||
|
|
2318e44de4 | ||
|
|
d73812bce5 | ||
|
|
ee36d09a08 | ||
|
|
0cb7fd2e7c | ||
|
|
3b414bd7b5 | ||
|
|
37bb6ebd2c | ||
|
|
2ecf52c342 | ||
|
|
cda07c239e | ||
|
|
fa60b79abe | ||
|
|
39e1a76e8f | ||
|
|
953ef9d04a | ||
|
|
94f3031765 | ||
|
|
ad7bf07ddf | ||
|
|
f30308ac30 | ||
|
|
6eaaeb15e9 | ||
|
|
8b3f0696a5 | ||
|
|
c69a157832 | ||
|
|
60af446338 | ||
|
|
ce85444760 | ||
|
|
142abe3eea | ||
|
|
da211e6cbe | ||
|
|
79ad6156ed | ||
|
|
28f287d63e | ||
|
|
1ab77d0e11 | ||
|
|
787c1ff7d0 | ||
|
|
208b3b4a43 | ||
|
|
c4f6330f70 | ||
|
|
44127068e1 | ||
|
|
9e3f1aacf7 | ||
|
|
83051c6862 | ||
|
|
f1acab67e6 | ||
|
|
6a31ed3216 | ||
|
|
0998fc0b98 | ||
|
|
bd20d750c2 | ||
|
|
8e29b4df0c | ||
|
|
9632a56d45 | ||
|
|
12161b9613 | ||
|
|
a3b5276a5a | ||
|
|
7c1078b9a9 | ||
|
|
37d3461155 | ||
|
|
6111662df7 | ||
|
|
5da6549c48 |
2
.github/ISSUE_TEMPLATE/1_bug_framework.yml
vendored
2
.github/ISSUE_TEMPLATE/1_bug_framework.yml
vendored
@@ -11,7 +11,7 @@ body:
|
||||
|
||||
### Important :exclamation:
|
||||
|
||||
_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._
|
||||
_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._
|
||||
|
||||
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.
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/2_bug_provider.yml
vendored
2
.github/ISSUE_TEMPLATE/2_bug_provider.yml
vendored
@@ -31,12 +31,14 @@ body:
|
||||
- "Authentik"
|
||||
- "Azure Active Directory"
|
||||
- "Azure Active Directory B2C"
|
||||
- "Azure DevOps"
|
||||
- "Battlenet"
|
||||
- "Beyond Identity"
|
||||
- "Box"
|
||||
- "Bungie"
|
||||
- "Cognito"
|
||||
- "Coinbase"
|
||||
- "Descope"
|
||||
- "Discord"
|
||||
- "Dropbox"
|
||||
- "EVE Online"
|
||||
|
||||
30
.github/ISSUE_TEMPLATE/3_bug_adapter.yml
vendored
30
.github/ISSUE_TEMPLATE/3_bug_adapter.yml
vendored
@@ -21,20 +21,22 @@ body:
|
||||
multiple: true
|
||||
options:
|
||||
- "Custom 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"
|
||||
- "@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"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
||||
36
.github/issue-labeler.yml
vendored
36
.github/issue-labeler.yml
vendored
@@ -1,43 +1,49 @@
|
||||
# https://github.com/github/issue-labeler#basic-examples
|
||||
|
||||
dgraph:
|
||||
- "@next-auth/dgraph-adapter"
|
||||
- "@auth/dgraph-adapter"
|
||||
|
||||
drizzle:
|
||||
- "@auth/drizzle-adapter"
|
||||
|
||||
dynamodb:
|
||||
- "@next-auth/dynamodb-adapter"
|
||||
- "@auth/dynamodb-adapter"
|
||||
|
||||
fauna:
|
||||
- "@next-auth/fauna-adapter"
|
||||
- "@auth/fauna-adapter"
|
||||
|
||||
firebase:
|
||||
- "@next-auth/firebase-adapter"
|
||||
- "@auth/firebase-adapter"
|
||||
|
||||
kysely:
|
||||
- "@auth/kysely-adapter"
|
||||
|
||||
mikro-orm:
|
||||
- "@next-auth/mikro-orm-adapter"
|
||||
- "@auth/mikro-orm-adapter"
|
||||
|
||||
mongodb:
|
||||
- "@next-auth/mongodb-adapter"
|
||||
- "@auth/mongodb-adapter"
|
||||
|
||||
neo4j:
|
||||
- "@next-auth/neo4j-adapter"
|
||||
- "@auth/neo4j-adapter"
|
||||
|
||||
pouchdb:
|
||||
- "@next-auth/pouchdb-adapter"
|
||||
- "@auth/pouchdb-adapter"
|
||||
|
||||
prisma:
|
||||
- "@next-auth/prisma-adapter"
|
||||
- "@auth/prisma-adapter"
|
||||
|
||||
sequelize:
|
||||
- "@next-auth/sequelize-adapter"
|
||||
- "@auth/sequelize-adapter"
|
||||
|
||||
supabase:
|
||||
- "@next-auth/supabase-adapter"
|
||||
- "@auth/supabase-adapter"
|
||||
|
||||
typeorm-legacy:
|
||||
- "@next-auth/typeorm-legacy-adapter"
|
||||
typeorm:
|
||||
- "@auth/typeorm-adapter"
|
||||
|
||||
upstash-redis:
|
||||
- "@next-auth/upstash-redis-adapter"
|
||||
- "@auth/upstash-redis-adapter"
|
||||
|
||||
xata:
|
||||
- "@next-auth/xata-adapter"
|
||||
- "@auth/xata-adapter"
|
||||
|
||||
3
.github/pr-labeler.yml
vendored
3
.github/pr-labeler.yml
vendored
@@ -15,12 +15,13 @@ 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-legacy: ["packages/adapter-typeorm-legacy/**/*"]
|
||||
typeorm: ["packages/adapter-typeorm/**/*"]
|
||||
upstash-redis: ["packages/adapter-upstash-redis/**/*"]
|
||||
xata: ["packages/adapter-xata/**/*"]
|
||||
|
||||
9
.github/version-pr/index.js
vendored
9
.github/version-pr/index.js
vendored
@@ -5,14 +5,15 @@ const core = require("@actions/core")
|
||||
try {
|
||||
const packageJSONPath = path.join(
|
||||
process.cwd(),
|
||||
"packages/next-auth/package.json"
|
||||
`packages/${process.env.PACKAGE_PATH || "next-auth"}/package.json`
|
||||
)
|
||||
const packageJSON = JSON.parse(fs.readFileSync(packageJSONPath, "utf8"))
|
||||
|
||||
const sha8 = process.env.GITHUB_SHA.substring(0, 8)
|
||||
const prNumber = process.env.PR_NUMBER
|
||||
|
||||
const packageVersion = `0.0.0-pr.${prNumber}.${sha8}`
|
||||
const prefix = "0.0.0-"
|
||||
const pr = process.env.PR_NUMBER
|
||||
const source = pr ? `pr.${pr}` : "manual"
|
||||
const packageVersion = `${prefix}${source}.${sha8}`
|
||||
packageJSON.version = packageVersion
|
||||
core.setOutput("version", packageVersion)
|
||||
fs.writeFileSync(packageJSONPath, JSON.stringify(packageJSON))
|
||||
|
||||
93
.github/workflows/release.yml
vendored
93
.github/workflows/release.yml
vendored
@@ -8,6 +8,57 @@ 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:
|
||||
@@ -24,16 +75,17 @@ 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
|
||||
@@ -42,7 +94,7 @@ jobs:
|
||||
# AUTH0_USERNAME: ${{ secrets.AUTH0_USERNAME }}
|
||||
# AUTH0_PASSWORD: ${{ secrets.AUTH0_PASSWORD }}
|
||||
# TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
# TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||
# TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
# - name: Upload E2E artifacts
|
||||
# if: github.repository == 'nextauthjs/next-auth'
|
||||
# uses: actions/upload-artifact@v3
|
||||
@@ -73,6 +125,7 @@ jobs:
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: "pnpm"
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
- name: Publish to npm and GitHub
|
||||
@@ -97,6 +150,7 @@ jobs:
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: "pnpm"
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
- name: Determine version
|
||||
@@ -122,3 +176,36 @@ 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 }}
|
||||
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -6,6 +6,8 @@
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
packages/*/.npmrc
|
||||
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
@@ -38,6 +40,7 @@ packages/next-auth/next
|
||||
packages/*/*.js
|
||||
packages/*/*.d.ts
|
||||
packages/*/*.d.ts.map
|
||||
packages/*/lib
|
||||
|
||||
# Development app
|
||||
apps/dev/src/css
|
||||
@@ -64,6 +67,7 @@ packages/adapter-prisma/prisma/dev.db
|
||||
packages/adapter-prisma/prisma/migrations
|
||||
db.sqlite
|
||||
packages/adapter-supabase/supabase/.branches
|
||||
packages/adapter-drizzle/.drizzle
|
||||
|
||||
# Tests
|
||||
coverage
|
||||
@@ -96,5 +100,7 @@ packages/frameworks-sveltekit/vite.config.js.timestamp-*
|
||||
packages/frameworks-sveltekit/vite.config.ts.timestamp-*
|
||||
|
||||
# Adapters
|
||||
docs/docs/reference/adapter
|
||||
|
||||
docs/docs/reference/adapter
|
||||
## Drizzle migration folder
|
||||
.drizzle
|
||||
153
README.md
Normal file
153
README.md
Normal file
@@ -0,0 +1,153 @@
|
||||
<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
|
||||
@@ -13,6 +13,9 @@ AUTH0_ID=
|
||||
AUTH0_SECRET=
|
||||
AUTH0_ISSUER=
|
||||
|
||||
DESCOPE_ID=
|
||||
DESCOPE_SECRET=
|
||||
|
||||
KEYCLOAK_ID=
|
||||
KEYCLOAK_SECRET=
|
||||
KEYCLOAK_ISSUER=
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
This folder contains a Next.js app using NextAuth.js for local development. See the following section on how to start:
|
||||
|
||||
[Setting up local environment
|
||||
](https://github.com/nextauthjs/next-auth/blob/main/CONTRIBUTING.md#setting-up-local-environment)
|
||||
](https://github.com/nextauthjs/.github/blob/main/CONTRIBUTING.md#setting-up-local-environment)
|
||||
@@ -14,10 +14,10 @@
|
||||
},
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@next-auth/fauna-adapter": "workspace:*",
|
||||
"@next-auth/prisma-adapter": "workspace:*",
|
||||
"@next-auth/supabase-adapter": "workspace:*",
|
||||
"@next-auth/typeorm-legacy-adapter": "workspace:*",
|
||||
"@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",
|
||||
|
||||
@@ -37,22 +37,22 @@ import WorkOS from "next-auth/providers/workos"
|
||||
|
||||
// // Prisma
|
||||
// import { PrismaClient } from "@prisma/client"
|
||||
// import { PrismaAdapter } from "@next-auth/prisma-adapter"
|
||||
// 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 "@next-auth/fauna-adapter"
|
||||
// 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 { TypeORMLegacyAdapter } from "@next-auth/typeorm-legacy-adapter"
|
||||
// const adapter = TypeORMLegacyAdapter({
|
||||
// import { TypeORMAdapter } from "@auth/typeorm-adapter"
|
||||
// const adapter = TypeORMAdapter({
|
||||
// type: "sqlite",
|
||||
// name: "next-auth-test-memory",
|
||||
// database: "./typeorm/dev.db",
|
||||
@@ -60,7 +60,7 @@ import WorkOS from "next-auth/providers/workos"
|
||||
// })
|
||||
|
||||
// // Supabase
|
||||
// import { SupabaseAdapter } from "@next-auth/supabase-adapter"
|
||||
// import { SupabaseAdapter } from "@auth/supabase-adapter"
|
||||
// const adapter = SupabaseAdapter({
|
||||
// url: process.env.NEXT_PUBLIC_SUPABASE_URL,
|
||||
// secret: process.env.SUPABASE_SERVICE_ROLE_KEY,
|
||||
@@ -78,45 +78,130 @@ export const authOptions: NextAuthOptions = {
|
||||
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: "" }
|
||||
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 }),
|
||||
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" }),
|
||||
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 }),
|
||||
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 }),
|
||||
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 }),
|
||||
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 }),
|
||||
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 }),
|
||||
Wikimedia({
|
||||
clientId: process.env.WIKIMEDIA_ID,
|
||||
clientSecret: process.env.WIKIMEDIA_SECRET,
|
||||
}),
|
||||
WorkOS({
|
||||
clientId: process.env.WORKOS_ID,
|
||||
clientSecret: process.env.WORKOS_SECRET,
|
||||
}),
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ BEYOND_IDENTITY_CLIENT_ID=
|
||||
BEYOND_IDENTITY_CLIENT_SECRET=
|
||||
BEYOND_IDENTITY_ISSUER=
|
||||
|
||||
DESCOPE_ID=
|
||||
DESCOPE_SECRET=
|
||||
|
||||
GITHUB_ID=
|
||||
GITHUB_SECRET=
|
||||
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@auth/core": "workspace:*",
|
||||
"@next-auth/fauna-adapter": "workspace:*",
|
||||
"@next-auth/prisma-adapter": "workspace:*",
|
||||
"@next-auth/supabase-adapter": "workspace:*",
|
||||
"@next-auth/typeorm-legacy-adapter": "workspace:*",
|
||||
"@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": "13.4.0",
|
||||
"next-auth": "workspace:*",
|
||||
"nodemailer": "^6",
|
||||
"react": "^18",
|
||||
|
||||
@@ -2,14 +2,15 @@ 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"
|
||||
@@ -41,22 +42,22 @@ import WorkOS from "@auth/core/providers/workos"
|
||||
|
||||
// // Prisma
|
||||
// import { PrismaClient } from "@prisma/client"
|
||||
// import { PrismaAdapter } from "@next-auth/prisma-adapter"
|
||||
// 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 "@next-auth/fauna-adapter"
|
||||
// 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 { TypeORMLegacyAdapter } from "@next-auth/typeorm-legacy-adapter"
|
||||
// const adapter = TypeORMLegacyAdapter({
|
||||
// import { TypeORMAdapter } from "@auth/typeorm-adapter"
|
||||
// const adapter = TypeORMAdapter({
|
||||
// type: "sqlite",
|
||||
// name: "next-auth-test-memory",
|
||||
// database: "./typeorm/dev.db",
|
||||
@@ -64,7 +65,7 @@ import WorkOS from "@auth/core/providers/workos"
|
||||
// })
|
||||
|
||||
// // Supabase
|
||||
// import { SupabaseAdapter } from "@next-auth/supabase-adapter"
|
||||
// import { SupabaseAdapter } from "@auth/supabase-adapter"
|
||||
// const adapter = SupabaseAdapter({
|
||||
// url: process.env.NEXT_PUBLIC_SUPABASE_URL,
|
||||
// secret: process.env.SUPABASE_SERVICE_ROLE_KEY,
|
||||
@@ -85,8 +86,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 }),
|
||||
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 as string }),
|
||||
// Asgardeo({ clientId: process.env.ASGARDEO_CLIENT_ID, clientSecret: process.env.ASGARDEO_CLIENT_SECRET, issuer: process.env.ASGARDEO_ISSUER }),
|
||||
Auth0({ clientId: process.env.AUTH0_ID, clientSecret: process.env.AUTH0_SECRET, issuer: process.env.AUTH0_ISSUER }),
|
||||
AzureAD({
|
||||
clientId: process.env.AZURE_AD_CLIENT_ID,
|
||||
@@ -94,15 +95,20 @@ 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, redirectProxy: process.env.AUTH_REDIRECT_PROXY_URL }),
|
||||
GitHub({ clientId: process.env.GITHUB_ID, clientSecret: process.env.GITHUB_SECRET, redirectProxyUrl: process.env.AUTH_REDIRECT_PROXY_URL }),
|
||||
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 }),
|
||||
@@ -111,7 +117,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 }),
|
||||
Notion({ clientId: process.env.NOTION_ID, clientSecret: process.env.NOTION_SECRET, redirectUri: process.env.NOTION_REDIRECT_URI as string }),
|
||||
// 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 }),
|
||||
@@ -156,4 +162,4 @@ function AuthHandler(...args: any[]) {
|
||||
|
||||
export default AuthHandler(authConfig)
|
||||
|
||||
export const config = { runtime: "experimental-edge" }
|
||||
export const config = { runtime: "edge" }
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
"@sveltejs/kit": "next",
|
||||
"svelte": "3.55.0",
|
||||
"svelte-check": "2.10.2",
|
||||
"typescript": "4.9.4",
|
||||
"vite": "4.0.1"
|
||||
"typescript": "5.2.2",
|
||||
"vite": "4.0.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@auth/core": "workspace:*",
|
||||
|
||||
@@ -6,6 +6,9 @@ AUTH0_ID=
|
||||
AUTH0_SECRET=
|
||||
AUTH0_ISSUER=
|
||||
|
||||
DESCOPE_ID=
|
||||
DESCOPE_SECRET=
|
||||
|
||||
FACEBOOK_ID=
|
||||
FACEBOOK_SECRET=
|
||||
|
||||
|
||||
@@ -26,6 +26,6 @@
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.16.2",
|
||||
"@types/react": "^18.2.0",
|
||||
"typescript": "^5.0.4"
|
||||
"typescript": "5.2.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
<a href="https://docs-git-misc-docs-nextauthjs.vercel.app/configuration/nextjs#middleware">
|
||||
<a href="https://next-auth.js.org/configuration/nextjs#middleware">
|
||||
the docs
|
||||
</a>
|
||||
.
|
||||
|
||||
2
apps/examples/nextjs/process.d.ts
vendored
2
apps/examples/nextjs/process.d.ts
vendored
@@ -12,5 +12,7 @@ declare namespace NodeJS {
|
||||
GOOGLE_SECRET: string
|
||||
AUTH0_ID: string
|
||||
AUTH0_SECRET: string
|
||||
DESCOPE_ID: string
|
||||
DESCOPE_SECRET: string
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"solid-start-node": "^0.2.9",
|
||||
"solid-start-vercel": "^0.2.9",
|
||||
"tailwindcss": "^3.2.4",
|
||||
"typescript": "^4.8.3",
|
||||
"typescript": "5.2.2",
|
||||
"vite": "^3.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"@sveltejs/kit": "next",
|
||||
"svelte": "3.55.0",
|
||||
"svelte-check": "2.10.2",
|
||||
"typescript": "4.9.4",
|
||||
"typescript": "5.2.2",
|
||||
"vite": "4.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Session } from "@auth/core"
|
||||
import { Session } from "@auth/core/types"
|
||||
|
||||
export default function useSession() {
|
||||
return useState<Session | null>("session", () => null)
|
||||
|
||||
@@ -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("/api/auth/csrf")
|
||||
const { csrfToken } = await $fetch<{ csrfToken: string }>("/api/auth/csrf")
|
||||
|
||||
console.log(_signInUrl)
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { AuthHandler, AuthOptions, Session } from "@auth/core"
|
||||
import { AuthConfig, Session } from "@auth/core/types"
|
||||
import { Auth } from "@auth/core"
|
||||
import { fromNodeMiddleware, H3Event } from "h3"
|
||||
import getURL from "requrl"
|
||||
import { createMiddleware } from "@hattip/adapter-node"
|
||||
|
||||
export function NuxtAuthHandler(options: AuthOptions) {
|
||||
export function NuxtAuthHandler(options: AuthConfig) {
|
||||
async function handler(ctx: { request: Request }) {
|
||||
options.trustHost ??= true
|
||||
|
||||
return AuthHandler(ctx.request, options)
|
||||
return Auth(ctx.request, options)
|
||||
}
|
||||
|
||||
const middleware = createMiddleware(handler)
|
||||
@@ -17,7 +18,7 @@ export function NuxtAuthHandler(options: AuthOptions) {
|
||||
|
||||
export async function getSession(
|
||||
event: H3Event,
|
||||
options: AuthOptions
|
||||
options: AuthConfig
|
||||
): Promise<Session | null> {
|
||||
options.trustHost ??= true
|
||||
|
||||
@@ -30,7 +31,7 @@ export async function getSession(
|
||||
nodeHeaders.append(key, headers[key] as any)
|
||||
})
|
||||
|
||||
const response = await AuthHandler(
|
||||
const response = await Auth(
|
||||
new Request(url, { headers: nodeHeaders }),
|
||||
options
|
||||
)
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
{
|
||||
"name": "playground-nuxt",
|
||||
"name": "next-auth-nuxt",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "nuxt prepare && nuxt build",
|
||||
"dev": "nuxt prepare && export NODE_OPTIONS='--no-experimental-fetch' && nuxt dev",
|
||||
"build": "nuxt build",
|
||||
"dev": "nuxt prepare && nuxt dev",
|
||||
"generate": "nuxt generate",
|
||||
"preview": "nuxt preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxt/eslint-config": "^0.1.1",
|
||||
"eslint": "^8.29.0",
|
||||
"h3": "1.0.2",
|
||||
"nuxt": "3.0.0"
|
||||
"h3": "1.6.6",
|
||||
"nuxt": "3.5.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@auth/core": "workspace:*",
|
||||
"@hattip/adapter-node": "^0.0.22",
|
||||
"@hattip/adapter-node": "^0.0.34",
|
||||
"requrl": "^3.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Session } from "@auth/core"
|
||||
import { Session } from "@auth/core/types"
|
||||
|
||||
export default defineNuxtPlugin(async () => {
|
||||
const session = useSession()
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { NuxtAuthHandler } from "@/lib/auth/server"
|
||||
import GithubProvider from "@auth/core/providers/github"
|
||||
import type { AuthOptions } from "@auth/core"
|
||||
import type { AuthConfig } from "@auth/core"
|
||||
|
||||
const runtimeConfig = useRuntimeConfig()
|
||||
|
||||
export const authOptions: AuthOptions = {
|
||||
export const authOptions = {
|
||||
secret: runtimeConfig.secret,
|
||||
providers: [
|
||||
GithubProvider({
|
||||
@@ -12,6 +12,6 @@ export const authOptions: AuthOptions = {
|
||||
clientSecret: runtimeConfig.github.clientSecret,
|
||||
}),
|
||||
],
|
||||
}
|
||||
} as AuthConfig
|
||||
|
||||
export default NuxtAuthHandler(authOptions)
|
||||
|
||||
@@ -37,22 +37,31 @@ This documentation site is based on the [Docusaurus](https://docusaurus.io) fram
|
||||
|
||||
To start a local environment of this project, please do the following.
|
||||
|
||||
1. Clone the repository.
|
||||
1. Clone the repo:
|
||||
|
||||
```bash
|
||||
$ git clone https://github.com/nextauthjs/docs.git
|
||||
```sh
|
||||
git clone git@github.com:nextauthjs/next-auth.git
|
||||
cd next-auth
|
||||
```
|
||||
|
||||
2. Install dependencies
|
||||
2. Set up the correct pnpm version, using [Corepack](https://nodejs.org/api/corepack.html). Run the following in the project'a root:
|
||||
|
||||
```bash
|
||||
$ npm install
|
||||
```sh
|
||||
corepack enable pnpm
|
||||
```
|
||||
|
||||
3. Start the development server
|
||||
(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
|
||||
$ npm start
|
||||
$ pnpm dev:docs
|
||||
```
|
||||
|
||||
And thats all! Now you should have a local copy of this docs site running at [localhost:3000](http://localhost:3000)!
|
||||
|
||||
@@ -7,7 +7,7 @@ title: Frequently Asked Questions
|
||||
|
||||
### Is Auth.js commercial software?
|
||||
|
||||
Auth.js is an open source project built by individual contributors.
|
||||
Auth.js is an open-source project built by individual contributors.
|
||||
|
||||
It is not commercial software and is not associated with a commercial organization.
|
||||
|
||||
@@ -17,7 +17,7 @@ It is not commercial software and is not associated with a commercial organizati
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>What databases does Auth.js support?</h3>
|
||||
<h3 style={{display: "inline-block"}}>What databases does Auth.js support?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
@@ -30,7 +30,7 @@ You can use also Auth.js with any database using a custom database adapter, or b
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>What authentication services does Auth.js support?</h3>
|
||||
<h3 style={{display: "inline-block"}}>What authentication services does Auth.js support?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
@@ -38,16 +38,16 @@ You can use also Auth.js with any database using a custom database adapter, or b
|
||||
(See also: <a href="/reference/providers/oauth-builtin">Providers</a>)
|
||||
</p>
|
||||
|
||||
Auth.js also supports email for passwordless sign in, which is useful for account recovery or for people who are not able to use an account with the configured OAuth services (e.g. due to service outage, account suspension or otherwise becoming locked out of an account).
|
||||
Auth.js also supports email for passwordless sign-in, which is useful for account recovery or for people who are not able to use an account with the configured OAuth services (e.g. due to service outage, account suspension or otherwise becoming locked out of an account).
|
||||
|
||||
You can also use a custom based provider to support signing in with a username and password stored in an external database and/or using two factor authentication.
|
||||
You can also use a custom-based provider to support signing in with a username and password stored in an external database and/or using two-factor authentication.
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>Does Auth.js support signing in with a username and password?</h3>
|
||||
<h3 style={{display: "inline-block"}}>Does Auth.js support signing in with a username and password?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
@@ -55,44 +55,44 @@ Auth.js is designed to avoid the need to store passwords for user accounts.
|
||||
|
||||
If you have an existing database of usernames and passwords, you can use a custom credentials provider to allow signing in with a username and password stored in an existing database.
|
||||
|
||||
_If you use a custom credentials provider user accounts will not be persisted in a database by Auth.js (even if one is configured). The option to use JSON Web Tokens for session tokens (which allow sign in without using a session database) must be enabled to use a custom credentials provider._
|
||||
_If you use a custom credentials provider user accounts will not be persisted in a database by Auth.js (even if one is configured). The option to use JSON Web Tokens for session tokens (which allow sign-in without using a session database) must be enabled to use a custom credentials provider._
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>Can I use Auth.js with a website that does not use Next.js?</h3>
|
||||
<h3 style={{display: "inline-block"}}>Can I use Auth.js with a website that does not use Next.js?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
Auth.js is designed for use with Next.js and Serverless.
|
||||
|
||||
If you are using a different framework for your website, you can create a website that handles sign in with Next.js and then access those sessions on a website that does not use Next.js as long as the websites are on the same domain.
|
||||
If you are using a different framework for your website, you can create a website that handles sign-in with Next.js and then access those sessions on a website that does not use Next.js as long as the websites are on the same domain.
|
||||
|
||||
If you use Auth.js on a website with a different subdomain then the rest of your website (e.g. `auth.example.com` vs `www.example.com`) you will need to set a custom cookie domain policy for the Session Token cookie. (See also: [Cookies](/reference/configuration/auth-config#cookies))
|
||||
If you use Auth.js on a website with a different subdomain than the rest of your website (e.g. `auth.example.com` vs `www.example.com`) you will need to set a custom cookie domain policy for the Session Token cookie. (See also: [Cookies](/reference/configuration/auth-config#cookies))
|
||||
|
||||
Auth.js does not currently support automatically signing into sites on different top level domains (e.g. `www.example.com` vs `www.example.org`) using a single session.
|
||||
Auth.js does not currently support automatically signing into sites on different top-level domains (e.g. `www.example.com` vs `www.example.org`) using a single session.
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>Can I use Auth.js with React Native?</h3>
|
||||
<h3 style={{display: "inline-block"}}>Can I use Auth.js with React Native?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
Auth.js is designed as a secure, confidential client and implements a server side authentication flow.
|
||||
Auth.js is designed as a secure, confidential client and implements a server-side authentication flow.
|
||||
|
||||
It is not intended to be used in native applications on desktop or mobile applications, which typically implement public clients (e.g. with client / secrets embedded in the application).
|
||||
It is not intended to be used in native applications on desktop or mobile applications, which typically implement public clients (e.g. with client/secrets embedded in the application).
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>Is Auth.js supporting TypeScript?</h3>
|
||||
<h3 style={{display: "inline-block"}}>Is Auth.js supporting TypeScript?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
@@ -103,11 +103,11 @@ Yes! Check out the [TypeScript docs](/getting-started/typescript)
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>Is Auth.js compatible with Next.js 12 Middleware?</h3>
|
||||
<h3 style={{display: "inline-block"}}>Is Auth.js compatible with Next.js 12 Middleware?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
[Next.js Middleware](https://nextjs.org/docs/middleware) is supported. Head over to the [this page](/reference/nextjs/#middleware)
|
||||
[Next.js Middleware](https://nextjs.org/docs/middleware) is supported. Head over to [this page](https://next-auth.js.org/configuration/nextjs#middleware)
|
||||
|
||||
</p>
|
||||
</details>
|
||||
@@ -118,7 +118,7 @@ Yes! Check out the [TypeScript docs](/getting-started/typescript)
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>What databases are supported by Auth.js?</h3>
|
||||
<h3 style={{display: "inline-block"}}>What databases are supported by Auth.js?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
@@ -131,13 +131,13 @@ It also provides an Adapter API which allows you to connect it to any database.
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>What does Auth.js use databases for?</h3>
|
||||
<h3 style={{display: "inline-block"}}>What does Auth.js use databases for?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
Databases in Auth.js are used for persisting users, OAuth accounts, email sign in tokens and sessions.
|
||||
Databases in Auth.js are used for persisting users, OAuth accounts, email sign-in tokens and sessions.
|
||||
|
||||
Specifying a database is optional if you don't need to persist user data or support email sign in. If you don't specify a database then JSON Web Tokens will be enabled for session storage and used to store session data.
|
||||
Specifying a database is optional if you don't need to persist user data or support email sign-in. If you don't specify a database then JSON Web Tokens will be enabled for session storage and used to store session data.
|
||||
|
||||
If you are using a database with Auth.js, you can still explicitly enable JSON Web Tokens for sessions (instead of using database sessions).
|
||||
|
||||
@@ -146,24 +146,24 @@ If you are using a database with Auth.js, you can still explicitly enable JSON W
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>Should I use a database?</h3>
|
||||
<h3 style={{display: "inline-block"}}>Should I use a database?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
- Using Auth.js without a database works well for internal tools - where you need to control who is able to sign in, but when you do not need to create user accounts for them in your application.
|
||||
- Using Auth.js without a database works well for internal tools - where you need to control who can sign in, but when you do not need to create user accounts for them in your application.
|
||||
|
||||
- Using Auth.js with a database is usually a better approach for a consumer facing application where you need to persist accounts (e.g. for billing, to contact customers, etc).
|
||||
- Using Auth.js with a database is usually a better approach for a consumer-facing application where you need to persist accounts (e.g. for billing, to contact customers, etc).
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>What database should I use?</h3>
|
||||
<h3 style={{display: "inline-block"}}>What database should I use?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
Managed database solutions for MySQL, Postgres and MongoDB (and compatible databases) are well supported from cloud providers such as Amazon, Google, Microsoft and Atlas.
|
||||
Managed database solutions for MySQL, Postgres and MongoDB (and compatible databases) are well supported by cloud providers such as Amazon, Google, Microsoft and Atlas.
|
||||
|
||||
If you are deploying directly to a particular cloud platform you may also want to consider serverless database offerings they have (e.g. [Amazon Aurora Serverless on AWS](https://aws.amazon.com/rds/aurora/serverless/)).
|
||||
|
||||
@@ -174,51 +174,49 @@ If you are deploying directly to a particular cloud platform you may also want t
|
||||
|
||||
## Security
|
||||
|
||||
Parts of this section has been moved to its [own page](/security).
|
||||
Parts of this section have been moved to their [page](/security)](/security).
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>How do I get Refresh Tokens and Access Tokens for an OAuth account?</h3>
|
||||
<h3 style={{display: "inline-block"}}>How do I get Refresh Tokens and Access Tokens for an OAuth account?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
Auth.js provides a solution for authentication, session management and user account creation.
|
||||
|
||||
Auth.js records Refresh Tokens and Access Tokens on sign in (if supplied by the provider) and it will pass them, along with the User ID, Provider and Provider Account ID, to either:
|
||||
Auth.js records Refresh Tokens and Access Tokens on sign-in (if supplied by the provider) and it will pass them, along with the User ID, Provider and Provider Account ID, to either:
|
||||
|
||||
1. A database - if a database connection string is provided
|
||||
2. The JSON Web Token callback - if JWT sessions are enabled (e.g. if no database specified)
|
||||
2. The JSON Web Token callback - if JWT sessions are enabled (e.g. if no database is specified)
|
||||
|
||||
You can then look them up from the database or persist them to the JSON Web Token.
|
||||
|
||||
Note: Auth.js does not currently handle Access Token rotation for OAuth providers for you, however you can check out [this tutorial](/guides/basics/refresh-token-rotation) if you want to implement it.
|
||||
|
||||
We also have an [example repository](https://github.com/nextauthjs/next-auth-refresh-token-example) / project based upon Auth.js v4 where we demonstrate how to use a refresh token to refresh the provided access token.
|
||||
Note: Auth.js does not currently handle Access Token rotation for OAuth providers for you, however, you can check out [this tutorial](/guides/basics/refresh-token-rotation) if you want to implement it.
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>When I sign in with another account with the same email address, why are accounts not linked automatically?</h3>
|
||||
<h3 style={{display: "inline-block"}}>When I sign in with another account with the same email address, why are accounts not linked automatically?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
Automatic account linking on sign in is not secure between arbitrary providers - with the exception of allowing users to sign in via an email addresses as a fallback (as they must verify their email address as part of the flow).
|
||||
Automatic account linking on sign-in is not secure between arbitrary providers - except for allowing users to sign in via email addresses as a fallback (as they must verify their email address as part of the flow).
|
||||
|
||||
When an email address is associated with an OAuth account it does not necessarily mean that it has been verified as belonging to account holder — how email address verification is handled is not part of the OAuth specification and varies between providers (e.g. some do not verify first, some do verify first, others return metadata indicating the verification status).
|
||||
When an email address is associated with an OAuth account it does not necessarily mean that it has been verified as belonging to the account holder — how email address verification is handled is not part of the OAuth specification and varies between providers (e.g. some do not verify first, some do verify first, others return metadata indicating the verification status).
|
||||
|
||||
With automatic account linking on sign in, this can be exploited by bad actors to hijack accounts by creating an OAuth account associated with the email address of another user.
|
||||
With automatic account linking on sign-in, this can be exploited by bad parties to hijack accounts by creating an OAuth account associated with the email address of another user.
|
||||
|
||||
For this reason it is not secure to automatically link accounts between arbitrary providers on sign in, which is why this feature is generally not provided by authentication service and is not provided by Auth.js.
|
||||
For this reason, it is not secure to automatically link accounts between arbitrary providers on sign-in, which is why this feature is generally not provided by an authentication service and is not provided by Auth.js.
|
||||
|
||||
Automatic account linking is seen on some sites, sometimes insecurely. It can be technically possible to do automatic account linking securely if you trust all the providers involved to ensure they have securely verified the email address associated with the account, but requires placing trust (and transferring the risk) to those providers to handle the process securely.
|
||||
|
||||
Examples of scenarios where this is secure include with an OAuth provider you control (e.g. that only authorizes users internal to your organization) or with a provider you explicitly trust to have verified the users email address.
|
||||
Examples of scenarios where this is secure include an OAuth provider you control (e.g. that only authorizes users internal to your organization) or a provider you explicitly trust to have verified the users' email address.
|
||||
|
||||
Automatic account linking is not a planned feature of Auth.js, however there is scope to improve the user experience of account linking and of handling this flow, in a secure way. Typically this involves providing a fallback option to sign in via email, which is already possible (and recommended), but the current implementation of this flow could be improved on.
|
||||
Automatic account linking is not a planned feature of Auth.js, however, there is scope to improve the user experience of account linking and of handling this flow, securely. Typically this involves providing a fallback option to sign in via email, which is already possible (and recommended), but the current implementation of this flow could be improved.
|
||||
|
||||
Providing support for secure account linking and unlinking of additional providers - which can only be done if a user is already signed in already - was originally a feature in v1.x but has not been present since v2.0, is planned to return in a future release.
|
||||
Providing support for secure account linking and unlinking of additional providers - which can only be done if a user is already signed in - was originally a feature in v1.x but has not been present since v2.0, and is planned to return in a future release.
|
||||
|
||||
</p>
|
||||
</details>
|
||||
@@ -229,11 +227,11 @@ Providing support for secure account linking and unlinking of additional provide
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>Why doesn't Auth.js support [a particular feature]?</h3>
|
||||
<h3 style={{display: "inline-block"}}>Why doesn't Auth.js support [a particular feature]?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
Auth.js is an open source project built by individual contributors who are volunteers writing code and providing support in their spare time.
|
||||
Auth.js is an open-source project built by individual contributors who are volunteers writing code and providing support in their spare time.
|
||||
|
||||
If you would like Auth.js to support a particular feature, the best way to help make it happen is to raise a feature request describing the feature and offer to work with other contributors to develop and test it.
|
||||
|
||||
@@ -244,13 +242,13 @@ If you are not able to develop a feature yourself, you can offer to sponsor some
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style={{display:"inline-block"}}>I disagree with a design decision, how can I change your mind?</h3>
|
||||
<h3 style={{display: "inline-block"}}>I disagree with a design decision, how can I change your mind?</h3>
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
Product design decisions on Auth.js are made by core team members.
|
||||
|
||||
You can raise suggestions as feature requests / requests for enhancement.
|
||||
You can raise suggestions as feature requests for enhancement.
|
||||
|
||||
Requests that provide the detail requested in the template and follow the format requested may be more likely to be supported, as additional detail prompted in the templates often provides important context.
|
||||
|
||||
@@ -269,7 +267,7 @@ Ultimately if your request is not accepted or is not actively in development, yo
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
Auth.js by default uses JSON Web Tokens for saving the user's session. However, if you use a [database adapter](/guides/adapters/using-a-database-adapter), the database will be used to persist the user's session. You can force the usage of JWT when using a database [through the configuration options](/reference/configuration/auth-config#session). Since v4 all our JWT tokens are now encrypted by default with A256GCM.
|
||||
Auth.js by default uses JSON Web Tokens for saving the user's session. However, if you use a [database adapter](/guides/adapters/using-a-database-adapter), the database will be used to persist the user's session. You can force the usage of JWT when using a database [through the configuration options](/reference/configuration/auth-config#session). Since v4 all our JWTs are now encrypted by default with A256GCM.
|
||||
|
||||
</p>
|
||||
</details>
|
||||
@@ -286,7 +284,7 @@ JSON Web Tokens can be used for session tokens, but are also used for lots of ot
|
||||
|
||||
- JSON Web Tokens in Auth.js are secured using cryptographic encryption (JWE) to store the included information directly in a JWT session token. You may then use the token to pass information between services and APIs on the same domain without having to contact a database to verify the included information.
|
||||
|
||||
- You can use JWT to securely store information you do not mind the client knowing even without encryption, as the JWT is stored in a server-readable-only cookie so data in the JWT is not accessible to third party JavaScript running on your site.
|
||||
- You can use JWT to securely store information you do not mind the client knowing even without encryption, as the JWT is stored in a server-readable-only cookie so data in the JWT is not accessible to third-party JavaScript running on your site.
|
||||
|
||||
</p>
|
||||
</details>
|
||||
@@ -297,15 +295,15 @@ JSON Web Tokens can be used for session tokens, but are also used for lots of ot
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
- You cannot as easily expire a JSON Web Token - doing so requires maintaining a server side blocklist of invalid tokens (at least until they expire) and checking every token against the list every time a token is presented.
|
||||
- It's difficult to invalidate a JSON Web Token - doing so requires maintaining a server-side blocklist of the tokens (at least until they expire) and checking every token against the list every time a token is presented.
|
||||
|
||||
Shorter session expiry times are used when using JSON Web Tokens as session tokens to allow sessions to be invalidated sooner and simplify this problem.
|
||||
|
||||
Auth.js client includes advanced features to mitigate the downsides of using shorter session expiry times on the user experience, including automatic session token rotation, optionally sending keep alive messages to prevent short lived sessions from expiring if there is an window or tab open, background re-validation, and automatic tab/window syncing that keeps sessions in sync across windows any time session state changes or a window or tab gains or loses focus.
|
||||
Auth.js client includes advanced features to mitigate the downsides of using shorter session expiry times on the user experience, including automatic session token rotation, optionally sending keep-alive messages to prevent short-lived sessions from expiring if there is a window or tab opened, background re-validation, and automatic tab/window syncing that keeps sessions in sync across windows any time session state changes or a window or tab gains or loses focus.
|
||||
|
||||
- As with database session tokens, JSON Web Tokens are limited in the amount of data you can store in them. There is typically a limit of around 4096 bytes per cookie, though the exact limit varies between browsers, proxies and hosting services. If you want to support most browsers, then do not exceed 4096 bytes per cookie. If you want to save more data, you will need to persist your sessions in a database (Source: [browsercookielimits.iain.guru](http://browsercookielimits.iain.guru/))
|
||||
|
||||
The more data you try to store in a token and the more other cookies you set, the closer you will come to this limit. Since v4 we have implemented cookie chunking so that cookies over the 4kb limit get split and reassembled upon parsing. However since this data needs to be transmitted on every request, if you wish to store more than ~4 KB of data you're probably at the point where you want to store a unique ID in the token and persist the data elsewhere (e.g. in a server-side key/value store).
|
||||
The more data you try to store in a token and the more other cookies you set, the closer you will come to this limit. Auth.js uses cookie chunking so that cookies over the 4kb limit get split and reassembled upon parsing. However, since this data needs to be transmitted on every request, in case you wish to store more than ~4 KB of data you're probably at the point where you want to store a unique ID in the token and persist the data elsewhere (e.g. in a server-side key/value store).
|
||||
|
||||
- Data stored in an encrypted JSON Web Token (JWE) may be compromised at some point.
|
||||
|
||||
@@ -313,7 +311,7 @@ JSON Web Tokens can be used for session tokens, but are also used for lots of ot
|
||||
|
||||
Avoid storing any data in a token that might be problematic if it were to be decrypted in the future.
|
||||
|
||||
- If you do not explicitly specify a secret for for Auth.js, existing sessions will be invalidated any time your Auth.js configuration changes, as Auth.js will default to an auto-generated secret. Since v4 this only impacts development and generating a secret is required in production.
|
||||
- If you do not explicitly specify a secret for Auth.js, existing sessions will be invalidated any time your Auth.js configuration changes, as Auth.js will default to an auto-generated secret. Since v4 this only impacts development and generating a secret is required in production.
|
||||
|
||||
</p>
|
||||
</details>
|
||||
@@ -324,12 +322,10 @@ JSON Web Tokens can be used for session tokens, but are also used for lots of ot
|
||||
</summary>
|
||||
<p>
|
||||
|
||||
By default tokens are not signed (JWS) but are encrypted (JWE). Since v4 we have implemented cookie chunking so that cookies over the 4kb limit get split and reassembled upon parsing.
|
||||
By default, tokens are encrypted (JWE).
|
||||
|
||||
You can specify other valid algorithms - [as specified in RFC 7518](https://tools.ietf.org/html/rfc7517) - with either a secret (for symmetric encryption) or a public/private key pair (for asymmetric encryption).
|
||||
|
||||
Auth.js will generate keys for you, but this will generate a warning at start up.
|
||||
|
||||
Using explicit public/private keys for signing is strongly recommended.
|
||||
|
||||
</p>
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
title: Credentials authentication
|
||||
---
|
||||
|
||||
import Tabs from "@theme/Tabs"
|
||||
import TabItem from "@theme/TabItem"
|
||||
|
||||
Auth.js is built in a way that is flexible to integrate it with any authentication back-end you or your company may already have.
|
||||
|
||||
This library has been designed to handle the user session client-wise, to support multiple authentication methods (OAuth, Email, etc...) so that you're not forced to run your own authentication service.
|
||||
@@ -16,34 +19,49 @@ The functionality provided for credentials based authentication is intentionally
|
||||
|
||||
Integrating the Credentials Provider is as simple as initializing it in the Auth.js configuration file:
|
||||
|
||||
```ts title="pages/api/auth/[...nextauth].ts"
|
||||
import NextAuth from "next-auth"
|
||||
import CredentialsProvider from "next-auth/providers/credentials"
|
||||
<Tabs groupId="frameworks" queryString>
|
||||
<TabItem value="next" label="Next.js" default>
|
||||
|
||||
export default NextAuth({
|
||||
providers: [
|
||||
CredentialsProvider({
|
||||
async authorize(credentials) {
|
||||
const authResponse = await fetch("/users/login", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(credentials),
|
||||
})
|
||||
```ts title="pages/api/auth/[...nextauth].ts"
|
||||
import NextAuth from "next-auth"
|
||||
import CredentialsProvider from "next-auth/providers/credentials"
|
||||
|
||||
if (!authResponse.ok) {
|
||||
return null
|
||||
}
|
||||
export default NextAuth({
|
||||
providers: [
|
||||
CredentialsProvider({
|
||||
async authorize(credentials) {
|
||||
const authResponse = await fetch("/users/login", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(credentials),
|
||||
})
|
||||
|
||||
const user = await authResponse.json()
|
||||
if (!authResponse.ok) {
|
||||
return null
|
||||
}
|
||||
|
||||
return user
|
||||
},
|
||||
}),
|
||||
],
|
||||
})
|
||||
```
|
||||
const user = await authResponse.json()
|
||||
|
||||
return user
|
||||
},
|
||||
}),
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="sveltekit" label="SvelteKit">
|
||||
TODO SvelteKit
|
||||
</TabItem>
|
||||
<TabItem value="solidstart" label="SolidStart">
|
||||
TODO SolidStart
|
||||
</TabItem>
|
||||
<TabItem value="core" label="Vanilla (No Framework)">
|
||||
TODO Core
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
:::note
|
||||
Check the [Credentials Provider options](/reference/core/providers_credentials) for further customization
|
||||
|
||||
@@ -7,6 +7,8 @@ import startPageImg from "./img/email-tutorial-start.png"
|
||||
import checkPageImg from "./img/email-tutorial-check.png"
|
||||
import mailboxImg from "./img/email-tutorial-mailbox.png"
|
||||
import loggedInImg from "./img/email-tutorial-logged.png"
|
||||
import Tabs from "@theme/Tabs"
|
||||
import TabItem from "@theme/TabItem"
|
||||
|
||||
Aside from authenticating users in Auth.js via [OAuth](/getting-started/oauth-tutorial), you can also enable the option to authenticate them via "magic links". These are links that are sent to the user's email and when clicking on them they'll sign up the user automatically.
|
||||
|
||||
@@ -34,7 +36,7 @@ npm install -D nodemailer
|
||||
|
||||
## 2. Setting up a SMTP service
|
||||
|
||||
Next we need a [SMTP service](https://sendgrid.com/blog/what-is-an-smtp-server/) which will be in charge of sending emails from our application. There's a number of services available for this, however [here are the ones](http://nodemailer.com/smtp/well-known/) known to work with `nodemailer`.
|
||||
Next we need a [SMTP service](https://sendgrid.com/blog/what-is-an-smtp-server/) which will be in charge of sending emails from our application. There's a number of services available for this, however [here are the ones](https://community.nodemailer.com/2-0-0-beta/setup-smtp/well-known-services) known to work with `nodemailer`.
|
||||
|
||||
:::info
|
||||
For this tutorial, we're going to be using [Sendgrid](https://sendgrid.com/), but any of the services linked above should work the same
|
||||
@@ -64,26 +66,43 @@ Note that we're also specifying from which domain email are going to be sent fro
|
||||
|
||||
Nice! We're getting there. Now we need to read supply this values as the configuration for our Email Provider. Open `pages/api/auth/[...nextauth].ts` and do the following:
|
||||
|
||||
```ts title="pages/api/auth/[...nextauth].ts"
|
||||
import NextAuth from "next-auth"
|
||||
import Email from "next-auth/providers/email"
|
||||
<Tabs groupId="frameworks" queryString>
|
||||
<TabItem value="next" label="Next.js" default>
|
||||
|
||||
export default NextAuth({
|
||||
providers: [
|
||||
Email({
|
||||
server: {
|
||||
host: process.env.SMTP_HOST,
|
||||
port: Number(process.env.SMTP_PORT),
|
||||
auth: {
|
||||
user: process.env.SMTP_USER,
|
||||
pass: process.env.SMTP_PASSWORD,
|
||||
|
||||
```ts title="pages/api/auth/[...nextauth].ts"
|
||||
import NextAuth from "next-auth"
|
||||
import Email from "next-auth/providers/email"
|
||||
|
||||
export default NextAuth({
|
||||
providers: [
|
||||
Email({
|
||||
server: {
|
||||
host: process.env.SMTP_HOST,
|
||||
port: Number(process.env.SMTP_PORT),
|
||||
auth: {
|
||||
user: process.env.SMTP_USER,
|
||||
pass: process.env.SMTP_PASSWORD,
|
||||
},
|
||||
},
|
||||
},
|
||||
from: process.env.EMAIL_FROM,
|
||||
}),
|
||||
],
|
||||
})
|
||||
```
|
||||
from: process.env.EMAIL_FROM,
|
||||
}),
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="sveltekit" label="SvelteKit">
|
||||
TODO SvelteKit
|
||||
</TabItem>
|
||||
<TabItem value="solidstart" label="SolidStart">
|
||||
TODO SolidStart
|
||||
</TabItem>
|
||||
<TabItem value="core" label="Vanilla (No Framework)">
|
||||
TODO Core
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
## 3. Setting up an adapter
|
||||
|
||||
@@ -91,12 +110,12 @@ Finally, we'll need to set up a database adapter to store verification tokens th
|
||||
|
||||
An **Adapter** in Auth.js connects your application to whatever database or backend system you want to use to store data for users, their accounts, sessions, etc...
|
||||
|
||||
For this tutorial, we're going to use the **MongoDB** adapter, other any of the other adapters will work just fine.
|
||||
For this tutorial, we're going to use the **MongoDB** adapter, but any of the other adapters will work just fine.
|
||||
|
||||
First, let's start by installing the adapter package:
|
||||
|
||||
```bash npm2yarn2pnpm
|
||||
npm install -D @next-auth/mongodb-adapter mongodb
|
||||
npm install -D @auth/mongodb-adapter mongodb
|
||||
```
|
||||
|
||||
and create a simple MongoDB client:
|
||||
@@ -139,30 +158,46 @@ export default clientPromise
|
||||
|
||||
And now let's reference this new adapter from our Auth.js configuration file:
|
||||
|
||||
```diff title="pages/api/auth/[...nextauth].ts"
|
||||
import NextAuth from "next-auth"
|
||||
import EmailProvider from "next-auth/providers/email"
|
||||
+ import { MongoDBAdapter } from "@next-auth/mongodb-adapter"
|
||||
+ import clientPromise from "../../../lib/mongodb/client"
|
||||
<Tabs groupId="frameworks" queryString>
|
||||
<TabItem value="next" label="Next.js" default>
|
||||
|
||||
```diff title="pages/api/auth/[...nextauth].ts"
|
||||
import NextAuth from "next-auth"
|
||||
import EmailProvider from "next-auth/providers/email"
|
||||
+ import { MongoDBAdapter } from "@auth/mongodb-adapter"
|
||||
+ import clientPromise from "../../../lib/mongodb/client"
|
||||
|
||||
export default NextAuth({
|
||||
secret: process.env.NEXTAUTH_SECRET,
|
||||
+ adapter: MongoDBAdapter(clientPromise),
|
||||
providers: [
|
||||
EmailProvider({
|
||||
server: {
|
||||
host: process.env.EMAIL_SERVER_HOST,
|
||||
port: process.env.EMAIL_SERVER_PORT,
|
||||
auth: {
|
||||
user: process.env.EMAIL_SERVER_USER,
|
||||
pass: process.env.EMAIL_SERVER_PASSWORD
|
||||
}
|
||||
},
|
||||
from: process.env.EMAIL_FROM
|
||||
}),
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="sveltekit" label="SvelteKit">
|
||||
TODO SvelteKit
|
||||
</TabItem>
|
||||
<TabItem value="solidstart" label="SolidStart">
|
||||
TODO SolidStart
|
||||
</TabItem>
|
||||
<TabItem value="core" label="Vanilla (No Framework)">
|
||||
TODO Core
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
export default NextAuth({
|
||||
secret: process.env.NEXTAUTH_SECRET,
|
||||
+ adapter: MongoDBAdapter(clientPromise),
|
||||
providers: [
|
||||
EmailProvider({
|
||||
server: {
|
||||
host: process.env.EMAIL_SERVER_HOST,
|
||||
port: process.env.EMAIL_SERVER_PORT,
|
||||
auth: {
|
||||
user: process.env.EMAIL_SERVER_USER,
|
||||
pass: process.env.EMAIL_SERVER_PASSWORD
|
||||
}
|
||||
},
|
||||
from: process.env.EMAIL_FROM
|
||||
}),
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
## 4. Wiring all together
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ npm install next-auth
|
||||
```
|
||||
|
||||
:::info
|
||||
We are working on a new `@auth/nextjs` package that will make it easier to set up Auth.js with Next.js. Stay tuned! For now, you can use the `next-auth` package.
|
||||
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
|
||||
@@ -100,11 +100,12 @@ NextAuth.js provides [`useSession()`](/reference/react/#usesession) - a [React H
|
||||
|
||||
```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} />
|
||||
@@ -155,7 +156,7 @@ export default function CamperVanPage() {
|
||||
|
||||
### Protecting API Routes
|
||||
|
||||
To protect your API Routes (blocking unauthorized access to resources), you can use [`getServerSession()`](/reference/nextjs#getserversession) to know whether a session exists or not:
|
||||
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"
|
||||
@@ -217,7 +218,7 @@ The callback URL we insert should have the following pattern:
|
||||
|
||||
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">
|
||||
<Tabs groupId="frameworks" queryString>
|
||||
<TabItem value="next" label="Next.js" default>
|
||||
|
||||
```
|
||||
@@ -243,10 +244,13 @@ http://localhost:5173/auth/callback/github
|
||||
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**:
|
||||
@@ -270,7 +274,7 @@ Note that, for each provider, the configuration process will be similar to what
|
||||
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
|
||||
|
||||
@@ -285,7 +289,7 @@ GITHUB_SECRET=67890
|
||||
|
||||
Here is our server configuration file again:
|
||||
|
||||
<Tabs groupId="frameworks">
|
||||
<Tabs groupId="frameworks" queryString>
|
||||
<TabItem value="next" label="Next.js" default>
|
||||
|
||||
```ts title="pages/api/auth/[...nextauth].ts"
|
||||
|
||||
@@ -9,6 +9,9 @@ https://github.com/nextauthjs/next-auth-example
|
||||
|
||||
---
|
||||
|
||||
import Tabs from "@theme/Tabs"
|
||||
import TabItem from "@theme/TabItem"
|
||||
|
||||
## Adapters
|
||||
|
||||
If you're writing your own custom Adapter, you can take advantage of the types to make sure your implementation conforms to what's expected:
|
||||
@@ -46,30 +49,45 @@ This will work in code editors with a strong TypeScript integration like VSCode
|
||||
|
||||
Let's look at `Session`:
|
||||
|
||||
```ts title="pages/api/auth/[...nextauth].ts"
|
||||
import NextAuth from "next-auth"
|
||||
<Tabs groupId="frameworks" queryString>
|
||||
<TabItem value="next" label="Next.js" default>
|
||||
|
||||
export default NextAuth({
|
||||
callbacks: {
|
||||
session({ session, token, user }) {
|
||||
return session // The return type will match the one returned in `useSession()`
|
||||
```ts title="pages/api/auth/[...nextauth].ts"
|
||||
import NextAuth from "next-auth"
|
||||
|
||||
export default NextAuth({
|
||||
callbacks: {
|
||||
session({ session, token, user }) {
|
||||
return session // The return type will match the one returned in `useSession()`
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
})
|
||||
```
|
||||
|
||||
```ts title="pages/index.ts"
|
||||
import { useSession } from "next-auth/react"
|
||||
```ts title="pages/index.ts"
|
||||
import { useSession } from "next-auth/react"
|
||||
|
||||
export default function IndexPage() {
|
||||
// `session` will match the returned value of `callbacks.session()` from `NextAuth()`
|
||||
const { data: session } = useSession()
|
||||
export default function IndexPage() {
|
||||
// `session` will match the returned value of `callbacks.session()` from `NextAuth()`
|
||||
const { data: session } = useSession()
|
||||
|
||||
return (
|
||||
// Your component
|
||||
)
|
||||
}
|
||||
```
|
||||
return (
|
||||
// Your component
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="sveltekit" label="SvelteKit">
|
||||
TODO SvelteKit
|
||||
</TabItem>
|
||||
<TabItem value="solidstart" label="SolidStart">
|
||||
TODO SolidStart
|
||||
</TabItem>
|
||||
<TabItem value="core" label="Vanilla (No Framework)">
|
||||
TODO Core
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
To extend/augment this type, create a `types/next-auth.d.ts` file in your project:
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ Using a custom adapter you can connect to any database back-end or even several
|
||||
|
||||
## How to create an adapter
|
||||
|
||||
For more information about the data these methods need to manage see [models](/reference/adapters/models).
|
||||
For more information about the data these methods need to manage see [models](/reference/adapters#models).
|
||||
|
||||
_See the code below for practical example._
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
title: Using a database adapter
|
||||
---
|
||||
|
||||
An **Adapter** in Auth.js connects your application to whatever database or backend system you want to use to store data for users, their accounts, sessions, etc. Adapters are optional, unless you need to persist user information in your own database, or you want to implement certain flows. The [Email Provider](/getting-started/email-tutorial) requires an adapter to be able to save [Verification Tokens](/reference/adapters/models#verification-token).
|
||||
An **Adapter** in Auth.js connects your application to whatever database or backend system you want to use to store data for users, their accounts, sessions, etc. Adapters are optional, unless you need to persist user information in your own database, or you want to implement certain flows. The [Email Provider](/getting-started/email-tutorial) requires an adapter to be able to save [Verification Tokens](/reference/adapters#verification-token).
|
||||
|
||||
:::tip
|
||||
When using a database, you can still use JWT for session handling for fast access. See the [`session.strategy`](/reference/configuration/auth-config#session) option. Read about the trade-offs of JWT in the [FAQ](/concepts/faq#json-web-tokens).
|
||||
:::
|
||||
|
||||
We have a list of official adapters that are distributed as their own packages under the `@next-auth/{name}-adapter` namespace. Their source code is available in their various adapters package directories at [`nextauthjs/next-auth`](https://github.com/nextauthjs/next-auth/tree/main/packages):
|
||||
We have a list of official adapters that are distributed as their own packages under the `@auth/{name}-adapter` namespace. Their source code is available in their various adapters package directories at [`nextauthjs/next-auth`](https://github.com/nextauthjs/next-auth/tree/main/packages):
|
||||
|
||||
- [All available adapters](/reference/adapters)
|
||||
|
||||
@@ -12,7 +12,7 @@ If you want to pass data such as an Access Token or User ID to the browser when
|
||||
|
||||
You can specify a handler for any of the callbacks below.
|
||||
|
||||
```js title="pages/api/auth/[...nextauth].js"s
|
||||
```js title="auth.js"
|
||||
callbacks: {
|
||||
async signIn({ user, account, profile, email, credentials }) {
|
||||
return true
|
||||
@@ -35,7 +35,7 @@ The documentation below shows how to implement each callback, their default beha
|
||||
|
||||
Use the `signIn()` callback to control if a user is allowed to sign in.
|
||||
|
||||
```js title="pages/api/auth/[...nextauth].js"
|
||||
```js title="auth.js"
|
||||
callbacks: {
|
||||
async signIn({ user, account, profile, email, credentials }) {
|
||||
const isAllowedToSignIn = true
|
||||
@@ -79,7 +79,7 @@ By default only URLs on the same URL as the site are allowed, you can use the re
|
||||
|
||||
The default redirect callback looks like this:
|
||||
|
||||
```js title="pages/api/auth/[...nextauth].js"
|
||||
```js title="auth.js"
|
||||
callbacks: {
|
||||
async redirect({ url, baseUrl }) {
|
||||
// Allows relative callback URLs
|
||||
@@ -107,7 +107,7 @@ Requests to `/api/auth/signin`, `/api/auth/session` and calls to `getSession()`,
|
||||
|
||||
The contents _user_, _account_, _profile_ and _isNewUser_ will vary depending on the provider and on if you are using a database or not. You can persist data such as User ID, OAuth Access Token in this token. To make it available in the browser, check out the [`session()` callback](#session-callback) as well.
|
||||
|
||||
```js title="pages/api/auth/[...nextauth].js"
|
||||
```js title="auth.js"
|
||||
callbacks: {
|
||||
async jwt({ token, account }) {
|
||||
// Persist the OAuth access_token to the token right after signin
|
||||
@@ -132,7 +132,7 @@ e.g. `getSession()`, `useSession()`, `/api/auth/session`
|
||||
- When using database sessions, the User object is passed as an argument.
|
||||
- When using JSON Web Tokens for sessions, the JWT payload is provided instead.
|
||||
|
||||
```js title="pages/api/auth/[...nextauth].js"
|
||||
```js title="auth.js"
|
||||
callbacks: {
|
||||
async session({ session, token, user }) {
|
||||
// Send properties to the client, like an access_token from a provider.
|
||||
|
||||
@@ -34,7 +34,7 @@ Most OAuth providers cannot be configured with multiple callback URLs or using a
|
||||
|
||||
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`),
|
||||
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`)
|
||||
|
||||
@@ -42,6 +42,9 @@ However, Auth.js **supports Preview deployments**, even **with OAuth providers**
|
||||
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>
|
||||
|
||||
@@ -29,7 +29,7 @@ Sent when the user signs out.
|
||||
|
||||
The message object will contain one of these depending on if you use JWT or database persisted sessions:
|
||||
|
||||
- `token`: The JWT token for this session.
|
||||
- `token`: The JWT for this session.
|
||||
- `session`: The session object from your adapter that is being ended
|
||||
|
||||
### createUser
|
||||
@@ -60,5 +60,5 @@ Sent at the end of a request for the current session.
|
||||
|
||||
The message object will contain one of these depending on if you use JWT or database persisted sessions:
|
||||
|
||||
- `token`: The JWT token for this session.
|
||||
- `token`: The JWT for this session.
|
||||
- `session`: The session object from your adapter.
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
title: Custom Initialization
|
||||
---
|
||||
|
||||
import Tabs from "@theme/Tabs"
|
||||
import TabItem from "@theme/TabItem"
|
||||
|
||||
|
||||
<Tabs groupId="frameworks" queryString>
|
||||
<TabItem value="next" label="Next.js" default>
|
||||
|
||||
|
||||
In Next.js, you can define an API route that will catch all requests that begin with a certain path. Conveniently, this is called [Catch all API routes](https://nextjs.org/docs/api-routes/dynamic-api-routes#catch-all-api-routes).
|
||||
|
||||
When you define a `/pages/api/auth/[...nextauth]` JS/TS file, you instruct Auth.js that every API request beginning with `/api/auth/*` should be handled by the code written in the `[...nextauth]` file.
|
||||
@@ -120,3 +128,15 @@ This way of initializing `NextAuth` is very powerful, but should be used sparing
|
||||
:::warning
|
||||
Changing parts of the request that is essential to `NextAuth` to do it's job - like messing with the [default cookies](/reference/configuration/auth-config#cookies) - can have unforeseen consequences, and have the potential to introduce security holes if done incorrectly. Only change those if you understand consequences.
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="sveltekit" label="SvelteKit">
|
||||
TODO SvelteKit
|
||||
</TabItem>
|
||||
<TabItem value="solidstart" label="SolidStart">
|
||||
TODO SolidStart
|
||||
</TabItem>
|
||||
<TabItem value="core" label="Vanilla (No Framework)">
|
||||
TODO Core
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
@@ -4,10 +4,10 @@ sidebar_label: Custom JWT encoding
|
||||
---
|
||||
|
||||
:::warning
|
||||
If you use middleware to protect routes, make sure the same method is also set in the [`_middleware.ts` options](/reference/nextjs/#custom-jwt-decode-method)
|
||||
If you use middleware to protect routes, make sure the same method is also set in the [`middleware.ts` options](https://next-auth.js.org/configuration/nextjs#custom-jwt-decode-method)
|
||||
:::
|
||||
|
||||
Auth.js uses encrypted JSON Web Tokens ([JWE](https://datatracker.ietf.org/doc/html/rfc7516)) by default. Unless you have a good reason, we recommend keeping this behaviour. Although you can override this using the `encode` and `decode` methods. Both methods must be defined at the same time.
|
||||
Auth.js uses encrypted JSON Web Tokens ([JWE](https://datatracker.ietf.org/doc/html/rfc7516)) by default. Unless you have a good reason, we recommend keeping this behavior. Although you can override this using the `encode` and `decode` methods. Both methods must be defined at the same time.
|
||||
|
||||
```js
|
||||
jwt: {
|
||||
|
||||
@@ -2,13 +2,16 @@
|
||||
title: Pages
|
||||
---
|
||||
|
||||
import Tabs from "@theme/Tabs"
|
||||
import TabItem from "@theme/TabItem"
|
||||
|
||||
Auth.js automatically creates simple, unbranded authentication pages for handling Sign in, Sign out, Email Verification and displaying error messages.
|
||||
|
||||
The options displayed on the sign-up page are automatically generated based on the providers specified in the options passed to Auth.js.
|
||||
|
||||
To add a custom login page, you can use the `pages` option:
|
||||
|
||||
```javascript title="pages/api/auth/[...nextauth].js"
|
||||
```javascript title="auth.js"
|
||||
...
|
||||
pages: {
|
||||
signIn: '/auth/signin',
|
||||
@@ -76,6 +79,11 @@ In addition, you can define the background color and text color of the button wi
|
||||
|
||||
In order to get the available authentication providers and the URLs to use for them, you can make a request to the API endpoint `/api/auth/providers`:
|
||||
|
||||
|
||||
<Tabs groupId="frameworks" queryString>
|
||||
<TabItem value="next" label="Next.js" default>
|
||||
|
||||
|
||||
```jsx title="pages/auth/signin.js"
|
||||
import { getProviders, signIn } from "next-auth/react"
|
||||
|
||||
@@ -100,6 +108,18 @@ export async function getServerSideProps(context) {
|
||||
}
|
||||
}
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="sveltekit" label="SvelteKit">
|
||||
TODO SvelteKit
|
||||
</TabItem>
|
||||
<TabItem value="solidstart" label="SolidStart">
|
||||
TODO SolidStart
|
||||
</TabItem>
|
||||
<TabItem value="core" label="Vanilla (No Framework)">
|
||||
TODO Core
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
There is another, more fully styled example signin page available [here](https://github.com/ndom91/next-auth-example-sign-in-page).
|
||||
|
||||
@@ -107,6 +127,10 @@ There is another, more fully styled example signin page available [here](https:/
|
||||
|
||||
If you create a custom sign in form for email sign in, you will need to submit both fields for the **email** address and **csrfToken** from **/api/auth/csrf** in a POST request to **/api/auth/signin/email**.
|
||||
|
||||
<Tabs groupId="frameworks" queryString>
|
||||
<TabItem value="next" label="Next.js" default>
|
||||
|
||||
|
||||
```jsx title="pages/auth/email-signin.js"
|
||||
import { getCsrfToken } from "next-auth/react"
|
||||
|
||||
@@ -131,6 +155,18 @@ export async function getServerSideProps(context) {
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="sveltekit" label="SvelteKit">
|
||||
TODO SvelteKit
|
||||
</TabItem>
|
||||
<TabItem value="solidstart" label="SolidStart">
|
||||
TODO SolidStart
|
||||
</TabItem>
|
||||
<TabItem value="core" label="Vanilla (No Framework)">
|
||||
TODO Core
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
You can also use the `signIn()` function which will handle obtaining the CSRF token for you:
|
||||
|
||||
```js
|
||||
@@ -141,6 +177,10 @@ signIn("email", { email: "jsmith@example.com" })
|
||||
|
||||
If you create a sign in form for credentials based authentication, you will need to pass a **csrfToken** from **/api/auth/csrf** in a `POST` request to **/api/auth/callback/credentials**.
|
||||
|
||||
<Tabs groupId="frameworks" queryString>
|
||||
<TabItem value="next" label="Next.js" default>
|
||||
|
||||
|
||||
```jsx title="pages/auth/credentials-signin.js"
|
||||
import { getCsrfToken } from "next-auth/react"
|
||||
|
||||
@@ -170,6 +210,18 @@ export async function getServerSideProps(context) {
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="sveltekit" label="SvelteKit">
|
||||
TODO SvelteKit
|
||||
</TabItem>
|
||||
<TabItem value="solidstart" label="SolidStart">
|
||||
TODO SolidStart
|
||||
</TabItem>
|
||||
<TabItem value="core" label="Vanilla (No Framework)">
|
||||
TODO Core
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
You can also use the `signIn()` function which will handle obtaining the CSRF token for you:
|
||||
|
||||
```js
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
title: Refresh token rotation
|
||||
---
|
||||
|
||||
import Tabs from "@theme/Tabs"
|
||||
import TabItem from "@theme/TabItem"
|
||||
|
||||
Refresh token rotation is the practice of updating an `access_token` on behalf of the user, without requiring interaction (eg.: re-sign in). `access_token`s are usually issued for a limited time. After they expire, the service verifying them will ignore the value. Instead of asking the user to sign in again to obtain a new `access_token`, certain providers support exchanging a `refresh_token` for a new `access_token`, renewing the expiry time. Let's see how this can be achieved.
|
||||
|
||||
:::note
|
||||
@@ -26,6 +29,20 @@ Using the [jwt](../../reference/core/types#jwt) and [session](../../reference/co
|
||||
|
||||
Below is a sample implementation using Google's Identity Provider. Please note that the OAuth 2.0 request in the `refreshAccessToken()` function will vary between different providers, but the core logic should remain similar.
|
||||
|
||||
<Tabs groupId="frameworks" queryString>
|
||||
<TabItem value="next" label="Next.js">
|
||||
|
||||
TODO Next.js
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="sveltekit" label="SvelteKit">
|
||||
TODO SvelteKit
|
||||
</TabItem>
|
||||
<TabItem value="solidstart" label="SolidStart">
|
||||
TODO SolidStart
|
||||
</TabItem>
|
||||
<TabItem value="core" label="Vanilla (No Framework)" default>
|
||||
|
||||
```ts
|
||||
import { Auth } from "@auth/core"
|
||||
import { type TokenSet } from "@auth/core/types"
|
||||
@@ -109,15 +126,32 @@ declare module "@auth/core/jwt" {
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
#### Database strategy
|
||||
|
||||
Using the database strategy is very similar, but instead of preserving the `access_token` and `refresh_token`, we save it, well, in the database.
|
||||
|
||||
<Tabs groupId="frameworks" queryString>
|
||||
<TabItem value="next" label="Next.js">
|
||||
|
||||
What
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="sveltekit" label="SvelteKit">
|
||||
TODO SvelteKit
|
||||
</TabItem>
|
||||
<TabItem value="solidstart" label="SolidStart">
|
||||
TODO SolidStart
|
||||
</TabItem>
|
||||
<TabItem value="core" label="Vanilla (No Framework)" default>
|
||||
|
||||
```ts
|
||||
import { Auth } from "@auth/core"
|
||||
import { type TokenSet } from "@auth/core/types"
|
||||
import Google from "@auth/core/providers/google"
|
||||
import { PrismaAdapter } from "@next-auth/prisma-adapter"
|
||||
import { PrismaAdapter } from "@auth/prisma-adapter"
|
||||
import { PrismaClient } from "@prisma/client"
|
||||
|
||||
const prisma = new PrismaClient()
|
||||
@@ -195,6 +229,8 @@ declare module "@auth/core/jwt" {
|
||||
}
|
||||
}
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Client Side
|
||||
|
||||
|
||||
@@ -2,19 +2,19 @@
|
||||
title: Securing Pages & API routes
|
||||
---
|
||||
|
||||
You can easily protect client and server side rendered pages and API routes with Auth.js.
|
||||
You can protect client and server-side rendered pages and API routes with Auth.js.
|
||||
|
||||
_You can find working examples of the approaches shown below in the [example project](https://github.com/nextauthjs/next-auth-example/)._
|
||||
|
||||
:::tip
|
||||
The methods `getSession()` and `getToken()` both return an `object` if a session is valid and `null` if a session is invalid or has expired.
|
||||
The methods `getSession()` and `getToken()` both return an `object` if a session is valid and `null` if a session is not valid or has expired.
|
||||
:::
|
||||
|
||||
## Securing Pages
|
||||
|
||||
### Client Side
|
||||
|
||||
If data on a page is fetched using calls to secure API routes - i.e. routes which use `getSession()` or `getToken()` to access the session - you can use the `useSession` React Hook to secure pages.
|
||||
If data on a page is fetched using calls to secure API routes - i.e. routes that use `getSession(`)` or `getToken()` to access the session - you can use the `useSession` React Hook to secure pages.
|
||||
|
||||
```js title="pages/client-side-example.js"
|
||||
import { useSession, getSession } from "next-auth/react"
|
||||
@@ -41,7 +41,7 @@ export default function Page() {
|
||||
|
||||
### Next.js (Middleware)
|
||||
|
||||
With Auth.js 4.2.0 and Next.js 12, you can now protect your pages via the middleware pattern more easily. If you would like to protect all pages, you can create a `_middleware.js` file in your root `pages` directory which looks like this.
|
||||
With NextAuth.js 4.2.0 and Next.js 12, you can now protect your pages via the middleware pattern more easily. If you would like to protect all pages, you can create a `_middleware.js` file in your root `pages` directory which looks like this.
|
||||
|
||||
```js title="/middleware.js"
|
||||
export { default } from "next-auth/middleware"
|
||||
@@ -49,15 +49,15 @@ export { default } from "next-auth/middleware"
|
||||
|
||||
Otherwise, if you only want to protect a subset of pages, you could put it in a subdirectory as well, for example in `/pages/admin/_middleware.js` would protect all pages under `/admin`.
|
||||
|
||||
For the time being, the `withAuth` middleware only supports `"jwt"` as [session strategy](/reference/configuration/auth-config#session).
|
||||
For the time being, the `withAuth` middleware only supports `"jwt"` as a [session strategy](/reference/configuration/auth-config#session).
|
||||
|
||||
More details can be found [here](/reference/nextjs/#middleware).
|
||||
More details can be found [here](https://next-auth.js.org/configuration/nextjs#middleware).
|
||||
|
||||
### Server Side
|
||||
|
||||
You can protect server side rendered pages using the `unstable_getServerSession` method. This is different from the old `getSession()` method, in that it does not do an extra fetch out over the internet to confirm data from itself, increasing performance significantly.
|
||||
You can protect server-side rendered pages using the `unstable_getServerSession` method. This is different from the old `getSession()` method, in that it does not do an extra fetch out over the internet to confirm data from itself, increasing performance significantly.
|
||||
|
||||
You need to add this to every server rendered page you want to protect. Be aware, `unstable_getServerSession` takes slightly different arguments than the method it is replacing, `getSession`.
|
||||
You need to add this to every server-rendered page you want to protect. Be aware, `unstable_getServerSession` takes slightly different arguments than the method it is replacing, `getSession`.
|
||||
|
||||
```js title="pages/server-side-example.js"
|
||||
import { unstable_getServerSession } from "next-auth/next"
|
||||
@@ -136,7 +136,7 @@ export default async (req, res) => {
|
||||
|
||||
### Using getToken()
|
||||
|
||||
If you are using JSON Web Tokens you can use the `getToken()` helper to access the contents of the JWT without having to handle JWT decryption / verification yourself. This method can only be used server side.
|
||||
If you are using JSON Web Tokens you can use the `getToken()` helper to access the contents of the JWT without having to handle JWT decryption/verification yourself. This method can only be used server side.
|
||||
|
||||
```js title="pages/api/get-token-example.js"
|
||||
// This is an example of how to read a JSON Web Token from an API route
|
||||
@@ -157,7 +157,7 @@ export default async (req, res) => {
|
||||
```
|
||||
|
||||
:::tip
|
||||
You can use the `getToken()` helper function in any application as long as you set the `NEXTAUTH_URL` environment variable and the application is able to read the JWT cookie (e.g. is on the same domain).
|
||||
You can use the `getToken()` helper function in any application as long as you set the `NEXTAUTH_URL` environment variable and the application can read the JWT cookie (e.g. is on the same domain).
|
||||
:::
|
||||
|
||||
:::note
|
||||
|
||||
@@ -22,7 +22,7 @@ This can be done by simply returning a `200` response on `HEAD` requests at the
|
||||
|
||||
For example
|
||||
|
||||
```jsx title="/pages/api/auth/[...nextauth].js"
|
||||
```jsx title="auth.js"
|
||||
import type { NextApiRequest, NextApiResponse } from "next"
|
||||
import NextAuth from "next-auth"
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ index 77161bd..1082fba 100644
|
||||
|
||||
var _openidClient = require("openid-client");
|
||||
|
||||
+var HttpsProxyAgent = require("https-proxy-agent");
|
||||
+import { HttpsProxyAgent } from 'https-proxy-agent';
|
||||
+
|
||||
async function openidClient(options) {
|
||||
const provider = options.provider;
|
||||
|
||||
@@ -35,7 +35,7 @@ If you return an object it will be persisted to the JSON Web Token and the user
|
||||
|
||||
The Credentials provider's `authorize()` method also provides the request object as the second parameter (see the example below).
|
||||
|
||||
```js title="pages/api/auth/[...nextauth].js"
|
||||
```js title="auth.js"
|
||||
import CredentialsProvider from "next-auth/providers/credentials";
|
||||
...
|
||||
providers: [
|
||||
|
||||
@@ -17,15 +17,15 @@ We will also refer to the [Prisma Adapter](/reference/adapter/prisma). A [databa
|
||||
|
||||
## Setup
|
||||
|
||||
First, if you do not have a project using Auth.js, clone and set up a basic Auth.js project like the one [provided in](https://github.com/nextauthjs/next-auth-example.git) our example repo](https://github.com/nextauthjs/next-auth-example.git).
|
||||
First, if you do not have a project using Auth.js, clone and set up a basic Auth.js project like the one [provided in our example repo](https://github.com/nextauthjs/next-auth-example).
|
||||
|
||||
- Install the [Prisma Adapter](/reference/adapter/prisma)
|
||||
- Generate an API key from your cloud Email provider of choice and add it to your `.env.*` file. For example, mine is going to be called `SENDGRID_API`
|
||||
- Add the following configuration to your configuration file:
|
||||
|
||||
```js title="pages/api/auth/[...nextauth].ts"
|
||||
```js title="auth.ts"
|
||||
import NextAuth, { NextAuthOptions } from "next-auth"
|
||||
import { PrismaAdapter } from "@next-auth/prisma-adapter"
|
||||
import { PrismaAdapter } from "@auth/prisma-adapter"
|
||||
import { PrismaClient } from "@prisma/client"
|
||||
|
||||
const prisma = new PrismaClient()
|
||||
@@ -42,16 +42,15 @@ export const authOptions: NextAuthOptions = {
|
||||
],
|
||||
}
|
||||
|
||||
export default NextAuth(authOptions)
|
||||
```
|
||||
|
||||
Next, all that's left to do is call the HTTP endpoint from our cloud email provider and pass it the required metadata like the `to` address, the email `body`, and any other fields we may need to include.
|
||||
|
||||
As mentioned earlier, we're going to be using SendGrid in this example, so the appropriate endpoint is `https://api.sendgrid.com/v3/mail/send` ([more info](https://docs.sendgrid.com/for-developers/sending-email/api-getting-started)). Therefore, we're going to pull out some of the important information from the `params` argument and use it in a `fetch()` call to the previously mentioned SendGrid API.
|
||||
|
||||
```js title="pages/api/auth/[...nextauth].ts"
|
||||
```js title="auth.ts"
|
||||
import NextAuth, { NextAuthOptions } from "next-auth"
|
||||
import { PrismaAdapter } from "@next-auth/prisma-adapter"
|
||||
import { PrismaAdapter } from "@auth/prisma-adapter"
|
||||
import { PrismaClient } from "@prisma/client"
|
||||
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
@@ -30,7 +30,7 @@ You can override any of the options to suit your own use case.
|
||||
## Configuration
|
||||
|
||||
1. Auth.js does not include `nodemailer` as a dependency, so you'll need to install it yourself if you want to use the Email Provider. Run `npm install nodemailer` or `yarn add nodemailer`.
|
||||
2. You will need an SMTP account; ideally for one of the [services known to work with `nodemailer`](https://community.nodemailer.com/2-0-0-beta/setup-smtp/well-known-services/).
|
||||
2. You will need an SMTP account; such as [the official Nodemailer recommended service](https://nodemailer.com/about/#example) of [Forward Email](https://forwardemail.net).
|
||||
3. There are two ways to configure the SMTP server connection.
|
||||
|
||||
You can either use a connection string or a `nodemailer` configuration object.
|
||||
@@ -40,13 +40,13 @@ You can either use a connection string or a `nodemailer` configuration object.
|
||||
Create an `.env` file to the root of your project and add the connection string and email address.
|
||||
|
||||
```js title=".env" {1}
|
||||
EMAIL_SERVER=smtp://username:password@smtp.example.com:587
|
||||
EMAIL_FROM=noreply@example.com
|
||||
EMAIL_SERVER=smtp://username:password@smtp.forwardemail.net:587
|
||||
EMAIL_FROM=support@example.com
|
||||
```
|
||||
|
||||
Now you can add the email provider like this:
|
||||
|
||||
```js {3} title="pages/api/auth/[...nextauth].js"
|
||||
```js {3} title="auth.js"
|
||||
import EmailProvider from "next-auth/providers/email";
|
||||
...
|
||||
providers: [
|
||||
@@ -64,14 +64,14 @@ In your `.env` file in the root of your project simply add the configuration obj
|
||||
```js title=".env"
|
||||
EMAIL_SERVER_USER=username
|
||||
EMAIL_SERVER_PASSWORD=password
|
||||
EMAIL_SERVER_HOST=smtp.example.com
|
||||
EMAIL_SERVER_HOST=smtp.forwardemail.net
|
||||
EMAIL_SERVER_PORT=587
|
||||
EMAIL_FROM=noreply@example.com
|
||||
```
|
||||
|
||||
Now you can add the provider settings to the NextAuth options object in the Email Provider.
|
||||
|
||||
```js title="pages/api/auth/[...nextauth].js"
|
||||
```js title="auth.js"
|
||||
import EmailProvider from "next-auth/providers/email";
|
||||
...
|
||||
providers: [
|
||||
@@ -101,7 +101,7 @@ You can fully customize the sign in email that is sent by passing a custom funct
|
||||
|
||||
e.g.
|
||||
|
||||
```js {3} title="pages/api/auth/[...nextauth].js"
|
||||
```js {3} title="auth.js"
|
||||
import EmailProvider from "next-auth/providers/email";
|
||||
...
|
||||
providers: [
|
||||
@@ -112,6 +112,7 @@ providers: [
|
||||
identifier: email,
|
||||
url,
|
||||
provider: { server, from },
|
||||
request // for example can be used to get the user agent (`request.headers.get("user-agent")`) to parse and pass on to the user in the email so they can be more confident they originated the request
|
||||
}) {
|
||||
/* your function */
|
||||
},
|
||||
@@ -205,7 +206,7 @@ If you want to generate great looking email client compatible HTML with React, c
|
||||
|
||||
By default, we are generating a random verification token. You can define a `generateVerificationToken` method in your provider options if you want to override it:
|
||||
|
||||
```js title="pages/api/auth/[...nextauth].js"
|
||||
```js title="auth.js"
|
||||
providers: [
|
||||
EmailProvider({
|
||||
async generateVerificationToken() {
|
||||
|
||||
@@ -22,11 +22,18 @@ Next you will have to create some configuration files for Cypress.
|
||||
|
||||
First, the primary cypress config:
|
||||
|
||||
```js title="cypress.json"
|
||||
{
|
||||
"baseUrl": "http://localhost:3000",
|
||||
"chromeWebSecurity": false
|
||||
}
|
||||
```ts title="cypress.config.ts"
|
||||
import { defineConfig } from 'cypress'
|
||||
|
||||
export default defineConfig({
|
||||
e2e: {
|
||||
baseUrl: 'http://localhost:3000',
|
||||
chromeWebSecurity: false,
|
||||
setupNodeEvents(on, config) {
|
||||
// implement node event listeners here
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
This initial Cypress config will tell Cypress where to find your site on initial launch as well as allow it to open up URLs at domains that aren't your page, for example to be able to login to a social provider.
|
||||
@@ -46,14 +53,24 @@ You must change the login credentials you want to use, but you can also redefine
|
||||
|
||||
Third, if you're using the `cypress-social-login` plugin, you must add this to your `/cypress/plugins/index.js` file like so:
|
||||
|
||||
```js title="cypress/plugins/index.js"
|
||||
const { GoogleSocialLogin } = require("cypress-social-logins").plugins
|
||||
```js title="cypress.config.ts" {3-4,10-14}
|
||||
import { defineConfig } from 'cypress'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const { GoogleSocialLogin } = require('cypress-social-logins').plugins
|
||||
|
||||
export default defineConfig({
|
||||
e2e: {
|
||||
baseUrl: 'http://localhost:3000',
|
||||
chromeWebSecurity: false,
|
||||
setupNodeEvents(on, config) {
|
||||
on('task', {
|
||||
GoogleSocialLogin,
|
||||
})
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
module.exports = (on, config) => {
|
||||
on("task", {
|
||||
GoogleSocialLogin: GoogleSocialLogin,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
Finally, you can also add the following npm scripts to your `package.json`:
|
||||
@@ -110,10 +127,6 @@ describe("Login page", () => {
|
||||
secure: cookie.secure,
|
||||
})
|
||||
|
||||
Cypress.Cookies.defaults({
|
||||
preserve: cookieName,
|
||||
})
|
||||
|
||||
// remove the two lines below if you need to stay logged in
|
||||
// for your remaining tests
|
||||
cy.visit("/api/auth/signout")
|
||||
|
||||
@@ -2,12 +2,16 @@
|
||||
title: Overview
|
||||
---
|
||||
|
||||
Using a Auth.js / NextAuth.js adapter you can connect to any database service or even several different services at the same time. The following listed official adapters are created and maintained by the community:
|
||||
Using an Auth.js / NextAuth.js adapter you can connect to any database service or even several different services at the same time. The following listed official adapters are created and maintained by the community:
|
||||
|
||||
<div class="adapter-card-list">
|
||||
<a href="/reference/adapter/dgraph" class="adapter-card">
|
||||
<img src="/img/adapters/dgraph.png" width="30" />
|
||||
<h4 class="adapter-card__title">Dgraph Adapter</h4>
|
||||
</a>
|
||||
<a href="/reference/adapter/drizzle" class="adapter-card">
|
||||
<img src="/img/adapters/drizzle-orm.png" width="30" />
|
||||
<h4 class="adapter-card__title">Drizzle Adapter</h4>
|
||||
</a>
|
||||
<a href="/reference/adapter/dynamodb" class="adapter-card">
|
||||
<img src="/img/adapters/dynamodb.png" width="30" />
|
||||
@@ -21,6 +25,10 @@ Using a Auth.js / NextAuth.js adapter you can connect to any database service or
|
||||
<img src="/img/adapters/firebase.svg" width="40" />
|
||||
<h4 class="adapter-card__title">Firebase Adapter</h4>
|
||||
</a>
|
||||
<a href="/reference/adapter/kysely" class="adapter-card">
|
||||
<img src="/img/adapters/kysely.svg" width="40" />
|
||||
<h4 class="adapter-card__title">Kysely Adapter</h4>
|
||||
</a>
|
||||
<a href="/reference/adapter/mikro-orm" class="adapter-card">
|
||||
<img src="/img/adapters/mikro-orm.png" width="30" />
|
||||
<h4 class="adapter-card__title">Mikro ORM Adapter</h4>
|
||||
@@ -67,11 +75,9 @@ Using a Auth.js / NextAuth.js adapter you can connect to any database service or
|
||||
If you don't find an adapter for the database or service you use, you can always create one yourself. Have a look at our guide on [how to create a database adapter](/guides/adapters/creating-a-database-adapter).
|
||||
:::
|
||||
|
||||
|
||||
## Models
|
||||
|
||||
|
||||
Auth.js can be used with any database. Models tell you what structures Auth.js expects from your database. Models will vary slightly depending on which adapter you use, but in general, will look something like this. Each adapter's model/schema will be slightly adapted for its needs, but will look very much like this schema below:
|
||||
Auth.js can be used with any database. Models tell you what structures Auth.js expects from your database. Models will vary slightly depending on which adapter you use, but in general, will look something like this:
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
@@ -103,8 +109,6 @@ erDiagram
|
||||
string scope
|
||||
string id_token
|
||||
string session_state
|
||||
string oauth_token_secret
|
||||
string oauth_token
|
||||
}
|
||||
VerificationToken {
|
||||
string identifier
|
||||
@@ -113,10 +117,10 @@ erDiagram
|
||||
}
|
||||
```
|
||||
|
||||
More information about each Model / Table can be found below.
|
||||
More information about each Model/Table can be found below.
|
||||
|
||||
:::note
|
||||
You can [create your own adapter](/guides/adapters/creating-a-database-adapter) if you want to use Auth.js with a database that is not supported out of the box, or you have to change fields on any of the models.
|
||||
You can [create your adapter](/guides/adapters/creating-a-database-adapter) if you want to use Auth.js with a database that is not supported out of the box, or you have to change fields on any of the models.
|
||||
:::
|
||||
|
||||
---
|
||||
@@ -125,30 +129,31 @@ You can [create your own adapter](/guides/adapters/creating-a-database-adapter)
|
||||
|
||||
The User model is for information such as the user's name and email address.
|
||||
|
||||
Email address is optional, but if one is specified for a User then it must be unique.
|
||||
Email address is optional, but if one is specified for a User, then it must be unique.
|
||||
|
||||
:::note
|
||||
If a user first signs in with OAuth then their email address is automatically populated using the one from their OAuth profile, if the OAuth provider returns one.
|
||||
If a user first signs in with an OAuth provider, then their email address is automatically populated using the one from their OAuth profile if the OAuth provider returns one.
|
||||
|
||||
This provides a way to contact users and for users to maintain access to their account and sign in using email in the event they are unable to sign in with the OAuth provider in future (if the [Email Provider](/getting-started/email-tutorial) is configured).
|
||||
This provides a way to contact users and for users to maintain access to their account and sign in using email in the event they are unable to sign in with the OAuth provider in the future (if the [Email Provider](/reference/core/providers_email) is configured).
|
||||
:::
|
||||
|
||||
User creation in the database is automatic, and happens when the user is logging in for the first time with a provider. The default data saved is `id`, `name`, `email` and `image`. You can add more profile data by returning extra fields in your [OAuth provider](/guides/providers/custom-provider)'s [`profile()`](/reference/core/providers#profile) callback.
|
||||
User creation in the database is automatic and happens when the user is logging in for the first time with a provider.
|
||||
If the first sign-in is via the [OAuth Provider](/reference/core/providers_oauth), the default data saved is `id`, `name`, `email` and `image`. You can add more profile data by returning extra fields in your [OAuth provider](/guides/providers/custom-provider)'s [`profile()`](/reference/core/providers#profile) callback.
|
||||
|
||||
If the first sign-in is via the [Email Provider](/reference/core/providers_email), then the saved user will have `id`, `email`, `emailVerified`, where `emailVerified` is the timestamp of when the user was created.
|
||||
|
||||
### Account
|
||||
|
||||
The Account model is for information about OAuth accounts associated with a User. It will usually contain `access_token`, `id_token` and other OAuth specific data. [`TokenSet`](https://github.com/panva/node-openid-client/blob/main/docs/README.md#new-tokensetinput) from `openid-client` might give you an idea of all the fields.
|
||||
|
||||
:::note
|
||||
In case of an OAuth 1.0 provider (like Twitter), you will have to look for `oauth_token` and `oauth_token_secret` string fields. GitHub also has an extra `refresh_token_expires_in` integer field. You have to make sure that your database schema includes these fields.
|
||||
:::
|
||||
The Account model is for information about OAuth accounts associated with a User
|
||||
|
||||
A single User can have multiple Accounts, but each Account can only have one User.
|
||||
|
||||
Linking Accounts to Users happen automatically, only when they have the same e-mail address, and the user is currently signed in. Check the [FAQ](/concepts/faq#security) for more information why this is a requirement.
|
||||
Account creation in the database is automatic and happens when the user is logging in for the first time with a provider, or the [`Adapter.linkAccount`](/reference/core/adapters#linkaccount) method is invoked. The default data saved is `access_token`, `expires_at`, `refresh_token`, `id_token`, `token_type`, `scope` and `session_state`. You can save other fields or remove the ones you don't need by returning them in the [OAuth provider](/guides/providers/custom-provider)'s [`account()`](/reference/core/providers#account) callback.
|
||||
|
||||
Linking Accounts to Users happen automatically, only when they have the same e-mail address, and the user is currently signed in. Check the [FAQ](/concepts/faq#security) for more information on why this is a requirement.
|
||||
|
||||
:::tip
|
||||
You can manually unlink accounts, if your adapter implements the `unlinkAccount` method. Make sure to take all the necessary security steps to avoid data loss.
|
||||
You can manually unlink accounts if your adapter implements the `unlinkAccount` method. Make sure to take all the necessary security steps to avoid data loss.
|
||||
:::
|
||||
|
||||
:::note
|
||||
@@ -162,7 +167,7 @@ The Session model is used for database sessions. It is not used if JSON Web Toke
|
||||
A single User can have multiple Sessions, each Session can only have one User.
|
||||
|
||||
:::tip
|
||||
When a Session is read, we check if it's `expires` field indicates an invalid session, and delete it from the database. You can also do this clean-up periodically in the background to avoid our extra delete call to the database during an active session retrieval. This might result in a slight performance increase in a few cases.
|
||||
When a Session is read, we check if its `expires` field indicates an invalid session, and delete it from the database. You can also do this clean-up periodically in the background to avoid our extra delete call to the database during an active session retrieval. This might result in a slight performance increase in a few cases.
|
||||
:::
|
||||
|
||||
### Verification Token
|
||||
@@ -171,7 +176,7 @@ The Verification Token model is used to store tokens for passwordless sign in.
|
||||
|
||||
A single User can have multiple open Verification Tokens (e.g. to sign in to different devices).
|
||||
|
||||
It has been designed to be extendable for other verification purposes in the future (e.g. 2FA / short codes).
|
||||
It has been designed to be extendable for other verification purposes in the future (e.g. 2FA / magic codes, etc.).
|
||||
|
||||
:::note
|
||||
Auth.js makes sure that every token is usable only once, and by default has a short (1 day, can be configured by [`maxAge`](/guides/providers/email)) lifetime. If your user did not manage to finish the sign-in flow in time, they will have to start the sign-in process again.
|
||||
@@ -183,8 +188,7 @@ Due to users forgetting or failing at the sign-in flow, you might end up with un
|
||||
|
||||
## RDBMS Naming Convention
|
||||
|
||||
Auth.js / NextAuth.js uses `camelCase` for its own database rows, while respecting the conventional `snake_case` formatting for OAuth related values. If mixed casing is an issue for you, most adapters have a dedicated section on how to use a single naming convention.
|
||||
|
||||
Auth.js / NextAuth.js uses `camelCase` for its database rows while respecting the conventional `snake_case` formatting for OAuth-related values. If the mixed casing is an issue for you, most adapters have a dedicated documentation section on how to force a casing convention.
|
||||
|
||||
## TypeScript
|
||||
|
||||
|
||||
@@ -4,29 +4,30 @@ title: Overview
|
||||
|
||||
This section of the documentation contains the API reference for all the official packages under the `@auth/*` and `@next-auth/*` scopes.
|
||||
|
||||
:::warning Warning
|
||||
The API reference is being migrated from the [old documentation page](https://next-auth.js.org), so there are going to be references to `next-auth` still. We are continuously working on updating the naming/references.
|
||||
:::
|
||||
|
||||
## Roadmap
|
||||
|
||||
Here are the _currently_ planned and released packages under the `@auth/*` scope. This is not an exhaustive list, but the set of packages that we would like to focus on to begin with.
|
||||
Here are the _state_ of planned and released packages under the `@auth/*` scope. This is not an exhaustive list, but the set of packages that we would like to focus on, to begin with.
|
||||
|
||||
| Feature | Status |
|
||||
| ------------------- | -------- |
|
||||
| `@auth/nextjs` | Planned |
|
||||
| `@auth/*-adapter` | Planned |
|
||||
| `@auth/core` | Experimental |
|
||||
| `@auth/sveltekit` | Experimental |
|
||||
| `@auth/solid-start` | Experimental |
|
||||
| Feature | Status |
|
||||
| ---------------------- | -------- |
|
||||
| `@auth/*-adapter` | Released (stable). Fully compatible with `next-auth` and all `@auth/*` libraries. |
|
||||
| `@next-auth/*-adapter` | Maintenance has stopped. Update to `@auth/*-adapter`. See above. |
|
||||
| `@auth/core` | Released (experimental). |
|
||||
| `@auth/sveltekit` | Released (experimental, [help needed](#help-needed)). |
|
||||
| `@auth/solid-start` | Released (experimental, [help needed](#help-needed)). Community package: [`@solid-mediakit/auth`](https://www.npmjs.com/package/@solid-mediakit/auth) |
|
||||
| `@auth/express` | [Planned](https://github.com/nextauthjs/next-auth/issues/8257). |
|
||||
| `@auth/remix` | Planned, [help needed](#help-needed). |
|
||||
| `@auth/astro` | Planned, [help needed](#help-needed). |
|
||||
| `@auth/nuxt` | Planned, [help needed](#help-needed). Community packages: [`@sidebase/nuxt-auth`](https://github.com/sidebase/nuxt-auth), [`@hebilicious/authjs-nuxt`](https://authjs-nuxt.pages.dev/) |
|
||||
|
||||
### Community Packages
|
||||
|
||||
While we are migrating the documentation and working on stabilizing the core package, the community has been working on some packages that are already available. With collaboration, we hope to make these packages official in the future.
|
||||
|
||||
:::note
|
||||
If you are a maintainer of a package, [reach out](https://twitter.com/balazsorban44) if you want to collaborate on making it official or open a PR to add it to the list below, so others can discover it more easily.
|
||||
:::info
|
||||
`next-auth` is still the official package for Next.js. The documentation is at [next-auth.js.org](https://next-auth.js.org), while guides are being migrated over to the new documentation page. A major refactor of `next-auth` is on the way, you can [follow this PR](https://github.com/nextauthjs/next-auth/pull/7443) for updates.
|
||||
:::
|
||||
|
||||
- ...
|
||||
- ...
|
||||
### Help needed
|
||||
|
||||
In case you are a maintainer of a package that uses `@auth/core`, feel free to [reach out to Balázs](https://twitter.com/balazsorban44), if you want to collaborate on making it an official package, maintained in our repository. If you are interested in bringing `@auth/core` support to your favorite framework, we would love to hear from you!
|
||||
|
||||
#### Community Packages
|
||||
|
||||
While we are migrating the documentation and working on stabilizing the core package, the community has been working on some packages that are already available. With collaboration, we hope to make these packages official in the future.
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
title: Client
|
||||
---
|
||||
|
||||
:::warning WIP
|
||||
`@auth/nextjs/client` is work in progress. For now, please use [NextAuth.js Client API](https://next-auth.js.org/getting-started/client).
|
||||
:::
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
title: Next.js Auth
|
||||
---
|
||||
|
||||
:::warning WIP
|
||||
`@auth/nextjs` is work in progress. For now, please use [NextAuth.js](https://next-auth.js.org).
|
||||
:::
|
||||
@@ -7,7 +7,7 @@ const path = require("path")
|
||||
const coreSrc = "../packages/core/src"
|
||||
const providers = fs
|
||||
.readdirSync(path.join(__dirname, coreSrc, "/providers"))
|
||||
.filter((file) => file.endsWith(".ts") && !file.startsWith("oauth"))
|
||||
.filter((file) => file.endsWith(".ts"))
|
||||
.map((p) => `${coreSrc}/providers/${p}`)
|
||||
|
||||
const typedocConfig = require("./typedoc.json")
|
||||
@@ -213,7 +213,25 @@ const docusaurusConfig = {
|
||||
breadcrumbs: false,
|
||||
routeBasePath: "/",
|
||||
sidebarPath: require.resolve("./sidebars.js"),
|
||||
editUrl: "https://github.com/nextauthjs/next-auth/edit/main/docs",
|
||||
/**
|
||||
*
|
||||
* @param {{
|
||||
* version: string;
|
||||
* versionDocsDirPath: string;
|
||||
* docPath: string;
|
||||
* permalink: string;
|
||||
* locale: string;
|
||||
*}} params
|
||||
*/
|
||||
editUrl({ docPath }) {
|
||||
// TODO: support other packages, fix directory links like "providers"
|
||||
if (docPath.includes("reference/core")) {
|
||||
const file = docPath.split("reference/core/")[1].replace(".md", ".ts").replace("_", "/")
|
||||
const base = `https://github.com/nextauthjs/next-auth/edit/main/packages/core/src/${file}`
|
||||
return base
|
||||
}
|
||||
return "https://github.com/nextauthjs/next-auth/edit/main/docs"
|
||||
},
|
||||
lastVersion: "current",
|
||||
showLastUpdateAuthor: true,
|
||||
showLastUpdateTime: true,
|
||||
@@ -265,27 +283,17 @@ const docusaurusConfig = {
|
||||
? []
|
||||
: [
|
||||
typedocAdapter("Dgraph"),
|
||||
typedocAdapter("Drizzle"),
|
||||
typedocAdapter("DynamoDB"),
|
||||
typedocAdapter("Fauna"),
|
||||
typedocAdapter("Firebase"),
|
||||
typedocAdapter("Kysely"),
|
||||
typedocAdapter("Mikro ORM"),
|
||||
typedocAdapter("MongoDB"),
|
||||
typedocAdapter("Neo4j"),
|
||||
typedocAdapter("PouchDB"),
|
||||
typedocAdapter("Prisma"),
|
||||
[
|
||||
"docusaurus-plugin-typedoc",
|
||||
{
|
||||
...typedocConfig,
|
||||
id: "typeorm",
|
||||
plugin: [require.resolve("./typedoc-mdn-links")],
|
||||
watch: process.env.TYPEDOC_WATCH,
|
||||
entryPoints: [`../packages/adapter-typeorm-legacy/src/index.ts`],
|
||||
tsconfig: `../packages/adapter-typeorm-legacy/tsconfig.json`,
|
||||
out: `reference/adapter/typeorm`,
|
||||
sidebar: { indexLabel: "TypeORM" },
|
||||
},
|
||||
],
|
||||
typedocAdapter("TypeORM"),
|
||||
typedocAdapter("Sequelize"),
|
||||
typedocAdapter("Supabase"),
|
||||
typedocAdapter("Upstash Redis"),
|
||||
|
||||
@@ -27,15 +27,15 @@
|
||||
"styled-components": "5.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/core": "2.3.1",
|
||||
"@docusaurus/eslint-plugin": "2.3.1",
|
||||
"@docusaurus/module-type-aliases": "2.3.1",
|
||||
"@docusaurus/preset-classic": "2.3.1",
|
||||
"@docusaurus/theme-common": "2.3.1",
|
||||
"@docusaurus/theme-mermaid": "2.3.1",
|
||||
"@docusaurus/types": "2.3.1",
|
||||
"@docusaurus/core": "2.4.1",
|
||||
"@docusaurus/eslint-plugin": "2.4.1",
|
||||
"@docusaurus/module-type-aliases": "2.4.1",
|
||||
"@docusaurus/preset-classic": "2.4.1",
|
||||
"@docusaurus/theme-common": "2.4.1",
|
||||
"@docusaurus/theme-mermaid": "2.4.1",
|
||||
"@docusaurus/types": "2.4.1",
|
||||
"docusaurus-plugin-typedoc": "1.0.0-next.5",
|
||||
"typedoc": "^0.24.4",
|
||||
"typedoc": "^0.24.8",
|
||||
"typedoc-plugin-markdown": "4.0.0-next.6"
|
||||
},
|
||||
"browserslist": {
|
||||
|
||||
@@ -34,17 +34,9 @@ module.exports = {
|
||||
items: [{ type: "autogenerated", dirName: "reference/solidstart" }],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "@auth/nextjs",
|
||||
link: { type: "doc", id: "reference/nextjs/index" },
|
||||
items: [
|
||||
"reference/nextjs/client",
|
||||
{
|
||||
type: "link",
|
||||
label: "NextAuth.js (next-auth)",
|
||||
href: "https://next-auth.js.org",
|
||||
},
|
||||
],
|
||||
type: "link",
|
||||
label: "NextAuth.js (next-auth)",
|
||||
href: "https://next-auth.js.org",
|
||||
},
|
||||
...(process.env.TYPEDOC_SKIP_ADAPTERS
|
||||
? []
|
||||
@@ -55,9 +47,11 @@ module.exports = {
|
||||
link: { type: "doc", id: "reference/adapters/index" },
|
||||
items: [
|
||||
{ type: "doc", id: "reference/adapter/dgraph/index" },
|
||||
{ type: "doc", id: "reference/adapter/drizzle/index" },
|
||||
{ type: "doc", id: "reference/adapter/dynamodb/index" },
|
||||
{ type: "doc", id: "reference/adapter/fauna/index" },
|
||||
{ type: "doc", id: "reference/adapter/firebase/index" },
|
||||
{ type: "doc", id: "reference/adapter/kysely/index" },
|
||||
{ type: "doc", id: "reference/adapter/mikro-orm/index" },
|
||||
{ type: "doc", id: "reference/adapter/mongodb/index" },
|
||||
{ type: "doc", id: "reference/adapter/neo4j/index" },
|
||||
|
||||
@@ -7,6 +7,7 @@ const icons = [
|
||||
"/img/providers/apple.svg",
|
||||
"/img/providers/auth0.svg",
|
||||
"/img/providers/cognito.svg",
|
||||
"/img/providers/descope.svg",
|
||||
"/img/providers/battlenet.svg",
|
||||
"/img/providers/box.svg",
|
||||
"/img/providers/facebook.svg",
|
||||
|
||||
@@ -29,6 +29,7 @@ html[data-theme="dark"] .adapter-card {
|
||||
color: #f5f5f5;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .adapter-card:hover,
|
||||
.adapter-card:hover {
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
|
||||
@@ -91,7 +91,7 @@ html[data-theme="dark"] .navbar__item.navbar__link[href*="npm"]:before {
|
||||
position: absolute;
|
||||
color: #000;
|
||||
top: -10px;
|
||||
right: -45px;
|
||||
right: 4px;
|
||||
font-size: 9px;
|
||||
background-color: #ccc;
|
||||
padding: 2px 5px;
|
||||
|
||||
@@ -101,13 +101,13 @@ export default function Home() {
|
||||
.fetch("https://api.github.com/repos/nextauthjs/next-auth")
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
const navLinks = document.getElementsByClassName(
|
||||
"navbar__item navbar__link"
|
||||
const githubLink = document.querySelector(
|
||||
".navbar__item.navbar__link[href*='github']"
|
||||
)
|
||||
const githubStat = document.createElement("span")
|
||||
githubStat.innerHTML = kFormatter(data.stargazers_count)
|
||||
githubStat.className = "github-counter"
|
||||
navLinks[4].appendChild(githubStat)
|
||||
githubLink.appendChild(githubStat)
|
||||
})
|
||||
}, [])
|
||||
return (
|
||||
|
||||
BIN
docs/static/img/adapters/drizzle-orm.png
vendored
Normal file
BIN
docs/static/img/adapters/drizzle-orm.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 94 KiB |
14
docs/static/img/adapters/kysely.svg
vendored
Normal file
14
docs/static/img/adapters/kysely.svg
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<svg width="132" height="132" viewBox="0 0 132 132" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_8_3)">
|
||||
<rect x="2" y="2" width="128" height="128" rx="16" fill="white" />
|
||||
<path
|
||||
d="M41.2983 109V23.9091H46.4918V73.31H47.0735L91.9457 23.9091H98.8427L61.9062 64.1694L98.5103 109H92.0288L58.5824 67.9087L46.4918 81.2873V109H41.2983Z"
|
||||
fill="black" />
|
||||
</g>
|
||||
<rect x="2" y="2" width="128" height="128" rx="16" stroke="#121212" stroke-width="4" />
|
||||
<defs>
|
||||
<clipPath id="clip0_8_3">
|
||||
<rect x="2" y="2" width="128" height="128" rx="16" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 637 B |
50
docs/static/img/providers/descope.svg
vendored
Normal file
50
docs/static/img/providers/descope.svg
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="194.7px" height="215.2px" viewBox="0 0 194.7 215.2" style="enable-background:new 0 0 194.7 215.2;" xml:space="preserve"
|
||||
>
|
||||
<style type="text/css">
|
||||
.st0{fill:url(#SVGID_1_);}
|
||||
.st1{fill:url(#SVGID_00000004519561486438896460000001266960168497785022_);}
|
||||
.st2{fill:url(#SVGID_00000049204468076180615810000015113731544435055266_);}
|
||||
.st3{fill:url(#SVGID_00000116951257355544416270000003794629356563808950_);}
|
||||
</style>
|
||||
<g>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="68.3919" y1="222.1531" x2="185.0265" y2="41.0264">
|
||||
<stop offset="1.481436e-07" style="stop-color:#0083B5"/>
|
||||
<stop offset="0.4173" style="stop-color:#00FFFF"/>
|
||||
<stop offset="0.9952" style="stop-color:#6FF12D"/>
|
||||
</linearGradient>
|
||||
<path class="st0" d="M129.8,174.7c7.6-1.6,14-4.8,19.2-9.7c7.7-7.3,8.8-17.1,8.8-29.4V80.7c0-12.3-1.1-22.1-8.8-29.4
|
||||
c-5.2-4.9-11.6-8.1-19.2-9.7V15.4c12.5,1.8,22.9,6.5,31,14.2c10.6,10,19.9,23.5,19.9,40.5v75c0,17-9.3,30.5-19.9,40.5
|
||||
c-8.1,7.7-18.5,12.4-31,14.2V174.7z"/>
|
||||
|
||||
<linearGradient id="SVGID_00000040544740507634666800000017273841385603649669_" gradientUnits="userSpaceOnUse" x1="5.037" y1="181.3564" x2="121.6716" y2="0.2297">
|
||||
<stop offset="1.481436e-07" style="stop-color:#0083B5"/>
|
||||
<stop offset="0.4173" style="stop-color:#00FFFF"/>
|
||||
<stop offset="0.9952" style="stop-color:#6FF12D"/>
|
||||
</linearGradient>
|
||||
<path style="fill:url(#SVGID_00000040544740507634666800000017273841385603649669_);" d="M33.9,29.6c8.1-7.7,18.5-12.4,31-14.2
|
||||
v26.3c-7.6,1.6-14,4.8-19.2,9.7c-7.7,7.3-8.8,17-8.8,29.2v55.1c0,12.3,1.1,22.1,8.8,29.4c5.2,4.9,11.6,8.1,19.2,9.7v25.1
|
||||
c-12.5-1.8-22.9-6.5-31-14.2c-10.6-10-19.9-23.5-19.9-40.5V69.8C13.9,53,23.2,39.6,33.9,29.6z"/>
|
||||
<g>
|
||||
|
||||
<linearGradient id="SVGID_00000060713993868866928010000000698955780952733088_" gradientUnits="userSpaceOnUse" x1="22.0278" y1="192.2974" x2="138.6624" y2="11.1707">
|
||||
<stop offset="1.481436e-07" style="stop-color:#0083B5"/>
|
||||
<stop offset="0.4173" style="stop-color:#00FFFF"/>
|
||||
<stop offset="0.9952" style="stop-color:#6FF12D"/>
|
||||
</linearGradient>
|
||||
<path style="fill:url(#SVGID_00000060713993868866928010000000698955780952733088_);" d="M120.2,87.8l8.5-13.7l-17.8-9.4
|
||||
l-7.5,14.2c-1.1,2.1-3.1,3.3-5.5,3.3c-2.3,0-4.4-1.2-5.5-3.3L85,64.7L67.3,74l12.3,19.7L120.2,87.8z"/>
|
||||
|
||||
<linearGradient id="SVGID_00000115475840050352750520000000840372054167564949_" gradientUnits="userSpaceOnUse" x1="37.9651" y1="202.5601" x2="154.5998" y2="21.4334">
|
||||
<stop offset="1.481436e-07" style="stop-color:#0083B5"/>
|
||||
<stop offset="0.4173" style="stop-color:#00FFFF"/>
|
||||
<stop offset="0.9952" style="stop-color:#6FF12D"/>
|
||||
</linearGradient>
|
||||
<path style="fill:url(#SVGID_00000115475840050352750520000000840372054167564949_);" d="M142.4,97.7l-87.8,0.8v17.7l27.5-0.1
|
||||
l-14.8,23.8l17.7,9.3l7.5-14.2c1.1-2.1,3.1-3.3,5.5-3.3c2.3,0,4.4,1.2,5.5,3.3l7.5,14.2l17.8-9.4l-12-19.3L93.7,116l48.7-0.2V97.7
|
||||
z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
11
docs/static/img/providers/passage.svg
vendored
Normal file
11
docs/static/img/providers/passage.svg
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg width="250" height="250" viewBox="0 0 250 250" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path opacity="0.8" d="M37.8419 71.6921L65.1425 87.4541C86.2537 99.6426 93.4921 126.639 81.3036 147.75C80.2315 149.607 79.045 151.353 77.7583 152.989L12.6182 115.381C8.67588 113.105 5.58417 109.922 3.44179 106.254C-0.964934 98.6912 -1.30613 89.0446 3.38448 80.9202C10.348 68.8591 25.7752 64.7254 37.8419 71.6921Z" fill="#3D53F6"/>
|
||||
<path opacity="0.5" d="M37.8419 180.052L65.1425 164.29C86.2537 152.101 93.4921 125.105 81.3036 103.994C80.2315 102.137 79.045 100.391 77.7583 98.7544L12.6182 136.363C8.67588 138.639 5.58417 141.822 3.44179 145.49C-0.964934 153.053 -1.30613 162.699 3.38448 170.824C10.348 182.885 25.7752 187.019 37.8419 180.052Z" fill="#3D53F6"/>
|
||||
<path opacity="0.5" d="M212.753 71.6921L185.452 87.4541C164.341 99.6426 157.103 126.639 169.291 147.75C170.363 149.607 171.55 151.353 172.836 152.989L237.976 115.381C241.919 113.105 245.01 109.922 247.153 106.254C251.56 98.6912 251.901 89.0446 247.21 80.9202C240.247 68.8591 224.819 64.7254 212.753 71.6921Z" fill="#3D53F6"/>
|
||||
<path opacity="0.8" d="M212.753 180.052L185.452 164.29C164.341 152.101 157.103 125.105 169.291 103.994C170.363 102.137 171.55 100.391 172.836 98.7544L237.976 136.363C241.919 138.639 245.01 141.822 247.153 145.49C251.56 153.053 251.901 162.699 247.21 170.824C240.247 182.885 224.819 187.019 212.753 180.052Z" fill="#3D53F6"/>
|
||||
<path d="M125.297 150.525C139.228 150.525 150.52 139.232 150.52 125.302C150.52 111.372 139.228 100.079 125.297 100.079C111.367 100.079 100.075 111.372 100.075 125.302C100.075 139.232 111.367 150.525 125.297 150.525Z" fill="#FF2F1D"/>
|
||||
<path opacity="0.8" d="M71.1172 212.753L86.8793 185.452C99.0678 164.341 126.064 157.102 147.175 169.291C149.032 170.363 150.778 171.55 152.415 172.836L114.806 237.976C112.53 241.919 109.347 245.01 105.679 247.153C98.1164 251.559 88.4698 251.901 80.3454 247.21C68.2842 240.247 64.1505 224.819 71.1172 212.753Z" fill="#FF2F1D"/>
|
||||
<path opacity="0.5" d="M179.477 212.752L163.715 185.452C151.526 164.341 124.53 157.102 103.419 169.291C101.562 170.363 99.8157 171.549 98.1794 172.836L135.788 237.976C138.064 241.919 141.247 245.01 144.915 247.153C152.478 251.559 162.124 251.901 170.249 247.21C182.31 240.246 186.444 224.819 179.477 212.752Z" fill="#FF2F1D"/>
|
||||
<path opacity="0.5" d="M71.1172 37.8419L86.8793 65.1425C99.0678 86.2537 126.064 93.4921 147.175 81.3036C149.032 80.2315 150.778 79.045 152.415 77.7583L114.806 12.6182C112.53 8.67589 109.347 5.58416 105.679 3.44179C98.1164 -0.964936 88.4698 -1.30613 80.3454 3.38449C68.2842 10.348 64.1505 25.7752 71.1172 37.8419Z" fill="#3D53F6"/>
|
||||
<path opacity="0.8" d="M179.477 37.8419L163.715 65.1425C151.526 86.2537 124.53 93.4921 103.419 81.3036C101.562 80.2315 99.8157 79.045 98.1794 77.7583L135.788 12.6182C138.064 8.67589 141.247 5.58416 144.915 3.44179C152.478 -0.964936 162.124 -1.30613 170.249 3.38449C182.31 10.348 186.444 25.7752 179.477 37.8419Z" fill="#3D53F6"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"$schema": "https://openapi.vercel.sh/vercel.json",
|
||||
"cleanUrls": true,
|
||||
"headers": [
|
||||
{
|
||||
@@ -76,10 +77,15 @@
|
||||
"has": [{ "type": "host", "value": "warnings.authjs.dev" }],
|
||||
"destination": "https://authjs.dev/reference/warnings/:path*"
|
||||
},
|
||||
{
|
||||
"source": "/",
|
||||
"has": [{ "type": "host", "value": "adapters.authjs.dev" }],
|
||||
"destination": "https://authjs.dev/reference/adapters"
|
||||
},
|
||||
{
|
||||
"source": "/:path(.*)",
|
||||
"has": [{ "type": "host", "value": "adapters.authjs.dev" }],
|
||||
"destination": "https://authjs.dev/reference/adapters/:path*"
|
||||
"destination": "https://authjs.dev/reference/adapter/:path*"
|
||||
},
|
||||
{
|
||||
"source": "/:path",
|
||||
|
||||
10
package.json
10
package.json
@@ -6,8 +6,8 @@
|
||||
"scripts": {
|
||||
"build:app": "turbo run build --filter=next-auth-app",
|
||||
"build:docs": "turbo run build --filter=docs",
|
||||
"build": "turbo run build --filter=next-auth --filter=@next-auth/* --filter=@auth/* --no-deps",
|
||||
"test": "turbo run test --concurrency=1 --filter=[HEAD^1] --filter=./packages/* --filter=!@*upstash* --filter=!*dynamodb-*",
|
||||
"build": "turbo run build --filter=next-auth --filter=@auth/* --no-deps",
|
||||
"test": "turbo run test --concurrency=1 --filter=[HEAD^1] --filter=./packages/* --filter=!@*upstash* --filter=!*dynamodb-* --filter=!*app*",
|
||||
"clean": "turbo run clean --no-cache",
|
||||
"dev:db": "turbo run dev --parallel --continue --filter=next-auth-app...",
|
||||
"dev": "turbo run dev --parallel --continue --filter=next-auth-app... --filter=!./packages/adapter-*",
|
||||
@@ -43,13 +43,13 @@
|
||||
"eslint-plugin-svelte3": "^4.0.0",
|
||||
"prettier": "2.8.1",
|
||||
"prettier-plugin-svelte": "^2.8.1",
|
||||
"turbo": "1.8.8",
|
||||
"typescript": "4.9.4"
|
||||
"turbo": "^1.10.12",
|
||||
"typescript": "5.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.13.0 || ^18.12.0"
|
||||
},
|
||||
"packageManager": "pnpm@7.23.0",
|
||||
"packageManager": "pnpm@8.7.1",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
||||
@@ -8,14 +8,14 @@
|
||||
</a>
|
||||
<h3 align="center"><b>Dgraph Adapter</b> - NextAuth.js / Auth.js</a></h3>
|
||||
<p align="center" style="align: center;">
|
||||
<a href="https://npm.im/@next-auth/dgraph-adapter">
|
||||
<a href="https://npm.im/@auth/dgraph-adapter">
|
||||
<img src="https://img.shields.io/badge/TypeScript-blue?style=flat-square" alt="TypeScript" />
|
||||
</a>
|
||||
<a href="https://npm.im/@next-auth/dgraph-adapter">
|
||||
<img alt="npm" src="https://img.shields.io/npm/v/@next-auth/dgraph-adapter?color=green&label=@next-auth/dgraph-adapter&style=flat-square">
|
||||
<a href="https://npm.im/@auth/dgraph-adapter">
|
||||
<img alt="npm" src="https://img.shields.io/npm/v/@auth/dgraph-adapter?color=green&label=@auth/dgraph-adapter&style=flat-square">
|
||||
</a>
|
||||
<a href="https://www.npmtrends.com/@next-auth/dgraph-adapter">
|
||||
<img src="https://img.shields.io/npm/dm/@next-auth/dgraph-adapter?label=%20downloads&style=flat-square" alt="Downloads" />
|
||||
<a href="https://www.npmtrends.com/@auth/dgraph-adapter">
|
||||
<img src="https://img.shields.io/npm/dm/@auth/dgraph-adapter?label=%20downloads&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" />
|
||||
|
||||
@@ -1,19 +1,30 @@
|
||||
{
|
||||
"name": "@next-auth/dgraph-adapter",
|
||||
"version": "1.0.6",
|
||||
"description": "Dgraph adapter for next-auth.",
|
||||
"name": "@auth/dgraph-adapter",
|
||||
"version": "1.0.0",
|
||||
"description": "Dgraph adapter for Auth.js",
|
||||
"homepage": "https://authjs.dev",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
"bugs": {
|
||||
"url": "https://github.com/nextauthjs/next-auth/issues"
|
||||
},
|
||||
"author": "Arnaud Derbey <arnaud@derbey.dev>",
|
||||
"contributors": [],
|
||||
"main": "dist/index.js",
|
||||
"files": [
|
||||
"dist",
|
||||
"index.d.ts"
|
||||
"contributors": [
|
||||
"Balázs Orbán <info@balazsorban.com>"
|
||||
],
|
||||
"type": "module",
|
||||
"types": "./index.d.ts",
|
||||
"files": [
|
||||
"*.js",
|
||||
"*.d.ts*",
|
||||
"lib",
|
||||
"src"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./index.d.ts",
|
||||
"import": "./index.js"
|
||||
}
|
||||
},
|
||||
"license": "ISC",
|
||||
"keywords": [
|
||||
"next-auth",
|
||||
@@ -29,25 +40,21 @@
|
||||
"build": "tsc",
|
||||
"test": "./tests/test.sh"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"next-auth": "^4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next-auth/adapter-test": "workspace:*",
|
||||
"@next-auth/tsconfig": "workspace:*",
|
||||
"@auth/adapter-test": "workspace:*",
|
||||
"@auth/tsconfig": "workspace:*",
|
||||
"@types/jest": "^26.0.24",
|
||||
"@types/jsonwebtoken": "^8.5.5",
|
||||
"@types/node-fetch": "^2.5.11",
|
||||
"jest": "^27.4.3",
|
||||
"next-auth": "workspace:*",
|
||||
"ts-jest": "^27.0.3"
|
||||
"ts-jest": "^27.0.3",
|
||||
"typescript": "5.2.2",
|
||||
"undici": "5.22.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"node-fetch": "^2.6.1"
|
||||
"@auth/core": "workspace:*",
|
||||
"jsonwebtoken": "^8.5.1"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "@next-auth/adapter-test/jest"
|
||||
"preset": "@auth/adapter-test/jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,18 +9,18 @@
|
||||
* ## Installation
|
||||
*
|
||||
* ```bash npm2yarn2pnpm
|
||||
* npm install next-auth @next-auth/dgraph-adapter
|
||||
* npm install next-auth @auth/dgraph-adapter
|
||||
* ```
|
||||
*
|
||||
* @module @next-auth/dgraph-adapter
|
||||
* @module @auth/dgraph-adapter
|
||||
*/
|
||||
import { client as dgraphClient } from "./client"
|
||||
import { format } from "./utils"
|
||||
import type { Adapter } from "next-auth/adapters"
|
||||
import type { DgraphClientParams } from "./client"
|
||||
import * as defaultFragments from "./graphql/fragments"
|
||||
import { client as dgraphClient } from "./lib/client"
|
||||
import { format } from "./lib/utils"
|
||||
import type { Adapter } from "@auth/core/adapters"
|
||||
import type { DgraphClientParams } from "./lib/client"
|
||||
import * as defaultFragments from "./lib/graphql/fragments"
|
||||
|
||||
export type { DgraphClientParams, DgraphClientError } from "./client"
|
||||
export type { DgraphClientParams, DgraphClientError } from "./lib/client"
|
||||
|
||||
/** This is the interface of the Dgraph adapter options. */
|
||||
export interface DgraphAdapterOptions {
|
||||
@@ -28,7 +28,7 @@ export interface DgraphAdapterOptions {
|
||||
* The GraphQL {@link https://dgraph.io/docs/query-language/fragments/ Fragments} you can supply to the adapter
|
||||
* to define how the shapes of the `user`, `account`, `session`, `verificationToken` entities look.
|
||||
*
|
||||
* By default the adapter will uses the [default defined fragments](https://github.com/nextauthjs/next-auth/blob/main/packages/adapter-dgraph/src/graphql/fragments.ts)
|
||||
* By default the adapter will uses the [default defined fragments](https://github.com/nextauthjs/next-auth/blob/main/packages/adapter-dgraph/src/lib/graphql/fragments.ts)
|
||||
* , this config option allows to extend them.
|
||||
*/
|
||||
fragments?: {
|
||||
@@ -48,7 +48,7 @@ export { format }
|
||||
*
|
||||
* ```ts title="pages/api/auth/[...nextauth].js"
|
||||
* import NextAuth from "next-auth"
|
||||
* import { DgraphAdapter } from "@next-auth/dgraph-adapter"
|
||||
* import { DgraphAdapter } from "@auth/dgraph-adapter"
|
||||
*
|
||||
* export default NextAuth({
|
||||
* providers: [],
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import * as jwt from "jsonwebtoken"
|
||||
import fetch from "node-fetch"
|
||||
|
||||
import type { HeadersInit } from "node-fetch"
|
||||
|
||||
export interface DgraphClientParams {
|
||||
endpoint: string
|
||||
@@ -1,12 +1,14 @@
|
||||
import { DgraphAdapter, format } from "../src"
|
||||
import { client as dgraphClient } from "../src/client"
|
||||
import * as fragments from "../src/graphql/fragments"
|
||||
import { runBasicTests } from "@next-auth/adapter-test"
|
||||
import { client as dgraphClient } from "../src/lib/client"
|
||||
import * as fragments from "../src/lib/graphql/fragments"
|
||||
import { runBasicTests } from "@auth/adapter-test"
|
||||
import fs from "fs"
|
||||
import path from "path"
|
||||
|
||||
import type { DgraphClientParams } from "../src"
|
||||
|
||||
globalThis.fetch ??= require("undici").fetch
|
||||
|
||||
const params: DgraphClientParams = {
|
||||
endpoint: "http://localhost:8080/graphql",
|
||||
authToken: "test",
|
||||
|
||||
@@ -21,7 +21,7 @@ dgraph/standalone
|
||||
|
||||
echo "Waiting 15 sec for db to start..." && sleep 15
|
||||
|
||||
head -n -1 src/graphql/schema.gql > tests/test.schema.gql
|
||||
head -n -1 src/lib/graphql/schema.gql > tests/test.schema.gql
|
||||
PUBLIC_KEY=$(sed 's/$/\\n/' tests/public.key | tr -d '\n')
|
||||
echo "# Dgraph.Authorization {\"VerificationKey\":\"$PUBLIC_KEY\",\"Namespace\":\"https://dgraph.io/jwt/claims\",\"Header\":\"Authorization\",\"Algo\":\"RS256\"}" >> tests/test.schema.gql
|
||||
|
||||
|
||||
@@ -1,8 +1,25 @@
|
||||
{
|
||||
"extends": "@next-auth/tsconfig/tsconfig.adapters.json",
|
||||
"extends": "@auth/tsconfig/tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"baseUrl": ".",
|
||||
"isolatedModules": true,
|
||||
"target": "ES2020",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"outDir": ".",
|
||||
"rootDir": "src",
|
||||
"outDir": "dist"
|
||||
"skipDefaultLibCheck": true,
|
||||
"strictNullChecks": true,
|
||||
"stripInternal": true,
|
||||
"declarationMap": true,
|
||||
"declaration": true
|
||||
},
|
||||
"exclude": ["tests", "dist", "jest.config.js"]
|
||||
}
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"*.js",
|
||||
"*.d.ts",
|
||||
]
|
||||
}
|
||||
28
packages/adapter-drizzle/README.md
Normal file
28
packages/adapter-drizzle/README.md
Normal file
@@ -0,0 +1,28 @@
|
||||
<p align="center">
|
||||
<br/>
|
||||
<a href="https://authjs.dev" target="_blank">
|
||||
<img height="64px" src="https://authjs.dev/img/logo/logo-sm.png" />
|
||||
</a>
|
||||
<a href="https://github.com/drizzle-team/drizzle-orm" target="_blank">
|
||||
<img height="64px" src="https://authjs.dev/img/adapters/drizzle-orm.png"/>
|
||||
</a>
|
||||
<h3 align="center"><b>Drizzle ORM Adapter</b> - NextAuth.js / Auth.js</a></h3>
|
||||
<p align="center" style="align: center;">
|
||||
<a href="https://npm.im/@auth/drizzle-adapter">
|
||||
<img src="https://img.shields.io/badge/TypeScript-blue?style=flat-square" alt="TypeScript" />
|
||||
</a>
|
||||
<a href="https://npm.im/@auth/drizzle-adapter">
|
||||
<img alt="npm" src="https://img.shields.io/npm/v/@auth/drizzle-adapter?color=green&label=@auth/drizzle-adapter&style=flat-square">
|
||||
</a>
|
||||
<a href="https://www.npmtrends.com/@auth/drizzle-adapter">
|
||||
<img src="https://img.shields.io/npm/dm/@auth/drizzle-adapter?label=%20downloads&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>
|
||||
</p>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
Check out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/drizzle).
|
||||
65
packages/adapter-drizzle/package.json
Normal file
65
packages/adapter-drizzle/package.json
Normal file
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"name": "@auth/drizzle-adapter",
|
||||
"version": "0.3.2",
|
||||
"description": "Drizzle adapter for Auth.js.",
|
||||
"homepage": "https://authjs.dev",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
"bugs": {
|
||||
"url": "https://github.com/nextauthjs/next-auth/issues"
|
||||
},
|
||||
"author": "Anthony Shew",
|
||||
"type": "module",
|
||||
"types": "./index.d.ts",
|
||||
"files": [
|
||||
"*.d.ts*",
|
||||
"*.js",
|
||||
"lib",
|
||||
"src"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./index.d.ts",
|
||||
"import": "./index.js"
|
||||
}
|
||||
},
|
||||
"license": "ISC",
|
||||
"keywords": [
|
||||
"next-auth",
|
||||
"@auth",
|
||||
"Auth.js",
|
||||
"next.js",
|
||||
"oauth",
|
||||
"drizzle"
|
||||
],
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "find . -type d -name \".drizzle\" | xargs rm -rf",
|
||||
"test": "pnpm test:mysql && pnpm test:sqlite && pnpm test:pg",
|
||||
"test:mysql": "pnpm clean && ./tests/mysql/test.sh",
|
||||
"test:sqlite": "pnpm clean && ./tests/sqlite/test.sh",
|
||||
"test:pg": "pnpm clean && ./tests/pg/test.sh",
|
||||
"build": "tsc",
|
||||
"dev": "drizzle-kit generate:mysql --schema=src/schema.ts --out=.drizzle && tsc -w"
|
||||
},
|
||||
"dependencies": {
|
||||
"@auth/core": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@auth/adapter-test": "workspace:*",
|
||||
"@auth/tsconfig": "workspace:*",
|
||||
"@types/better-sqlite3": "^7.6.4",
|
||||
"@types/uuid": "^8.3.3",
|
||||
"better-sqlite3": "^8.4.0",
|
||||
"drizzle-kit": "^0.19.5",
|
||||
"drizzle-orm": "^0.27.0",
|
||||
"jest": "^27.4.3",
|
||||
"mysql2": "^3.2.0",
|
||||
"postgres": "^3.3.4"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "@auth/adapter-test/jest"
|
||||
}
|
||||
}
|
||||
269
packages/adapter-drizzle/src/index.ts
Normal file
269
packages/adapter-drizzle/src/index.ts
Normal file
@@ -0,0 +1,269 @@
|
||||
/**
|
||||
* <div style={{display: "flex", justifyContent: "space-between", alignItems: "center", padding: 16}}>
|
||||
* <p style={{fontWeight: "normal"}}>Official <a href="https://orm.drizzle.team">Drizzle ORM</a> adapter for Auth.js / NextAuth.js.</p>
|
||||
* <a href="https://orm.drizzle.team">
|
||||
* <img style={{display: "block"}} src="/img/adapters/drizzle-orm.png" width="38" />
|
||||
* </a>
|
||||
* </div>
|
||||
*
|
||||
* ## Installation
|
||||
*
|
||||
* ```bash npm2yarn2pnpm
|
||||
* npm install drizzle-orm @auth/drizzle-adapter
|
||||
* npm install drizzle-kit --save-dev
|
||||
* ```
|
||||
*
|
||||
* @module @auth/drizzle-adapter
|
||||
*/
|
||||
|
||||
import { MySqlDatabase, MySqlTableFn } from "drizzle-orm/mysql-core"
|
||||
import { PgDatabase, PgTableFn } from "drizzle-orm/pg-core"
|
||||
import { BaseSQLiteDatabase, SQLiteTableFn } from "drizzle-orm/sqlite-core"
|
||||
import { mySqlDrizzleAdapter } from "./lib/mysql.js"
|
||||
import { pgDrizzleAdapter } from "./lib/pg.js"
|
||||
import { SQLiteDrizzleAdapter } from "./lib/sqlite.js"
|
||||
import { SqlFlavorOptions, TableFn } from "./lib/utils.js"
|
||||
import { is } from "drizzle-orm"
|
||||
|
||||
import type { Adapter } from "@auth/core/adapters"
|
||||
|
||||
/**
|
||||
* Add the adapter to your `pages/api/[...nextauth].ts` next-auth configuration object.
|
||||
*
|
||||
* ```ts title="pages/api/auth/[...nextauth].ts"
|
||||
* import NextAuth from "next-auth"
|
||||
* import GoogleProvider from "next-auth/providers/google"
|
||||
* import { DrizzleAdapter } from "@auth/drizzle-adapter"
|
||||
* import { db } from "./schema"
|
||||
*
|
||||
* export default NextAuth({
|
||||
* adapter: DrizzleAdapter(db),
|
||||
* providers: [
|
||||
* GoogleProvider({
|
||||
* clientId: process.env.GOOGLE_CLIENT_ID,
|
||||
* clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
||||
* }),
|
||||
* ],
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* :::info
|
||||
* If you're using multi-project schemas, you can pass your table function as a second argument
|
||||
* :::
|
||||
*
|
||||
* ## Setup
|
||||
*
|
||||
* First, create a schema that includes [the minimum requirements for a `next-auth` adapter](/reference/adapters#models). You can select your favorite SQL flavor below and copy it.
|
||||
* Additionally, you may extend the schema from the minimum requirements to suit your needs.
|
||||
*
|
||||
* - [Postgres](#postgres)
|
||||
* - [MySQL](#mysql)
|
||||
* - [SQLite](#sqlite)
|
||||
*
|
||||
* ### Postgres
|
||||
|
||||
* ```ts title="schema.ts"
|
||||
* import {
|
||||
* timestamp,
|
||||
* pgTable,
|
||||
* text,
|
||||
* primaryKey,
|
||||
* integer
|
||||
* } from "drizzle-orm/pg-core"
|
||||
* import type { AdapterAccount } from '@auth/core/adapters'
|
||||
*
|
||||
* export const users = pgTable("user", {
|
||||
* id: text("id").notNull().primaryKey(),
|
||||
* name: text("name"),
|
||||
* email: text("email").notNull(),
|
||||
* emailVerified: timestamp("emailVerified", { mode: "date" }),
|
||||
* image: text("image"),
|
||||
* })
|
||||
*
|
||||
* export const accounts = pgTable(
|
||||
* "account",
|
||||
* {
|
||||
* userId: text("userId")
|
||||
* .notNull()
|
||||
* .references(() => users.id, { onDelete: "cascade" }),
|
||||
* type: text("type").$type<AdapterAccount["type"]>().notNull(),
|
||||
* provider: text("provider").notNull(),
|
||||
* providerAccountId: text("providerAccountId").notNull(),
|
||||
* refresh_token: text("refresh_token"),
|
||||
* access_token: text("access_token"),
|
||||
* expires_at: integer("expires_at"),
|
||||
* token_type: text("token_type"),
|
||||
* scope: text("scope"),
|
||||
* id_token: text("id_token"),
|
||||
* session_state: text("session_state"),
|
||||
* },
|
||||
* (account) => ({
|
||||
* compoundKey: primaryKey(account.provider, account.providerAccountId),
|
||||
* })
|
||||
* )
|
||||
*
|
||||
* export const sessions = pgTable("session", {
|
||||
* sessionToken: text("sessionToken").notNull().primaryKey(),
|
||||
* userId: text("userId")
|
||||
* .notNull()
|
||||
* .references(() => users.id, { onDelete: "cascade" }),
|
||||
* expires: timestamp("expires", { mode: "date" }).notNull(),
|
||||
* })
|
||||
*
|
||||
* export const verificationTokens = pgTable(
|
||||
* "verificationToken",
|
||||
* {
|
||||
* identifier: text("identifier").notNull(),
|
||||
* token: text("token").notNull(),
|
||||
* expires: timestamp("expires", { mode: "date" }).notNull(),
|
||||
* },
|
||||
* (vt) => ({
|
||||
* compoundKey: primaryKey(vt.identifier, vt.token),
|
||||
* })
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* ### MySQL
|
||||
*
|
||||
* ```ts title="schema.ts"
|
||||
* import {
|
||||
* int,
|
||||
* timestamp,
|
||||
* mysqlTable,
|
||||
* primaryKey,
|
||||
* varchar,
|
||||
* } from "drizzle-orm/mysql-core"
|
||||
* import type { AdapterAccount } from "@auth/core/adapters"
|
||||
*
|
||||
* export const users = mysqlTable("user", {
|
||||
* id: varchar("id", { length: 255 }).notNull().primaryKey(),
|
||||
* name: varchar("name", { length: 255 }),
|
||||
* email: varchar("email", { length: 255 }).notNull(),
|
||||
* emailVerified: timestamp("emailVerified", { mode: "date", fsp: 3 }).defaultNow(),
|
||||
* image: varchar("image", { length: 255 }),
|
||||
* })
|
||||
*
|
||||
* export const accounts = mysqlTable(
|
||||
* "account",
|
||||
* {
|
||||
* userId: varchar("userId", { length: 255 })
|
||||
* .notNull()
|
||||
* .references(() => users.id, { onDelete: "cascade" }),
|
||||
* type: varchar("type", { length: 255 }).$type<AdapterAccount["type"]>().notNull(),
|
||||
* provider: varchar("provider", { length: 255 }).notNull(),
|
||||
* providerAccountId: varchar("providerAccountId", { length: 255 }).notNull(),
|
||||
* refresh_token: varchar("refresh_token", { length: 255 }),
|
||||
* access_token: varchar("access_token", { length: 255 }),
|
||||
* expires_at: int("expires_at"),
|
||||
* token_type: varchar("token_type", { length: 255 }),
|
||||
* scope: varchar("scope", { length: 255 }),
|
||||
* id_token: varchar("id_token", { length: 255 }),
|
||||
* session_state: varchar("session_state", { length: 255 }),
|
||||
* },
|
||||
* (account) => ({
|
||||
* compoundKey: primaryKey(account.provider, account.providerAccountId),
|
||||
* })
|
||||
* )
|
||||
*
|
||||
* export const sessions = mysqlTable("session", {
|
||||
* sessionToken: varchar("sessionToken", { length: 255 }).notNull().primaryKey(),
|
||||
* userId: varchar("userId", { length: 255 })
|
||||
* .notNull()
|
||||
* .references(() => users.id, { onDelete: "cascade" }),
|
||||
* expires: timestamp("expires", { mode: "date" }).notNull(),
|
||||
* })
|
||||
*
|
||||
* export const verificationTokens = mysqlTable(
|
||||
* "verificationToken",
|
||||
* {
|
||||
* identifier: varchar("identifier", { length: 255 }).notNull(),
|
||||
* token: varchar("token", { length: 255 }).notNull(),
|
||||
* expires: timestamp("expires", { mode: "date" }).notNull(),
|
||||
* },
|
||||
* (vt) => ({
|
||||
* compoundKey: primaryKey(vt.identifier, vt.token),
|
||||
* })
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* ### SQLite
|
||||
*
|
||||
* ```ts title="schema.ts"
|
||||
* import { integer, sqliteTable, text, primaryKey } from "drizzle-orm/sqlite-core"
|
||||
* import type { AdapterAccount } from "@auth/core/adapters"
|
||||
*
|
||||
* export const users = sqliteTable("user", {
|
||||
* id: text("id").notNull().primaryKey(),
|
||||
* name: text("name"),
|
||||
* email: text("email").notNull(),
|
||||
* emailVerified: integer("emailVerified", { mode: "timestamp_ms" }),
|
||||
* image: text("image"),
|
||||
* })
|
||||
*
|
||||
* export const accounts = sqliteTable(
|
||||
* "account",
|
||||
* {
|
||||
* userId: text("userId")
|
||||
* .notNull()
|
||||
* .references(() => users.id, { onDelete: "cascade" }),
|
||||
* type: text("type").$type<AdapterAccount["type"]>().notNull(),
|
||||
* provider: text("provider").notNull(),
|
||||
* providerAccountId: text("providerAccountId").notNull(),
|
||||
* refresh_token: text("refresh_token"),
|
||||
* access_token: text("access_token"),
|
||||
* expires_at: integer("expires_at"),
|
||||
* token_type: text("token_type"),
|
||||
* scope: text("scope"),
|
||||
* id_token: text("id_token"),
|
||||
* session_state: text("session_state"),
|
||||
* },
|
||||
* (account) => ({
|
||||
* compoundKey: primaryKey(account.provider, account.providerAccountId),
|
||||
* })
|
||||
* )
|
||||
*
|
||||
* export const sessions = sqliteTable("session", {
|
||||
* sessionToken: text("sessionToken").notNull().primaryKey(),
|
||||
* userId: text("userId")
|
||||
* .notNull()
|
||||
* .references(() => users.id, { onDelete: "cascade" }),
|
||||
* expires: integer("expires", { mode: "timestamp_ms" }).notNull(),
|
||||
* })
|
||||
*
|
||||
* export const verificationTokens = sqliteTable(
|
||||
* "verificationToken",
|
||||
* {
|
||||
* identifier: text("identifier").notNull(),
|
||||
* token: text("token").notNull(),
|
||||
* expires: integer("expires", { mode: "timestamp_ms" }).notNull(),
|
||||
* },
|
||||
* (vt) => ({
|
||||
* compoundKey: primaryKey(vt.identifier, vt.token),
|
||||
* })
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* ## Migrating your database
|
||||
* With your schema now described in your code, you'll need to migrate your database to your schema.
|
||||
*
|
||||
* For full documentation on how to run migrations with Drizzle, [visit the Drizzle documentation](https://orm.drizzle.team/kit-docs/overview#running-migrations).
|
||||
*
|
||||
* ---
|
||||
*
|
||||
**/
|
||||
export function DrizzleAdapter<SqlFlavor extends SqlFlavorOptions>(
|
||||
db: SqlFlavor,
|
||||
table?: TableFn<SqlFlavor>
|
||||
): Adapter {
|
||||
if (is(db, MySqlDatabase)) {
|
||||
return mySqlDrizzleAdapter(db, table as MySqlTableFn)
|
||||
} else if (is(db, PgDatabase)) {
|
||||
return pgDrizzleAdapter(db, table as PgTableFn)
|
||||
} else if (is(db, BaseSQLiteDatabase)) {
|
||||
return SQLiteDrizzleAdapter(db, table as SQLiteTableFn)
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Unsupported database type (${typeof db}) in Auth.js Drizzle adapter.`
|
||||
)
|
||||
}
|
||||
264
packages/adapter-drizzle/src/lib/mysql.ts
Normal file
264
packages/adapter-drizzle/src/lib/mysql.ts
Normal file
@@ -0,0 +1,264 @@
|
||||
import { and, eq } from "drizzle-orm"
|
||||
import {
|
||||
int,
|
||||
timestamp,
|
||||
mysqlTable as defaultMySqlTableFn,
|
||||
primaryKey,
|
||||
varchar,
|
||||
MySqlTableFn,
|
||||
MySqlDatabase,
|
||||
} from "drizzle-orm/mysql-core"
|
||||
|
||||
import type { Adapter, AdapterAccount } from "@auth/core/adapters"
|
||||
|
||||
export function createTables(mySqlTable: MySqlTableFn) {
|
||||
const users = mySqlTable("user", {
|
||||
id: varchar("id", { length: 255 }).notNull().primaryKey(),
|
||||
name: varchar("name", { length: 255 }),
|
||||
email: varchar("email", { length: 255 }).notNull(),
|
||||
emailVerified: timestamp("emailVerified", {
|
||||
mode: "date",
|
||||
fsp: 3,
|
||||
}).defaultNow(),
|
||||
image: varchar("image", { length: 255 }),
|
||||
})
|
||||
|
||||
const accounts = mySqlTable(
|
||||
"account",
|
||||
{
|
||||
userId: varchar("userId", { length: 255 })
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: "cascade" }),
|
||||
type: varchar("type", { length: 255 })
|
||||
.$type<AdapterAccount["type"]>()
|
||||
.notNull(),
|
||||
provider: varchar("provider", { length: 255 }).notNull(),
|
||||
providerAccountId: varchar("providerAccountId", {
|
||||
length: 255,
|
||||
}).notNull(),
|
||||
refresh_token: varchar("refresh_token", { length: 255 }),
|
||||
access_token: varchar("access_token", { length: 255 }),
|
||||
expires_at: int("expires_at"),
|
||||
token_type: varchar("token_type", { length: 255 }),
|
||||
scope: varchar("scope", { length: 255 }),
|
||||
id_token: varchar("id_token", { length: 255 }),
|
||||
session_state: varchar("session_state", { length: 255 }),
|
||||
},
|
||||
(account) => ({
|
||||
compoundKey: primaryKey(account.provider, account.providerAccountId),
|
||||
})
|
||||
)
|
||||
|
||||
const sessions = mySqlTable("session", {
|
||||
sessionToken: varchar("sessionToken", { length: 255 })
|
||||
.notNull()
|
||||
.primaryKey(),
|
||||
userId: varchar("userId", { length: 255 })
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: "cascade" }),
|
||||
expires: timestamp("expires", { mode: "date" }).notNull(),
|
||||
})
|
||||
|
||||
const verificationTokens = mySqlTable(
|
||||
"verificationToken",
|
||||
{
|
||||
identifier: varchar("identifier", { length: 255 }).notNull(),
|
||||
token: varchar("token", { length: 255 }).notNull(),
|
||||
expires: timestamp("expires", { mode: "date" }).notNull(),
|
||||
},
|
||||
(vt) => ({
|
||||
compoundKey: primaryKey(vt.identifier, vt.token),
|
||||
})
|
||||
)
|
||||
|
||||
return { users, accounts, sessions, verificationTokens }
|
||||
}
|
||||
|
||||
export type DefaultSchema = ReturnType<typeof createTables>
|
||||
|
||||
export function mySqlDrizzleAdapter(
|
||||
client: InstanceType<typeof MySqlDatabase>,
|
||||
tableFn = defaultMySqlTableFn
|
||||
): Adapter {
|
||||
const { users, accounts, sessions, verificationTokens } =
|
||||
createTables(tableFn)
|
||||
|
||||
return {
|
||||
async createUser(data) {
|
||||
const id = crypto.randomUUID()
|
||||
|
||||
await client.insert(users).values({ ...data, id })
|
||||
|
||||
return await client
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.id, id))
|
||||
.then((res) => res[0])
|
||||
},
|
||||
async getUser(data) {
|
||||
const thing =
|
||||
(await client
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.id, data))
|
||||
.then((res) => res[0])) ?? null
|
||||
|
||||
return thing
|
||||
},
|
||||
async getUserByEmail(data) {
|
||||
const user =
|
||||
(await client
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.email, data))
|
||||
.then((res) => res[0])) ?? null
|
||||
|
||||
return user
|
||||
},
|
||||
async createSession(data) {
|
||||
await client.insert(sessions).values(data)
|
||||
|
||||
return await client
|
||||
.select()
|
||||
.from(sessions)
|
||||
.where(eq(sessions.sessionToken, data.sessionToken))
|
||||
.then((res) => res[0])
|
||||
},
|
||||
async getSessionAndUser(data) {
|
||||
const sessionAndUser =
|
||||
(await client
|
||||
.select({
|
||||
session: sessions,
|
||||
user: users,
|
||||
})
|
||||
.from(sessions)
|
||||
.where(eq(sessions.sessionToken, data))
|
||||
.innerJoin(users, eq(users.id, sessions.userId))
|
||||
.then((res) => res[0])) ?? null
|
||||
|
||||
return sessionAndUser
|
||||
},
|
||||
async updateUser(data) {
|
||||
if (!data.id) {
|
||||
throw new Error("No user id.")
|
||||
}
|
||||
|
||||
await client.update(users).set(data).where(eq(users.id, data.id))
|
||||
|
||||
return await client
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.id, data.id))
|
||||
.then((res) => res[0])
|
||||
},
|
||||
async updateSession(data) {
|
||||
await client
|
||||
.update(sessions)
|
||||
.set(data)
|
||||
.where(eq(sessions.sessionToken, data.sessionToken))
|
||||
|
||||
return await client
|
||||
.select()
|
||||
.from(sessions)
|
||||
.where(eq(sessions.sessionToken, data.sessionToken))
|
||||
.then((res) => res[0])
|
||||
},
|
||||
async linkAccount(rawAccount) {
|
||||
await client.insert(accounts).values(rawAccount)
|
||||
},
|
||||
async getUserByAccount(account) {
|
||||
const dbAccount =
|
||||
(await client
|
||||
.select()
|
||||
.from(accounts)
|
||||
.where(
|
||||
and(
|
||||
eq(accounts.providerAccountId, account.providerAccountId),
|
||||
eq(accounts.provider, account.provider)
|
||||
)
|
||||
)
|
||||
.leftJoin(users, eq(accounts.userId, users.id))
|
||||
.then((res) => res[0])) ?? null
|
||||
|
||||
if (!dbAccount) {
|
||||
return null
|
||||
}
|
||||
|
||||
return dbAccount.user
|
||||
},
|
||||
async deleteSession(sessionToken) {
|
||||
const session =
|
||||
(await client
|
||||
.select()
|
||||
.from(sessions)
|
||||
.where(eq(sessions.sessionToken, sessionToken))
|
||||
.then((res) => res[0])) ?? null
|
||||
|
||||
await client
|
||||
.delete(sessions)
|
||||
.where(eq(sessions.sessionToken, sessionToken))
|
||||
|
||||
return session
|
||||
},
|
||||
async createVerificationToken(token) {
|
||||
await client.insert(verificationTokens).values(token)
|
||||
|
||||
return await client
|
||||
.select()
|
||||
.from(verificationTokens)
|
||||
.where(eq(verificationTokens.identifier, token.identifier))
|
||||
.then((res) => res[0])
|
||||
},
|
||||
async useVerificationToken(token) {
|
||||
try {
|
||||
const deletedToken =
|
||||
(await client
|
||||
.select()
|
||||
.from(verificationTokens)
|
||||
.where(
|
||||
and(
|
||||
eq(verificationTokens.identifier, token.identifier),
|
||||
eq(verificationTokens.token, token.token)
|
||||
)
|
||||
)
|
||||
.then((res) => res[0])) ?? null
|
||||
|
||||
await client
|
||||
.delete(verificationTokens)
|
||||
.where(
|
||||
and(
|
||||
eq(verificationTokens.identifier, token.identifier),
|
||||
eq(verificationTokens.token, token.token)
|
||||
)
|
||||
)
|
||||
|
||||
return deletedToken
|
||||
} catch (err) {
|
||||
throw new Error("No verification token found.")
|
||||
}
|
||||
},
|
||||
async deleteUser(id) {
|
||||
const user = await client
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.id, id))
|
||||
.then((res) => res[0] ?? null)
|
||||
|
||||
await client.delete(users).where(eq(users.id, id))
|
||||
|
||||
return user
|
||||
},
|
||||
async unlinkAccount(account) {
|
||||
await client
|
||||
.delete(accounts)
|
||||
.where(
|
||||
and(
|
||||
eq(accounts.providerAccountId, account.providerAccountId),
|
||||
eq(accounts.provider, account.provider)
|
||||
)
|
||||
)
|
||||
|
||||
return undefined
|
||||
},
|
||||
}
|
||||
}
|
||||
233
packages/adapter-drizzle/src/lib/pg.ts
Normal file
233
packages/adapter-drizzle/src/lib/pg.ts
Normal file
@@ -0,0 +1,233 @@
|
||||
import { and, eq } from "drizzle-orm"
|
||||
import {
|
||||
timestamp,
|
||||
pgTable as defaultPgTableFn,
|
||||
text,
|
||||
primaryKey,
|
||||
integer,
|
||||
PgTableFn,
|
||||
PgDatabase,
|
||||
} from "drizzle-orm/pg-core"
|
||||
|
||||
import type { Adapter, AdapterAccount } from "@auth/core/adapters"
|
||||
|
||||
export function createTables(pgTable: PgTableFn) {
|
||||
const users = pgTable("user", {
|
||||
id: text("id").notNull().primaryKey(),
|
||||
name: text("name"),
|
||||
email: text("email").notNull(),
|
||||
emailVerified: timestamp("emailVerified", { mode: "date" }),
|
||||
image: text("image"),
|
||||
})
|
||||
|
||||
const accounts = pgTable(
|
||||
"account",
|
||||
{
|
||||
userId: text("userId")
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: "cascade" }),
|
||||
type: text("type").$type<AdapterAccount["type"]>().notNull(),
|
||||
provider: text("provider").notNull(),
|
||||
providerAccountId: text("providerAccountId").notNull(),
|
||||
refresh_token: text("refresh_token"),
|
||||
access_token: text("access_token"),
|
||||
expires_at: integer("expires_at"),
|
||||
token_type: text("token_type"),
|
||||
scope: text("scope"),
|
||||
id_token: text("id_token"),
|
||||
session_state: text("session_state"),
|
||||
},
|
||||
(account) => ({
|
||||
compoundKey: primaryKey(account.provider, account.providerAccountId),
|
||||
})
|
||||
)
|
||||
|
||||
const sessions = pgTable("session", {
|
||||
sessionToken: text("sessionToken").notNull().primaryKey(),
|
||||
userId: text("userId")
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: "cascade" }),
|
||||
expires: timestamp("expires", { mode: "date" }).notNull(),
|
||||
})
|
||||
|
||||
const verificationTokens = pgTable(
|
||||
"verificationToken",
|
||||
{
|
||||
identifier: text("identifier").notNull(),
|
||||
token: text("token").notNull(),
|
||||
expires: timestamp("expires", { mode: "date" }).notNull(),
|
||||
},
|
||||
(vt) => ({
|
||||
compoundKey: primaryKey(vt.identifier, vt.token),
|
||||
})
|
||||
)
|
||||
|
||||
return { users, accounts, sessions, verificationTokens }
|
||||
}
|
||||
|
||||
export type DefaultSchema = ReturnType<typeof createTables>
|
||||
|
||||
export function pgDrizzleAdapter(
|
||||
client: InstanceType<typeof PgDatabase>,
|
||||
tableFn = defaultPgTableFn
|
||||
): Adapter {
|
||||
const { users, accounts, sessions, verificationTokens } =
|
||||
createTables(tableFn)
|
||||
|
||||
return {
|
||||
async createUser(data) {
|
||||
return await client
|
||||
.insert(users)
|
||||
.values({ ...data, id: crypto.randomUUID() })
|
||||
.returning()
|
||||
.then((res) => res[0] ?? null)
|
||||
},
|
||||
async getUser(data) {
|
||||
return await client
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.id, data))
|
||||
.then((res) => res[0] ?? null)
|
||||
},
|
||||
async getUserByEmail(data) {
|
||||
return await client
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.email, data))
|
||||
.then((res) => res[0] ?? null)
|
||||
},
|
||||
async createSession(data) {
|
||||
return await client
|
||||
.insert(sessions)
|
||||
.values(data)
|
||||
.returning()
|
||||
.then((res) => res[0])
|
||||
},
|
||||
async getSessionAndUser(data) {
|
||||
return await client
|
||||
.select({
|
||||
session: sessions,
|
||||
user: users,
|
||||
})
|
||||
.from(sessions)
|
||||
.where(eq(sessions.sessionToken, data))
|
||||
.innerJoin(users, eq(users.id, sessions.userId))
|
||||
.then((res) => res[0] ?? null)
|
||||
},
|
||||
async updateUser(data) {
|
||||
if (!data.id) {
|
||||
throw new Error("No user id.")
|
||||
}
|
||||
|
||||
return await client
|
||||
.update(users)
|
||||
.set(data)
|
||||
.where(eq(users.id, data.id))
|
||||
.returning()
|
||||
.then((res) => res[0])
|
||||
},
|
||||
async updateSession(data) {
|
||||
return await client
|
||||
.update(sessions)
|
||||
.set(data)
|
||||
.where(eq(sessions.sessionToken, data.sessionToken))
|
||||
.returning()
|
||||
.then((res) => res[0])
|
||||
},
|
||||
async linkAccount(rawAccount) {
|
||||
const updatedAccount = await client
|
||||
.insert(accounts)
|
||||
.values(rawAccount)
|
||||
.returning()
|
||||
.then((res) => res[0])
|
||||
|
||||
// Drizzle will return `null` for fields that are not defined.
|
||||
// However, the return type is expecting `undefined`.
|
||||
const account = {
|
||||
...updatedAccount,
|
||||
access_token: updatedAccount.access_token ?? undefined,
|
||||
token_type: updatedAccount.token_type ?? undefined,
|
||||
id_token: updatedAccount.id_token ?? undefined,
|
||||
refresh_token: updatedAccount.refresh_token ?? undefined,
|
||||
scope: updatedAccount.scope ?? undefined,
|
||||
expires_at: updatedAccount.expires_at ?? undefined,
|
||||
session_state: updatedAccount.session_state ?? undefined,
|
||||
}
|
||||
|
||||
return account
|
||||
},
|
||||
async getUserByAccount(account) {
|
||||
const dbAccount =
|
||||
(await client
|
||||
.select()
|
||||
.from(accounts)
|
||||
.where(
|
||||
and(
|
||||
eq(accounts.providerAccountId, account.providerAccountId),
|
||||
eq(accounts.provider, account.provider)
|
||||
)
|
||||
)
|
||||
.leftJoin(users, eq(accounts.userId, users.id))
|
||||
.then((res) => res[0])) ?? null
|
||||
|
||||
if (!dbAccount) {
|
||||
return null
|
||||
}
|
||||
|
||||
return dbAccount.user
|
||||
},
|
||||
async deleteSession(sessionToken) {
|
||||
const session = await client
|
||||
.delete(sessions)
|
||||
.where(eq(sessions.sessionToken, sessionToken))
|
||||
.returning()
|
||||
.then((res) => res[0] ?? null)
|
||||
|
||||
return session
|
||||
},
|
||||
async createVerificationToken(token) {
|
||||
return await client
|
||||
.insert(verificationTokens)
|
||||
.values(token)
|
||||
.returning()
|
||||
.then((res) => res[0])
|
||||
},
|
||||
async useVerificationToken(token) {
|
||||
try {
|
||||
return await client
|
||||
.delete(verificationTokens)
|
||||
.where(
|
||||
and(
|
||||
eq(verificationTokens.identifier, token.identifier),
|
||||
eq(verificationTokens.token, token.token)
|
||||
)
|
||||
)
|
||||
.returning()
|
||||
.then((res) => res[0] ?? null)
|
||||
} catch (err) {
|
||||
throw new Error("No verification token found.")
|
||||
}
|
||||
},
|
||||
async deleteUser(id) {
|
||||
await client
|
||||
.delete(users)
|
||||
.where(eq(users.id, id))
|
||||
.returning()
|
||||
.then((res) => res[0] ?? null)
|
||||
},
|
||||
async unlinkAccount(account) {
|
||||
const { type, provider, providerAccountId, userId } = await client
|
||||
.delete(accounts)
|
||||
.where(
|
||||
and(
|
||||
eq(accounts.providerAccountId, account.providerAccountId),
|
||||
eq(accounts.provider, account.provider)
|
||||
)
|
||||
)
|
||||
.returning()
|
||||
.then((res) => res[0] ?? null)
|
||||
|
||||
return { provider, type, providerAccountId, userId }
|
||||
},
|
||||
}
|
||||
}
|
||||
211
packages/adapter-drizzle/src/lib/sqlite.ts
Normal file
211
packages/adapter-drizzle/src/lib/sqlite.ts
Normal file
@@ -0,0 +1,211 @@
|
||||
import { eq, and } from "drizzle-orm"
|
||||
import {
|
||||
integer,
|
||||
sqliteTable as defaultSqliteTableFn,
|
||||
text,
|
||||
primaryKey,
|
||||
BaseSQLiteDatabase,
|
||||
SQLiteTableFn,
|
||||
} from "drizzle-orm/sqlite-core"
|
||||
|
||||
import type { Adapter, AdapterAccount } from "@auth/core/adapters"
|
||||
|
||||
export function createTables(sqliteTable: SQLiteTableFn) {
|
||||
const users = sqliteTable("user", {
|
||||
id: text("id").notNull().primaryKey(),
|
||||
name: text("name"),
|
||||
email: text("email").notNull(),
|
||||
emailVerified: integer("emailVerified", { mode: "timestamp_ms" }),
|
||||
image: text("image"),
|
||||
})
|
||||
|
||||
const accounts = sqliteTable(
|
||||
"account",
|
||||
{
|
||||
userId: text("userId")
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: "cascade" }),
|
||||
type: text("type").$type<AdapterAccount["type"]>().notNull(),
|
||||
provider: text("provider").notNull(),
|
||||
providerAccountId: text("providerAccountId").notNull(),
|
||||
refresh_token: text("refresh_token"),
|
||||
access_token: text("access_token"),
|
||||
expires_at: integer("expires_at"),
|
||||
token_type: text("token_type"),
|
||||
scope: text("scope"),
|
||||
id_token: text("id_token"),
|
||||
session_state: text("session_state"),
|
||||
},
|
||||
(account) => ({
|
||||
compoundKey: primaryKey(account.provider, account.providerAccountId),
|
||||
})
|
||||
)
|
||||
|
||||
const sessions = sqliteTable("session", {
|
||||
sessionToken: text("sessionToken").notNull().primaryKey(),
|
||||
userId: text("userId")
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: "cascade" }),
|
||||
expires: integer("expires", { mode: "timestamp_ms" }).notNull(),
|
||||
})
|
||||
|
||||
const verificationTokens = sqliteTable(
|
||||
"verificationToken",
|
||||
{
|
||||
identifier: text("identifier").notNull(),
|
||||
token: text("token").notNull(),
|
||||
expires: integer("expires", { mode: "timestamp_ms" }).notNull(),
|
||||
},
|
||||
(vt) => ({
|
||||
compoundKey: primaryKey(vt.identifier, vt.token),
|
||||
})
|
||||
)
|
||||
|
||||
return { users, accounts, sessions, verificationTokens }
|
||||
}
|
||||
|
||||
export type DefaultSchema = ReturnType<typeof createTables>
|
||||
|
||||
export function SQLiteDrizzleAdapter(
|
||||
client: InstanceType<typeof BaseSQLiteDatabase>,
|
||||
tableFn = defaultSqliteTableFn
|
||||
): Adapter {
|
||||
const { users, accounts, sessions, verificationTokens } =
|
||||
createTables(tableFn)
|
||||
|
||||
return {
|
||||
createUser(data) {
|
||||
return client
|
||||
.insert(users)
|
||||
.values({ ...data, id: crypto.randomUUID() })
|
||||
.returning()
|
||||
.get()
|
||||
},
|
||||
getUser(data) {
|
||||
return client.select().from(users).where(eq(users.id, data)).get() ?? null
|
||||
},
|
||||
getUserByEmail(data) {
|
||||
return (
|
||||
client.select().from(users).where(eq(users.email, data)).get() ?? null
|
||||
)
|
||||
},
|
||||
createSession(data) {
|
||||
return client.insert(sessions).values(data).returning().get()
|
||||
},
|
||||
getSessionAndUser(data) {
|
||||
return (
|
||||
client
|
||||
.select({
|
||||
session: sessions,
|
||||
user: users,
|
||||
})
|
||||
.from(sessions)
|
||||
.where(eq(sessions.sessionToken, data))
|
||||
.innerJoin(users, eq(users.id, sessions.userId))
|
||||
.get() ?? null
|
||||
)
|
||||
},
|
||||
updateUser(data) {
|
||||
if (!data.id) {
|
||||
throw new Error("No user id.")
|
||||
}
|
||||
|
||||
return client
|
||||
.update(users)
|
||||
.set(data)
|
||||
.where(eq(users.id, data.id))
|
||||
.returning()
|
||||
.get()
|
||||
},
|
||||
updateSession(data) {
|
||||
return client
|
||||
.update(sessions)
|
||||
.set(data)
|
||||
.where(eq(sessions.sessionToken, data.sessionToken))
|
||||
.returning()
|
||||
.get()
|
||||
},
|
||||
linkAccount(rawAccount) {
|
||||
const updatedAccount = client
|
||||
.insert(accounts)
|
||||
.values(rawAccount)
|
||||
.returning()
|
||||
.get()
|
||||
|
||||
const account: AdapterAccount = {
|
||||
...updatedAccount,
|
||||
type: updatedAccount.type,
|
||||
access_token: updatedAccount.access_token ?? undefined,
|
||||
token_type: updatedAccount.token_type ?? undefined,
|
||||
id_token: updatedAccount.id_token ?? undefined,
|
||||
refresh_token: updatedAccount.refresh_token ?? undefined,
|
||||
scope: updatedAccount.scope ?? undefined,
|
||||
expires_at: updatedAccount.expires_at ?? undefined,
|
||||
session_state: updatedAccount.session_state ?? undefined,
|
||||
}
|
||||
|
||||
return account
|
||||
},
|
||||
getUserByAccount(account) {
|
||||
const results = client
|
||||
.select()
|
||||
.from(accounts)
|
||||
.leftJoin(users, eq(users.id, accounts.userId))
|
||||
.where(
|
||||
and(
|
||||
eq(accounts.provider, account.provider),
|
||||
eq(accounts.providerAccountId, account.providerAccountId)
|
||||
)
|
||||
)
|
||||
.get()
|
||||
|
||||
return results?.user ?? null
|
||||
},
|
||||
deleteSession(sessionToken) {
|
||||
return (
|
||||
client
|
||||
.delete(sessions)
|
||||
.where(eq(sessions.sessionToken, sessionToken))
|
||||
.returning()
|
||||
.get() ?? null
|
||||
)
|
||||
},
|
||||
createVerificationToken(token) {
|
||||
return client.insert(verificationTokens).values(token).returning().get()
|
||||
},
|
||||
useVerificationToken(token) {
|
||||
try {
|
||||
return (
|
||||
client
|
||||
.delete(verificationTokens)
|
||||
.where(
|
||||
and(
|
||||
eq(verificationTokens.identifier, token.identifier),
|
||||
eq(verificationTokens.token, token.token)
|
||||
)
|
||||
)
|
||||
.returning()
|
||||
.get() ?? null
|
||||
)
|
||||
} catch (err) {
|
||||
throw new Error("No verification token found.")
|
||||
}
|
||||
},
|
||||
deleteUser(id) {
|
||||
return client.delete(users).where(eq(users.id, id)).returning().get()
|
||||
},
|
||||
unlinkAccount(account) {
|
||||
client
|
||||
.delete(accounts)
|
||||
.where(
|
||||
and(
|
||||
eq(accounts.providerAccountId, account.providerAccountId),
|
||||
eq(accounts.provider, account.provider)
|
||||
)
|
||||
)
|
||||
.run()
|
||||
|
||||
return undefined
|
||||
},
|
||||
}
|
||||
}
|
||||
41
packages/adapter-drizzle/src/lib/utils.ts
Normal file
41
packages/adapter-drizzle/src/lib/utils.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { MySqlDatabase } from "drizzle-orm/mysql-core"
|
||||
import { PgDatabase } from "drizzle-orm/pg-core"
|
||||
import { BaseSQLiteDatabase } from "drizzle-orm/sqlite-core"
|
||||
|
||||
import type { AnyMySqlTable, MySqlTableFn } from "drizzle-orm/mysql-core"
|
||||
import type { AnyPgTable, PgTableFn } from "drizzle-orm/pg-core"
|
||||
import type { AnySQLiteTable, SQLiteTableFn } from "drizzle-orm/sqlite-core"
|
||||
import type { DefaultSchema as PgSchema } from "./pg.js"
|
||||
import type { DefaultSchema as MySqlSchema } from "./mysql.js"
|
||||
import type { DefaultSchema as SQLiteSchema } from "./sqlite.js"
|
||||
|
||||
export type AnyMySqlDatabase = MySqlDatabase<any, any>
|
||||
export type AnyPgDatabase = PgDatabase<any, any, any>
|
||||
export type AnySQLiteDatabase = BaseSQLiteDatabase<any, any, any, any>
|
||||
|
||||
export interface MinimumSchema {
|
||||
mysql: MySqlSchema & Record<string, AnyMySqlTable>
|
||||
pg: PgSchema & Record<string, AnyPgTable>
|
||||
sqlite: SQLiteSchema & Record<string, AnySQLiteTable>
|
||||
}
|
||||
|
||||
export type SqlFlavorOptions =
|
||||
| AnyMySqlDatabase
|
||||
| AnyPgDatabase
|
||||
| AnySQLiteDatabase
|
||||
|
||||
export type ClientFlavors<Flavor> = Flavor extends AnyMySqlDatabase
|
||||
? MinimumSchema["mysql"]
|
||||
: Flavor extends AnyPgDatabase
|
||||
? MinimumSchema["pg"]
|
||||
: Flavor extends AnySQLiteDatabase
|
||||
? MinimumSchema["sqlite"]
|
||||
: never
|
||||
|
||||
export type TableFn<Flavor> = Flavor extends AnyMySqlDatabase
|
||||
? MySqlTableFn
|
||||
: Flavor extends AnyPgDatabase
|
||||
? PgTableFn
|
||||
: Flavor extends AnySQLiteDatabase
|
||||
? SQLiteTableFn
|
||||
: AnySQLiteTable
|
||||
43
packages/adapter-drizzle/tests/fixtures.ts
Normal file
43
packages/adapter-drizzle/tests/fixtures.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
// This work is needed as workaround to Drizzle truncating millisecond precision.
|
||||
// https://github.com/drizzle-team/drizzle-orm/pull/668
|
||||
|
||||
import { randomUUID } from "../../adapter-test"
|
||||
|
||||
const emailVerified = new Date()
|
||||
emailVerified.setMilliseconds(0)
|
||||
|
||||
const ONE_WEEK_FROM_NOW = new Date(Date.now() + 1000 * 60 * 60 * 24 * 7)
|
||||
ONE_WEEK_FROM_NOW.setMilliseconds(0)
|
||||
const FIFTEEN_MINUTES_FROM_NOW = new Date(Date.now() + 15 * 60 * 1000)
|
||||
FIFTEEN_MINUTES_FROM_NOW.setMilliseconds(0)
|
||||
|
||||
const ONE_MONTH = 1000 * 60 * 60 * 24 * 30
|
||||
const ONE_MONTH_FROM_NOW = new Date(Date.now() + ONE_MONTH)
|
||||
ONE_MONTH_FROM_NOW.setMilliseconds(0)
|
||||
|
||||
export const fixtures = {
|
||||
user: {
|
||||
email: "fill@murray.com",
|
||||
image: "https://www.fillmurray.com/460/300",
|
||||
name: "Fill Murray",
|
||||
emailVerified,
|
||||
},
|
||||
session: {
|
||||
sessionToken: randomUUID(),
|
||||
expires: ONE_WEEK_FROM_NOW,
|
||||
},
|
||||
sessionUpdateExpires: ONE_MONTH_FROM_NOW,
|
||||
verificationTokenExpires: FIFTEEN_MINUTES_FROM_NOW,
|
||||
account: {
|
||||
provider: "github",
|
||||
providerAccountId: randomUUID(),
|
||||
type: "oauth",
|
||||
access_token: randomUUID(),
|
||||
expires_at: ONE_MONTH / 1000,
|
||||
id_token: randomUUID(),
|
||||
refresh_token: randomUUID(),
|
||||
token_type: "bearer",
|
||||
scope: "user",
|
||||
session_state: randomUUID(),
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import type { Config } from "drizzle-kit"
|
||||
|
||||
export default {
|
||||
schema: "./tests/mysql/schema.ts",
|
||||
out: "./tests/mysql/.drizzle",
|
||||
driver: "mysql2",
|
||||
dbCredentials: {
|
||||
host: "localhost",
|
||||
user: "root",
|
||||
password: "password",
|
||||
database: "next-auth",
|
||||
},
|
||||
} satisfies Config
|
||||
@@ -0,0 +1,71 @@
|
||||
import { runBasicTests } from "../../../adapter-test"
|
||||
import { DrizzleAdapter } from "../../src"
|
||||
import { db, sessions, verificationTokens, accounts, users } from "./schema"
|
||||
import { eq, and } from "drizzle-orm"
|
||||
import { fixtures } from "../fixtures"
|
||||
|
||||
globalThis.crypto ??= require("node:crypto").webcrypto
|
||||
|
||||
runBasicTests({
|
||||
adapter: DrizzleAdapter(db),
|
||||
fixtures,
|
||||
db: {
|
||||
connect: async () => {
|
||||
await Promise.all([
|
||||
db.delete(sessions),
|
||||
db.delete(accounts),
|
||||
db.delete(verificationTokens),
|
||||
db.delete(users),
|
||||
])
|
||||
},
|
||||
disconnect: async () => {
|
||||
await Promise.all([
|
||||
db.delete(sessions),
|
||||
db.delete(accounts),
|
||||
db.delete(verificationTokens),
|
||||
db.delete(users),
|
||||
])
|
||||
},
|
||||
user: async (id) => {
|
||||
const user = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.id, id))
|
||||
.then((res) => res[0] ?? null)
|
||||
return user
|
||||
},
|
||||
session: async (sessionToken) => {
|
||||
const session = await db
|
||||
.select()
|
||||
.from(sessions)
|
||||
.where(eq(sessions.sessionToken, sessionToken))
|
||||
.then((res) => res[0] ?? null)
|
||||
|
||||
return session
|
||||
},
|
||||
account: (provider_providerAccountId) => {
|
||||
const account = db
|
||||
.select()
|
||||
.from(accounts)
|
||||
.where(
|
||||
eq(
|
||||
accounts.providerAccountId,
|
||||
provider_providerAccountId.providerAccountId
|
||||
)
|
||||
)
|
||||
.then((res) => res[0] ?? null)
|
||||
return account
|
||||
},
|
||||
verificationToken: (identifier_token) =>
|
||||
db
|
||||
.select()
|
||||
.from(verificationTokens)
|
||||
.where(
|
||||
and(
|
||||
eq(verificationTokens.token, identifier_token.token),
|
||||
eq(verificationTokens.identifier, identifier_token.identifier)
|
||||
)
|
||||
)
|
||||
.then((res) => res[0]) ?? null,
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,19 @@
|
||||
import { mysqlTableCreator } from "drizzle-orm/mysql-core"
|
||||
import { drizzle } from "drizzle-orm/mysql2"
|
||||
import { createPool } from "mysql2"
|
||||
import { createTables } from "../../src/lib/mysql"
|
||||
|
||||
const poolConnection = createPool({
|
||||
host: "localhost",
|
||||
user: "root",
|
||||
password: "password",
|
||||
database: "next-auth",
|
||||
})
|
||||
|
||||
const mysqlTable = mysqlTableCreator((name) => `foobar_${name}`)
|
||||
|
||||
export const { users, accounts, sessions, verificationTokens } =
|
||||
createTables(mysqlTable)
|
||||
export const schema = { users, accounts, sessions, verificationTokens }
|
||||
|
||||
export const db = drizzle(poolConnection, { schema })
|
||||
22
packages/adapter-drizzle/tests/mysql-multi-project-schema/test.sh
Executable file
22
packages/adapter-drizzle/tests/mysql-multi-project-schema/test.sh
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo "Initializing container for MySQL tests."
|
||||
|
||||
MYSQL_DATABASE=next-auth
|
||||
MYSQL_ROOT_PASSWORD=password
|
||||
MYSQL_CONTAINER_NAME=next-auth-mysql-test
|
||||
|
||||
docker run -d --rm \
|
||||
-e MYSQL_DATABASE=${MYSQL_DATABASE} \
|
||||
-e MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} \
|
||||
--name "${MYSQL_CONTAINER_NAME}" \
|
||||
-p 3306:3306 \
|
||||
mysql:8 \
|
||||
--default-authentication-plugin=mysql_native_password
|
||||
|
||||
echo "Waiting 15 sec for db to start..." && sleep 15
|
||||
|
||||
drizzle-kit generate:mysql --config=./tests/mysql/drizzle.config.ts
|
||||
drizzle-kit push:mysql --config=./tests/mysql/drizzle.config.ts
|
||||
jest ./tests/mysql/index.test.ts --forceExit
|
||||
docker stop ${MYSQL_CONTAINER_NAME}
|
||||
13
packages/adapter-drizzle/tests/mysql/drizzle.config.ts
Normal file
13
packages/adapter-drizzle/tests/mysql/drizzle.config.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { Config } from "drizzle-kit"
|
||||
|
||||
export default {
|
||||
schema: "./tests/mysql/schema.ts",
|
||||
out: "./tests/mysql/.drizzle",
|
||||
driver: "mysql2",
|
||||
dbCredentials: {
|
||||
host: "localhost",
|
||||
user: "root",
|
||||
password: "password",
|
||||
database: "next-auth",
|
||||
},
|
||||
} satisfies Config
|
||||
71
packages/adapter-drizzle/tests/mysql/index.test.ts
Normal file
71
packages/adapter-drizzle/tests/mysql/index.test.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { runBasicTests } from "../../../adapter-test"
|
||||
import { DrizzleAdapter } from "../../src"
|
||||
import { db, sessions, verificationTokens, accounts, users } from "./schema"
|
||||
import { eq, and } from "drizzle-orm"
|
||||
import { fixtures } from "../fixtures"
|
||||
|
||||
globalThis.crypto ??= require("node:crypto").webcrypto
|
||||
|
||||
runBasicTests({
|
||||
adapter: DrizzleAdapter(db),
|
||||
fixtures,
|
||||
db: {
|
||||
connect: async () => {
|
||||
await Promise.all([
|
||||
db.delete(sessions),
|
||||
db.delete(accounts),
|
||||
db.delete(verificationTokens),
|
||||
db.delete(users),
|
||||
])
|
||||
},
|
||||
disconnect: async () => {
|
||||
await Promise.all([
|
||||
db.delete(sessions),
|
||||
db.delete(accounts),
|
||||
db.delete(verificationTokens),
|
||||
db.delete(users),
|
||||
])
|
||||
},
|
||||
user: async (id) => {
|
||||
const user = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.id, id))
|
||||
.then((res) => res[0] ?? null)
|
||||
return user
|
||||
},
|
||||
session: async (sessionToken) => {
|
||||
const session = await db
|
||||
.select()
|
||||
.from(sessions)
|
||||
.where(eq(sessions.sessionToken, sessionToken))
|
||||
.then((res) => res[0] ?? null)
|
||||
|
||||
return session
|
||||
},
|
||||
account: (provider_providerAccountId) => {
|
||||
const account = db
|
||||
.select()
|
||||
.from(accounts)
|
||||
.where(
|
||||
eq(
|
||||
accounts.providerAccountId,
|
||||
provider_providerAccountId.providerAccountId
|
||||
)
|
||||
)
|
||||
.then((res) => res[0] ?? null)
|
||||
return account
|
||||
},
|
||||
verificationToken: (identifier_token) =>
|
||||
db
|
||||
.select()
|
||||
.from(verificationTokens)
|
||||
.where(
|
||||
and(
|
||||
eq(verificationTokens.token, identifier_token.token),
|
||||
eq(verificationTokens.identifier, identifier_token.identifier)
|
||||
)
|
||||
)
|
||||
.then((res) => res[0]) ?? null,
|
||||
},
|
||||
})
|
||||
17
packages/adapter-drizzle/tests/mysql/schema.ts
Normal file
17
packages/adapter-drizzle/tests/mysql/schema.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { mysqlTable } from "drizzle-orm/mysql-core"
|
||||
import { drizzle } from "drizzle-orm/mysql2"
|
||||
import { createPool } from "mysql2"
|
||||
import { createTables } from "../../src/lib/mysql"
|
||||
|
||||
const poolConnection = createPool({
|
||||
host: "localhost",
|
||||
user: "root",
|
||||
password: "password",
|
||||
database: "next-auth",
|
||||
})
|
||||
|
||||
export const { users, accounts, sessions, verificationTokens } =
|
||||
createTables(mysqlTable)
|
||||
export const schema = { users, accounts, sessions, verificationTokens }
|
||||
|
||||
export const db = drizzle(poolConnection, { schema })
|
||||
22
packages/adapter-drizzle/tests/mysql/test.sh
Executable file
22
packages/adapter-drizzle/tests/mysql/test.sh
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo "Initializing container for MySQL tests."
|
||||
|
||||
MYSQL_DATABASE=next-auth
|
||||
MYSQL_ROOT_PASSWORD=password
|
||||
MYSQL_CONTAINER_NAME=next-auth-mysql-test
|
||||
|
||||
docker run -d --rm \
|
||||
-e MYSQL_DATABASE=${MYSQL_DATABASE} \
|
||||
-e MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} \
|
||||
--name "${MYSQL_CONTAINER_NAME}" \
|
||||
-p 3306:3306 \
|
||||
mysql:8 \
|
||||
--default-authentication-plugin=mysql_native_password
|
||||
|
||||
echo "Waiting 15 sec for db to start..." && sleep 15
|
||||
|
||||
drizzle-kit generate:mysql --config=./tests/mysql/drizzle.config.ts
|
||||
drizzle-kit push:mysql --config=./tests/mysql/drizzle.config.ts
|
||||
jest ./tests/mysql/index.test.ts --forceExit
|
||||
docker stop ${MYSQL_CONTAINER_NAME}
|
||||
@@ -0,0 +1,13 @@
|
||||
import type { Config } from "drizzle-kit"
|
||||
|
||||
export default {
|
||||
schema: "./tests/pg/schema.ts",
|
||||
out: "./tests/pg/.drizzle",
|
||||
dbCredentials: {
|
||||
database: "nextauth",
|
||||
host: "nextauth",
|
||||
user: "nextauth",
|
||||
password: "nextauth",
|
||||
port: 5432,
|
||||
},
|
||||
} satisfies Config
|
||||
@@ -0,0 +1,65 @@
|
||||
import { runBasicTests } from "../../../adapter-test"
|
||||
import { DrizzleAdapter } from "../../src"
|
||||
import { db, accounts, sessions, users, verificationTokens } from "./schema"
|
||||
import { eq, and } from "drizzle-orm"
|
||||
import { fixtures } from "../fixtures"
|
||||
|
||||
globalThis.crypto ??= require("node:crypto").webcrypto
|
||||
|
||||
runBasicTests({
|
||||
adapter: DrizzleAdapter(db),
|
||||
fixtures,
|
||||
db: {
|
||||
connect: async () => {
|
||||
await Promise.all([
|
||||
db.delete(sessions),
|
||||
db.delete(accounts),
|
||||
db.delete(verificationTokens),
|
||||
db.delete(users),
|
||||
])
|
||||
},
|
||||
disconnect: async () => {
|
||||
await Promise.all([
|
||||
db.delete(sessions),
|
||||
db.delete(accounts),
|
||||
db.delete(verificationTokens),
|
||||
db.delete(users),
|
||||
])
|
||||
},
|
||||
user: async (id) =>
|
||||
db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.id, id))
|
||||
.then((res) => res[0] ?? null),
|
||||
session: (sessionToken) =>
|
||||
db
|
||||
.select()
|
||||
.from(sessions)
|
||||
.where(eq(sessions.sessionToken, sessionToken))
|
||||
.then((res) => res[0] ?? null),
|
||||
account: (provider_providerAccountId) => {
|
||||
return db
|
||||
.select()
|
||||
.from(accounts)
|
||||
.where(
|
||||
eq(
|
||||
accounts.providerAccountId,
|
||||
provider_providerAccountId.providerAccountId
|
||||
)
|
||||
)
|
||||
.then((res) => res[0] ?? null)
|
||||
},
|
||||
verificationToken: (identifier_token) =>
|
||||
db
|
||||
.select()
|
||||
.from(verificationTokens)
|
||||
.where(
|
||||
and(
|
||||
eq(verificationTokens.token, identifier_token.token),
|
||||
eq(verificationTokens.identifier, identifier_token.identifier)
|
||||
)
|
||||
)
|
||||
.then((res) => res[0] ?? null),
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,10 @@
|
||||
import { migrate } from "drizzle-orm/postgres-js/migrator"
|
||||
import { db } from "./schema"
|
||||
|
||||
const migrator = async () => {
|
||||
await migrate(db, { migrationsFolder: "./tests/pg/.drizzle" })
|
||||
}
|
||||
|
||||
migrator()
|
||||
.then(() => process.exit(0))
|
||||
.catch(() => process.exit(1))
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user