mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
Compare commits
57 Commits
@auth/fire
...
@auth/core
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
1
.github/ISSUE_TEMPLATE/2_bug_provider.yml
vendored
1
.github/ISSUE_TEMPLATE/2_bug_provider.yml
vendored
@@ -37,6 +37,7 @@ body:
|
|||||||
- "Bungie"
|
- "Bungie"
|
||||||
- "Cognito"
|
- "Cognito"
|
||||||
- "Coinbase"
|
- "Coinbase"
|
||||||
|
- "Descope"
|
||||||
- "Discord"
|
- "Discord"
|
||||||
- "Dropbox"
|
- "Dropbox"
|
||||||
- "EVE Online"
|
- "EVE Online"
|
||||||
|
|||||||
88
.github/workflows/release.yml
vendored
88
.github/workflows/release.yml
vendored
@@ -15,47 +15,49 @@ on:
|
|||||||
type: choice
|
type: choice
|
||||||
description: Package name (npm)
|
description: Package name (npm)
|
||||||
options:
|
options:
|
||||||
- "@auth/core"
|
- "@auth/core"
|
||||||
- "@auth/nextjs"
|
- "@auth/nextjs"
|
||||||
- "@auth/dgraph-adapter"
|
- "@auth/dgraph-adapter"
|
||||||
- "@auth/drizzle-adapter"
|
- "@auth/drizzle-adapter"
|
||||||
- "@auth/dynamodb-adapter"
|
- "@auth/dynamodb-adapter"
|
||||||
- "@auth/fauna-adapter"
|
- "@auth/fauna-adapter"
|
||||||
- "@auth/firebase-adapter"
|
- "@auth/firebase-adapter"
|
||||||
- "@auth/mikro-orm-adapter"
|
- "@auth/mikro-orm-adapter"
|
||||||
- "@auth/mongodb-adapter"
|
- "@auth/mongodb-adapter"
|
||||||
- "@auth/neo4j-adapter"
|
- "@auth/neo4j-adapter"
|
||||||
- "@auth/pouchdb-adapter"
|
- "@auth/pouchdb-adapter"
|
||||||
- "@auth/prisma-adapter"
|
- "@auth/prisma-adapter"
|
||||||
- "@auth/sequelize-adapter"
|
- "@auth/sequelize-adapter"
|
||||||
- "@auth/supabase-adapter"
|
- "@auth/supabase-adapter"
|
||||||
- "@auth/typeorm-adapter"
|
- "@auth/typeorm-adapter"
|
||||||
- "@auth/upstash-redis-adapter"
|
- "@auth/upstash-redis-adapter"
|
||||||
- "@auth/xata-adapter"
|
- "@auth/xata-adapter"
|
||||||
- "next-auth"
|
- "next-auth"
|
||||||
# TODO: Infer from package name
|
# TODO: Infer from package name
|
||||||
path:
|
path:
|
||||||
type: choice
|
type: choice
|
||||||
description: Directory name (packages/*)
|
description: Directory name (packages/*)
|
||||||
options:
|
options:
|
||||||
- "core"
|
- "core"
|
||||||
- "frameworks-nextjs"
|
- "frameworks-nextjs"
|
||||||
- "adapter-dgraph"
|
- "adapter-dgraph"
|
||||||
- "adapter-drizzle"
|
- "adapter-drizzle"
|
||||||
- "adapter-dynamodb"
|
- "adapter-dynamodb"
|
||||||
- "adapter-fauna"
|
- "adapter-fauna"
|
||||||
- "adapter-firebase"
|
- "adapter-firebase"
|
||||||
- "adapter-mikro-orm"
|
- "adapter-mikro-orm"
|
||||||
- "adapter-mongodb"
|
- "adapter-mongodb"
|
||||||
- "adapter-neo4j"
|
- "adapter-neo4j"
|
||||||
- "adapter-pouchdb"
|
- "adapter-pouchdb"
|
||||||
- "adapter-prisma"
|
- "adapter-prisma"
|
||||||
- "adapter-sequelize"
|
- "adapter-sequelize"
|
||||||
- "adapter-supabase"
|
- "adapter-supabase"
|
||||||
- "adapter-typeorm"
|
- "adapter-typeorm"
|
||||||
- "adapter-upstash-redis"
|
- "adapter-upstash-redis"
|
||||||
- "adapter-xata"
|
- "adapter-xata"
|
||||||
- "next-auth"
|
- "next-auth"
|
||||||
|
env:
|
||||||
|
FORCE_COLOR: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
@@ -75,6 +77,11 @@ jobs:
|
|||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
- name: Build
|
||||||
|
run: pnpm build
|
||||||
|
env:
|
||||||
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||||
|
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: pnpm test
|
run: pnpm test
|
||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
@@ -82,7 +89,12 @@ jobs:
|
|||||||
UPSTASH_REDIS_URL: ${{ secrets.UPSTASH_REDIS_URL }}
|
UPSTASH_REDIS_URL: ${{ secrets.UPSTASH_REDIS_URL }}
|
||||||
UPSTASH_REDIS_KEY: ${{ secrets.UPSTASH_REDIS_KEY }}
|
UPSTASH_REDIS_KEY: ${{ secrets.UPSTASH_REDIS_KEY }}
|
||||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||||
|
- name: Upload Turbo artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: turbo-report
|
||||||
|
path: .turbo/runs/
|
||||||
# - name: Run E2E tests
|
# - name: Run E2E tests
|
||||||
# if: github.repository == 'nextauthjs/next-auth'
|
# if: github.repository == 'nextauthjs/next-auth'
|
||||||
# run: pnpm e2e
|
# run: pnpm e2e
|
||||||
@@ -91,7 +103,7 @@ jobs:
|
|||||||
# AUTH0_USERNAME: ${{ secrets.AUTH0_USERNAME }}
|
# AUTH0_USERNAME: ${{ secrets.AUTH0_USERNAME }}
|
||||||
# AUTH0_PASSWORD: ${{ secrets.AUTH0_PASSWORD }}
|
# AUTH0_PASSWORD: ${{ secrets.AUTH0_PASSWORD }}
|
||||||
# TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
# TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||||
# TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
# TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||||
# - name: Upload E2E artifacts
|
# - name: Upload E2E artifacts
|
||||||
# if: github.repository == 'nextauthjs/next-auth'
|
# if: github.repository == 'nextauthjs/next-auth'
|
||||||
# uses: actions/upload-artifact@v3
|
# uses: actions/upload-artifact@v3
|
||||||
|
|||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -65,6 +65,7 @@ packages/adapter-prisma/prisma/dev.db
|
|||||||
packages/adapter-prisma/prisma/migrations
|
packages/adapter-prisma/prisma/migrations
|
||||||
db.sqlite
|
db.sqlite
|
||||||
packages/adapter-supabase/supabase/.branches
|
packages/adapter-supabase/supabase/.branches
|
||||||
|
packages/adapter-drizzle/.drizzle
|
||||||
|
|
||||||
# Tests
|
# Tests
|
||||||
coverage
|
coverage
|
||||||
@@ -97,5 +98,7 @@ packages/frameworks-sveltekit/vite.config.js.timestamp-*
|
|||||||
packages/frameworks-sveltekit/vite.config.ts.timestamp-*
|
packages/frameworks-sveltekit/vite.config.ts.timestamp-*
|
||||||
|
|
||||||
# Adapters
|
# Adapters
|
||||||
|
|
||||||
docs/docs/reference/adapter
|
docs/docs/reference/adapter
|
||||||
|
|
||||||
|
## Drizzle migration folder
|
||||||
|
.drizzle
|
||||||
@@ -13,6 +13,9 @@ AUTH0_ID=
|
|||||||
AUTH0_SECRET=
|
AUTH0_SECRET=
|
||||||
AUTH0_ISSUER=
|
AUTH0_ISSUER=
|
||||||
|
|
||||||
|
DESCOPE_ID=
|
||||||
|
DESCOPE_SECRET=
|
||||||
|
|
||||||
KEYCLOAK_ID=
|
KEYCLOAK_ID=
|
||||||
KEYCLOAK_SECRET=
|
KEYCLOAK_SECRET=
|
||||||
KEYCLOAK_ISSUER=
|
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:
|
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
|
[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)
|
||||||
@@ -22,6 +22,9 @@ BEYOND_IDENTITY_CLIENT_ID=
|
|||||||
BEYOND_IDENTITY_CLIENT_SECRET=
|
BEYOND_IDENTITY_CLIENT_SECRET=
|
||||||
BEYOND_IDENTITY_ISSUER=
|
BEYOND_IDENTITY_ISSUER=
|
||||||
|
|
||||||
|
DESCOPE_ID=
|
||||||
|
DESCOPE_SECRET=
|
||||||
|
|
||||||
GITHUB_ID=
|
GITHUB_ID=
|
||||||
GITHUB_SECRET=
|
GITHUB_SECRET=
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
"@prisma/client": "^3",
|
"@prisma/client": "^3",
|
||||||
"@supabase/supabase-js": "^2.0.5",
|
"@supabase/supabase-js": "^2.0.5",
|
||||||
"faunadb": "^4",
|
"faunadb": "^4",
|
||||||
"next": "13.3.0",
|
"next": "13.4.0",
|
||||||
"next-auth": "workspace:*",
|
"next-auth": "workspace:*",
|
||||||
"nodemailer": "^6",
|
"nodemailer": "^6",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
|
|||||||
@@ -2,14 +2,15 @@ import { Auth, type AuthConfig } from "@auth/core"
|
|||||||
|
|
||||||
// Providers
|
// Providers
|
||||||
import Apple from "@auth/core/providers/apple"
|
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 Auth0 from "@auth/core/providers/auth0"
|
||||||
import AzureAD from "@auth/core/providers/azure-ad"
|
import AzureAD from "@auth/core/providers/azure-ad"
|
||||||
import AzureB2C from "@auth/core/providers/azure-ad-b2c"
|
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 BoxyHQSAML from "@auth/core/providers/boxyhq-saml"
|
||||||
// import Cognito from "@auth/core/providers/cognito"
|
// import Cognito from "@auth/core/providers/cognito"
|
||||||
import Credentials from "@auth/core/providers/credentials"
|
import Credentials from "@auth/core/providers/credentials"
|
||||||
|
import Descope from "@auth/core/providers/descope"
|
||||||
import Discord from "@auth/core/providers/discord"
|
import Discord from "@auth/core/providers/discord"
|
||||||
import DuendeIDS6 from "@auth/core/providers/duende-identity-server6"
|
import DuendeIDS6 from "@auth/core/providers/duende-identity-server6"
|
||||||
// import Email from "@auth/core/providers/email"
|
// import Email from "@auth/core/providers/email"
|
||||||
@@ -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: "" }
|
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 }),
|
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 }),
|
// 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 }),
|
Auth0({ clientId: process.env.AUTH0_ID, clientSecret: process.env.AUTH0_SECRET, issuer: process.env.AUTH0_ISSUER }),
|
||||||
AzureAD({
|
AzureAD({
|
||||||
clientId: process.env.AZURE_AD_CLIENT_ID,
|
clientId: process.env.AZURE_AD_CLIENT_ID,
|
||||||
@@ -94,19 +95,20 @@ export const authConfig: AuthConfig = {
|
|||||||
tenantId: process.env.AZURE_AD_TENANT_ID,
|
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 }),
|
AzureB2C({ clientId: process.env.AZURE_B2C_ID, clientSecret: process.env.AZURE_B2C_SECRET, issuer: process.env.AZURE_B2C_ISSUER }),
|
||||||
BeyondIdentity({
|
// BeyondIdentity({
|
||||||
clientId: process.env.BEYOND_IDENTITY_CLIENT_ID,
|
// clientId: process.env.BEYOND_IDENTITY_CLIENT_ID,
|
||||||
clientSecret: process.env.BEYOND_IDENTITY_CLIENT_SECRET,
|
// clientSecret: process.env.BEYOND_IDENTITY_CLIENT_SECRET,
|
||||||
issuer: process.env.BEYOND_IDENTITY_ISSUER,
|
// issuer: process.env.BEYOND_IDENTITY_ISSUER,
|
||||||
}),
|
// }),
|
||||||
BoxyHQSAML({ issuer: "https://jackson-demo.boxyhq.com", clientId: "tenant=boxyhq.com&product=saml-demo.boxyhq.com", clientSecret: "dummy" }),
|
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 }),
|
// 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 }),
|
Discord({ clientId: process.env.DISCORD_ID, clientSecret: process.env.DISCORD_SECRET }),
|
||||||
DuendeIDS6({ clientId: "interactive.confidential", clientSecret: "secret", issuer: "https://demo.duendesoftware.com" }),
|
DuendeIDS6({ clientId: "interactive.confidential", clientSecret: "secret", issuer: "https://demo.duendesoftware.com" }),
|
||||||
Facebook({ clientId: process.env.FACEBOOK_ID, clientSecret: process.env.FACEBOOK_SECRET }),
|
Facebook({ clientId: process.env.FACEBOOK_ID, clientSecret: process.env.FACEBOOK_SECRET }),
|
||||||
Foursquare({ clientId: process.env.FOURSQUARE_ID, clientSecret: process.env.FOURSQUARE_SECRET }),
|
Foursquare({ clientId: process.env.FOURSQUARE_ID, clientSecret: process.env.FOURSQUARE_SECRET }),
|
||||||
Freshbooks({ clientId: process.env.FRESHBOOKS_ID, clientSecret: process.env.FRESHBOOKS_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 }),
|
Gitlab({ clientId: process.env.GITLAB_ID, clientSecret: process.env.GITLAB_SECRET }),
|
||||||
Google({ clientId: process.env.GOOGLE_ID, clientSecret: process.env.GOOGLE_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 }),
|
// IDS4({ clientId: process.env.IDS4_ID, clientSecret: process.env.IDS4_SECRET, issuer: process.env.IDS4_ISSUER }),
|
||||||
@@ -115,7 +117,7 @@ export const authConfig: AuthConfig = {
|
|||||||
Line({ clientId: process.env.LINE_ID, clientSecret: process.env.LINE_SECRET }),
|
Line({ clientId: process.env.LINE_ID, clientSecret: process.env.LINE_SECRET }),
|
||||||
LinkedIn({ clientId: process.env.LINKEDIN_ID, clientSecret: process.env.LINKEDIN_SECRET }),
|
LinkedIn({ clientId: process.env.LINKEDIN_ID, clientSecret: process.env.LINKEDIN_SECRET }),
|
||||||
Mailchimp({ clientId: process.env.MAILCHIMP_ID, clientSecret: process.env.MAILCHIMP_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 }),
|
// 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 }),
|
Osu({ clientId: process.env.OSU_CLIENT_ID, clientSecret: process.env.OSU_CLIENT_SECRET }),
|
||||||
Patreon({ clientId: process.env.PATREON_ID, clientSecret: process.env.PATREON_SECRET }),
|
Patreon({ clientId: process.env.PATREON_ID, clientSecret: process.env.PATREON_SECRET }),
|
||||||
@@ -160,4 +162,4 @@ function AuthHandler(...args: any[]) {
|
|||||||
|
|
||||||
export default AuthHandler(authConfig)
|
export default AuthHandler(authConfig)
|
||||||
|
|
||||||
export const config = { runtime: "experimental-edge" }
|
export const config = { runtime: "edge" }
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
"svelte": "3.55.0",
|
"svelte": "3.55.0",
|
||||||
"svelte-check": "2.10.2",
|
"svelte-check": "2.10.2",
|
||||||
"typescript": "4.9.4",
|
"typescript": "4.9.4",
|
||||||
"vite": "4.0.1"
|
"vite": "4.0.5"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@auth/core": "workspace:*",
|
"@auth/core": "workspace:*",
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ AUTH0_ID=
|
|||||||
AUTH0_SECRET=
|
AUTH0_SECRET=
|
||||||
AUTH0_ISSUER=
|
AUTH0_ISSUER=
|
||||||
|
|
||||||
|
DESCOPE_ID=
|
||||||
|
DESCOPE_SECRET=
|
||||||
|
|
||||||
FACEBOOK_ID=
|
FACEBOOK_ID=
|
||||||
FACEBOOK_SECRET=
|
FACEBOOK_SECRET=
|
||||||
|
|
||||||
|
|||||||
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
|
GOOGLE_SECRET: string
|
||||||
AUTH0_ID: string
|
AUTH0_ID: string
|
||||||
AUTH0_SECRET: string
|
AUTH0_SECRET: string
|
||||||
|
DESCOPE_ID: string
|
||||||
|
DESCOPE_SECRET: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
To start a local environment of this project, please do the following.
|
||||||
|
|
||||||
1. Clone the repository.
|
1. Clone the repo:
|
||||||
|
|
||||||
```bash
|
```sh
|
||||||
$ git clone https://github.com/nextauthjs/docs.git
|
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
|
```sh
|
||||||
$ npm install
|
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
|
```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)!
|
And thats all! Now you should have a local copy of this docs site running at [localhost:3000](http://localhost:3000)!
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ npm install -D nodemailer
|
|||||||
|
|
||||||
## 2. Setting up a SMTP service
|
## 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
|
:::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
|
For this tutorial, we're going to be using [Sendgrid](https://sendgrid.com/), but any of the services linked above should work the same
|
||||||
|
|||||||
@@ -100,11 +100,12 @@ NextAuth.js provides [`useSession()`](/reference/react/#usesession) - a [React H
|
|||||||
|
|
||||||
```ts title="pages/_app.tsx"
|
```ts title="pages/_app.tsx"
|
||||||
import { SessionProvider } from "next-auth/react"
|
import { SessionProvider } from "next-auth/react"
|
||||||
|
import type { AppProps } from 'next/app'
|
||||||
|
|
||||||
export default function App({
|
export default function App({
|
||||||
Component,
|
Component,
|
||||||
pageProps: { session, ...pageProps },
|
pageProps: { session, ...pageProps },
|
||||||
}) {
|
}: AppProps) {
|
||||||
return (
|
return (
|
||||||
<SessionProvider session={session}>
|
<SessionProvider session={session}>
|
||||||
<Component {...pageProps} />
|
<Component {...pageProps} />
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title: Using a database adapter
|
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
|
:::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).
|
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).
|
||||||
|
|||||||
@@ -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**:
|
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`)
|
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`)
|
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.
|
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>
|
<details>
|
||||||
<summary>
|
<summary>
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ You can override any of the options to suit your own use case.
|
|||||||
## Configuration
|
## 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`.
|
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.
|
3. There are two ways to configure the SMTP server connection.
|
||||||
|
|
||||||
You can either use a connection string or a `nodemailer` configuration object.
|
You can either use a connection string or a `nodemailer` configuration object.
|
||||||
@@ -40,8 +40,8 @@ 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.
|
Create an `.env` file to the root of your project and add the connection string and email address.
|
||||||
|
|
||||||
```js title=".env" {1}
|
```js title=".env" {1}
|
||||||
EMAIL_SERVER=smtp://username:password@smtp.example.com:587
|
EMAIL_SERVER=smtp://username:password@smtp.forwardemail.net:587
|
||||||
EMAIL_FROM=noreply@example.com
|
EMAIL_FROM=support@example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
Now you can add the email provider like this:
|
Now you can add the email provider like this:
|
||||||
@@ -64,7 +64,7 @@ In your `.env` file in the root of your project simply add the configuration obj
|
|||||||
```js title=".env"
|
```js title=".env"
|
||||||
EMAIL_SERVER_USER=username
|
EMAIL_SERVER_USER=username
|
||||||
EMAIL_SERVER_PASSWORD=password
|
EMAIL_SERVER_PASSWORD=password
|
||||||
EMAIL_SERVER_HOST=smtp.example.com
|
EMAIL_SERVER_HOST=smtp.forwardemail.net
|
||||||
EMAIL_SERVER_PORT=587
|
EMAIL_SERVER_PORT=587
|
||||||
EMAIL_FROM=noreply@example.com
|
EMAIL_FROM=noreply@example.com
|
||||||
```
|
```
|
||||||
@@ -112,6 +112,7 @@ providers: [
|
|||||||
identifier: email,
|
identifier: email,
|
||||||
url,
|
url,
|
||||||
provider: { server, from },
|
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 */
|
/* your function */
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ Using an Auth.js / NextAuth.js adapter you can connect to any database service o
|
|||||||
<a href="/reference/adapter/dgraph" class="adapter-card">
|
<a href="/reference/adapter/dgraph" class="adapter-card">
|
||||||
<img src="/img/adapters/dgraph.png" width="30" />
|
<img src="/img/adapters/dgraph.png" width="30" />
|
||||||
<h4 class="adapter-card__title">Dgraph Adapter</h4>
|
<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>
|
||||||
<a href="/reference/adapter/dynamodb" class="adapter-card">
|
<a href="/reference/adapter/dynamodb" class="adapter-card">
|
||||||
<img src="/img/adapters/dynamodb.png" width="30" />
|
<img src="/img/adapters/dynamodb.png" width="30" />
|
||||||
@@ -67,10 +71,8 @@ Using an Auth.js / NextAuth.js adapter you can connect to any database service o
|
|||||||
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).
|
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
|
## 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:
|
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
|
```mermaid
|
||||||
|
|||||||
@@ -265,6 +265,7 @@ const docusaurusConfig = {
|
|||||||
? []
|
? []
|
||||||
: [
|
: [
|
||||||
typedocAdapter("Dgraph"),
|
typedocAdapter("Dgraph"),
|
||||||
|
typedocAdapter("Drizzle"),
|
||||||
typedocAdapter("DynamoDB"),
|
typedocAdapter("DynamoDB"),
|
||||||
typedocAdapter("Fauna"),
|
typedocAdapter("Fauna"),
|
||||||
typedocAdapter("Firebase"),
|
typedocAdapter("Firebase"),
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ module.exports = {
|
|||||||
link: { type: "doc", id: "reference/adapters/index" },
|
link: { type: "doc", id: "reference/adapters/index" },
|
||||||
items: [
|
items: [
|
||||||
{ type: "doc", id: "reference/adapter/dgraph/index" },
|
{ 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/dynamodb/index" },
|
||||||
{ type: "doc", id: "reference/adapter/fauna/index" },
|
{ type: "doc", id: "reference/adapter/fauna/index" },
|
||||||
{ type: "doc", id: "reference/adapter/firebase/index" },
|
{ type: "doc", id: "reference/adapter/firebase/index" },
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const icons = [
|
|||||||
"/img/providers/apple.svg",
|
"/img/providers/apple.svg",
|
||||||
"/img/providers/auth0.svg",
|
"/img/providers/auth0.svg",
|
||||||
"/img/providers/cognito.svg",
|
"/img/providers/cognito.svg",
|
||||||
|
"/img/providers/descope.svg",
|
||||||
"/img/providers/battlenet.svg",
|
"/img/providers/battlenet.svg",
|
||||||
"/img/providers/box.svg",
|
"/img/providers/box.svg",
|
||||||
"/img/providers/facebook.svg",
|
"/img/providers/facebook.svg",
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ html[data-theme="dark"] .navbar__item.navbar__link[href*="npm"]:before {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
color: #000;
|
color: #000;
|
||||||
top: -10px;
|
top: -10px;
|
||||||
right: -45px;
|
right: 4px;
|
||||||
font-size: 9px;
|
font-size: 9px;
|
||||||
background-color: #ccc;
|
background-color: #ccc;
|
||||||
padding: 2px 5px;
|
padding: 2px 5px;
|
||||||
|
|||||||
@@ -101,13 +101,13 @@ export default function Home() {
|
|||||||
.fetch("https://api.github.com/repos/nextauthjs/next-auth")
|
.fetch("https://api.github.com/repos/nextauthjs/next-auth")
|
||||||
.then((res) => res.json())
|
.then((res) => res.json())
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
const navLinks = document.getElementsByClassName(
|
const githubLink = document.querySelector(
|
||||||
"navbar__item navbar__link"
|
".navbar__item.navbar__link[href*='github']"
|
||||||
)
|
)
|
||||||
const githubStat = document.createElement("span")
|
const githubStat = document.createElement("span")
|
||||||
githubStat.innerHTML = kFormatter(data.stargazers_count)
|
githubStat.innerHTML = kFormatter(data.stargazers_count)
|
||||||
githubStat.className = "github-counter"
|
githubStat.className = "github-counter"
|
||||||
navLinks[4].appendChild(githubStat)
|
githubLink.appendChild(githubStat)
|
||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
return (
|
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 |
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 |
@@ -7,7 +7,7 @@
|
|||||||
"build:app": "turbo run build --filter=next-auth-app",
|
"build:app": "turbo run build --filter=next-auth-app",
|
||||||
"build:docs": "turbo run build --filter=docs",
|
"build:docs": "turbo run build --filter=docs",
|
||||||
"build": "turbo run build --filter=next-auth --filter=@next-auth/* --filter=@auth/* --no-deps",
|
"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-*",
|
"test": "turbo run test --concurrency=1 --filter=[HEAD^1] --filter=./packages/* --filter=!@*upstash* --filter=!*dynamodb-* --filter=!*app*",
|
||||||
"clean": "turbo run clean --no-cache",
|
"clean": "turbo run clean --no-cache",
|
||||||
"dev:db": "turbo run dev --parallel --continue --filter=next-auth-app...",
|
"dev:db": "turbo run dev --parallel --continue --filter=next-auth-app...",
|
||||||
"dev": "turbo run dev --parallel --continue --filter=next-auth-app... --filter=!./packages/adapter-*",
|
"dev": "turbo run dev --parallel --continue --filter=next-auth-app... --filter=!./packages/adapter-*",
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
"eslint-plugin-svelte3": "^4.0.0",
|
"eslint-plugin-svelte3": "^4.0.0",
|
||||||
"prettier": "2.8.1",
|
"prettier": "2.8.1",
|
||||||
"prettier-plugin-svelte": "^2.8.1",
|
"prettier-plugin-svelte": "^2.8.1",
|
||||||
"turbo": "1.10.1",
|
"turbo": "^1.10.3",
|
||||||
"typescript": "4.9.4"
|
"typescript": "4.9.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
1
packages/adapter-drizzle/.npmrc
Normal file
1
packages/adapter-drizzle/.npmrc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
||||||
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://pbs.twimg.com/profile_images/1598308842391179266/CtXrfLnk_400x400.jpg"/>
|
||||||
|
</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.1.0",
|
||||||
|
"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": [
|
||||||
|
"*.js",
|
||||||
|
"*.d.ts*",
|
||||||
|
"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": {
|
||||||
|
"@next-auth/adapter-test": "workspace:*",
|
||||||
|
"@next-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": "@next-auth/adapter-test/jest"
|
||||||
|
}
|
||||||
|
}
|
||||||
268
packages/adapter-drizzle/src/index.ts
Normal file
268
packages/adapter-drizzle/src/index.ts
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
/**
|
||||||
|
* <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 { mySqlDrizzleAdapter } from "./lib/mysql.js"
|
||||||
|
import { pgDrizzleAdapter } from "./lib/pg.js"
|
||||||
|
import { SQLiteDrizzleAdapter } from "./lib/sqlite.js"
|
||||||
|
import {
|
||||||
|
isMySqlDatabase,
|
||||||
|
isPgDatabase,
|
||||||
|
isSQLiteDatabase,
|
||||||
|
SqlFlavorOptions,
|
||||||
|
} from "./lib/utils.js"
|
||||||
|
|
||||||
|
import type { Adapter } from "@auth/core/adapters"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the adapter to your `app/api/[...nextauth]/route.js` 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,
|
||||||
|
* }),
|
||||||
|
* ],
|
||||||
|
* })
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* ## 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("users", {
|
||||||
|
* id: text("id").notNull().primaryKey(),
|
||||||
|
* name: text("name"),
|
||||||
|
* email: text("email").notNull(),
|
||||||
|
* emailVerified: timestamp("emailVerified", { mode: "date" }),
|
||||||
|
* image: text("image"),
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
* export const accounts = pgTable(
|
||||||
|
* "accounts",
|
||||||
|
* {
|
||||||
|
* 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("sessions", {
|
||||||
|
* 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("users", {
|
||||||
|
* 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(
|
||||||
|
* "accounts",
|
||||||
|
* {
|
||||||
|
* 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("sessions", {
|
||||||
|
* 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("users", {
|
||||||
|
* 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(
|
||||||
|
* "accounts",
|
||||||
|
* {
|
||||||
|
* 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("sessions", {
|
||||||
|
* 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
|
||||||
|
): Adapter {
|
||||||
|
if (isMySqlDatabase(db)) {
|
||||||
|
// We need to cast to unknown since the type overlaps (PScale is MySQL based)
|
||||||
|
return mySqlDrizzleAdapter(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPgDatabase(db)) {
|
||||||
|
return pgDrizzleAdapter(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSQLiteDatabase(db)) {
|
||||||
|
return SQLiteDrizzleAdapter(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error("Unsupported database type in Auth.js Drizzle adapter.")
|
||||||
|
}
|
||||||
255
packages/adapter-drizzle/src/lib/mysql.ts
Normal file
255
packages/adapter-drizzle/src/lib/mysql.ts
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
import { and, eq } from "drizzle-orm"
|
||||||
|
import {
|
||||||
|
int,
|
||||||
|
timestamp,
|
||||||
|
mysqlTable,
|
||||||
|
primaryKey,
|
||||||
|
varchar,
|
||||||
|
} from "drizzle-orm/mysql-core"
|
||||||
|
|
||||||
|
import type { Adapter, AdapterAccount } from "@auth/core/adapters"
|
||||||
|
import type { MySql2Database } from "drizzle-orm/mysql2"
|
||||||
|
|
||||||
|
export const users = mysqlTable("users", {
|
||||||
|
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(
|
||||||
|
"accounts",
|
||||||
|
{
|
||||||
|
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("sessions", {
|
||||||
|
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),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
export const schema = { users, accounts, sessions, verificationTokens }
|
||||||
|
export type DefaultSchema = typeof schema
|
||||||
|
|
||||||
|
export function mySqlDrizzleAdapter(
|
||||||
|
client: MySql2Database<Record<string, never>>
|
||||||
|
): Adapter {
|
||||||
|
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)
|
||||||
|
.then((res) => res[0])
|
||||||
|
},
|
||||||
|
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.users
|
||||||
|
},
|
||||||
|
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
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
225
packages/adapter-drizzle/src/lib/pg.ts
Normal file
225
packages/adapter-drizzle/src/lib/pg.ts
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
import { and, eq } from "drizzle-orm"
|
||||||
|
import {
|
||||||
|
timestamp,
|
||||||
|
pgTable,
|
||||||
|
text,
|
||||||
|
primaryKey,
|
||||||
|
integer,
|
||||||
|
} from "drizzle-orm/pg-core"
|
||||||
|
|
||||||
|
import type { PostgresJsDatabase } from "drizzle-orm/postgres-js"
|
||||||
|
import type { Adapter, AdapterAccount } from "@auth/core/adapters"
|
||||||
|
|
||||||
|
export const users = pgTable("users", {
|
||||||
|
id: text("id").notNull().primaryKey(),
|
||||||
|
name: text("name"),
|
||||||
|
email: text("email").notNull(),
|
||||||
|
emailVerified: timestamp("emailVerified", { mode: "date" }),
|
||||||
|
image: text("image"),
|
||||||
|
})
|
||||||
|
|
||||||
|
export const accounts = pgTable(
|
||||||
|
"accounts",
|
||||||
|
{
|
||||||
|
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("sessions", {
|
||||||
|
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),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
export const schema = { users, accounts, sessions, verificationTokens }
|
||||||
|
export type DefaultSchema = typeof schema
|
||||||
|
|
||||||
|
export function pgDrizzleAdapter(
|
||||||
|
client: PostgresJsDatabase<Record<string, never>>
|
||||||
|
): Adapter {
|
||||||
|
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.users
|
||||||
|
},
|
||||||
|
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 }
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
203
packages/adapter-drizzle/src/lib/sqlite.ts
Normal file
203
packages/adapter-drizzle/src/lib/sqlite.ts
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
import { eq, and } from "drizzle-orm"
|
||||||
|
import {
|
||||||
|
integer,
|
||||||
|
sqliteTable,
|
||||||
|
text,
|
||||||
|
primaryKey,
|
||||||
|
BaseSQLiteDatabase,
|
||||||
|
} from "drizzle-orm/sqlite-core"
|
||||||
|
|
||||||
|
import type { Adapter, AdapterAccount } from "@auth/core/adapters"
|
||||||
|
|
||||||
|
export const users = sqliteTable("users", {
|
||||||
|
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(
|
||||||
|
"accounts",
|
||||||
|
{
|
||||||
|
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("sessions", {
|
||||||
|
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),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
export const schema = { users, accounts, sessions, verificationTokens }
|
||||||
|
export type DefaultSchema = typeof schema
|
||||||
|
|
||||||
|
export function SQLiteDrizzleAdapter(
|
||||||
|
client: BaseSQLiteDatabase<any, any>
|
||||||
|
): Adapter {
|
||||||
|
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?.users ?? 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
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
47
packages/adapter-drizzle/src/lib/utils.ts
Normal file
47
packages/adapter-drizzle/src/lib/utils.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { MySqlDatabase } from "drizzle-orm/mysql-core"
|
||||||
|
import { PgDatabase } from "drizzle-orm/pg-core"
|
||||||
|
import { BaseSQLiteDatabase } from "drizzle-orm/sqlite-core"
|
||||||
|
|
||||||
|
import type { AnyMySqlTable } from "drizzle-orm/mysql-core"
|
||||||
|
import type { AnyPgTable } from "drizzle-orm/pg-core"
|
||||||
|
import type { AnySQLiteTable } 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 function isMySqlDatabase(
|
||||||
|
db: any
|
||||||
|
): db is MySqlDatabase<any, any, any, any> {
|
||||||
|
return db instanceof MySqlDatabase
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isPgDatabase(db: any): db is PgDatabase<any, any, any> {
|
||||||
|
return db instanceof PgDatabase
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isSQLiteDatabase(db: any): db is AnySQLiteDatabase {
|
||||||
|
return db instanceof BaseSQLiteDatabase
|
||||||
|
}
|
||||||
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(),
|
||||||
|
},
|
||||||
|
}
|
||||||
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,
|
||||||
|
},
|
||||||
|
})
|
||||||
29
packages/adapter-drizzle/tests/mysql/schema.ts
Normal file
29
packages/adapter-drizzle/tests/mysql/schema.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import type { AdapterAccount } from "@auth/core/adapters"
|
||||||
|
import {
|
||||||
|
mysqlTable,
|
||||||
|
varchar,
|
||||||
|
timestamp,
|
||||||
|
int,
|
||||||
|
primaryKey,
|
||||||
|
} from "drizzle-orm/mysql-core"
|
||||||
|
import { drizzle } from "drizzle-orm/mysql2"
|
||||||
|
import { createPool } from "mysql2"
|
||||||
|
import {
|
||||||
|
users,
|
||||||
|
accounts,
|
||||||
|
sessions,
|
||||||
|
verificationTokens,
|
||||||
|
schema,
|
||||||
|
} from "../../src/lib/mysql"
|
||||||
|
|
||||||
|
const poolConnection = createPool({
|
||||||
|
host: "localhost",
|
||||||
|
user: "root",
|
||||||
|
password: "password",
|
||||||
|
database: "next-auth",
|
||||||
|
})
|
||||||
|
|
||||||
|
export { users, accounts, sessions, verificationTokens }
|
||||||
|
export const db = drizzle(poolConnection, {
|
||||||
|
schema: 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}
|
||||||
13
packages/adapter-drizzle/tests/pg/drizzle.config.ts
Normal file
13
packages/adapter-drizzle/tests/pg/drizzle.config.ts
Normal file
@@ -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
|
||||||
65
packages/adapter-drizzle/tests/pg/index.test.ts
Normal file
65
packages/adapter-drizzle/tests/pg/index.test.ts
Normal file
@@ -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),
|
||||||
|
},
|
||||||
|
})
|
||||||
10
packages/adapter-drizzle/tests/pg/migrator.ts
Normal file
10
packages/adapter-drizzle/tests/pg/migrator.ts
Normal file
@@ -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))
|
||||||
11
packages/adapter-drizzle/tests/pg/schema.ts
Normal file
11
packages/adapter-drizzle/tests/pg/schema.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { drizzle } from "drizzle-orm/postgres-js"
|
||||||
|
import postgres from "postgres"
|
||||||
|
import { users, accounts, sessions, verificationTokens } from "../../src/lib/pg"
|
||||||
|
|
||||||
|
const connectionString = "postgres://nextauth:nextauth@localhost:5432/nextauth"
|
||||||
|
const sql = postgres(connectionString, { max: 1 })
|
||||||
|
|
||||||
|
export const db = drizzle(sql, {
|
||||||
|
schema: { users, accounts, sessions, verificationTokens },
|
||||||
|
})
|
||||||
|
export { users, accounts, sessions, verificationTokens }
|
||||||
25
packages/adapter-drizzle/tests/pg/test.sh
Executable file
25
packages/adapter-drizzle/tests/pg/test.sh
Executable file
@@ -0,0 +1,25 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
echo "Initializing container for PostgreSQL tests."
|
||||||
|
|
||||||
|
PGUSER=nextauth
|
||||||
|
PGPASSWORD=nextauth
|
||||||
|
PGDATABASE=nextauth
|
||||||
|
PGPORT=5432
|
||||||
|
PG_CONTAINER_NAME=next-auth-postgres-test
|
||||||
|
|
||||||
|
docker run -d --rm \
|
||||||
|
-e POSTGRES_USER=${PGUSER} \
|
||||||
|
-e POSTGRES_PASSWORD=${PGUSER} \
|
||||||
|
-e POSTGRES_DB=${PGDATABASE} \
|
||||||
|
-e POSTGRES_HOST_AUTH_METHOD=trust \
|
||||||
|
--name "${PG_CONTAINER_NAME}" \
|
||||||
|
-p ${PGPORT}:5432 \
|
||||||
|
postgres:15.3
|
||||||
|
|
||||||
|
echo "Waiting 15 sec for db to start..." && sleep 15
|
||||||
|
|
||||||
|
drizzle-kit generate:pg --config=./tests/pg/drizzle.config.ts
|
||||||
|
npx tsx ./tests/pg/migrator.ts
|
||||||
|
jest ./tests/pg/index.test.ts --forceExit
|
||||||
|
docker stop ${PG_CONTAINER_NAME}
|
||||||
10
packages/adapter-drizzle/tests/sqlite/drizzle.config.ts
Normal file
10
packages/adapter-drizzle/tests/sqlite/drizzle.config.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import type { Config } from "drizzle-kit"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
schema: "./tests/sqlite/schema.ts",
|
||||||
|
out: "./tests/sqlite/.drizzle",
|
||||||
|
driver: "better-sqlite",
|
||||||
|
dbCredentials: {
|
||||||
|
url: "./db.sqlite",
|
||||||
|
},
|
||||||
|
} satisfies Config
|
||||||
60
packages/adapter-drizzle/tests/sqlite/index.test.ts
Normal file
60
packages/adapter-drizzle/tests/sqlite/index.test.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { runBasicTests } from "../../../adapter-test"
|
||||||
|
import { DrizzleAdapter } from "../../src"
|
||||||
|
import { db, accounts, sessions, users, verificationTokens } from "./schema"
|
||||||
|
import { eq, and } from "drizzle-orm"
|
||||||
|
|
||||||
|
globalThis.crypto ??= require("node:crypto").webcrypto
|
||||||
|
|
||||||
|
runBasicTests({
|
||||||
|
adapter: DrizzleAdapter(db),
|
||||||
|
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: (id) => db.select().from(users).where(eq(users.id, id)).get() ?? null,
|
||||||
|
session: (sessionToken) =>
|
||||||
|
db
|
||||||
|
.select()
|
||||||
|
.from(sessions)
|
||||||
|
.where(eq(sessions.sessionToken, sessionToken))
|
||||||
|
.get() ?? null,
|
||||||
|
account: (provider_providerAccountId) => {
|
||||||
|
return (
|
||||||
|
db
|
||||||
|
.select()
|
||||||
|
.from(accounts)
|
||||||
|
.where(
|
||||||
|
eq(
|
||||||
|
accounts.providerAccountId,
|
||||||
|
provider_providerAccountId.providerAccountId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.get() ?? null
|
||||||
|
)
|
||||||
|
},
|
||||||
|
verificationToken: (identifier_token) =>
|
||||||
|
db
|
||||||
|
.select()
|
||||||
|
.from(verificationTokens)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(verificationTokens.token, identifier_token.token),
|
||||||
|
eq(verificationTokens.identifier, identifier_token.identifier)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.get() ?? null,
|
||||||
|
},
|
||||||
|
})
|
||||||
20
packages/adapter-drizzle/tests/sqlite/schema.ts
Normal file
20
packages/adapter-drizzle/tests/sqlite/schema.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { drizzle } from "drizzle-orm/better-sqlite3"
|
||||||
|
import Database from "better-sqlite3"
|
||||||
|
import {
|
||||||
|
users,
|
||||||
|
accounts,
|
||||||
|
sessions,
|
||||||
|
verificationTokens,
|
||||||
|
} from "../../src/lib/sqlite"
|
||||||
|
|
||||||
|
const sqlite = new Database("db.sqlite")
|
||||||
|
|
||||||
|
export { users, accounts, sessions, verificationTokens }
|
||||||
|
export const db = drizzle(sqlite, {
|
||||||
|
schema: {
|
||||||
|
users,
|
||||||
|
accounts,
|
||||||
|
sessions,
|
||||||
|
verificationTokens,
|
||||||
|
},
|
||||||
|
})
|
||||||
12
packages/adapter-drizzle/tests/sqlite/test.sh
Executable file
12
packages/adapter-drizzle/tests/sqlite/test.sh
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
|
||||||
|
echo "Running SQLite tests."
|
||||||
|
|
||||||
|
rm -f db.sqlite
|
||||||
|
|
||||||
|
drizzle-kit generate:sqlite --config=./tests/sqlite/drizzle.config.ts
|
||||||
|
drizzle-kit push:sqlite --config=./tests/sqlite/drizzle.config.ts
|
||||||
|
jest ./tests/sqlite/index.test.ts --forceExit
|
||||||
25
packages/adapter-drizzle/tsconfig.json
Normal file
25
packages/adapter-drizzle/tsconfig.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"extends": "@next-auth/tsconfig/tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowJs": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"isolatedModules": true,
|
||||||
|
"target": "ES2020",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"outDir": ".",
|
||||||
|
"rootDir": "src",
|
||||||
|
"skipDefaultLibCheck": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"stripInternal": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"declaration": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"*.js",
|
||||||
|
"*.d.ts",
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@auth/dynamodb-adapter",
|
"name": "@auth/dynamodb-adapter",
|
||||||
"repository": "https://github.com/nextauthjs/next-auth",
|
"repository": "https://github.com/nextauthjs/next-auth",
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"description": "AWS DynamoDB adapter for next-auth.",
|
"description": "AWS DynamoDB adapter for next-auth.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"next-auth",
|
"next-auth",
|
||||||
|
|||||||
@@ -265,9 +265,8 @@ export function DynamoDBAdapter(
|
|||||||
const data = await client.update({
|
const data = await client.update({
|
||||||
TableName,
|
TableName,
|
||||||
Key: {
|
Key: {
|
||||||
// next-auth type is incorrect it should be Partial<AdapterUser> & {id: string} instead of just Partial<AdapterUser>
|
[pk]: `USER#${user.id}`,
|
||||||
[pk]: `USER#${user.id as string}`,
|
[sk]: `USER#${user.id}`,
|
||||||
[sk]: `USER#${user.id as string}`,
|
|
||||||
},
|
},
|
||||||
UpdateExpression,
|
UpdateExpression,
|
||||||
ExpressionAttributeNames,
|
ExpressionAttributeNames,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@auth/mikro-orm-adapter",
|
"name": "@auth/mikro-orm-adapter",
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"description": "MikroORM adapter for Auth.js",
|
"description": "MikroORM adapter for Auth.js",
|
||||||
"homepage": "https://authjs.dev",
|
"homepage": "https://authjs.dev",
|
||||||
"repository": "https://github.com/nextauthjs/next-auth",
|
"repository": "https://github.com/nextauthjs/next-auth",
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import type { Adapter } from "@auth/core/adapters"
|
|||||||
|
|
||||||
import { MikroORM, wrap } from "@mikro-orm/core"
|
import { MikroORM, wrap } from "@mikro-orm/core"
|
||||||
|
|
||||||
import * as defaultEntities from "./lib/entities"
|
import * as defaultEntities from "./lib/entities.js"
|
||||||
|
|
||||||
export { defaultEntities }
|
export { defaultEntities }
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@auth/prisma-adapter",
|
"name": "@auth/prisma-adapter",
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"description": "Prisma adapter for Auth.js",
|
"description": "Prisma adapter for Auth.js",
|
||||||
"homepage": "https://authjs.dev/reference/adapter/prisma",
|
"homepage": "https://authjs.dev/reference/adapter/prisma",
|
||||||
"repository": "https://github.com/nextauthjs/next-auth",
|
"repository": "https://github.com/nextauthjs/next-auth",
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import type { Adapter, AdapterAccount } from "@auth/core/adapters"
|
|||||||
/**
|
/**
|
||||||
* ## Setup
|
* ## Setup
|
||||||
*
|
*
|
||||||
* Add this adapter to your `pages/api/[...nextauth].js` next-auth configuration object:
|
* Add this adapter to your `pages/api/auth/[...nextauth].js` next-auth configuration object:
|
||||||
*
|
*
|
||||||
* ```js title="pages/api/auth/[...nextauth].js"
|
* ```js title="pages/api/auth/[...nextauth].js"
|
||||||
* import NextAuth from "next-auth"
|
* import NextAuth from "next-auth"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@auth/sequelize-adapter",
|
"name": "@auth/sequelize-adapter",
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"description": "Sequelize adapter for Auth.js",
|
"description": "Sequelize adapter for Auth.js",
|
||||||
"homepage": "https://authjs.dev",
|
"homepage": "https://authjs.dev",
|
||||||
"repository": "https://github.com/nextauthjs/next-auth",
|
"repository": "https://github.com/nextauthjs/next-auth",
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import type {
|
|||||||
VerificationToken,
|
VerificationToken,
|
||||||
} from "@auth/core/adapters"
|
} from "@auth/core/adapters"
|
||||||
import { Sequelize, Model, ModelCtor } from "sequelize"
|
import { Sequelize, Model, ModelCtor } from "sequelize"
|
||||||
import * as defaultModels from "./models"
|
import * as defaultModels from "./models.js"
|
||||||
|
|
||||||
export { defaultModels as models }
|
export { defaultModels as models }
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@auth/supabase-adapter",
|
"name": "@auth/supabase-adapter",
|
||||||
"version": "0.1.0",
|
"version": "0.1.2",
|
||||||
"description": "Supabase adapter for Auth.js",
|
"description": "Supabase adapter for Auth.js",
|
||||||
"homepage": "https://authjs.dev",
|
"homepage": "https://authjs.dev",
|
||||||
"repository": "https://github.com/nextauthjs/next-auth",
|
"repository": "https://github.com/nextauthjs/next-auth",
|
||||||
|
|||||||
@@ -460,7 +460,7 @@ export function SupabaseAdapter(options: SupabaseAdapterOptions): Adapter {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
user: format<AdapterUser>(
|
user: format<AdapterUser>(
|
||||||
user as Database["next_auth"]["Tables"]["users"]["Row"]
|
user as Database["next_auth"]["Tables"]["users"]["Row"][]
|
||||||
),
|
),
|
||||||
session: format<AdapterSession>(session),
|
session: format<AdapterSession>(session),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,25 @@
|
|||||||
{
|
{
|
||||||
"extends": "@next-auth/tsconfig/tsconfig.adapters.json",
|
"extends": "@next-auth/tsconfig/tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"allowJs": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"isolatedModules": true,
|
||||||
|
"target": "ES2020",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"outDir": ".",
|
||||||
"rootDir": "src",
|
"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",
|
||||||
|
]
|
||||||
}
|
}
|
||||||
@@ -15,6 +15,13 @@ const requiredMethods = [
|
|||||||
]
|
]
|
||||||
export interface TestOptions {
|
export interface TestOptions {
|
||||||
adapter: Adapter
|
adapter: Adapter
|
||||||
|
fixtures?: {
|
||||||
|
user?: any
|
||||||
|
session?: any
|
||||||
|
account?: any
|
||||||
|
sessionUpdateExpires?: Date
|
||||||
|
verificationTokenExpires?: Date
|
||||||
|
},
|
||||||
db: {
|
db: {
|
||||||
/** Generates UUID v4 by default. Use it to override how the test suite should generate IDs, like user id. */
|
/** Generates UUID v4 by default. Use it to override how the test suite should generate IDs, like user id. */
|
||||||
id?: () => string
|
id?: () => string
|
||||||
@@ -67,11 +74,11 @@ export async function runBasicTests(options: TestOptions) {
|
|||||||
await options.db.disconnect?.()
|
await options.db.disconnect?.()
|
||||||
})
|
})
|
||||||
|
|
||||||
let user: any = {
|
let user: any = options.fixtures?.user ?? {
|
||||||
email: "fill@murray.com",
|
email: "fill@murray.com",
|
||||||
image: "https://www.fillmurray.com/460/300",
|
image: "https://www.fillmurray.com/460/300",
|
||||||
name: "Fill Murray",
|
name: "Fill Murray",
|
||||||
emailVerified: new Date(),
|
emailVerified: new Date()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.env.CUSTOM_MODEL === "1") {
|
if (process.env.CUSTOM_MODEL === "1") {
|
||||||
@@ -79,12 +86,12 @@ export async function runBasicTests(options: TestOptions) {
|
|||||||
user.phone = "00000000000"
|
user.phone = "00000000000"
|
||||||
}
|
}
|
||||||
|
|
||||||
const session: any = {
|
const session: any = options.fixtures?.session ?? {
|
||||||
sessionToken: randomUUID(),
|
sessionToken: randomUUID(),
|
||||||
expires: ONE_WEEK_FROM_NOW,
|
expires: ONE_WEEK_FROM_NOW,
|
||||||
}
|
}
|
||||||
|
|
||||||
const account: any = {
|
const account: any = options.fixtures?.account ?? {
|
||||||
provider: "github",
|
provider: "github",
|
||||||
providerAccountId: randomUUID(),
|
providerAccountId: randomUUID(),
|
||||||
type: "oauth",
|
type: "oauth",
|
||||||
@@ -175,15 +182,17 @@ export async function runBasicTests(options: TestOptions) {
|
|||||||
test("updateSession", async () => {
|
test("updateSession", async () => {
|
||||||
let dbSession = await db.session(session.sessionToken)
|
let dbSession = await db.session(session.sessionToken)
|
||||||
|
|
||||||
expect(dbSession.expires.valueOf()).not.toBe(ONE_MONTH_FROM_NOW.valueOf())
|
const expires = options.fixtures?.sessionUpdateExpires ?? ONE_MONTH_FROM_NOW
|
||||||
|
|
||||||
|
expect(dbSession.expires.valueOf()).not.toBe(expires.valueOf())
|
||||||
|
|
||||||
await adapter.updateSession({
|
await adapter.updateSession({
|
||||||
sessionToken: session.sessionToken,
|
sessionToken: session.sessionToken,
|
||||||
expires: ONE_MONTH_FROM_NOW,
|
expires,
|
||||||
})
|
})
|
||||||
|
|
||||||
dbSession = await db.session(session.sessionToken)
|
dbSession = await db.session(session.sessionToken)
|
||||||
expect(dbSession.expires.valueOf()).toBe(ONE_MONTH_FROM_NOW.valueOf())
|
expect(dbSession.expires.valueOf()).toBe(expires.valueOf())
|
||||||
})
|
})
|
||||||
|
|
||||||
test("linkAccount", async () => {
|
test("linkAccount", async () => {
|
||||||
@@ -232,7 +241,7 @@ export async function runBasicTests(options: TestOptions) {
|
|||||||
const verificationToken = {
|
const verificationToken = {
|
||||||
token: hashedToken,
|
token: hashedToken,
|
||||||
identifier,
|
identifier,
|
||||||
expires: FIFTEEN_MINUTES_FROM_NOW,
|
expires: options.fixtures?.verificationTokenExpires ?? FIFTEEN_MINUTES_FROM_NOW,
|
||||||
}
|
}
|
||||||
await adapter.createVerificationToken?.(verificationToken)
|
await adapter.createVerificationToken?.(verificationToken)
|
||||||
|
|
||||||
@@ -251,7 +260,7 @@ export async function runBasicTests(options: TestOptions) {
|
|||||||
const verificationToken = {
|
const verificationToken = {
|
||||||
token: hashedToken,
|
token: hashedToken,
|
||||||
identifier,
|
identifier,
|
||||||
expires: FIFTEEN_MINUTES_FROM_NOW,
|
expires: options.fixtures?.verificationTokenExpires ?? FIFTEEN_MINUTES_FROM_NOW,
|
||||||
}
|
}
|
||||||
await adapter.createVerificationToken?.(verificationToken)
|
await adapter.createVerificationToken?.(verificationToken)
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,27 @@
|
|||||||
|
// @ts-check
|
||||||
|
|
||||||
|
/** @type {import("@swc/core").Config} */
|
||||||
const swcConfig = {
|
const swcConfig = {
|
||||||
jsc: {
|
jsc: {
|
||||||
parser: { syntax: "typescript", decorators: true },
|
parser: { syntax: "typescript", decorators: true },
|
||||||
transform: { legacyDecorator: true, decoratorMetadata: true },
|
transform: { legacyDecorator: true, decoratorMetadata: true },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @type {import("jest").Config} */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
transform: {
|
transform: {
|
||||||
".(ts|tsx)$": ["@swc/jest", swcConfig],
|
".(ts|tsx)$": ["@swc/jest", swcConfig],
|
||||||
".(js|jsx)$": ["@swc/jest", swcConfig],
|
".(js|jsx)$": ["@swc/jest", swcConfig],
|
||||||
},
|
},
|
||||||
transformIgnorePatterns: ["[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$"],
|
transformIgnorePatterns: ["[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$"],
|
||||||
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
|
moduleFileExtensions: ["mjs", "cjs", "ts", "tsx", "js", "jsx", "json", "node"],
|
||||||
rootDir: ".",
|
rootDir: ".",
|
||||||
// coverageDirectory: "<rootDir>/coverage/",
|
// coverageDirectory: "<rootDir>/coverage/",
|
||||||
// collectCoverageFrom: ["<rootDir>/packages/*/src/**/*.{ts,tsx}"],
|
// collectCoverageFrom: ["<rootDir>/packages/*/src/**/*.{ts,tsx}"],
|
||||||
testURL: "http://localhost/",
|
testURL: "http://localhost/",
|
||||||
moduleDirectories: ["node_modules"],
|
moduleDirectories: ["node_modules"],
|
||||||
|
moduleNameMapper: {
|
||||||
|
'^(\\.{1,2}/.*)\\.js$': '$1',
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,10 @@
|
|||||||
"@babel/cli": "^7.14.3",
|
"@babel/cli": "^7.14.3",
|
||||||
"@babel/plugin-transform-runtime": "^7.14.3",
|
"@babel/plugin-transform-runtime": "^7.14.3",
|
||||||
"@babel/preset-env": "^7.14.2",
|
"@babel/preset-env": "^7.14.2",
|
||||||
"@types/jest": "^26.0.23",
|
"@swc/core": "^1.2.198",
|
||||||
|
"@types/jest": "^29.5.2",
|
||||||
"@types/nodemailer": "^6.4.4",
|
"@types/nodemailer": "^6.4.4",
|
||||||
"jest": "^27.0.3",
|
"jest": "^29.5.0",
|
||||||
"ts-jest": "^27.0.3",
|
"ts-jest": "^27.0.3",
|
||||||
"typescript": "^4.2.4"
|
"typescript": "^4.2.4"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@auth/typeorm-adapter",
|
"name": "@auth/typeorm-adapter",
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"description": "TypeORM adapter for Auth.js.",
|
"description": "TypeORM adapter for Auth.js.",
|
||||||
"homepage": "https://authjs.dev/reference/adapter/typeorm",
|
"homepage": "https://authjs.dev/reference/adapter/typeorm",
|
||||||
"repository": "https://github.com/nextauthjs/next-auth",
|
"repository": "https://github.com/nextauthjs/next-auth",
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ import type {
|
|||||||
AdapterSession,
|
AdapterSession,
|
||||||
} from "@auth/core/adapters"
|
} from "@auth/core/adapters"
|
||||||
import { DataSourceOptions, DataSource, EntityManager } from "typeorm"
|
import { DataSourceOptions, DataSource, EntityManager } from "typeorm"
|
||||||
import * as defaultEntities from "./entities"
|
import * as defaultEntities from "./entities.js"
|
||||||
import { parseDataSourceConfig, updateConnectionEntities } from "./utils"
|
import { parseDataSourceConfig, updateConnectionEntities } from "./utils.js"
|
||||||
|
|
||||||
export const entities = defaultEntities
|
export const entities = defaultEntities
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { DataSource, DataSourceOptions } from "typeorm"
|
import type { DataSource, DataSourceOptions } from "typeorm"
|
||||||
import * as defaultEntities from "./entities"
|
import * as defaultEntities from "./entities.js"
|
||||||
|
|
||||||
/** Ensure configOrString is normalized to an object. */
|
/** Ensure configOrString is normalized to an object. */
|
||||||
export function parseDataSourceConfig(
|
export function parseDataSourceConfig(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@auth/core",
|
"name": "@auth/core",
|
||||||
"version": "0.8.2",
|
"version": "0.10.1",
|
||||||
"description": "Authentication for the Web.",
|
"description": "Authentication for the Web.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"authentication",
|
"authentication",
|
||||||
|
|||||||
@@ -223,7 +223,7 @@ export interface Adapter {
|
|||||||
getUserByAccount?(
|
getUserByAccount?(
|
||||||
providerAccountId: Pick<AdapterAccount, "provider" | "providerAccountId">
|
providerAccountId: Pick<AdapterAccount, "provider" | "providerAccountId">
|
||||||
): Awaitable<AdapterUser | null>
|
): Awaitable<AdapterUser | null>
|
||||||
updateUser?(user: Partial<AdapterUser>): Awaitable<AdapterUser>
|
updateUser?(user: Partial<AdapterUser> & Pick<AdapterUser, 'id'>): Awaitable<AdapterUser>
|
||||||
/** @todo This method is currently not invoked yet. */
|
/** @todo This method is currently not invoked yet. */
|
||||||
deleteUser?(
|
deleteUser?(
|
||||||
userId: string
|
userId: string
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ export function assertConfig(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { callbackUrl: defaultCallbackUrl } = defaultCookies(
|
const { callbackUrl: defaultCallbackUrl } = defaultCookies(
|
||||||
options.useSecureCookies ?? url.protocol === "https://"
|
options.useSecureCookies ?? url.protocol === "https:"
|
||||||
)
|
)
|
||||||
const callbackUrlCookie =
|
const callbackUrlCookie =
|
||||||
request.cookies?.[
|
request.cookies?.[
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import { createHash, randomString } from "../web.js"
|
import { createHash, randomString, toRequest } from "../web.js"
|
||||||
|
|
||||||
import type { InternalOptions } from "../../types.js"
|
import type { InternalOptions, RequestInternal } from "../../types.js"
|
||||||
/**
|
/**
|
||||||
* Starts an e-mail login flow, by generating a token,
|
* Starts an e-mail login flow, by generating a token,
|
||||||
* and sending it to the user's e-mail (with the help of a DB adapter)
|
* and sending it to the user's e-mail (with the help of a DB adapter)
|
||||||
*/
|
*/
|
||||||
export default async function email(
|
export default async function email(
|
||||||
identifier: string,
|
identifier: string,
|
||||||
options: InternalOptions<"email">
|
options: InternalOptions<"email">,
|
||||||
|
request: RequestInternal
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const { url, adapter, provider, callbackUrl, theme } = options
|
const { url, adapter, provider, callbackUrl, theme } = options
|
||||||
const token =
|
const token =
|
||||||
@@ -31,6 +32,7 @@ export default async function email(
|
|||||||
url: _url,
|
url: _url,
|
||||||
provider,
|
provider,
|
||||||
theme,
|
theme,
|
||||||
|
request: toRequest(request),
|
||||||
}),
|
}),
|
||||||
// @ts-expect-error -- Verified in `assertConfig`.
|
// @ts-expect-error -- Verified in `assertConfig`.
|
||||||
adapter.createVerificationToken?.({
|
adapter.createVerificationToken?.({
|
||||||
|
|||||||
@@ -137,8 +137,7 @@ export async function AuthInternal<
|
|||||||
case "signin":
|
case "signin":
|
||||||
if ((csrfDisabled || options.csrfTokenVerified) && options.provider) {
|
if ((csrfDisabled || options.csrfTokenVerified) && options.provider) {
|
||||||
const signin = await routes.signin(
|
const signin = await routes.signin(
|
||||||
request.query,
|
request,
|
||||||
request.body,
|
|
||||||
options
|
options
|
||||||
)
|
)
|
||||||
if (signin.cookies) cookies.push(...signin.cookies)
|
if (signin.cookies) cookies.push(...signin.cookies)
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ export async function handleOAuth(
|
|||||||
throw new Error("TODO: Handle www-authenticate challenges as needed")
|
throw new Error("TODO: Handle www-authenticate challenges as needed")
|
||||||
}
|
}
|
||||||
|
|
||||||
let profile: Profile
|
let profile: Profile = {}
|
||||||
let tokens: TokenSet & Pick<Account, "expires_at">
|
let tokens: TokenSet & Pick<Account, "expires_at">
|
||||||
|
|
||||||
if (provider.type === "oidc") {
|
if (provider.type === "oidc") {
|
||||||
@@ -153,7 +153,8 @@ export async function handleOAuth(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (userinfo?.request) {
|
if (userinfo?.request) {
|
||||||
profile = await userinfo.request({ tokens, provider })
|
const _profile = await userinfo.request({ tokens, provider })
|
||||||
|
if (_profile instanceof Object) profile = _profile
|
||||||
} else if (userinfo?.url) {
|
} else if (userinfo?.url) {
|
||||||
const userinfoResponse = await o.userInfoRequest(
|
const userinfoResponse = await o.userInfoRequest(
|
||||||
as,
|
as,
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ import type {
|
|||||||
* For Email, sends an email with a sign in link.
|
* For Email, sends an email with a sign in link.
|
||||||
*/
|
*/
|
||||||
export async function signin(
|
export async function signin(
|
||||||
query: RequestInternal["query"],
|
request: RequestInternal,
|
||||||
body: RequestInternal["body"],
|
|
||||||
options: InternalOptions<"oauth" | "oidc" | "email">
|
options: InternalOptions<"oauth" | "oidc" | "email">
|
||||||
): Promise<ResponseInternal> {
|
): Promise<ResponseInternal> {
|
||||||
|
const { query, body } = request
|
||||||
const { url, logger, provider } = options
|
const { url, logger, provider } = options
|
||||||
try {
|
try {
|
||||||
if (provider.type === "oauth" || provider.type === "oidc") {
|
if (provider.type === "oauth" || provider.type === "oidc") {
|
||||||
@@ -48,7 +48,7 @@ export async function signin(
|
|||||||
|
|
||||||
if (unauthorizedOrError) return unauthorizedOrError
|
if (unauthorizedOrError) return unauthorizedOrError
|
||||||
|
|
||||||
const redirect = await emailSignin(email, options)
|
const redirect = await emailSignin(email, options, request)
|
||||||
return { redirect }
|
return { redirect }
|
||||||
}
|
}
|
||||||
return { redirect: `${url}/signin` }
|
return { redirect: `${url}/signin` }
|
||||||
|
|||||||
@@ -72,6 +72,17 @@ export async function toInternalRequest(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function toRequest(request: RequestInternal): Request {
|
||||||
|
return new Request(request.url, {
|
||||||
|
headers: request.headers,
|
||||||
|
method: request.method,
|
||||||
|
body:
|
||||||
|
request.method === "POST"
|
||||||
|
? JSON.stringify(request.body ?? {})
|
||||||
|
: undefined,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export function toResponse(res: ResponseInternal): Response {
|
export function toResponse(res: ResponseInternal): Response {
|
||||||
const headers = new Headers(res.headers)
|
const headers = new Headers(res.headers)
|
||||||
|
|
||||||
|
|||||||
@@ -131,6 +131,11 @@ export default function AzureAD<P extends AzureADProfile>(
|
|||||||
name: "Azure Active Directory",
|
name: "Azure Active Directory",
|
||||||
type: "oidc",
|
type: "oidc",
|
||||||
wellKnown: `${rest.issuer}}/.well-known/openid-configuration?appid=${options.clientId}`,
|
wellKnown: `${rest.issuer}}/.well-known/openid-configuration?appid=${options.clientId}`,
|
||||||
|
authorization: {
|
||||||
|
params: {
|
||||||
|
scope: 'openid profile email User.Read',
|
||||||
|
},
|
||||||
|
},
|
||||||
async profile(profile, tokens) {
|
async profile(profile, tokens) {
|
||||||
// https://docs.microsoft.com/en-us/graph/api/profilephoto-get?view=graph-rest-1.0#examples
|
// https://docs.microsoft.com/en-us/graph/api/profilephoto-get?view=graph-rest-1.0#examples
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export interface BoxyHQSAMLProfile extends Record<string, any> {
|
|||||||
/**
|
/**
|
||||||
* Add BoxyHQ SAML login to your page.
|
* Add BoxyHQ SAML login to your page.
|
||||||
*
|
*
|
||||||
* BoxyHQ SAML is an open source service that handles the SAML login flow as an OAuth 2.0 flow, abstracting away all the complexities of the SAML protocol.
|
* BoxyHQ SAML is an open source service that handles the SAML SSO login flow as an OAuth 2.0 flow, abstracting away all the complexities of the SAML protocol. Enable Enterprise single-sign-on in your app with ease.
|
||||||
*
|
*
|
||||||
* You can deploy BoxyHQ SAML as a separate service or embed it into your app using our NPM library. [Check out the documentation for more details](https://boxyhq.com/docs/jackson/deploy)
|
* You can deploy BoxyHQ SAML as a separate service or embed it into your app using our NPM library. [Check out the documentation for more details](https://boxyhq.com/docs/jackson/deploy)
|
||||||
*
|
*
|
||||||
@@ -32,13 +32,38 @@ export interface BoxyHQSAMLProfile extends Record<string, any> {
|
|||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* #### Configuration
|
* #### Configuration
|
||||||
|
*
|
||||||
|
* For OAuth 2.0 Flow:
|
||||||
*```js
|
*```js
|
||||||
* import Auth from "@auth/core"
|
* import Auth from "@auth/core"
|
||||||
* import BoxyHQ from "@auth/core/providers/boxyhq-saml"
|
* import BoxyHQ from "@auth/core/providers/boxyhq-saml"
|
||||||
*
|
*
|
||||||
* const request = new Request(origin)
|
* const request = new Request(origin)
|
||||||
* const response = await Auth(request, {
|
* const response = await Auth(request, {
|
||||||
* providers: [BoxyHQ({ clientId: BOXYHQ_SAML_CLIENT_ID, clientSecret: BOXYHQ_SAML_CLIENT_SECRET. issuer: BOXYHQ_SAML_ISSUER })],
|
* providers: [BoxyHQ({
|
||||||
|
* authorization: { params: { scope: "" } }, // This is needed for OAuth 2.0 flow, otherwise default to openid
|
||||||
|
* clientId: BOXYHQ_SAML_CLIENT_ID,
|
||||||
|
* clientSecret: BOXYHQ_SAML_CLIENT_SECRET,
|
||||||
|
* issuer: BOXYHQ_SAML_ISSUER
|
||||||
|
* })],
|
||||||
|
* })
|
||||||
|
* ```
|
||||||
|
* For OIDC Flow:
|
||||||
|
*
|
||||||
|
*```js
|
||||||
|
* import Auth from "@auth/core"
|
||||||
|
* import BoxyHQ from "@auth/core/providers/boxyhq-saml"
|
||||||
|
*
|
||||||
|
* const request = new Request(origin)
|
||||||
|
* const response = await Auth(request, {
|
||||||
|
* providers: [BoxyHQ({
|
||||||
|
* id: "boxyhq-saml-oidc",
|
||||||
|
* wellKnown: `http://localhost:5225/.well-known/openid-configuration`,
|
||||||
|
* authorization: { params: { scope: "openid email" } },
|
||||||
|
* clientId: BOXYHQ_SAML_CLIENT_ID,
|
||||||
|
* clientSecret: BOXYHQ_SAML_CLIENT_SECRET,
|
||||||
|
* issuer: BOXYHQ_SAML_ISSUER
|
||||||
|
* })],
|
||||||
* })
|
* })
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
|
|||||||
119
packages/core/src/providers/descope.ts
Normal file
119
packages/core/src/providers/descope.ts
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
/**
|
||||||
|
* <div style={{display: "flex", justifyContent: "space-between", alignItems: "center"}}>
|
||||||
|
* <span style={{fontSize: "1.35rem" }}>
|
||||||
|
* Built-in sign in with <b>Descope</b> integration.
|
||||||
|
* </span>
|
||||||
|
* <a href="https://descope.com" style={{backgroundColor: "#000000", padding: "12px", borderRadius: "100%" }}>
|
||||||
|
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/descope.svg" width="24"/>
|
||||||
|
* </a>
|
||||||
|
* </div>
|
||||||
|
*
|
||||||
|
* @module providers/descope
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { OIDCConfig, OIDCUserConfig } from "./index.js"
|
||||||
|
|
||||||
|
/** The returned user profile from Descope when using the profile callback.
|
||||||
|
* [See Load User](https://docs.descope.com/api/openapi/usermanagement/operation/LoadUser/)
|
||||||
|
*/
|
||||||
|
export interface DescopeProfile {
|
||||||
|
/** The user's unique Descope ID */
|
||||||
|
sub: string
|
||||||
|
/** The user's name */
|
||||||
|
name: string
|
||||||
|
/** The user's email */
|
||||||
|
email: string
|
||||||
|
/** A boolean indicating if the user's email is verified */
|
||||||
|
email_verified: boolean
|
||||||
|
/** The user's phone number */
|
||||||
|
phone_number: string
|
||||||
|
/** A boolean indicating if the user's phone number is verified */
|
||||||
|
phone_number_verified: boolean
|
||||||
|
/** The user's picture */
|
||||||
|
picture: string
|
||||||
|
/** The user's custom attributes */
|
||||||
|
[claim: string]: unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* ### Setup
|
||||||
|
*
|
||||||
|
* #### Callback URL
|
||||||
|
* ```
|
||||||
|
* https://example.com/api/auth/callback/descope
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* #### Configuration
|
||||||
|
*
|
||||||
|
* Import the provider and configure it in your **Auth.js** initialization file:
|
||||||
|
*
|
||||||
|
* ```ts title="pages/api/auth/[...nextauth].ts"
|
||||||
|
* import NextAuth from "next-auth"
|
||||||
|
* import DescopeProvider from "next-auth/providers/descope";
|
||||||
|
*
|
||||||
|
* export default NextAuth({
|
||||||
|
* providers: [
|
||||||
|
* DescopeProvider({
|
||||||
|
* clientId: process.env.DESCOPE_ID,
|
||||||
|
* clientSecret: process.env.DESCOPE_SECRET,
|
||||||
|
* }),
|
||||||
|
* ],
|
||||||
|
* })
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* ### Configuring Descope
|
||||||
|
*
|
||||||
|
* Follow these steps:
|
||||||
|
*
|
||||||
|
* 1. Log into the [Descope console](https://app.descope.com)
|
||||||
|
* 2. Follow the [OIDC instructions](https://docs.descope.com/customize/auth/oidc)
|
||||||
|
*
|
||||||
|
* Then, create a `.env.local` file in the project root add the following entries:
|
||||||
|
*
|
||||||
|
* Get the following from the Descope's console:
|
||||||
|
* ```
|
||||||
|
* DESCOPE_ID="<Descope Issuer's last url segment>" # Descope's Issuer can be found in "Authentication Methods > SSO > Identity Provider" (Can also be taken from "Project > Project ID")
|
||||||
|
* DESCOPE_SECRET="<Descope Access Key>" # Manage > Access Keys
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* ### Resources
|
||||||
|
*
|
||||||
|
* - [Descope OIDC](https://docs.descope.com/customize/auth/oidc)
|
||||||
|
* - [Descope Flows](https://docs.descope.com/customize/flows)
|
||||||
|
*
|
||||||
|
* ### Notes
|
||||||
|
*
|
||||||
|
* The Descope provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/descope.ts). To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
|
||||||
|
*
|
||||||
|
* :::info
|
||||||
|
* By default, Auth.js assumes that the Descope provider is based on the [OIDC](https://openid.net/specs/openid-connect-core-1_0.html) spec
|
||||||
|
* :::
|
||||||
|
*
|
||||||
|
* ## Help
|
||||||
|
*
|
||||||
|
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
|
||||||
|
*
|
||||||
|
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
|
||||||
|
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
|
||||||
|
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
|
||||||
|
*/
|
||||||
|
export default function Descope(
|
||||||
|
config: OIDCUserConfig<DescopeProfile>
|
||||||
|
): OIDCConfig<DescopeProfile> {
|
||||||
|
return {
|
||||||
|
id: "descope",
|
||||||
|
name: "Descope",
|
||||||
|
type: "oidc",
|
||||||
|
issuer: `https://api.descope.com/${config.clientId}`,
|
||||||
|
style: {
|
||||||
|
logo: "/descope.svg",
|
||||||
|
logoDark: "/descope.svg",
|
||||||
|
bg: "#1C1C23",
|
||||||
|
text: "#ffffff",
|
||||||
|
bgDark: "#1C1C23",
|
||||||
|
textDark: "#ffffff",
|
||||||
|
},
|
||||||
|
options: config,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,17 @@
|
|||||||
import { createTransport } from "nodemailer"
|
|
||||||
|
|
||||||
import type { CommonProviderOptions } from "./index.js"
|
import type { CommonProviderOptions } from "./index.js"
|
||||||
import type { Options as SMTPTransportOptions } from "nodemailer/lib/smtp-transport"
|
|
||||||
import type { Awaitable, Theme } from "../types.js"
|
import type { Awaitable, Theme } from "../types.js"
|
||||||
|
|
||||||
|
import { Transport, TransportOptions, createTransport } from "nodemailer"
|
||||||
|
import * as JSONTransport from "nodemailer/lib/json-transport/index.js"
|
||||||
|
import * as SendmailTransport from "nodemailer/lib/sendmail-transport/index.js"
|
||||||
|
import * as SESTransport from "nodemailer/lib/ses-transport/index.js"
|
||||||
|
import * as SMTPTransport from "nodemailer/lib/smtp-transport/index.js"
|
||||||
|
import * as SMTPPool from "nodemailer/lib/smtp-pool/index.js"
|
||||||
|
import * as StreamTransport from "nodemailer/lib/stream-transport/index.js"
|
||||||
|
|
||||||
|
// TODO: Make use of https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html for the string
|
||||||
|
type AllTransportOptions = string | SMTPTransport | SMTPTransport.Options | SMTPPool | SMTPPool.Options | SendmailTransport | SendmailTransport.Options | StreamTransport | StreamTransport.Options | JSONTransport | JSONTransport.Options | SESTransport | SESTransport.Options | Transport<any> | TransportOptions
|
||||||
|
|
||||||
export interface SendVerificationRequestParams {
|
export interface SendVerificationRequestParams {
|
||||||
identifier: string
|
identifier: string
|
||||||
url: string
|
url: string
|
||||||
@@ -11,6 +19,7 @@ export interface SendVerificationRequestParams {
|
|||||||
provider: EmailConfig
|
provider: EmailConfig
|
||||||
token: string
|
token: string
|
||||||
theme: Theme
|
theme: Theme
|
||||||
|
request: Request
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,10 +35,9 @@ export interface SendVerificationRequestParams {
|
|||||||
*
|
*
|
||||||
* [Custom email service with Auth.js](https://authjs.dev/guides/providers/email#custom-email-service)
|
* [Custom email service with Auth.js](https://authjs.dev/guides/providers/email#custom-email-service)
|
||||||
*/
|
*/
|
||||||
export interface EmailConfig extends CommonProviderOptions {
|
export interface EmailUserConfig {
|
||||||
type: "email"
|
server?: AllTransportOptions
|
||||||
// TODO: Make use of https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html
|
type?: "email"
|
||||||
server?: string | SMTPTransportOptions
|
|
||||||
/** @default `"Auth.js <no-reply@authjs.dev>"` */
|
/** @default `"Auth.js <no-reply@authjs.dev>"` */
|
||||||
from?: string
|
from?: string
|
||||||
/**
|
/**
|
||||||
@@ -40,7 +48,7 @@ export interface EmailConfig extends CommonProviderOptions {
|
|||||||
*/
|
*/
|
||||||
maxAge?: number
|
maxAge?: number
|
||||||
/** [Documentation](https://authjs.dev/guides/providers/email#customizing-emails) */
|
/** [Documentation](https://authjs.dev/guides/providers/email#customizing-emails) */
|
||||||
sendVerificationRequest: (
|
sendVerificationRequest?: (
|
||||||
params: SendVerificationRequestParams
|
params: SendVerificationRequestParams
|
||||||
) => Awaitable<void>
|
) => Awaitable<void>
|
||||||
/**
|
/**
|
||||||
@@ -77,6 +85,31 @@ export interface EmailConfig extends CommonProviderOptions {
|
|||||||
normalizeIdentifier?: (identifier: string) => string
|
normalizeIdentifier?: (identifier: string) => string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface EmailConfig extends CommonProviderOptions {
|
||||||
|
// defaults
|
||||||
|
id: "email"
|
||||||
|
type: "email"
|
||||||
|
name: "Email"
|
||||||
|
server: AllTransportOptions
|
||||||
|
from: string
|
||||||
|
maxAge: number
|
||||||
|
sendVerificationRequest: (
|
||||||
|
params: SendVerificationRequestParams
|
||||||
|
) => Awaitable<void>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is copied into EmailConfig in parseProviders() don't use elsewhere
|
||||||
|
*/
|
||||||
|
options: EmailUserConfig
|
||||||
|
|
||||||
|
// user options
|
||||||
|
// TODO figure out a better way than copying from EmailUserConfig
|
||||||
|
secret?: string
|
||||||
|
generateVerificationToken?: () => Awaitable<string>
|
||||||
|
normalizeIdentifier?: (identifier: string) => string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: Rename to Token provider
|
// TODO: Rename to Token provider
|
||||||
// when started working on https://github.com/nextauthjs/next-auth/discussions/1465
|
// when started working on https://github.com/nextauthjs/next-auth/discussions/1465
|
||||||
export type EmailProviderType = "email"
|
export type EmailProviderType = "email"
|
||||||
@@ -313,7 +346,7 @@ export type EmailProviderType = "email"
|
|||||||
* Always make sure this returns a single e-mail address, even if multiple ones were passed in.
|
* Always make sure this returns a single e-mail address, even if multiple ones were passed in.
|
||||||
* :::
|
* :::
|
||||||
*/
|
*/
|
||||||
export default function Email(config: EmailConfig): EmailConfig {
|
export default function Email(config: EmailUserConfig): EmailConfig {
|
||||||
return {
|
return {
|
||||||
id: "email",
|
id: "email",
|
||||||
type: "email",
|
type: "email",
|
||||||
@@ -337,7 +370,6 @@ export default function Email(config: EmailConfig): EmailConfig {
|
|||||||
throw new Error(`Email (${failed.join(", ")}) could not be sent`)
|
throw new Error(`Email (${failed.join(", ")}) could not be sent`)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// @ts-expect-error
|
|
||||||
options: config,
|
options: config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ interface InternalProviderOptions {
|
|||||||
* @see [Email (Passwordless) guide](https://authjs.dev/guides/providers/email)
|
* @see [Email (Passwordless) guide](https://authjs.dev/guides/providers/email)
|
||||||
* @see [Credentials guide](https://authjs.dev/guides/providers/credentials)
|
* @see [Credentials guide](https://authjs.dev/guides/providers/credentials)
|
||||||
*/
|
*/
|
||||||
export type Provider<P extends Profile = Profile> = (
|
export type Provider<P extends Profile = any> = (
|
||||||
| ((OIDCConfig<P> | OAuth2Config<P> | EmailConfig | CredentialsConfig) &
|
| ((OIDCConfig<P> | OAuth2Config<P> | EmailConfig | CredentialsConfig) &
|
||||||
InternalProviderOptions)
|
InternalProviderOptions)
|
||||||
| ((
|
| ((
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ type EndpointRequest<C, R, P> = (
|
|||||||
callbackUrl: string
|
callbackUrl: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
) => Awaitable<R>
|
) => Awaitable<R> | void
|
||||||
|
|
||||||
/** Gives granular control of the request to the given endpoint */
|
/** Gives granular control of the request to the given endpoint */
|
||||||
interface AdvancedEndpointHandler<P extends UrlParams, C, R> {
|
interface AdvancedEndpointHandler<P extends UrlParams, C, R> {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@auth/sveltekit",
|
"name": "@auth/sveltekit",
|
||||||
"version": "0.3.2",
|
"version": "0.3.6",
|
||||||
"description": "Authentication for SvelteKit.",
|
"description": "Authentication for SvelteKit.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"authentication",
|
"authentication",
|
||||||
@@ -41,14 +41,14 @@
|
|||||||
"svelte-check": "^2.9.2",
|
"svelte-check": "^2.9.2",
|
||||||
"tslib": "^2.4.1",
|
"tslib": "^2.4.1",
|
||||||
"typescript": "^4.9.3",
|
"typescript": "^4.9.3",
|
||||||
"vite": "^4.0.0",
|
"vite": "^4.0.5",
|
||||||
"vitest": "^0.25.3"
|
"vitest": "^0.25.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@auth/core": "workspace:*"
|
"@auth/core": "workspace:*"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"svelte": "^3.54.0",
|
"svelte": "^3.54.0 || ^4.0.0",
|
||||||
"@sveltejs/kit": "^1.0.0"
|
"@sveltejs/kit": "^1.0.0"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ export async function signIn<
|
|||||||
})
|
})
|
||||||
|
|
||||||
const data = await res.clone().json()
|
const data = await res.clone().json()
|
||||||
const error = new URL(data.url).searchParams.get("error")
|
|
||||||
|
|
||||||
if (redirect || !isSupportingReturn) {
|
if (redirect || !isSupportingReturn) {
|
||||||
// TODO: Do not redirect for Credentials and Email providers by default in next major
|
// TODO: Do not redirect for Credentials and Email providers by default in next major
|
||||||
|
|||||||
@@ -211,12 +211,13 @@ import type { AuthAction, AuthConfig, Session } from "@auth/core/types"
|
|||||||
|
|
||||||
export async function getSession(
|
export async function getSession(
|
||||||
req: Request,
|
req: Request,
|
||||||
config: AuthConfig
|
config: SvelteKitAuthConfig
|
||||||
): ReturnType<App.Locals["getSession"]> {
|
): ReturnType<App.Locals["getSession"]> {
|
||||||
config.secret ??= env.AUTH_SECRET
|
config.secret ??= env.AUTH_SECRET
|
||||||
config.trustHost ??= true
|
config.trustHost ??= true
|
||||||
|
|
||||||
const url = new URL("/api/auth/session", req.url)
|
const prefix = config.prefix ?? "/auth"
|
||||||
|
const url = new URL(prefix + "/session", req.url)
|
||||||
const request = new Request(url, { headers: req.headers })
|
const request = new Request(url, { headers: req.headers })
|
||||||
const response = await Auth(request, config)
|
const response = await Auth(request, config)
|
||||||
|
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ export type WithAuthArgs =
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Middleware that checks if the user is authenticated/authorized.
|
* Middleware that checks if the user is authenticated/authorized.
|
||||||
* If if they aren't, they will be redirected to the login page.
|
* If they aren't, they will be redirected to the login page.
|
||||||
* Otherwise, continue.
|
* Otherwise, continue.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
|
|||||||
9185
pnpm-lock.yaml
generated
9185
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
packages:
|
packages:
|
||||||
- "packages/*"
|
- "packages/*"
|
||||||
- "apps/dev/*"
|
- "apps/dev/*"
|
||||||
- "apps/playgrounds/*"
|
|
||||||
- "docs"
|
- "docs"
|
||||||
|
|
||||||
14
turbo.json
14
turbo.json
@@ -3,7 +3,8 @@
|
|||||||
"pipeline": {
|
"pipeline": {
|
||||||
"build": {
|
"build": {
|
||||||
"dependsOn": ["^build"],
|
"dependsOn": ["^build"],
|
||||||
"outputs": ["dist/**/*", "*.js", "*.d.ts", "*.d.ts.map"]
|
"outputs": ["dist/**/*", "*.js", "*.d.ts", "*.d.ts.map"],
|
||||||
|
"outputMode": "new-only"
|
||||||
},
|
},
|
||||||
"next-auth#build": {
|
"next-auth#build": {
|
||||||
"dependsOn": ["^build"],
|
"dependsOn": ["^build"],
|
||||||
@@ -17,7 +18,8 @@
|
|||||||
"react/**",
|
"react/**",
|
||||||
"*.js",
|
"*.js",
|
||||||
"*.d.ts"
|
"*.d.ts"
|
||||||
]
|
],
|
||||||
|
"outputMode": "new-only"
|
||||||
},
|
},
|
||||||
"@auth/core#build": {
|
"@auth/core#build": {
|
||||||
"dependsOn": ["^build"],
|
"dependsOn": ["^build"],
|
||||||
@@ -29,11 +31,13 @@
|
|||||||
"*.d.ts.map",
|
"*.d.ts.map",
|
||||||
"src/lib/pages/styles.ts",
|
"src/lib/pages/styles.ts",
|
||||||
"src/providers/oauth-types.ts"
|
"src/providers/oauth-types.ts"
|
||||||
]
|
],
|
||||||
|
"outputMode": "new-only"
|
||||||
},
|
},
|
||||||
"@auth/sveltekit#build": {
|
"@auth/sveltekit#build": {
|
||||||
"dependsOn": ["^build"],
|
"dependsOn": ["^build"],
|
||||||
"outputs": [".svelte-kit/**", "client.*", "index.*"]
|
"outputs": [".svelte-kit/**", "client.*", "index.*"],
|
||||||
|
"outputMode": "new-only"
|
||||||
},
|
},
|
||||||
"clean": {
|
"clean": {
|
||||||
"cache": false
|
"cache": false
|
||||||
@@ -42,7 +46,7 @@
|
|||||||
"cache": false
|
"cache": false
|
||||||
},
|
},
|
||||||
"test": {
|
"test": {
|
||||||
"outputs": []
|
"outputMode": "new-only"
|
||||||
},
|
},
|
||||||
"e2e": {
|
"e2e": {
|
||||||
"outputs": ["playwright-report/**"]
|
"outputs": ["playwright-report/**"]
|
||||||
|
|||||||
Reference in New Issue
Block a user