Compare commits

..

2 Commits

Author SHA1 Message Date
Thang Vu
98782a6a9b Update release.yml 2023-07-26 23:30:16 +07:00
Thang Vu
e9fefaad11 pin different turbo version 2023-07-26 23:08:13 +07:00
40 changed files with 294 additions and 2607 deletions

View File

@@ -8,54 +8,58 @@ on:
- next
- 3.x
pull_request:
paths-ignore:
- ".vscode/**"
- "**/*.md"
- ".github/ISSUE_TEMPLATE/**"
# TODO: Support latest releases
workflow_dispatch:
inputs:
name:
name:
type: choice
description: Package name (npm)
options:
- "@auth/core"
- "@auth/nextjs"
- "@auth/dgraph-adapter"
- "@auth/drizzle-adapter"
- "@auth/dynamodb-adapter"
- "@auth/fauna-adapter"
- "@auth/firebase-adapter"
- "@auth/mikro-orm-adapter"
- "@auth/mongodb-adapter"
- "@auth/neo4j-adapter"
- "@auth/pouchdb-adapter"
- "@auth/prisma-adapter"
- "@auth/sequelize-adapter"
- "@auth/supabase-adapter"
- "@auth/typeorm-adapter"
- "@auth/upstash-redis-adapter"
- "@auth/xata-adapter"
- "next-auth"
- "@auth/core"
- "@auth/nextjs"
- "@auth/dgraph-adapter"
- "@auth/drizzle-adapter"
- "@auth/dynamodb-adapter"
- "@auth/fauna-adapter"
- "@auth/firebase-adapter"
- "@auth/mikro-orm-adapter"
- "@auth/mongodb-adapter"
- "@auth/neo4j-adapter"
- "@auth/pouchdb-adapter"
- "@auth/prisma-adapter"
- "@auth/sequelize-adapter"
- "@auth/supabase-adapter"
- "@auth/typeorm-adapter"
- "@auth/upstash-redis-adapter"
- "@auth/xata-adapter"
- "next-auth"
# TODO: Infer from package name
path:
type: choice
description: Directory name (packages/*)
options:
- "core"
- "frameworks-nextjs"
- "adapter-dgraph"
- "adapter-drizzle"
- "adapter-dynamodb"
- "adapter-fauna"
- "adapter-firebase"
- "adapter-mikro-orm"
- "adapter-mongodb"
- "adapter-neo4j"
- "adapter-pouchdb"
- "adapter-prisma"
- "adapter-sequelize"
- "adapter-supabase"
- "adapter-typeorm"
- "adapter-upstash-redis"
- "adapter-xata"
- "next-auth"
- "core"
- "frameworks-nextjs"
- "adapter-dgraph"
- "adapter-drizzle"
- "adapter-dynamodb"
- "adapter-fauna"
- "adapter-firebase"
- "adapter-mikro-orm"
- "adapter-mongodb"
- "adapter-neo4j"
- "adapter-pouchdb"
- "adapter-prisma"
- "adapter-sequelize"
- "adapter-supabase"
- "adapter-typeorm"
- "adapter-upstash-redis"
- "adapter-xata"
- "next-auth"
env:
FORCE_COLOR: true
@@ -81,7 +85,7 @@ jobs:
run: pnpm build
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
- name: Run tests
run: pnpm test
timeout-minutes: 15
@@ -89,7 +93,7 @@ jobs:
UPSTASH_REDIS_URL: ${{ secrets.UPSTASH_REDIS_URL }}
UPSTASH_REDIS_KEY: ${{ secrets.UPSTASH_REDIS_KEY }}
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
- name: Upload Turbo artifacts
uses: actions/upload-artifact@v3
with:
@@ -103,7 +107,7 @@ jobs:
# AUTH0_USERNAME: ${{ secrets.AUTH0_USERNAME }}
# AUTH0_PASSWORD: ${{ secrets.AUTH0_PASSWORD }}
# TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
# TURBO_TEAM: ${{ vars.TURBO_TEAM }}
# TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
# - name: Upload E2E artifacts
# if: github.repository == 'nextauthjs/next-auth'
# uses: actions/upload-artifact@v3

5
.gitignore vendored
View File

@@ -65,7 +65,6 @@ packages/adapter-prisma/prisma/dev.db
packages/adapter-prisma/prisma/migrations
db.sqlite
packages/adapter-supabase/supabase/.branches
packages/adapter-drizzle/.drizzle
# Tests
coverage
@@ -98,7 +97,5 @@ packages/frameworks-sveltekit/vite.config.js.timestamp-*
packages/frameworks-sveltekit/vite.config.ts.timestamp-*
# Adapters
docs/docs/reference/adapter
## Drizzle migration folder
.drizzle
docs/docs/reference/adapter

View File

@@ -3,4 +3,4 @@
This folder contains a Next.js app using NextAuth.js for local development. See the following section on how to start:
[Setting up local environment
](https://github.com/nextauthjs/.github/blob/main/CONTRIBUTING.md#setting-up-local-environment)
](https://github.com/nextauthjs/next-auth/blob/main/CONTRIBUTING.md#setting-up-local-environment)

View File

@@ -16,7 +16,7 @@
"svelte": "3.55.0",
"svelte-check": "2.10.2",
"typescript": "4.9.4",
"vite": "4.0.5"
"vite": "4.0.1"
},
"dependencies": {
"@auth/core": "workspace:*",

View File

@@ -100,12 +100,11 @@ NextAuth.js provides [`useSession()`](/reference/react/#usesession) - a [React H
```ts title="pages/_app.tsx"
import { SessionProvider } from "next-auth/react"
import type { AppProps } from 'next/app'
export default function App({
Component,
pageProps: { session, ...pageProps },
}: AppProps) {
}) {
return (
<SessionProvider session={session}>
<Component {...pageProps} />

View File

@@ -2,7 +2,7 @@
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#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/models#verification-token).
:::tip
When using a database, you can still use JWT for session handling for fast access. See the [`session.strategy`](/reference/configuration/auth-config#session) option. Read about the trade-offs of JWT in the [FAQ](/concepts/faq#json-web-tokens).

View File

@@ -30,7 +30,7 @@ You can override any of the options to suit your own use case.
## Configuration
1. Auth.js does not include `nodemailer` as a dependency, so you'll need to install it yourself if you want to use the Email Provider. Run `npm install nodemailer` or `yarn add nodemailer`.
2. You will need an SMTP account; such as [the official Nodemailer recommended service](https://nodemailer.com/about/#example) of [Forward Email](https://forwardemail.net).
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/).
3. There are two ways to configure the SMTP server connection.
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.
```js title=".env" {1}
EMAIL_SERVER=smtp://username:password@smtp.forwardemail.net:587
EMAIL_FROM=support@example.com
EMAIL_SERVER=smtp://username:password@smtp.example.com:587
EMAIL_FROM=noreply@example.com
```
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"
EMAIL_SERVER_USER=username
EMAIL_SERVER_PASSWORD=password
EMAIL_SERVER_HOST=smtp.forwardemail.net
EMAIL_SERVER_HOST=smtp.example.com
EMAIL_SERVER_PORT=587
EMAIL_FROM=noreply@example.com
```

View File

@@ -8,10 +8,6 @@ Using an Auth.js / NextAuth.js adapter you can connect to any database service o
<a href="/reference/adapter/dgraph" class="adapter-card">
<img src="/img/adapters/dgraph.png" width="30" />
<h4 class="adapter-card__title">Dgraph Adapter</h4>
</a>
<a href="/reference/adapter/drizzle" class="adapter-card">
<img src="/img/adapters/drizzle-orm.png" width="30" />
<h4 class="adapter-card__title">Drizzle Adapter</h4>
</a>
<a href="/reference/adapter/dynamodb" class="adapter-card">
<img src="/img/adapters/dynamodb.png" width="30" />
@@ -71,8 +67,10 @@ 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).
:::
## 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:
```mermaid
@@ -133,7 +131,7 @@ If a user first signs in with an OAuth provider, then their email address is aut
This provides a way to contact users and for users to maintain access to their account and sign in using email in the event they are unable to sign in with the OAuth provider in the future (if the [Email Provider](/reference/core/providers_email) is configured).
:::
User creation in the database is automatic and happens when the user is logging in for the first time with a provider.
User creation in the database is automatic and happens when the user is logging in for the first time with a provider.
If the first sign-in is via the [OAuth Provider](/reference/core/providers_oauth), the default data saved is `id`, `name`, `email` and `image`. You can add more profile data by returning extra fields in your [OAuth provider](/guides/providers/custom-provider)'s [`profile()`](/reference/core/providers#profile) callback.
If the first sign-in is via the [Email Provider](/reference/core/providers_email), then the saved user will have `id`, `email`, `emailVerified`, where `emailVerified` is the timestamp of when the user was created.

View File

@@ -265,7 +265,6 @@ const docusaurusConfig = {
? []
: [
typedocAdapter("Dgraph"),
typedocAdapter("Drizzle"),
typedocAdapter("DynamoDB"),
typedocAdapter("Fauna"),
typedocAdapter("Firebase"),

View File

@@ -55,7 +55,6 @@ module.exports = {
link: { type: "doc", id: "reference/adapters/index" },
items: [
{ type: "doc", id: "reference/adapter/dgraph/index" },
{ type: "doc", id: "reference/adapter/drizzle/index" },
{ type: "doc", id: "reference/adapter/dynamodb/index" },
{ type: "doc", id: "reference/adapter/fauna/index" },
{ type: "doc", id: "reference/adapter/firebase/index" },

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

View File

@@ -6,8 +6,8 @@
"scripts": {
"build:app": "turbo run build --filter=next-auth-app",
"build:docs": "turbo run build --filter=docs",
"build": "turbo run build --filter=next-auth --filter=@next-auth/* --filter=@auth/* --no-deps",
"test": "turbo run test --concurrency=1 --filter=[HEAD^1] --filter=./packages/* --filter=!@*upstash* --filter=!*dynamodb-* --filter=!*app*",
"build": "turbo run build --filter=next-auth --filter=@next-auth/* --filter=@auth/* --no-deps --summarize",
"test": "turbo run test --concurrency=1 --summarize --filter=[HEAD^1] --filter=./packages/* --filter=!@*upstash* --filter=!*dynamodb-* --filter=!*app*",
"clean": "turbo run clean --no-cache",
"dev:db": "turbo run dev --parallel --continue --filter=next-auth-app...",
"dev": "turbo run dev --parallel --continue --filter=next-auth-app... --filter=!./packages/adapter-*",
@@ -43,7 +43,7 @@
"eslint-plugin-svelte3": "^4.0.0",
"prettier": "2.8.1",
"prettier-plugin-svelte": "^2.8.1",
"turbo": "^1.10.3",
"turbo": "1.10.3",
"typescript": "4.9.4"
},
"engines": {

View File

@@ -1 +0,0 @@
//registry.npmjs.org/:_authToken=${NPM_TOKEN}

View File

@@ -1,28 +0,0 @@
<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).

View File

@@ -1,65 +0,0 @@
{
"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"
}
}

View File

@@ -1,268 +0,0 @@
/**
* <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.")
}

View File

@@ -1,255 +0,0 @@
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
},
}
}

View File

@@ -1,225 +0,0 @@
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 }
},
}
}

View File

@@ -1,203 +0,0 @@
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
},
}
}

View File

@@ -1,47 +0,0 @@
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
}

View File

@@ -1,43 +0,0 @@
// 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(),
},
}

View File

@@ -1,13 +0,0 @@
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

View File

@@ -1,71 +0,0 @@
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,
},
})

View File

@@ -1,29 +0,0 @@
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,
})

View File

@@ -1,22 +0,0 @@
#!/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}

View File

@@ -1,13 +0,0 @@
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

View File

@@ -1,65 +0,0 @@
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),
},
})

View File

@@ -1,10 +0,0 @@
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))

View File

@@ -1,11 +0,0 @@
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 }

View File

@@ -1,25 +0,0 @@
#!/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}

View File

@@ -1,10 +0,0 @@
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

View File

@@ -1,60 +0,0 @@
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,
},
})

View File

@@ -1,20 +0,0 @@
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,
},
})

View File

@@ -1,12 +0,0 @@
#!/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

View File

@@ -1,25 +0,0 @@
{
"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",
]
}

View File

@@ -15,13 +15,6 @@ const requiredMethods = [
]
export interface TestOptions {
adapter: Adapter
fixtures?: {
user?: any
session?: any
account?: any
sessionUpdateExpires?: Date
verificationTokenExpires?: Date
},
db: {
/** Generates UUID v4 by default. Use it to override how the test suite should generate IDs, like user id. */
id?: () => string
@@ -74,11 +67,11 @@ export async function runBasicTests(options: TestOptions) {
await options.db.disconnect?.()
})
let user: any = options.fixtures?.user ?? {
let user: any = {
email: "fill@murray.com",
image: "https://www.fillmurray.com/460/300",
name: "Fill Murray",
emailVerified: new Date()
emailVerified: new Date(),
}
if (process.env.CUSTOM_MODEL === "1") {
@@ -86,12 +79,12 @@ export async function runBasicTests(options: TestOptions) {
user.phone = "00000000000"
}
const session: any = options.fixtures?.session ?? {
const session: any = {
sessionToken: randomUUID(),
expires: ONE_WEEK_FROM_NOW,
}
const account: any = options.fixtures?.account ?? {
const account: any = {
provider: "github",
providerAccountId: randomUUID(),
type: "oauth",
@@ -182,17 +175,15 @@ export async function runBasicTests(options: TestOptions) {
test("updateSession", async () => {
let dbSession = await db.session(session.sessionToken)
const expires = options.fixtures?.sessionUpdateExpires ?? ONE_MONTH_FROM_NOW
expect(dbSession.expires.valueOf()).not.toBe(expires.valueOf())
expect(dbSession.expires.valueOf()).not.toBe(ONE_MONTH_FROM_NOW.valueOf())
await adapter.updateSession({
sessionToken: session.sessionToken,
expires,
expires: ONE_MONTH_FROM_NOW,
})
dbSession = await db.session(session.sessionToken)
expect(dbSession.expires.valueOf()).toBe(expires.valueOf())
expect(dbSession.expires.valueOf()).toBe(ONE_MONTH_FROM_NOW.valueOf())
})
test("linkAccount", async () => {
@@ -241,7 +232,7 @@ export async function runBasicTests(options: TestOptions) {
const verificationToken = {
token: hashedToken,
identifier,
expires: options.fixtures?.verificationTokenExpires ?? FIFTEEN_MINUTES_FROM_NOW,
expires: FIFTEEN_MINUTES_FROM_NOW,
}
await adapter.createVerificationToken?.(verificationToken)
@@ -260,7 +251,7 @@ export async function runBasicTests(options: TestOptions) {
const verificationToken = {
token: hashedToken,
identifier,
expires: options.fixtures?.verificationTokenExpires ?? FIFTEEN_MINUTES_FROM_NOW,
expires: FIFTEEN_MINUTES_FROM_NOW,
}
await adapter.createVerificationToken?.(verificationToken)

View File

@@ -15,7 +15,7 @@ module.exports = {
".(js|jsx)$": ["@swc/jest", swcConfig],
},
transformIgnorePatterns: ["[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$"],
moduleFileExtensions: ["mjs", "cjs", "ts", "tsx", "js", "jsx", "json", "node"],
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
rootDir: ".",
// coverageDirectory: "<rootDir>/coverage/",
// collectCoverageFrom: ["<rootDir>/packages/*/src/**/*.{ts,tsx}"],

View File

@@ -1,6 +1,6 @@
{
"name": "@auth/core",
"version": "0.10.1",
"version": "0.10.0",
"description": "Authentication for the Web.",
"keywords": [
"authentication",

View File

@@ -41,7 +41,7 @@
"svelte-check": "^2.9.2",
"tslib": "^2.4.1",
"typescript": "^4.9.3",
"vite": "^4.0.5",
"vite": "^4.0.0",
"vitest": "^0.25.3"
},
"dependencies": {

1225
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff