Compare commits

..

42 Commits

Author SHA1 Message Date
GitHub Actions
99035b98f9 chore(release): bump package version(s) [skip ci] 2023-08-18 08:13:39 +00:00
Balázs Orbán
fabb0525d1 fix(adapters): use built-in is() to identify db type (#8342)
* fix(adapters): use built-in `is()` to identify db type

* remove unused .then

* fix imports
2023-08-18 09:11:33 +01:00
GitHub Actions
28e4328704 chore(release): bump package version(s) [skip ci] 2023-08-14 14:46:10 +00:00
Balázs Orbán
7ff4d9d280 feat: allow raw response for lib authors 2023-08-14 16:39:11 +02:00
Balázs Orbán
26815f7621 docs: clean up roadmap 2023-08-12 12:50:31 +02:00
Balázs Orbán
fe2c3dc6bf docs: fix links, references, grammar 2023-08-12 12:23:17 +02:00
GitHub Actions
c53435af8a chore(release): bump package version(s) [skip ci] 2023-08-12 09:41:43 +00:00
Balázs Orbán
f29a1f2778 fix(release): include lib in package
Related issue #8299
2023-08-12 11:38:01 +02:00
Balázs Orbán
a4c3270307 chore(turbo): include lib in cache outputs
Closes #8299
2023-08-12 11:35:44 +02:00
GitHub Actions
645d003d49 chore(release): bump package version(s) [skip ci] 2023-08-11 11:31:12 +00:00
Balázs Orbán
3f296615c5 fix(providers): docs typo 2023-08-11 13:27:33 +02:00
GitHub Actions
a7842077ec chore(release): bump package version(s) [skip ci] 2023-08-11 11:26:36 +00:00
Balázs Orbán
b2e5b9f6a8 feat(providers): add Passage by 1Password (#8295) 2023-08-11 12:23:27 +01:00
GitHub Actions
0681531627 chore(release): bump package version(s) [skip ci] 2023-08-11 10:29:05 +00:00
Balázs Orbán
ea81c467e9 fix(ts): compatibility with next-auth v4 types (#8294)
* fix(ts): compatibility with `next-auth` v4

* revert

* stricter types
2023-08-11 11:26:03 +01:00
GitHub Actions
bf2835d38f chore(release): bump package version(s) [skip ci] 2023-08-11 08:26:58 +00:00
Simon Sardorf
89d230666b feat(adapters): standardize default table names to be singular (#8282)
standardize all table names in drizzle adapter to be singular
2023-08-10 12:18:21 +01:00
Jonathan Edenström
f86e56f78a fix: sort cookie chunks correctly (#8278)
* fix: sort cookie chunks correctly

* chore: remove v4 next-auth change
2023-08-10 12:18:00 +01:00
Balázs Orbán
fe20b943ae docs: Update README.md 2023-08-10 00:57:02 +02:00
Balázs Orbán
4678c4d4fc docs: Update README.md 2023-08-10 00:56:24 +02:00
Balázs Orbán
3eb3f8f107 docs: typo 2023-08-09 23:15:35 +02:00
Balázs Orbán
7fd03f38e3 docs: remove heading from README.md 2023-08-09 23:14:53 +02:00
Balázs Orbán
ae44b72765 Merge branch 'main' of github.com:nextauthjs/next-auth 2023-08-09 23:08:13 +02:00
Balázs Orbán
a996ab57e8 🤖 lazy commit 2023-08-09 23:07:28 +02:00
Thang Vu
ebdeaf740d chore: move Turbo env vars to top level 2023-08-09 19:07:01 +07:00
GitHub Actions
c5c8a81462 chore(release): bump package version(s) [skip ci] 2023-08-09 09:39:28 +00:00
Balázs Orbán
61d30f3dcd fix(docs): correct broken link 2023-08-09 11:33:17 +02:00
Balázs Orbán
a9180a752b fix(docs): correct broken links 2023-08-09 11:33:08 +02:00
Balázs Orbán
6c4180146e chore(docs): add @auth/solid-start to turbo cache 2023-08-09 11:28:37 +02:00
Balázs Orbán
ec6c4ea2be docs: fix redirects 2023-08-09 11:20:10 +02:00
Balázs Orbán
3dfc86334e docs: fix redirects 2023-08-09 11:13:36 +02:00
Balázs Orbán
01d6019638 docs: fix redirects 2023-08-09 11:07:20 +02:00
GitHub Actions
4730429a9f chore(release): bump package version(s) [skip ci] 2023-08-09 09:05:20 +00:00
Adam James
a49236ef62 fix(ts): corrected sqlite condition (#8269) 2023-08-09 10:59:08 +02:00
Balázs Orbán
96ade948ef chore(docs): fix redirect 2023-08-09 01:08:58 +02:00
GitHub Actions
550507b2d1 chore(release): bump package version(s) [skip ci] 2023-08-08 23:07:59 +00:00
Mark
1eddcf643c feat(adapters): add Kysely adapter (#5464)
* feat: kysely-adapter with PostgreSQL and MySQL support

* feat: kysely-adapter with SQLite support

* docs: add docs for kysely-adapter

* chore: cleanup

* chore: update adapter lists

* chore: update column types

* chore: remove pgcrypto install

* chore: add indexes

* chore: Object.assign and cleanup

* feat: add AuthedKysely wrapper

* docs: add Naming Conventions section

* chore: add coerceReturnData to reduce repitition

* chore: add coerceInputData to reduce repitition

* chore: move AuthedKysely export to end

* chore: cleanup

* docs: remove unused import

* feat: add support for using AuthedKysely with generated types from kysely-codegen

* docs: formatting

* chore: CodeGen --> Codegen

* docs: wording update, ts

Co-authored-by: Julius Marminge <julius0216@outlook.com>

* chore: use latest kysely version, update model

* docs: move content to source code

* chore: update deps

* chore: update logo location, add link in overview

* chore: bump kysely version

Co-authored-by: Igal Klebanov <igalklebanov@gmail.com>

* chore: update docs

Co-authored-by: Igal Klebanov <igalklebanov@gmail.com>

* chore: update docs with links to new Kysely docs

Co-authored-by: Jie Peng <dean.leehom@gmail.com>

* feat: emailVerified shouldn't have a default

Co-authored-by: Lars Graubner <lgraubner@users.noreply.github.com>

* simplify, update code

* add README.md

* clean up docs

* fix adapter name

* add to turbo

* fix test

* revert some changes

* test fixes

---------

Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: Igal Klebanov <igalklebanov@gmail.com>
Co-authored-by: Jie Peng <dean.leehom@gmail.com>
Co-authored-by: Lars Graubner <lgraubner@users.noreply.github.com>
Co-authored-by: Balázs Orbán <info@balazsorban.com>
2023-08-09 00:01:59 +01:00
Julius Marminge
17d71a04d6 feat(adapters): support multi-project schema (#8266)
* feat: multi-project schema support

Ref: https://orm.drizzle.team/docs/goodies#multi-project-schema

* Update index.ts

* Update index.ts

* doc

* tests

---------

Co-authored-by: Balázs Orbán <info@balazsorban.com>
2023-08-08 23:59:50 +01:00
Balázs Orbán
3c65e264af chore: add drizzle to issue labeler 2023-08-09 00:44:54 +02:00
Balázs Orbán
28d8d4894d chore: add drizzle to turbo 2023-08-09 00:43:49 +02:00
Balázs Orbán
c6b98a8f08 chore: gitignore generated .npmrc files 2023-08-09 00:03:10 +02:00
Balázs Orbán
d042f933c6 fix(docs): update logo URL 2023-08-09 00:01:44 +02:00
78 changed files with 2078 additions and 427 deletions

View File

@@ -22,9 +22,12 @@ body:
options:
- "Custom adapter"
- "@auth/dgraph-adapter"
- "@auth/drizzle-adapter"
- "@auth/dynamodb-adapter"
- "@auth/drizzle-adapter"
- "@auth/fauna-adapter"
- "@auth/firebase-adapter"
- "@auth/kysely-adapter"
- "@auth/mikro-orm-adapter"
- "@auth/mongodb-adapter"
- "@auth/neo4j-adapter"

View File

@@ -3,6 +3,9 @@
dgraph:
- "@auth/dgraph-adapter"
drizzle:
- "@auth/drizzle-adapter"
dynamodb:
- "@auth/dynamodb-adapter"
@@ -12,6 +15,9 @@ fauna:
firebase:
- "@auth/firebase-adapter"
kysely:
- "@auth/kysely-adapter"
mikro-orm:
- "@auth/mikro-orm-adapter"

View File

@@ -15,6 +15,7 @@ neo4j: ["packages/adapter-neo4j/**/*"]
playgrounds: ["apps/playgrounds/**/*"]
pouchdb: ["packages/adapter-pouchdb/**/*"]
prisma: ["packages/adapter-prisma/**/*"]
kysely: ["packages/adapter-kysely/**/*"]
providers: ["packages/core/src/providers/**/*"]
sequelize: ["packages/adapter-sequelize/**/*"]
solidjs: ["packages/frameworks-solid-start/**/*"]

View File

@@ -16,7 +16,6 @@ on:
description: Package name (npm)
options:
- "@auth/core"
- "@auth/nextjs"
- "@auth/dgraph-adapter"
- "@auth/drizzle-adapter"
- "@auth/dynamodb-adapter"
@@ -57,6 +56,8 @@ on:
- "adapter-xata"
- "next-auth"
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
FORCE_COLOR: true
jobs:
@@ -79,22 +80,12 @@ jobs:
run: pnpm install
- name: Build
run: pnpm build
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
- name: Run tests
run: pnpm test
timeout-minutes: 15
env:
UPSTASH_REDIS_URL: ${{ secrets.UPSTASH_REDIS_URL }}
UPSTASH_REDIS_KEY: ${{ secrets.UPSTASH_REDIS_KEY }}
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
- name: Upload Turbo artifacts
uses: actions/upload-artifact@v3
with:
name: turbo-report
path: .turbo/runs/
# - name: Run E2E tests
# if: github.repository == 'nextauthjs/next-auth'
# run: pnpm e2e

2
.gitignore vendored
View File

@@ -6,6 +6,8 @@
.env.development.local
.env.test.local
.env.production.local
packages/*/.npmrc
npm-debug.log*
yarn-debug.log*

View File

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

153
README.md Normal file
View File

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

View File

@@ -7,7 +7,7 @@ title: Frequently Asked Questions
### Is Auth.js commercial software?
Auth.js is an open source project built by individual contributors.
Auth.js is an open-source project built by individual contributors.
It is not commercial software and is not associated with a commercial organization.
@@ -17,7 +17,7 @@ It is not commercial software and is not associated with a commercial organizati
<details>
<summary>
<h3 style={{display:"inline-block"}}>What databases does Auth.js support?</h3>
<h3 style={{display: "inline-block"}}>What databases does Auth.js support?</h3>
</summary>
<p>
@@ -30,7 +30,7 @@ You can use also Auth.js with any database using a custom database adapter, or b
<details>
<summary>
<h3 style={{display:"inline-block"}}>What authentication services does Auth.js support?</h3>
<h3 style={{display: "inline-block"}}>What authentication services does Auth.js support?</h3>
</summary>
<p>
@@ -38,16 +38,16 @@ You can use also Auth.js with any database using a custom database adapter, or b
(See also: <a href="/reference/providers/oauth-builtin">Providers</a>)
</p>
Auth.js also supports email for passwordless sign in, which is useful for account recovery or for people who are not able to use an account with the configured OAuth services (e.g. due to service outage, account suspension or otherwise becoming locked out of an account).
Auth.js also supports email for passwordless sign-in, which is useful for account recovery or for people who are not able to use an account with the configured OAuth services (e.g. due to service outage, account suspension or otherwise becoming locked out of an account).
You can also use a custom based provider to support signing in with a username and password stored in an external database and/or using two factor authentication.
You can also use a custom-based provider to support signing in with a username and password stored in an external database and/or using two-factor authentication.
</p>
</details>
<details>
<summary>
<h3 style={{display:"inline-block"}}>Does Auth.js support signing in with a username and password?</h3>
<h3 style={{display: "inline-block"}}>Does Auth.js support signing in with a username and password?</h3>
</summary>
<p>
@@ -55,44 +55,44 @@ Auth.js is designed to avoid the need to store passwords for user accounts.
If you have an existing database of usernames and passwords, you can use a custom credentials provider to allow signing in with a username and password stored in an existing database.
_If you use a custom credentials provider user accounts will not be persisted in a database by Auth.js (even if one is configured). The option to use JSON Web Tokens for session tokens (which allow sign in without using a session database) must be enabled to use a custom credentials provider._
_If you use a custom credentials provider user accounts will not be persisted in a database by Auth.js (even if one is configured). The option to use JSON Web Tokens for session tokens (which allow sign-in without using a session database) must be enabled to use a custom credentials provider._
</p>
</details>
<details>
<summary>
<h3 style={{display:"inline-block"}}>Can I use Auth.js with a website that does not use Next.js?</h3>
<h3 style={{display: "inline-block"}}>Can I use Auth.js with a website that does not use Next.js?</h3>
</summary>
<p>
Auth.js is designed for use with Next.js and Serverless.
If you are using a different framework for your website, you can create a website that handles sign in with Next.js and then access those sessions on a website that does not use Next.js as long as the websites are on the same domain.
If you are using a different framework for your website, you can create a website that handles sign-in with Next.js and then access those sessions on a website that does not use Next.js as long as the websites are on the same domain.
If you use Auth.js on a website with a different subdomain then the rest of your website (e.g. `auth.example.com` vs `www.example.com`) you will need to set a custom cookie domain policy for the Session Token cookie. (See also: [Cookies](/reference/configuration/auth-config#cookies))
If you use Auth.js on a website with a different subdomain than the rest of your website (e.g. `auth.example.com` vs `www.example.com`) you will need to set a custom cookie domain policy for the Session Token cookie. (See also: [Cookies](/reference/configuration/auth-config#cookies))
Auth.js does not currently support automatically signing into sites on different top level domains (e.g. `www.example.com` vs `www.example.org`) using a single session.
Auth.js does not currently support automatically signing into sites on different top-level domains (e.g. `www.example.com` vs `www.example.org`) using a single session.
</p>
</details>
<details>
<summary>
<h3 style={{display:"inline-block"}}>Can I use Auth.js with React Native?</h3>
<h3 style={{display: "inline-block"}}>Can I use Auth.js with React Native?</h3>
</summary>
<p>
Auth.js is designed as a secure, confidential client and implements a server side authentication flow.
Auth.js is designed as a secure, confidential client and implements a server-side authentication flow.
It is not intended to be used in native applications on desktop or mobile applications, which typically implement public clients (e.g. with client / secrets embedded in the application).
It is not intended to be used in native applications on desktop or mobile applications, which typically implement public clients (e.g. with client/secrets embedded in the application).
</p>
</details>
<details>
<summary>
<h3 style={{display:"inline-block"}}>Is Auth.js supporting TypeScript?</h3>
<h3 style={{display: "inline-block"}}>Is Auth.js supporting TypeScript?</h3>
</summary>
<p>
@@ -103,11 +103,11 @@ Yes! Check out the [TypeScript docs](/getting-started/typescript)
<details>
<summary>
<h3 style={{display:"inline-block"}}>Is Auth.js compatible with Next.js 12 Middleware?</h3>
<h3 style={{display: "inline-block"}}>Is Auth.js compatible with Next.js 12 Middleware?</h3>
</summary>
<p>
[Next.js Middleware](https://nextjs.org/docs/middleware) is supported. Head over to the [this page](/reference/nextjs/#middleware)
[Next.js Middleware](https://nextjs.org/docs/middleware) is supported. Head over to [this page](https://next-auth.js.org/configuration/nextjs#middleware)
</p>
</details>
@@ -118,7 +118,7 @@ Yes! Check out the [TypeScript docs](/getting-started/typescript)
<details>
<summary>
<h3 style={{display:"inline-block"}}>What databases are supported by Auth.js?</h3>
<h3 style={{display: "inline-block"}}>What databases are supported by Auth.js?</h3>
</summary>
<p>
@@ -131,13 +131,13 @@ It also provides an Adapter API which allows you to connect it to any database.
<details>
<summary>
<h3 style={{display:"inline-block"}}>What does Auth.js use databases for?</h3>
<h3 style={{display: "inline-block"}}>What does Auth.js use databases for?</h3>
</summary>
<p>
Databases in Auth.js are used for persisting users, OAuth accounts, email sign in tokens and sessions.
Databases in Auth.js are used for persisting users, OAuth accounts, email sign-in tokens and sessions.
Specifying a database is optional if you don't need to persist user data or support email sign in. If you don't specify a database then JSON Web Tokens will be enabled for session storage and used to store session data.
Specifying a database is optional if you don't need to persist user data or support email sign-in. If you don't specify a database then JSON Web Tokens will be enabled for session storage and used to store session data.
If you are using a database with Auth.js, you can still explicitly enable JSON Web Tokens for sessions (instead of using database sessions).
@@ -146,24 +146,24 @@ If you are using a database with Auth.js, you can still explicitly enable JSON W
<details>
<summary>
<h3 style={{display:"inline-block"}}>Should I use a database?</h3>
<h3 style={{display: "inline-block"}}>Should I use a database?</h3>
</summary>
<p>
- Using Auth.js without a database works well for internal tools - where you need to control who is able to sign in, but when you do not need to create user accounts for them in your application.
- Using Auth.js without a database works well for internal tools - where you need to control who can sign in, but when you do not need to create user accounts for them in your application.
- Using Auth.js with a database is usually a better approach for a consumer facing application where you need to persist accounts (e.g. for billing, to contact customers, etc).
- Using Auth.js with a database is usually a better approach for a consumer-facing application where you need to persist accounts (e.g. for billing, to contact customers, etc).
</p>
</details>
<details>
<summary>
<h3 style={{display:"inline-block"}}>What database should I use?</h3>
<h3 style={{display: "inline-block"}}>What database should I use?</h3>
</summary>
<p>
Managed database solutions for MySQL, Postgres and MongoDB (and compatible databases) are well supported from cloud providers such as Amazon, Google, Microsoft and Atlas.
Managed database solutions for MySQL, Postgres and MongoDB (and compatible databases) are well supported by cloud providers such as Amazon, Google, Microsoft and Atlas.
If you are deploying directly to a particular cloud platform you may also want to consider serverless database offerings they have (e.g. [Amazon Aurora Serverless on AWS](https://aws.amazon.com/rds/aurora/serverless/)).
@@ -174,51 +174,49 @@ If you are deploying directly to a particular cloud platform you may also want t
## Security
Parts of this section has been moved to its [own page](/security).
Parts of this section have been moved to their [page](/security)](/security).
<details>
<summary>
<h3 style={{display:"inline-block"}}>How do I get Refresh Tokens and Access Tokens for an OAuth account?</h3>
<h3 style={{display: "inline-block"}}>How do I get Refresh Tokens and Access Tokens for an OAuth account?</h3>
</summary>
<p>
Auth.js provides a solution for authentication, session management and user account creation.
Auth.js records Refresh Tokens and Access Tokens on sign in (if supplied by the provider) and it will pass them, along with the User ID, Provider and Provider Account ID, to either:
Auth.js records Refresh Tokens and Access Tokens on sign-in (if supplied by the provider) and it will pass them, along with the User ID, Provider and Provider Account ID, to either:
1. A database - if a database connection string is provided
2. The JSON Web Token callback - if JWT sessions are enabled (e.g. if no database specified)
2. The JSON Web Token callback - if JWT sessions are enabled (e.g. if no database is specified)
You can then look them up from the database or persist them to the JSON Web Token.
Note: Auth.js does not currently handle Access Token rotation for OAuth providers for you, however you can check out [this tutorial](/guides/basics/refresh-token-rotation) if you want to implement it.
We also have an [example repository](https://github.com/nextauthjs/next-auth-refresh-token-example) / project based upon Auth.js v4 where we demonstrate how to use a refresh token to refresh the provided access token.
Note: Auth.js does not currently handle Access Token rotation for OAuth providers for you, however, you can check out [this tutorial](/guides/basics/refresh-token-rotation) if you want to implement it.
</p>
</details>
<details>
<summary>
<h3 style={{display:"inline-block"}}>When I sign in with another account with the same email address, why are accounts not linked automatically?</h3>
<h3 style={{display: "inline-block"}}>When I sign in with another account with the same email address, why are accounts not linked automatically?</h3>
</summary>
<p>
Automatic account linking on sign in is not secure between arbitrary providers - with the exception of allowing users to sign in via an email addresses as a fallback (as they must verify their email address as part of the flow).
Automatic account linking on sign-in is not secure between arbitrary providers - except for allowing users to sign in via email addresses as a fallback (as they must verify their email address as part of the flow).
When an email address is associated with an OAuth account it does not necessarily mean that it has been verified as belonging to account holder — how email address verification is handled is not part of the OAuth specification and varies between providers (e.g. some do not verify first, some do verify first, others return metadata indicating the verification status).
When an email address is associated with an OAuth account it does not necessarily mean that it has been verified as belonging to the account holder — how email address verification is handled is not part of the OAuth specification and varies between providers (e.g. some do not verify first, some do verify first, others return metadata indicating the verification status).
With automatic account linking on sign in, this can be exploited by bad actors to hijack accounts by creating an OAuth account associated with the email address of another user.
With automatic account linking on sign-in, this can be exploited by bad parties to hijack accounts by creating an OAuth account associated with the email address of another user.
For this reason it is not secure to automatically link accounts between arbitrary providers on sign in, which is why this feature is generally not provided by authentication service and is not provided by Auth.js.
For this reason, it is not secure to automatically link accounts between arbitrary providers on sign-in, which is why this feature is generally not provided by an authentication service and is not provided by Auth.js.
Automatic account linking is seen on some sites, sometimes insecurely. It can be technically possible to do automatic account linking securely if you trust all the providers involved to ensure they have securely verified the email address associated with the account, but requires placing trust (and transferring the risk) to those providers to handle the process securely.
Examples of scenarios where this is secure include with an OAuth provider you control (e.g. that only authorizes users internal to your organization) or with a provider you explicitly trust to have verified the users email address.
Examples of scenarios where this is secure include an OAuth provider you control (e.g. that only authorizes users internal to your organization) or a provider you explicitly trust to have verified the users' email address.
Automatic account linking is not a planned feature of Auth.js, however there is scope to improve the user experience of account linking and of handling this flow, in a secure way. Typically this involves providing a fallback option to sign in via email, which is already possible (and recommended), but the current implementation of this flow could be improved on.
Automatic account linking is not a planned feature of Auth.js, however, there is scope to improve the user experience of account linking and of handling this flow, securely. Typically this involves providing a fallback option to sign in via email, which is already possible (and recommended), but the current implementation of this flow could be improved.
Providing support for secure account linking and unlinking of additional providers - which can only be done if a user is already signed in already - was originally a feature in v1.x but has not been present since v2.0, is planned to return in a future release.
Providing support for secure account linking and unlinking of additional providers - which can only be done if a user is already signed in - was originally a feature in v1.x but has not been present since v2.0, and is planned to return in a future release.
</p>
</details>
@@ -229,11 +227,11 @@ Providing support for secure account linking and unlinking of additional provide
<details>
<summary>
<h3 style={{display:"inline-block"}}>Why doesn't Auth.js support [a particular feature]?</h3>
<h3 style={{display: "inline-block"}}>Why doesn't Auth.js support [a particular feature]?</h3>
</summary>
<p>
Auth.js is an open source project built by individual contributors who are volunteers writing code and providing support in their spare time.
Auth.js is an open-source project built by individual contributors who are volunteers writing code and providing support in their spare time.
If you would like Auth.js to support a particular feature, the best way to help make it happen is to raise a feature request describing the feature and offer to work with other contributors to develop and test it.
@@ -244,13 +242,13 @@ If you are not able to develop a feature yourself, you can offer to sponsor some
<details>
<summary>
<h3 style={{display:"inline-block"}}>I disagree with a design decision, how can I change your mind?</h3>
<h3 style={{display: "inline-block"}}>I disagree with a design decision, how can I change your mind?</h3>
</summary>
<p>
Product design decisions on Auth.js are made by core team members.
You can raise suggestions as feature requests / requests for enhancement.
You can raise suggestions as feature requests for enhancement.
Requests that provide the detail requested in the template and follow the format requested may be more likely to be supported, as additional detail prompted in the templates often provides important context.
@@ -286,7 +284,7 @@ JSON Web Tokens can be used for session tokens, but are also used for lots of ot
- JSON Web Tokens in Auth.js are secured using cryptographic encryption (JWE) to store the included information directly in a JWT session token. You may then use the token to pass information between services and APIs on the same domain without having to contact a database to verify the included information.
- You can use JWT to securely store information you do not mind the client knowing even without encryption, as the JWT is stored in a server-readable-only cookie so data in the JWT is not accessible to third party JavaScript running on your site.
- You can use JWT to securely store information you do not mind the client knowing even without encryption, as the JWT is stored in a server-readable-only cookie so data in the JWT is not accessible to third-party JavaScript running on your site.
</p>
</details>
@@ -297,15 +295,15 @@ JSON Web Tokens can be used for session tokens, but are also used for lots of ot
</summary>
<p>
- You cannot as easily expire a JSON Web Token - doing so requires maintaining a server side blocklist of invalid tokens (at least until they expire) and checking every token against the list every time a token is presented.
- It's difficult to invalidate a JSON Web Token - doing so requires maintaining a server-side blocklist of the tokens (at least until they expire) and checking every token against the list every time a token is presented.
Shorter session expiry times are used when using JSON Web Tokens as session tokens to allow sessions to be invalidated sooner and simplify this problem.
Auth.js client includes advanced features to mitigate the downsides of using shorter session expiry times on the user experience, including automatic session token rotation, optionally sending keep alive messages to prevent short lived sessions from expiring if there is an window or tab open, background re-validation, and automatic tab/window syncing that keeps sessions in sync across windows any time session state changes or a window or tab gains or loses focus.
Auth.js client includes advanced features to mitigate the downsides of using shorter session expiry times on the user experience, including automatic session token rotation, optionally sending keep-alive messages to prevent short-lived sessions from expiring if there is a window or tab opened, background re-validation, and automatic tab/window syncing that keeps sessions in sync across windows any time session state changes or a window or tab gains or loses focus.
- As with database session tokens, JSON Web Tokens are limited in the amount of data you can store in them. There is typically a limit of around 4096 bytes per cookie, though the exact limit varies between browsers, proxies and hosting services. If you want to support most browsers, then do not exceed 4096 bytes per cookie. If you want to save more data, you will need to persist your sessions in a database (Source: [browsercookielimits.iain.guru](http://browsercookielimits.iain.guru/))
The more data you try to store in a token and the more other cookies you set, the closer you will come to this limit. Since v4 we have implemented cookie chunking so that cookies over the 4kb limit get split and reassembled upon parsing. However since this data needs to be transmitted on every request, if you wish to store more than ~4 KB of data you're probably at the point where you want to store a unique ID in the token and persist the data elsewhere (e.g. in a server-side key/value store).
The more data you try to store in a token and the more other cookies you set, the closer you will come to this limit. Auth.js uses cookie chunking so that cookies over the 4kb limit get split and reassembled upon parsing. However, since this data needs to be transmitted on every request, in case you wish to store more than ~4 KB of data you're probably at the point where you want to store a unique ID in the token and persist the data elsewhere (e.g. in a server-side key/value store).
- Data stored in an encrypted JSON Web Token (JWE) may be compromised at some point.
@@ -313,7 +311,7 @@ JSON Web Tokens can be used for session tokens, but are also used for lots of ot
Avoid storing any data in a token that might be problematic if it were to be decrypted in the future.
- If you do not explicitly specify a secret for for Auth.js, existing sessions will be invalidated any time your Auth.js configuration changes, as Auth.js will default to an auto-generated secret. Since v4 this only impacts development and generating a secret is required in production.
- If you do not explicitly specify a secret for Auth.js, existing sessions will be invalidated any time your Auth.js configuration changes, as Auth.js will default to an auto-generated secret. Since v4 this only impacts development and generating a secret is required in production.
</p>
</details>
@@ -324,12 +322,10 @@ JSON Web Tokens can be used for session tokens, but are also used for lots of ot
</summary>
<p>
By default tokens are not signed (JWS) but are encrypted (JWE). Since v4 we have implemented cookie chunking so that cookies over the 4kb limit get split and reassembled upon parsing.
By default, tokens are encrypted (JWE).
You can specify other valid algorithms - [as specified in RFC 7518](https://tools.ietf.org/html/rfc7517) - with either a secret (for symmetric encryption) or a public/private key pair (for asymmetric encryption).
Auth.js will generate keys for you, but this will generate a warning at start up.
Using explicit public/private keys for signing is strongly recommended.
</p>

View File

@@ -37,7 +37,7 @@ npm install next-auth
```
:::info
We are working on a new `@auth/nextjs` package that will make it easier to set up Auth.js with Next.js. Stay tuned! For now, you can use the `next-auth` package.
We are working on a new release of `next-auth` that will make it easier to set up Auth.js with Next.js. You can follow the development [on this PR](https://github.com/nextauthjs/next-auth/pull/7443)
:::
### Creating the server config
@@ -156,7 +156,7 @@ export default function CamperVanPage() {
### Protecting API Routes
To protect your API Routes (blocking unauthorized access to resources), you can use [`getServerSession()`](/reference/nextjs#getserversession) to know whether a session exists or not:
To protect your API Routes (blocking unauthorized access to resources), you can use [`getServerSession()`](https://next-auth.js.org/configuration/nextjs#getserversession) to know whether a session exists or not:
```ts title="pages/api/movies/list.ts"
import { getServerSession } from "next-auth/next"

View File

@@ -4,10 +4,10 @@ sidebar_label: Custom JWT encoding
---
:::warning
If you use middleware to protect routes, make sure the same method is also set in the [`_middleware.ts` options](/reference/nextjs/#custom-jwt-decode-method)
If you use middleware to protect routes, make sure the same method is also set in the [`middleware.ts` options](https://next-auth.js.org/configuration/nextjs#custom-jwt-decode-method)
:::
Auth.js uses encrypted JSON Web Tokens ([JWE](https://datatracker.ietf.org/doc/html/rfc7516)) by default. Unless you have a good reason, we recommend keeping this behaviour. Although you can override this using the `encode` and `decode` methods. Both methods must be defined at the same time.
Auth.js uses encrypted JSON Web Tokens ([JWE](https://datatracker.ietf.org/doc/html/rfc7516)) by default. Unless you have a good reason, we recommend keeping this behavior. Although you can override this using the `encode` and `decode` methods. Both methods must be defined at the same time.
```js
jwt: {

View File

@@ -2,19 +2,19 @@
title: Securing Pages & API routes
---
You can easily protect client and server side rendered pages and API routes with Auth.js.
You can protect client and server-side rendered pages and API routes with Auth.js.
_You can find working examples of the approaches shown below in the [example project](https://github.com/nextauthjs/next-auth-example/)._
:::tip
The methods `getSession()` and `getToken()` both return an `object` if a session is valid and `null` if a session is invalid or has expired.
The methods `getSession()` and `getToken()` both return an `object` if a session is valid and `null` if a session is not valid or has expired.
:::
## Securing Pages
### Client Side
If data on a page is fetched using calls to secure API routes - i.e. routes which use `getSession()` or `getToken()` to access the session - you can use the `useSession` React Hook to secure pages.
If data on a page is fetched using calls to secure API routes - i.e. routes that use `getSession(`)` or `getToken()` to access the session - you can use the `useSession` React Hook to secure pages.
```js title="pages/client-side-example.js"
import { useSession, getSession } from "next-auth/react"
@@ -41,7 +41,7 @@ export default function Page() {
### Next.js (Middleware)
With Auth.js 4.2.0 and Next.js 12, you can now protect your pages via the middleware pattern more easily. If you would like to protect all pages, you can create a `_middleware.js` file in your root `pages` directory which looks like this.
With NextAuth.js 4.2.0 and Next.js 12, you can now protect your pages via the middleware pattern more easily. If you would like to protect all pages, you can create a `_middleware.js` file in your root `pages` directory which looks like this.
```js title="/middleware.js"
export { default } from "next-auth/middleware"
@@ -49,15 +49,15 @@ export { default } from "next-auth/middleware"
Otherwise, if you only want to protect a subset of pages, you could put it in a subdirectory as well, for example in `/pages/admin/_middleware.js` would protect all pages under `/admin`.
For the time being, the `withAuth` middleware only supports `"jwt"` as [session strategy](/reference/configuration/auth-config#session).
For the time being, the `withAuth` middleware only supports `"jwt"` as a [session strategy](/reference/configuration/auth-config#session).
More details can be found [here](/reference/nextjs/#middleware).
More details can be found [here](https://next-auth.js.org/configuration/nextjs#middleware).
### Server Side
You can protect server side rendered pages using the `unstable_getServerSession` method. This is different from the old `getSession()` method, in that it does not do an extra fetch out over the internet to confirm data from itself, increasing performance significantly.
You can protect server-side rendered pages using the `unstable_getServerSession` method. This is different from the old `getSession()` method, in that it does not do an extra fetch out over the internet to confirm data from itself, increasing performance significantly.
You need to add this to every server rendered page you want to protect. Be aware, `unstable_getServerSession` takes slightly different arguments than the method it is replacing, `getSession`.
You need to add this to every server-rendered page you want to protect. Be aware, `unstable_getServerSession` takes slightly different arguments than the method it is replacing, `getSession`.
```js title="pages/server-side-example.js"
import { unstable_getServerSession } from "next-auth/next"
@@ -136,7 +136,7 @@ export default async (req, res) => {
### Using getToken()
If you are using JSON Web Tokens you can use the `getToken()` helper to access the contents of the JWT without having to handle JWT decryption / verification yourself. This method can only be used server side.
If you are using JSON Web Tokens you can use the `getToken()` helper to access the contents of the JWT without having to handle JWT decryption/verification yourself. This method can only be used server side.
```js title="pages/api/get-token-example.js"
// This is an example of how to read a JSON Web Token from an API route
@@ -157,7 +157,7 @@ export default async (req, res) => {
```
:::tip
You can use the `getToken()` helper function in any application as long as you set the `NEXTAUTH_URL` environment variable and the application is able to read the JWT cookie (e.g. is on the same domain).
You can use the `getToken()` helper function in any application as long as you set the `NEXTAUTH_URL` environment variable and the application can read the JWT cookie (e.g. is on the same domain).
:::
:::note

View File

@@ -25,6 +25,10 @@ Using an Auth.js / NextAuth.js adapter you can connect to any database service o
<img src="/img/adapters/firebase.svg" width="40" />
<h4 class="adapter-card__title">Firebase Adapter</h4>
</a>
<a href="/reference/adapter/kysely" class="adapter-card">
<img src="/img/adapters/kysely.svg" width="40" />
<h4 class="adapter-card__title">Kysely Adapter</h4>
</a>
<a href="/reference/adapter/mikro-orm" class="adapter-card">
<img src="/img/adapters/mikro-orm.png" width="30" />
<h4 class="adapter-card__title">Mikro ORM Adapter</h4>

View File

@@ -4,29 +4,30 @@ title: Overview
This section of the documentation contains the API reference for all the official packages under the `@auth/*` and `@next-auth/*` scopes.
:::warning Warning
The API reference is being migrated from the [old documentation page](https://next-auth.js.org), so there are going to be references to `next-auth` still. We are continuously working on updating the naming/references.
:::
## Roadmap
Here are the _currently_ planned and released packages under the `@auth/*` scope. This is not an exhaustive list, but the set of packages that we would like to focus on to begin with.
Here are the _state_ of planned and released packages under the `@auth/*` scope. This is not an exhaustive list, but the set of packages that we would like to focus on, to begin with.
| Feature | Status |
| ------------------- | -------- |
| `@auth/nextjs` | Planned |
| `@auth/*-adapter` | Planned |
| `@auth/core` | Experimental |
| `@auth/sveltekit` | Experimental |
| `@auth/solid-start` | Experimental |
| Feature | Status |
| ---------------------- | -------- |
| `@auth/*-adapter` | Released (stable). Fully compatible with `next-auth` and all `@auth/*` libraries. |
| `@next-auth/*-adapter` | Maintenance has stopped. Update to `@auth/*-adapter`. See above. |
| `@auth/core` | Released (experimental). |
| `@auth/sveltekit` | Released (experimental, [help needed](#help-needed)). |
| `@auth/solid-start` | Released (experimental, [help needed](#help-needed)). Community package: [`@solid-mediakit/auth`](https://www.npmjs.com/package/@solid-mediakit/auth) |
| `@auth/express` | [Planned](https://github.com/nextauthjs/next-auth/issues/8257). |
| `@auth/remix` | Planned, [help needed](#help-needed). |
| `@auth/astro` | Planned, [help needed](#help-needed). |
| `@auth/nuxt` | Planned, [help needed](#help-needed). Community packages: [`@sidebase/nuxt-auth`](https://github.com/sidebase/nuxt-auth), [`@hebilicious/authjs-nuxt`](https://authjs-nuxt.pages.dev/) |
### Community Packages
While we are migrating the documentation and working on stabilizing the core package, the community has been working on some packages that are already available. With collaboration, we hope to make these packages official in the future.
:::note
If you are a maintainer of a package, [reach out](https://twitter.com/balazsorban44) if you want to collaborate on making it official or open a PR to add it to the list below, so others can discover it more easily.
:::info
`next-auth` is still the official package for Next.js. The documentation is at [next-auth.js.org](https://next-auth.js.org), while guides are being migrated over to the new documentation page. A major refactor of `next-auth` is on the way, you can [follow this PR](https://github.com/nextauthjs/next-auth/pull/7443) for updates.
:::
- ...
- ...
### Help needed
In case you are a maintainer of a package that uses `@auth/core`, feel free to [reach out to Balázs](https://twitter.com/balazsorban44), if you want to collaborate on making it an official package, maintained in our repository. If you are interested in bringing `@auth/core` support to your favorite framework, we would love to hear from you!
#### Community Packages
While we are migrating the documentation and working on stabilizing the core package, the community has been working on some packages that are already available. With collaboration, we hope to make these packages official in the future.

View File

@@ -1,7 +0,0 @@
---
title: Client
---
:::warning WIP
`@auth/nextjs/client` is work in progress. For now, please use [NextAuth.js Client API](https://next-auth.js.org/getting-started/client).
:::

View File

@@ -1,7 +0,0 @@
---
title: Next.js Auth
---
:::warning WIP
`@auth/nextjs` is work in progress. For now, please use [NextAuth.js](https://next-auth.js.org).
:::

View File

@@ -269,6 +269,7 @@ const docusaurusConfig = {
typedocAdapter("DynamoDB"),
typedocAdapter("Fauna"),
typedocAdapter("Firebase"),
typedocAdapter("Kysely"),
typedocAdapter("Mikro ORM"),
typedocAdapter("MongoDB"),
typedocAdapter("Neo4j"),

View File

@@ -34,17 +34,9 @@ module.exports = {
items: [{ type: "autogenerated", dirName: "reference/solidstart" }],
},
{
type: "category",
label: "@auth/nextjs",
link: { type: "doc", id: "reference/nextjs/index" },
items: [
"reference/nextjs/client",
{
type: "link",
label: "NextAuth.js (next-auth)",
href: "https://next-auth.js.org",
},
],
type: "link",
label: "NextAuth.js (next-auth)",
href: "https://next-auth.js.org",
},
...(process.env.TYPEDOC_SKIP_ADAPTERS
? []
@@ -59,6 +51,7 @@ module.exports = {
{ type: "doc", id: "reference/adapter/dynamodb/index" },
{ type: "doc", id: "reference/adapter/fauna/index" },
{ type: "doc", id: "reference/adapter/firebase/index" },
{ type: "doc", id: "reference/adapter/kysely/index" },
{ type: "doc", id: "reference/adapter/mikro-orm/index" },
{ type: "doc", id: "reference/adapter/mongodb/index" },
{ type: "doc", id: "reference/adapter/neo4j/index" },

14
docs/static/img/adapters/kysely.svg vendored Normal file
View File

@@ -0,0 +1,14 @@
<svg width="132" height="132" viewBox="0 0 132 132" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_8_3)">
<rect x="2" y="2" width="128" height="128" rx="16" fill="white" />
<path
d="M41.2983 109V23.9091H46.4918V73.31H47.0735L91.9457 23.9091H98.8427L61.9062 64.1694L98.5103 109H92.0288L58.5824 67.9087L46.4918 81.2873V109H41.2983Z"
fill="black" />
</g>
<rect x="2" y="2" width="128" height="128" rx="16" stroke="#121212" stroke-width="4" />
<defs>
<clipPath id="clip0_8_3">
<rect x="2" y="2" width="128" height="128" rx="16" fill="white" />
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 637 B

11
docs/static/img/providers/passage.svg vendored Normal file
View File

@@ -0,0 +1,11 @@
<svg width="250" height="250" viewBox="0 0 250 250" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.8" d="M37.8419 71.6921L65.1425 87.4541C86.2537 99.6426 93.4921 126.639 81.3036 147.75C80.2315 149.607 79.045 151.353 77.7583 152.989L12.6182 115.381C8.67588 113.105 5.58417 109.922 3.44179 106.254C-0.964934 98.6912 -1.30613 89.0446 3.38448 80.9202C10.348 68.8591 25.7752 64.7254 37.8419 71.6921Z" fill="#3D53F6"/>
<path opacity="0.5" d="M37.8419 180.052L65.1425 164.29C86.2537 152.101 93.4921 125.105 81.3036 103.994C80.2315 102.137 79.045 100.391 77.7583 98.7544L12.6182 136.363C8.67588 138.639 5.58417 141.822 3.44179 145.49C-0.964934 153.053 -1.30613 162.699 3.38448 170.824C10.348 182.885 25.7752 187.019 37.8419 180.052Z" fill="#3D53F6"/>
<path opacity="0.5" d="M212.753 71.6921L185.452 87.4541C164.341 99.6426 157.103 126.639 169.291 147.75C170.363 149.607 171.55 151.353 172.836 152.989L237.976 115.381C241.919 113.105 245.01 109.922 247.153 106.254C251.56 98.6912 251.901 89.0446 247.21 80.9202C240.247 68.8591 224.819 64.7254 212.753 71.6921Z" fill="#3D53F6"/>
<path opacity="0.8" d="M212.753 180.052L185.452 164.29C164.341 152.101 157.103 125.105 169.291 103.994C170.363 102.137 171.55 100.391 172.836 98.7544L237.976 136.363C241.919 138.639 245.01 141.822 247.153 145.49C251.56 153.053 251.901 162.699 247.21 170.824C240.247 182.885 224.819 187.019 212.753 180.052Z" fill="#3D53F6"/>
<path d="M125.297 150.525C139.228 150.525 150.52 139.232 150.52 125.302C150.52 111.372 139.228 100.079 125.297 100.079C111.367 100.079 100.075 111.372 100.075 125.302C100.075 139.232 111.367 150.525 125.297 150.525Z" fill="#FF2F1D"/>
<path opacity="0.8" d="M71.1172 212.753L86.8793 185.452C99.0678 164.341 126.064 157.102 147.175 169.291C149.032 170.363 150.778 171.55 152.415 172.836L114.806 237.976C112.53 241.919 109.347 245.01 105.679 247.153C98.1164 251.559 88.4698 251.901 80.3454 247.21C68.2842 240.247 64.1505 224.819 71.1172 212.753Z" fill="#FF2F1D"/>
<path opacity="0.5" d="M179.477 212.752L163.715 185.452C151.526 164.341 124.53 157.102 103.419 169.291C101.562 170.363 99.8157 171.549 98.1794 172.836L135.788 237.976C138.064 241.919 141.247 245.01 144.915 247.153C152.478 251.559 162.124 251.901 170.249 247.21C182.31 240.246 186.444 224.819 179.477 212.752Z" fill="#FF2F1D"/>
<path opacity="0.5" d="M71.1172 37.8419L86.8793 65.1425C99.0678 86.2537 126.064 93.4921 147.175 81.3036C149.032 80.2315 150.778 79.045 152.415 77.7583L114.806 12.6182C112.53 8.67589 109.347 5.58416 105.679 3.44179C98.1164 -0.964936 88.4698 -1.30613 80.3454 3.38449C68.2842 10.348 64.1505 25.7752 71.1172 37.8419Z" fill="#3D53F6"/>
<path opacity="0.8" d="M179.477 37.8419L163.715 65.1425C151.526 86.2537 124.53 93.4921 103.419 81.3036C101.562 80.2315 99.8157 79.045 98.1794 77.7583L135.788 12.6182C138.064 8.67589 141.247 5.58416 144.915 3.44179C152.478 -0.964936 162.124 -1.30613 170.249 3.38449C182.31 10.348 186.444 25.7752 179.477 37.8419Z" fill="#3D53F6"/>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -1,4 +1,5 @@
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"cleanUrls": true,
"headers": [
{
@@ -76,10 +77,15 @@
"has": [{ "type": "host", "value": "warnings.authjs.dev" }],
"destination": "https://authjs.dev/reference/warnings/:path*"
},
{
"source": "/",
"has": [{ "type": "host", "value": "adapters.authjs.dev" }],
"destination": "https://authjs.dev/reference/adapters"
},
{
"source": "/:path(.*)",
"has": [{ "type": "host", "value": "adapters.authjs.dev" }],
"destination": "https://authjs.dev/reference/adapters/:path*"
"destination": "https://authjs.dev/reference/adapter/:path*"
},
{
"source": "/:path",

View File

@@ -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.12",
"typescript": "4.9.4"
},
"engines": {

View File

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

View File

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

View File

@@ -4,7 +4,7 @@
<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"/>
<img height="64px" src="https://authjs.dev/img/adapters/drizzle-orm.png"/>
</a>
<h3 align="center"><b>Drizzle ORM Adapter</b> - NextAuth.js / Auth.js</a></h3>
<p align="center" style="align: center;">

View File

@@ -1,6 +1,6 @@
{
"name": "@auth/drizzle-adapter",
"version": "0.1.0",
"version": "0.3.2",
"description": "Drizzle adapter for Auth.js.",
"homepage": "https://authjs.dev",
"repository": "https://github.com/nextauthjs/next-auth",
@@ -11,8 +11,8 @@
"type": "module",
"types": "./index.d.ts",
"files": [
"*.js",
"*.d.ts*",
"*.js",
"lib",
"src"
],

View File

@@ -16,20 +16,19 @@
* @module @auth/drizzle-adapter
*/
import { MySqlDatabase, MySqlTableFn } from "drizzle-orm/mysql-core"
import { PgDatabase, PgTableFn } from "drizzle-orm/pg-core"
import { BaseSQLiteDatabase, SQLiteTableFn } from "drizzle-orm/sqlite-core"
import { mySqlDrizzleAdapter } from "./lib/mysql.js"
import { pgDrizzleAdapter } from "./lib/pg.js"
import { SQLiteDrizzleAdapter } from "./lib/sqlite.js"
import {
isMySqlDatabase,
isPgDatabase,
isSQLiteDatabase,
SqlFlavorOptions,
} from "./lib/utils.js"
import { SqlFlavorOptions, TableFn } from "./lib/utils.js"
import { is } from "drizzle-orm"
import type { Adapter } from "@auth/core/adapters"
/**
* Add the adapter to your `app/api/[...nextauth]/route.js` next-auth configuration object.
* Add the adapter to your `pages/api/[...nextauth].ts` next-auth configuration object.
*
* ```ts title="pages/api/auth/[...nextauth].ts"
* import NextAuth from "next-auth"
@@ -47,6 +46,10 @@ import type { Adapter } from "@auth/core/adapters"
* ],
* })
* ```
*
* :::info
* If you're using multi-project schemas, you can pass your table function as a second argument
* :::
*
* ## Setup
*
@@ -69,7 +72,7 @@ import type { Adapter } from "@auth/core/adapters"
* } from "drizzle-orm/pg-core"
* import type { AdapterAccount } from '@auth/core/adapters'
*
* export const users = pgTable("users", {
* export const users = pgTable("user", {
* id: text("id").notNull().primaryKey(),
* name: text("name"),
* email: text("email").notNull(),
@@ -78,7 +81,7 @@ import type { Adapter } from "@auth/core/adapters"
* })
*
* export const accounts = pgTable(
* "accounts",
* "account",
* {
* userId: text("userId")
* .notNull()
@@ -99,7 +102,7 @@ import type { Adapter } from "@auth/core/adapters"
* })
* )
*
* export const sessions = pgTable("sessions", {
* export const sessions = pgTable("session", {
* sessionToken: text("sessionToken").notNull().primaryKey(),
* userId: text("userId")
* .notNull()
@@ -132,7 +135,7 @@ import type { Adapter } from "@auth/core/adapters"
* } from "drizzle-orm/mysql-core"
* import type { AdapterAccount } from "@auth/core/adapters"
*
* export const users = mysqlTable("users", {
* export const users = mysqlTable("user", {
* id: varchar("id", { length: 255 }).notNull().primaryKey(),
* name: varchar("name", { length: 255 }),
* email: varchar("email", { length: 255 }).notNull(),
@@ -141,7 +144,7 @@ import type { Adapter } from "@auth/core/adapters"
* })
*
* export const accounts = mysqlTable(
* "accounts",
* "account",
* {
* userId: varchar("userId", { length: 255 })
* .notNull()
@@ -162,7 +165,7 @@ import type { Adapter } from "@auth/core/adapters"
* })
* )
*
* export const sessions = mysqlTable("sessions", {
* export const sessions = mysqlTable("session", {
* sessionToken: varchar("sessionToken", { length: 255 }).notNull().primaryKey(),
* userId: varchar("userId", { length: 255 })
* .notNull()
@@ -189,7 +192,7 @@ import type { Adapter } from "@auth/core/adapters"
* import { integer, sqliteTable, text, primaryKey } from "drizzle-orm/sqlite-core"
* import type { AdapterAccount } from "@auth/core/adapters"
*
* export const users = sqliteTable("users", {
* export const users = sqliteTable("user", {
* id: text("id").notNull().primaryKey(),
* name: text("name"),
* email: text("email").notNull(),
@@ -198,7 +201,7 @@ import type { Adapter } from "@auth/core/adapters"
* })
*
* export const accounts = sqliteTable(
* "accounts",
* "account",
* {
* userId: text("userId")
* .notNull()
@@ -219,7 +222,7 @@ import type { Adapter } from "@auth/core/adapters"
* })
* )
*
* export const sessions = sqliteTable("sessions", {
* export const sessions = sqliteTable("session", {
* sessionToken: text("sessionToken").notNull().primaryKey(),
* userId: text("userId")
* .notNull()
@@ -249,20 +252,18 @@ import type { Adapter } from "@auth/core/adapters"
*
**/
export function DrizzleAdapter<SqlFlavor extends SqlFlavorOptions>(
db: SqlFlavor
db: SqlFlavor,
table?: TableFn<SqlFlavor>
): Adapter {
if (isMySqlDatabase(db)) {
// We need to cast to unknown since the type overlaps (PScale is MySQL based)
return mySqlDrizzleAdapter(db)
if (is(db, MySqlDatabase)) {
return mySqlDrizzleAdapter(db, table as MySqlTableFn)
} else if (is(db, PgDatabase)) {
return pgDrizzleAdapter(db, table as PgTableFn)
} else if (is(db, BaseSQLiteDatabase)) {
return SQLiteDrizzleAdapter(db, table as SQLiteTableFn)
}
if (isPgDatabase(db)) {
return pgDrizzleAdapter(db)
}
if (isSQLiteDatabase(db)) {
return SQLiteDrizzleAdapter(db)
}
throw new Error("Unsupported database type in Auth.js Drizzle adapter.")
throw new Error(
`Unsupported database type (${typeof db}) in Auth.js Drizzle adapter.`
)
}

View File

@@ -2,75 +2,87 @@ import { and, eq } from "drizzle-orm"
import {
int,
timestamp,
mysqlTable,
mysqlTable as defaultMySqlTableFn,
primaryKey,
varchar,
MySqlTableFn,
MySqlDatabase,
} 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 function createTables(mySqlTable: MySqlTableFn) {
const users = mySqlTable("user", {
id: varchar("id", { length: 255 }).notNull().primaryKey(),
name: varchar("name", { length: 255 }),
email: varchar("email", { length: 255 }).notNull(),
emailVerified: timestamp("emailVerified", {
mode: "date",
fsp: 3,
}).defaultNow(),
image: varchar("image", { length: 255 }),
})
export const accounts = mysqlTable(
"accounts",
{
const accounts = mySqlTable(
"account",
{
userId: varchar("userId", { length: 255 })
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
type: varchar("type", { length: 255 })
.$type<AdapterAccount["type"]>()
.notNull(),
provider: varchar("provider", { length: 255 }).notNull(),
providerAccountId: varchar("providerAccountId", {
length: 255,
}).notNull(),
refresh_token: varchar("refresh_token", { length: 255 }),
access_token: varchar("access_token", { length: 255 }),
expires_at: int("expires_at"),
token_type: varchar("token_type", { length: 255 }),
scope: varchar("scope", { length: 255 }),
id_token: varchar("id_token", { length: 255 }),
session_state: varchar("session_state", { length: 255 }),
},
(account) => ({
compoundKey: primaryKey(account.provider, account.providerAccountId),
})
)
const sessions = mySqlTable("session", {
sessionToken: varchar("sessionToken", { length: 255 })
.notNull()
.primaryKey(),
userId: varchar("userId", { length: 255 })
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
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
const verificationTokens = mySqlTable(
"verificationToken",
{
identifier: varchar("identifier", { length: 255 }).notNull(),
token: varchar("token", { length: 255 }).notNull(),
expires: timestamp("expires", { mode: "date" }).notNull(),
},
(vt) => ({
compoundKey: primaryKey(vt.identifier, vt.token),
})
)
return { users, accounts, sessions, verificationTokens }
}
export type DefaultSchema = ReturnType<typeof createTables>
export function mySqlDrizzleAdapter(
client: MySql2Database<Record<string, never>>
client: InstanceType<typeof MySqlDatabase>,
tableFn = defaultMySqlTableFn
): Adapter {
const { users, accounts, sessions, verificationTokens } =
createTables(tableFn)
return {
async createUser(data) {
const id = crypto.randomUUID()
@@ -152,10 +164,7 @@ export function mySqlDrizzleAdapter(
.then((res) => res[0])
},
async linkAccount(rawAccount) {
await client
.insert(accounts)
.values(rawAccount)
.then((res) => res[0])
await client.insert(accounts).values(rawAccount)
},
async getUserByAccount(account) {
const dbAccount =
@@ -175,7 +184,7 @@ export function mySqlDrizzleAdapter(
return null
}
return dbAccount.users
return dbAccount.user
},
async deleteSession(sessionToken) {
const session =

View File

@@ -1,71 +1,79 @@
import { and, eq } from "drizzle-orm"
import {
timestamp,
pgTable,
pgTable as defaultPgTableFn,
text,
primaryKey,
integer,
PgTableFn,
PgDatabase,
} 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 function createTables(pgTable: PgTableFn) {
const users = pgTable("user", {
id: text("id").notNull().primaryKey(),
name: text("name"),
email: text("email").notNull(),
emailVerified: timestamp("emailVerified", { mode: "date" }),
image: text("image"),
})
export const accounts = pgTable(
"accounts",
{
const accounts = pgTable(
"account",
{
userId: text("userId")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
type: text("type").$type<AdapterAccount["type"]>().notNull(),
provider: text("provider").notNull(),
providerAccountId: text("providerAccountId").notNull(),
refresh_token: text("refresh_token"),
access_token: text("access_token"),
expires_at: integer("expires_at"),
token_type: text("token_type"),
scope: text("scope"),
id_token: text("id_token"),
session_state: text("session_state"),
},
(account) => ({
compoundKey: primaryKey(account.provider, account.providerAccountId),
})
)
const sessions = pgTable("session", {
sessionToken: text("sessionToken").notNull().primaryKey(),
userId: text("userId")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
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
const verificationTokens = pgTable(
"verificationToken",
{
identifier: text("identifier").notNull(),
token: text("token").notNull(),
expires: timestamp("expires", { mode: "date" }).notNull(),
},
(vt) => ({
compoundKey: primaryKey(vt.identifier, vt.token),
})
)
return { users, accounts, sessions, verificationTokens }
}
export type DefaultSchema = ReturnType<typeof createTables>
export function pgDrizzleAdapter(
client: PostgresJsDatabase<Record<string, never>>
client: InstanceType<typeof PgDatabase>,
tableFn = defaultPgTableFn
): Adapter {
const { users, accounts, sessions, verificationTokens } =
createTables(tableFn)
return {
async createUser(data) {
return await client
@@ -166,7 +174,7 @@ export function pgDrizzleAdapter(
return null
}
return dbAccount.users
return dbAccount.user
},
async deleteSession(sessionToken) {
const session = await client

View File

@@ -1,70 +1,78 @@
import { eq, and } from "drizzle-orm"
import {
integer,
sqliteTable,
sqliteTable as defaultSqliteTableFn,
text,
primaryKey,
BaseSQLiteDatabase,
SQLiteTableFn,
} 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 function createTables(sqliteTable: SQLiteTableFn) {
const users = sqliteTable("user", {
id: text("id").notNull().primaryKey(),
name: text("name"),
email: text("email").notNull(),
emailVerified: integer("emailVerified", { mode: "timestamp_ms" }),
image: text("image"),
})
export const accounts = sqliteTable(
"accounts",
{
const accounts = sqliteTable(
"account",
{
userId: text("userId")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
type: text("type").$type<AdapterAccount["type"]>().notNull(),
provider: text("provider").notNull(),
providerAccountId: text("providerAccountId").notNull(),
refresh_token: text("refresh_token"),
access_token: text("access_token"),
expires_at: integer("expires_at"),
token_type: text("token_type"),
scope: text("scope"),
id_token: text("id_token"),
session_state: text("session_state"),
},
(account) => ({
compoundKey: primaryKey(account.provider, account.providerAccountId),
})
)
const sessions = sqliteTable("session", {
sessionToken: text("sessionToken").notNull().primaryKey(),
userId: text("userId")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
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
const verificationTokens = sqliteTable(
"verificationToken",
{
identifier: text("identifier").notNull(),
token: text("token").notNull(),
expires: integer("expires", { mode: "timestamp_ms" }).notNull(),
},
(vt) => ({
compoundKey: primaryKey(vt.identifier, vt.token),
})
)
return { users, accounts, sessions, verificationTokens }
}
export type DefaultSchema = ReturnType<typeof createTables>
export function SQLiteDrizzleAdapter(
client: BaseSQLiteDatabase<any, any>
client: InstanceType<typeof BaseSQLiteDatabase>,
tableFn = defaultSqliteTableFn
): Adapter {
const { users, accounts, sessions, verificationTokens } =
createTables(tableFn)
return {
createUser(data) {
return client
@@ -151,7 +159,7 @@ export function SQLiteDrizzleAdapter(
)
.get()
return results?.users ?? null
return results?.user ?? null
},
deleteSession(sessionToken) {
return (

View File

@@ -2,9 +2,9 @@ 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 { AnyMySqlTable, MySqlTableFn } from "drizzle-orm/mysql-core"
import type { AnyPgTable, PgTableFn } from "drizzle-orm/pg-core"
import type { AnySQLiteTable, SQLiteTableFn } from "drizzle-orm/sqlite-core"
import type { DefaultSchema as PgSchema } from "./pg.js"
import type { DefaultSchema as MySqlSchema } from "./mysql.js"
import type { DefaultSchema as SQLiteSchema } from "./sqlite.js"
@@ -32,16 +32,10 @@ export type ClientFlavors<Flavor> = Flavor extends AnyMySqlDatabase
? 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
}
export type TableFn<Flavor> = Flavor extends AnyMySqlDatabase
? MySqlTableFn
: Flavor extends AnyPgDatabase
? PgTableFn
: Flavor extends AnySQLiteDatabase
? SQLiteTableFn
: AnySQLiteTable

View 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

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

View File

@@ -0,0 +1,19 @@
import { mysqlTableCreator } from "drizzle-orm/mysql-core"
import { drizzle } from "drizzle-orm/mysql2"
import { createPool } from "mysql2"
import { createTables } from "../../src/lib/mysql"
const poolConnection = createPool({
host: "localhost",
user: "root",
password: "password",
database: "next-auth",
})
const mysqlTable = mysqlTableCreator((name) => `foobar_${name}`)
export const { users, accounts, sessions, verificationTokens } =
createTables(mysqlTable)
export const schema = { users, accounts, sessions, verificationTokens }
export const db = drizzle(poolConnection, { schema })

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

View File

@@ -1,20 +1,7 @@
import type { AdapterAccount } from "@auth/core/adapters"
import {
mysqlTable,
varchar,
timestamp,
int,
primaryKey,
} from "drizzle-orm/mysql-core"
import { mysqlTable } 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"
import { createTables } from "../../src/lib/mysql"
const poolConnection = createPool({
host: "localhost",
@@ -23,7 +10,8 @@ const poolConnection = createPool({
database: "next-auth",
})
export { users, accounts, sessions, verificationTokens }
export const db = drizzle(poolConnection, {
schema: schema,
})
export const { users, accounts, sessions, verificationTokens } =
createTables(mysqlTable)
export const schema = { users, accounts, sessions, verificationTokens }
export const db = drizzle(poolConnection, { schema })

View 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

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

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

View File

@@ -0,0 +1,17 @@
import { drizzle } from "drizzle-orm/postgres-js"
import postgres from "postgres"
import { createTables } from "../../src/lib/pg"
import { pgTableCreator } from "drizzle-orm/pg-core"
const connectionString = "postgres://nextauth:nextauth@localhost:5432/nextauth"
const sql = postgres(connectionString, { max: 1 })
const pgTable = pgTableCreator((name) => `foobar_${name}`)
export const { users, accounts, sessions, verificationTokens } =
createTables(pgTable)
export const schema = { users, accounts, sessions, verificationTokens }
export const db = drizzle(sql, {
schema,
})

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

View File

@@ -1,11 +1,15 @@
import { drizzle } from "drizzle-orm/postgres-js"
import postgres from "postgres"
import { users, accounts, sessions, verificationTokens } from "../../src/lib/pg"
import { createTables } from "../../src/lib/pg"
import { pgTable } from "drizzle-orm/pg-core"
const connectionString = "postgres://nextauth:nextauth@localhost:5432/nextauth"
const sql = postgres(connectionString, { max: 1 })
export const { users, accounts, sessions, verificationTokens } =
createTables(pgTable)
export const schema = { users, accounts, sessions, verificationTokens }
export const db = drizzle(sql, {
schema: { users, accounts, sessions, verificationTokens },
schema,
})
export { users, accounts, sessions, verificationTokens }

View 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

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

View File

@@ -0,0 +1,14 @@
import { drizzle } from "drizzle-orm/better-sqlite3"
import Database from "better-sqlite3"
import { createTables } from "../../src/lib/sqlite"
import { sqliteTableCreator } from "drizzle-orm/sqlite-core"
const sqlite = new Database("db.sqlite")
const sqliteTable = sqliteTableCreator((name) => `foobar_${name}`)
export const { users, accounts, sessions, verificationTokens } =
createTables(sqliteTable)
export const schema = { users, accounts, sessions, verificationTokens }
export const db = drizzle(sqlite, { schema })

View 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

View File

@@ -1,20 +1,12 @@
import { drizzle } from "drizzle-orm/better-sqlite3"
import Database from "better-sqlite3"
import {
users,
accounts,
sessions,
verificationTokens,
} from "../../src/lib/sqlite"
import { createTables } from "../../src/lib/sqlite"
import { sqliteTable } from "drizzle-orm/sqlite-core"
const sqlite = new Database("db.sqlite")
export { users, accounts, sessions, verificationTokens }
export const db = drizzle(sqlite, {
schema: {
users,
accounts,
sessions,
verificationTokens,
},
})
export const { users, accounts, sessions, verificationTokens } =
createTables(sqliteTable)
export const schema = { users, accounts, sessions, verificationTokens }
export const db = drizzle(sqlite, { schema })

View File

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

View File

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

View File

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

View 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://kysely.io" target="_blank">
<img height="64px" src="https://authjs.dev/img/adapters/kysely.svg"/>
</a>
<h3 align="center"><b>Kysely Adapter</b> - NextAuth.js / Auth.js</a></h3>
<p align="center" style="align: center;">
<a href="https://npm.im/@auth/kysely-adapter">
<img src="https://img.shields.io/badge/TypeScript-blue?style=flat-square" alt="TypeScript" />
</a>
<a href="https://npm.im/@auth/kysely-adapter">
<img alt="npm" src="https://img.shields.io/npm/v/@auth/kysely-adapter?color=green&label=@auth/kysely-adapter&style=flat-square">
</a>
<a href="https://www.npmtrends.com/@auth/kysely-adapter">
<img src="https://img.shields.io/npm/dm/@auth/kysely-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/kysely).

View File

@@ -0,0 +1,56 @@
{
"name": "@auth/kysely-adapter",
"version": "0.1.1",
"description": "Kysely adapter for Auth.js",
"homepage": "https://authjs.dev/reference/adapter/kysely",
"repository": "https://github.com/nextauthjs/next-auth",
"bugs": {
"url": "https://github.com/nextauthjs/next-auth/issues"
},
"author": "mwojtul <mark.wojtul@gmail.com> (https://github.com/mwojtul)",
"license": "ISC",
"keywords": [
"authjs",
"next-auth",
"next.js",
"oauth",
"kysely"
],
"type": "module",
"types": "./index.d.ts",
"files": [
"*.js",
"*.d.ts*",
"src"
],
"exports": {
".": {
"types": "./index.d.ts",
"import": "./index.js"
}
},
"scripts": {
"build": "tsc",
"test": "./tests/test.sh"
},
"dependencies": {
"@auth/core": "workspace:*"
},
"peerDependencies": {
"kysely": "^0.26.1"
},
"devDependencies": {
"@next-auth/adapter-test": "workspace:*",
"@next-auth/tsconfig": "workspace:*",
"@types/better-sqlite3": "^7.6.3",
"@types/pg": "^8.6.5",
"better-sqlite3": "^8.2.0",
"jest": "^27.4.3",
"kysely": "^0.24.2",
"mysql2": "^3.2.0",
"pg": "^8.10.0"
},
"jest": {
"preset": "@next-auth/adapter-test/jest"
}
}

View File

@@ -0,0 +1,472 @@
/**
* <div style={{display: "flex", justifyContent: "space-between", alignItems: "center", padding: 16}}>
* <p style={{fontWeight: "normal"}}>Official <a href="https://kysely.dev/">Kysely</a> adapter for Auth.js / NextAuth.js.</p>
* <a href="https://kysely.dev/">
* <img style={{display: "block"}} src="/img/adapters/kysely.svg" width="38" />
* </a>
* </div>
*
* ## Installation
*
* ```bash npm2yarn2pnpm
* npm install kysely @auth/kysely-adapter
* ```
*
* @module @auth/kysely-adapter
*/
import { Kysely, SqliteAdapter } from "kysely"
import type { Adapter } from "@auth/core/adapters"
import type { GeneratedAlways } from "kysely"
export interface Database {
User: {
id: GeneratedAlways<string>
name: string | null
email: string
emailVerified: Date | string | null
image: string | null
}
Account: {
id: GeneratedAlways<string>
userId: string
type: string
provider: string
providerAccountId: string
refresh_token: string | null
access_token: string | null
expires_at: number | null
token_type: string | null
scope: string | null
id_token: string | null
session_state: string | null
}
Session: {
id: GeneratedAlways<string>
userId: string
sessionToken: string
expires: Date | string
}
VerificationToken: {
identifier: string
token: string
expires: Date | string
}
}
export const format = {
/**
* Helper function to return the passed in object and its specified prop
* as an ISO string if SQLite is being used.
*/
from<T extends Partial<Record<K, Date | null>>, K extends keyof T>(
data: T,
key: K,
isSqlite: boolean
) {
const value = data[key]
return {
...data,
[key]: value && isSqlite ? value.toISOString() : value,
}
},
to,
}
type ReturnData<T = never> = Record<string, Date | string | T>
/**
* Helper function to return the passed in object and its specified prop as a date.
* Necessary because SQLite has no date type so we store dates as ISO strings.
*/
function to<T extends Partial<ReturnData>, K extends keyof T>(
data: T,
key: K
): Omit<T, K> & Record<K, Date>
function to<T extends Partial<ReturnData<null>>, K extends keyof T>(
data: T,
key: K
): Omit<T, K> & Record<K, Date | null>
function to<T extends Partial<ReturnData<null>>, K extends keyof T>(
data: T,
key: K
) {
const value = data[key]
return Object.assign(data, {
[key]: value && typeof value === "string" ? new Date(value) : value,
})
}
/**
*
* ## Setup
*
* This adapter supports the same first party dialects that Kysely (as of v0.24.2) supports: PostgreSQL, MySQL, and SQLite. The examples below use PostgreSQL with the [pg](https://www.npmjs.com/package/pg) client.
*
* ```bash npm2yarn2pnpm
* npm install pg
* npm install --save-dev @types/pg
* ```
*
* ```typescript title="pages/api/auth/[...nextauth].ts"
* import NextAuth from "next-auth"
* import GoogleProvider from "next-auth/providers/google"
* import { KyselyAdapter } from "@auth/kysely-adapter"
* import { db } from "../../../db"
*
* export default NextAuth({
* adapter: KyselyAdapter(db),
* providers: [
* GoogleProvider({
* clientId: process.env.GOOGLE_CLIENT_ID,
* clientSecret: process.env.GOOGLE_CLIENT_SECRET,
* }),
* ],
* })
* ```
*
* Kysely's constructor requires a database interface that contains an entry with an interface for each of your tables. You can define these types manually, or use `kysely-codegen` / `prisma-kysely` to automatically generate them. Check out the default [models](/reference/adapters#models) required by Auth.js.
*
* ```ts title="db.ts"
* import { PostgresDialect } from "kysely"
* import { Pool } from "pg"
*
* // This adapter exports a wrapper of the original `Kysely` class called `KyselyAuth`,
* // that can be used to provide additional type-safety.
* // While using it isn't required, it is recommended as it will verify
* // that the database interface has all the fields that Auth.js expects.
* import { KyselyAuth } from "@auth/kysely-adapter"
*
* import type { GeneratedAlways } from "kysely"
*
* interface Database {
* User: {
* id: GeneratedAlways<string>
* name: string | null
* email: string
* emailVerified: Date | null
* image: string | null
* }
* Account: {
* id: GeneratedAlways<string>
* userId: string
* type: string
* provider: string
* providerAccountId: string
* refresh_token: string | null
* access_token: string | null
* expires_at: number | null
* token_type: string | null
* scope: string | null
* id_token: string | null
* session_state: string | null
* }
* Session: {
* id: GeneratedAlways<string>
* userId: string
* sessionToken: string
* expires: Date
* }
* VerificationToken: {
* identifier: string
* token: string
* expires: Date
* }
* }
*
* export const db = new KyselyAuth<Database>({
* dialect: new PostgresDialect({
* pool: new Pool({
* host: process.env.DATABASE_HOST,
* database: process.env.DATABASE_NAME,
* user: process.env.DATABASE_USER,
* password: process.env.DATABASE_PASSWORD,
* }),
* }),
* })
```
*
*
* :::note
* An alternative to manually defining types is generating them from the database schema using [kysely-codegen](https://github.com/RobinBlomberg/kysely-codegen), or from Prisma schemas using [prisma-kysely](https://github.com/valtyr/prisma-kysely). When using generated types with `KyselyAuth`, import `Codegen` and pass it as the second generic arg:
* ```ts
* import type { Codegen } from "@auth/kysely-adapter"
* new KyselyAuth<Database, Codegen>(...)
* ```
* :::
* ### Schema
* ```ts title="db/migrations/001_create_db.ts"
* import { Kysely, sql } from "kysely"
*
* export async function up(db: Kysely<any>): Promise<void> {
* await db.schema
* .createTable("User")
* .addColumn("id", "uuid", (col) =>
* col.primaryKey().defaultTo(sql`gen_random_uuid()`)
* )
* .addColumn("name", "text")
* .addColumn("email", "text", (col) => col.unique().notNull())
* .addColumn("emailVerified", "timestamptz")
* .addColumn("image", "text")
* .execute()
*
* await db.schema
* .createTable("Account")
* .addColumn("id", "uuid", (col) =>
* col.primaryKey().defaultTo(sql`gen_random_uuid()`)
* )
* .addColumn("userId", "uuid", (col) =>
* col.references("User.id").onDelete("cascade").notNull()
* )
* .addColumn("type", "text", (col) => col.notNull())
* .addColumn("provider", "text", (col) => col.notNull())
* .addColumn("providerAccountId", "text", (col) => col.notNull())
* .addColumn("refresh_token", "text")
* .addColumn("access_token", "text")
* .addColumn("expires_at", "bigint")
* .addColumn("token_type", "text")
* .addColumn("scope", "text")
* .addColumn("id_token", "text")
* .addColumn("session_state", "text")
* .execute()
*
* await db.schema
* .createTable("Session")
* .addColumn("id", "uuid", (col) =>
* col.primaryKey().defaultTo(sql`gen_random_uuid()`)
* )
* .addColumn("userId", "uuid", (col) =>
* col.references("User.id").onDelete("cascade").notNull()
* )
* .addColumn("sessionToken", "text", (col) => col.notNull().unique())
* .addColumn("expires", "timestamptz", (col) => col.notNull())
* .execute()
*
* await db.schema
* .createTable("VerificationToken")
* .addColumn("identifier", "text", (col) => col.notNull())
* .addColumn("token", "text", (col) => col.notNull().unique())
* .addColumn("expires", "timestamptz", (col) => col.notNull())
* .execute()
*
* await db.schema
* .createIndex("Account_userId_index")
* .on("Account")
* .column("userId")
* .execute()
*
* await db.schema
* .createIndex("Session_userId_index")
* .on("Session")
* .column("userId")
* .execute()
* }
*
* export async function down(db: Kysely<any>): Promise<void> {
* await db.schema.dropTable("Account").ifExists().execute()
* await db.schema.dropTable("Session").ifExists().execute()
* await db.schema.dropTable("User").ifExists().execute()
* await db.schema.dropTable("VerificationToken").ifExists().execute()
* }
* ```
* > This schema is adapted for use in Kysely and is based upon our main [schema](/reference/adapters#models).
*
* For more information about creating and running migrations with Kysely, refer to the [Kysely migrations documentation](https://kysely.dev/docs/migrations).
*
* ### Naming conventions
* If mixed snake_case and camelCase column names is an issue for you and/or your underlying database system, we recommend using Kysely's `CamelCasePlugin` ([see the documentation here](https://kysely-org.github.io/kysely/classes/CamelCasePlugin.html)) feature to change the field names. This won't affect NextAuth.js, but will allow you to have consistent casing when using Kysely.
*/
export function KyselyAdapter(db: Kysely<Database>): Adapter {
const { adapter } = db.getExecutor()
const supportsReturning = adapter.supportsReturning
const isSqlite = adapter instanceof SqliteAdapter
return {
async createUser(data) {
const userData = format.from(data, "emailVerified", isSqlite)
const query = db.insertInto("User").values(userData)
const result = supportsReturning
? await query.returningAll().executeTakeFirstOrThrow()
: await query.executeTakeFirstOrThrow().then(async () => {
return await db
.selectFrom("User")
.selectAll()
.where("email", "=", `${userData.email}`)
.executeTakeFirstOrThrow()
})
return to(result, "emailVerified")
},
async getUser(id) {
const result =
(await db
.selectFrom("User")
.selectAll()
.where("id", "=", id)
.executeTakeFirst()) ?? null
if (!result) return null
return to(result, "emailVerified")
},
async getUserByEmail(email) {
const result =
(await db
.selectFrom("User")
.selectAll()
.where("email", "=", email)
.executeTakeFirst()) ?? null
if (!result) return null
return to(result, "emailVerified")
},
async getUserByAccount({ providerAccountId, provider }) {
const result =
(await db
.selectFrom("User")
.innerJoin("Account", "User.id", "Account.userId")
.selectAll("User")
.where("Account.providerAccountId", "=", providerAccountId)
.where("Account.provider", "=", provider)
.executeTakeFirst()) ?? null
if (!result) return null
return to(result, "emailVerified")
},
async updateUser({ id, ...user }) {
if (!id) throw new Error("User not found")
const userData = format.from(user, "emailVerified", isSqlite)
const query = db.updateTable("User").set(userData).where("id", "=", id)
const result = supportsReturning
? await query.returningAll().executeTakeFirstOrThrow()
: await query.executeTakeFirstOrThrow().then(async () => {
return await db
.selectFrom("User")
.selectAll()
.where("id", "=", id)
.executeTakeFirstOrThrow()
})
return to(result, "emailVerified")
},
async deleteUser(userId) {
await db.deleteFrom("User").where("User.id", "=", userId).execute()
},
async linkAccount(account) {
await db.insertInto("Account").values(account).executeTakeFirstOrThrow()
},
async unlinkAccount({ providerAccountId, provider }) {
await db
.deleteFrom("Account")
.where("Account.providerAccountId", "=", providerAccountId)
.where("Account.provider", "=", provider)
.executeTakeFirstOrThrow()
},
async createSession(data) {
const sessionData = format.from(data, "expires", isSqlite)
const query = db.insertInto("Session").values(sessionData)
const result = supportsReturning
? await query.returningAll().executeTakeFirstOrThrow()
: await (async () => {
await query.executeTakeFirstOrThrow()
return await db
.selectFrom("Session")
.selectAll()
.where("sessionToken", "=", sessionData.sessionToken)
.executeTakeFirstOrThrow()
})()
return to(result, "expires")
},
async getSessionAndUser(sessionTokenArg) {
const result = await db
.selectFrom("Session")
.innerJoin("User", "User.id", "Session.userId")
.selectAll("User")
.select([
"Session.id as sessionId",
"Session.userId",
"Session.sessionToken",
"Session.expires",
])
.where("Session.sessionToken", "=", sessionTokenArg)
.executeTakeFirst()
if (!result) return null
const { sessionId: id, userId, sessionToken, expires, ...user } = result
return {
user: to({ ...user }, "emailVerified"),
session: to({ id, userId, sessionToken, expires }, "expires"),
}
},
async updateSession(session) {
const sessionData = format.from(session, "expires", isSqlite)
const query = db
.updateTable("Session")
.set(sessionData)
.where("Session.sessionToken", "=", session.sessionToken)
const result = supportsReturning
? await query.returningAll().executeTakeFirstOrThrow()
: await query.executeTakeFirstOrThrow().then(async () => {
return await db
.selectFrom("Session")
.selectAll()
.where("Session.sessionToken", "=", sessionData.sessionToken)
.executeTakeFirstOrThrow()
})
return to(result, "expires")
},
async deleteSession(sessionToken) {
await db
.deleteFrom("Session")
.where("Session.sessionToken", "=", sessionToken)
.executeTakeFirstOrThrow()
},
async createVerificationToken(verificationToken) {
const verificationTokenData = format.from(
verificationToken,
"expires",
isSqlite
)
const query = db
.insertInto("VerificationToken")
.values(verificationTokenData)
const result = supportsReturning
? await query.returningAll().executeTakeFirstOrThrow()
: await query.executeTakeFirstOrThrow().then(async () => {
return await db
.selectFrom("VerificationToken")
.selectAll()
.where("token", "=", verificationTokenData.token)
.executeTakeFirstOrThrow()
})
return to(result, "expires")
},
async useVerificationToken({ identifier, token }) {
const query = db
.deleteFrom("VerificationToken")
.where("VerificationToken.token", "=", token)
.where("VerificationToken.identifier", "=", identifier)
const result = supportsReturning
? (await query.returningAll().executeTakeFirst()) ?? null
: await db
.selectFrom("VerificationToken")
.selectAll()
.where("token", "=", token)
.executeTakeFirst()
.then(async (res) => {
await query.executeTakeFirst()
return res
})
if (!result) return null
return to(result, "expires")
},
}
}
/**
* Wrapper over the original `Kysely` class in order to validate the passed in
* database interface. A regular Kysely instance may also be used, but wrapping
* it ensures the database interface implements the fields that Auth.js
* requires. When used with `kysely-codegen`, the `Codegen` type can be passed as
* the second generic argument. The generated types will be used, and
* `KyselyAuth` will only verify that the correct fields exist.
**/
export class KyselyAuth<DB extends T, T = Database> extends Kysely<DB> {}
export type Codegen = {
[K in keyof Database]: { [J in keyof Database[K]]: unknown }
}

View File

@@ -0,0 +1,250 @@
import { runBasicTests } from "@next-auth/adapter-test"
import { Pool } from "pg"
import {
Kysely,
MysqlDialect,
PostgresDialect,
SchemaModule,
sql,
SqliteAdapter,
SqliteDialect,
} from "kysely"
import { KyselyAdapter, KyselyAuth } from "../src"
import { createPool } from "mysql2"
import SqliteDatabase from "better-sqlite3"
import type { Database } from "../src"
import { DataTypeExpression } from "kysely/dist/cjs/parser/data-type-parser"
type BuiltInDialect = "postgres" | "mysql" | "sqlite"
const POOL_SIZE = 20
const DIALECT_CONFIGS = {
postgres: {
host: "localhost",
database: "kysely_test",
user: "kysely",
port: 5434,
max: POOL_SIZE,
},
mysql: {
database: "kysely_test",
host: "localhost",
user: "kysely",
password: "kysely",
port: 3308,
supportBigNumbers: true,
bigNumberStrings: true,
connectionLimit: POOL_SIZE,
},
sqlite: {
databasePath: ":memory:",
},
} as const
async function dropDatabase(db: Kysely<Database>): Promise<void> {
await Promise.all([
db.schema.dropTable("Account").ifExists().execute(),
db.schema.dropTable("Session").ifExists().execute(),
db.schema.dropTable("User").ifExists().execute(),
db.schema.dropTable("VerificationToken").ifExists().execute(),
])
}
export function createTableWithId(
schema: SchemaModule,
dialect: BuiltInDialect,
tableName: string
) {
const builder = schema.createTable(tableName)
if (dialect === "postgres") {
return builder.addColumn("id", "uuid", (col) =>
col.primaryKey().defaultTo(sql`gen_random_uuid()`)
)
} else if (dialect === "mysql") {
return builder.addColumn("id", "varchar(36)", (col) =>
col.primaryKey().defaultTo(sql`(UUID())`)
)
} else {
return builder.addColumn("id", "integer", (col) =>
col.autoIncrement().primaryKey()
)
}
}
async function createDatabase(
db: Kysely<Database>,
dialect: BuiltInDialect
): Promise<void> {
const defaultTimestamp = {
postgres: sql`NOW()`,
mysql: sql`NOW(3)`,
sqlite: sql`CURRENT_TIMESTAMP`,
}[dialect]
const uuidColumnType: DataTypeExpression =
dialect === "mysql" ? "varchar(36)" : "uuid"
const dateColumnType: DataTypeExpression =
dialect === "mysql" ? sql`DATETIME(3)` : "timestamptz"
const textColumnType: DataTypeExpression =
dialect === "mysql" ? "varchar(255)" : "text"
await dropDatabase(db)
await createTableWithId(db.schema, dialect, "User")
.addColumn("name", textColumnType)
.addColumn("email", textColumnType, (col) => col.unique().notNull())
.addColumn("emailVerified", dateColumnType, (col) =>
col.defaultTo(defaultTimestamp)
)
.addColumn("image", textColumnType)
.execute()
let createAccountTable = createTableWithId(db.schema, dialect, "Account")
.addColumn("userId", uuidColumnType, (col) =>
col.references("User.id").onDelete("cascade").notNull()
)
.addColumn("type", textColumnType, (col) => col.notNull())
.addColumn("provider", textColumnType, (col) => col.notNull())
.addColumn("providerAccountId", textColumnType, (col) => col.notNull())
.addColumn("refresh_token", textColumnType)
.addColumn("access_token", textColumnType)
.addColumn("expires_at", "bigint")
.addColumn("token_type", textColumnType)
.addColumn("scope", textColumnType)
.addColumn("id_token", textColumnType)
.addColumn("session_state", textColumnType)
if (dialect === "mysql")
createAccountTable = createAccountTable.addForeignKeyConstraint(
"Account_userId_fk",
["userId"],
"User",
["id"],
(cb) => cb.onDelete("cascade")
)
await createAccountTable.execute()
let createSessionTable = createTableWithId(db.schema, dialect, "Session")
.addColumn("userId", uuidColumnType, (col) =>
col.references("User.id").onDelete("cascade").notNull()
)
.addColumn("sessionToken", textColumnType, (col) => col.notNull().unique())
.addColumn("expires", dateColumnType, (col) => col.notNull())
if (dialect === "mysql")
createSessionTable = createSessionTable.addForeignKeyConstraint(
"Session_userId_fk",
["userId"],
"User",
["id"],
(cb) => cb.onDelete("cascade")
)
await createSessionTable.execute()
await db.schema
.createTable("VerificationToken")
.addColumn("identifier", textColumnType, (col) => col.notNull())
.addColumn("token", textColumnType, (col) => col.notNull().unique())
.addColumn("expires", dateColumnType, (col) => col.notNull())
.execute()
await db.schema
.createIndex("Account_userId_index")
.on("Account")
.column("userId")
.execute()
}
const runDialectBasicTests = (
db: Kysely<Database>,
dialect: BuiltInDialect
) => {
const datesStoredAsISOStrings =
db.getExecutor().adapter instanceof SqliteAdapter
runBasicTests({
adapter: KyselyAdapter(db),
db: {
async connect() {
await dropDatabase(db)
await createDatabase(db, dialect)
},
async disconnect() {
await db.destroy()
},
async user(userId) {
const user =
(await db
.selectFrom("User")
.selectAll()
.where("id", "=", userId)
.executeTakeFirst()) ?? null
if (datesStoredAsISOStrings && user?.emailVerified)
user.emailVerified = new Date(user.emailVerified)
return user
},
async account({ provider, providerAccountId }) {
const result = await db
.selectFrom("Account")
.selectAll()
.where("provider", "=", provider)
.where("providerAccountId", "=", providerAccountId)
.executeTakeFirst()
if (!result) return null
const { ...account } = result
if (typeof account.expires_at === "string")
account.expires_at = Number(account.expires_at)
return account
},
async session(sessionToken) {
const session =
(await db
.selectFrom("Session")
.selectAll()
.where("sessionToken", "=", sessionToken)
.executeTakeFirst()) ?? null
if (datesStoredAsISOStrings && session?.expires)
session.expires = new Date(session.expires)
return session
},
async verificationToken({ identifier, token }) {
const verificationToken = await db
.selectFrom("VerificationToken")
.selectAll()
.where("identifier", "=", identifier)
.where("token", "=", token)
.executeTakeFirstOrThrow()
if (datesStoredAsISOStrings)
verificationToken.expires = new Date(verificationToken.expires)
return verificationToken
},
},
})
}
describe("Testing PostgresDialect", () => {
const db = new KyselyAuth<Database>({
dialect: new PostgresDialect({
pool: new Pool(DIALECT_CONFIGS.postgres),
}),
})
runDialectBasicTests(db, "postgres")
})
describe("Testing MysqlDialect", () => {
const db = new KyselyAuth<Database>({
dialect: new MysqlDialect({
pool: createPool(DIALECT_CONFIGS.mysql),
}),
})
runDialectBasicTests(db, "mysql")
})
describe("Testing SqliteDialect", () => {
const db = new KyselyAuth<Database>({
dialect: new SqliteDialect({
database: async () =>
new SqliteDatabase(DIALECT_CONFIGS.sqlite.databasePath),
}),
})
runDialectBasicTests(db, "sqlite")
})

View File

@@ -0,0 +1,3 @@
CREATE USER 'kysely'@'%' IDENTIFIED WITH mysql_native_password BY 'kysely';
GRANT ALL ON *.* TO 'kysely'@'%';
CREATE DATABASE kysely_test;

View File

@@ -0,0 +1,30 @@
#!/usr/bin/env bash
docker run -d \
--name mysql \
--rm \
-e MYSQL_ROOT_PASSWORD=root \
-e MYSQL_DATABASE=kysely_test \
-p 3308:3306 \
-v "$(pwd)"/tests/scripts/mysql-init.sql:/data/application/init.sql \
mysql/mysql-server \
--init-file /data/application/init.sql
docker run -d \
--name postgres \
--rm \
-e POSTGRES_DB=kysely_test \
-e POSTGRES_USER=kysely \
-e POSTGRES_HOST_AUTH_METHOD=trust \
-p 5434:5432 \
postgres
echo "waiting 15 seconds for databases to start..."
sleep 15
# Always stop container, but exit with 1 when tests are failing
if npx jest tests; then
docker stop mysql && docker stop postgres
else
docker stop mysql && docker stop postgres && exit 1
fi

View 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",
]
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -270,3 +270,18 @@ export interface Adapter {
token: string
}): Awaitable<VerificationToken | null>
}
// For compatibility with older versions of NextAuth.js
// @ts-expect-error
declare module "next-auth/adapters" {
type JsonObject = {
[Key in string]?: JsonValue
}
type JsonArray = JsonValue[]
type JsonPrimitive = string | number | boolean | null
type JsonValue = JsonPrimitive | JsonObject | JsonArray
interface AdapterAccount {
type: "oauth" | "email" | "oidc"
[key: string]: JsonValue | undefined
}
}

View File

@@ -38,7 +38,7 @@
import { assertConfig } from "./lib/assert.js"
import { ErrorPageLoop } from "./errors.js"
import { AuthInternal, skipCSRFCheck } from "./lib/index.js"
import { AuthInternal, raw, skipCSRFCheck } from "./lib/index.js"
import renderPage from "./lib/pages/index.js"
import { logger, setLogger, type LoggerInstance } from "./lib/utils/logger.js"
import { toInternalRequest, toResponse } from "./lib/web.js"
@@ -54,7 +54,7 @@ import type {
import type { Provider } from "./providers/index.js"
import { JWTOptions } from "./jwt.js"
export { skipCSRFCheck }
export { skipCSRFCheck, raw }
/**
* Core functionality provided by Auth.js.
@@ -137,6 +137,9 @@ export async function Auth(
const internalResponse = await AuthInternal(internalRequest, config)
// @ts-expect-error TODO: Fix return type
if (config.raw === raw) return internalResponse
const response = await toResponse(internalResponse)
// If the request expects a return URL, send it as JSON
@@ -337,6 +340,7 @@ export interface AuthConfig {
/** @todo */
trustHost?: boolean
skipCSRFCheck?: typeof skipCSRFCheck
raw?: typeof raw
/**
* When set, during an OAuth sign-in flow,
* the `redirect_uri` of the authorization request

View File

@@ -162,8 +162,8 @@ export class SessionStore {
get value() {
// Sort the chunks by their keys before joining
const sortedKeys = Object.keys(this.#chunks).sort((a, b) => {
const aSuffix = parseInt(a.split(".")[1] || "0");
const bSuffix = parseInt(b.split(".")[1] || "0");
const aSuffix = parseInt(a.split(".").pop() || "0");
const bSuffix = parseInt(b.split(".").pop() || "0");
return aSuffix - bSuffix;
});

View File

@@ -208,3 +208,14 @@ export async function AuthInternal<
* passing this value to {@link AuthConfig.skipCSRFCheck}.
*/
export const skipCSRFCheck = Symbol("skip-csrf-check")
/**
* :::danger
* This option is intended for framework authors.
* :::
*
* Auth.js returns a web standard {@link Response} by default, but
* if you are implementing a framework you might want to get access to the raw internal response
* by passing this value to {@link AuthConfig.raw}.
*/
export const raw = Symbol("return-type-raw")

View File

@@ -0,0 +1,102 @@
/**
* <div style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
* <span>Built-in <b>Passage by 1Password</b> integration.</span>
* <a href="https://passage.1password.com">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/passage.svg" height="48" width="48"/>
* </a>
* </div>
*
* @module providers/passage
*/
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
/** @see [Supported Scopes](https://docs.passage.id/hosted-login/oidc-client-configuration#supported-scopes) */
export interface PassageProfile {
iss: string
/** Unique identifer in Passage for the user */
sub: string
aud: string[]
exp: number
iat: number
auth_time: number
azp: string
client_id: string
at_hash: string
c_hash: string
/** The user's email address */
email: string
/** Whether the user has verified their email address */
email_verified: boolean
/** The user's phone number */
phone: string
/** Whether the user has verified their phone number */
phone_number_verified: boolean
}
/**
* Add Passage login to your page.
*
* ### Setup
*
* #### Callback URL
* ```
* https://example.com/api/auth/callback/passage
* ```
*
* #### Configuration
*```js
* import Auth from "@auth/core"
* import Passage from "@auth/core/providers/passage"
*
* const request = new Request(origin)
* const response = await Auth(request, {
* providers: [Passage({ clientId: PASSAGE_ID, clientSecret: PASSAGE_SECRET, issuer: PASSAGE_ISSUER })],
* })
* ```
*
* ### Resources
*
* - [Passage OIDC documentation](https://docs.passage.id/hosted-login/oidc-client-configuration)
*
* ### Notes
*
* By default, Auth.js assumes that the Passage provider is
* based on the [Open ID Connect](https://openid.net/specs/openid-connect-core-1_0.html) specification.
*
* :::tip
*
* The Passage provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/passage.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 **Disclaimer**
*
* 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 Passage(
config: OAuthUserConfig<PassageProfile>
): OAuthConfig<PassageProfile> {
return {
id: "passage",
name: "Passage",
type: "oidc",
client: { token_endpoint_auth_method: "client_secret_basic" },
style: {
logo: "/passage.svg",
logoDark: "/passage.svg",
bg: "#fff",
bgDark: "#fff",
text: "#000",
textDark: "#000",
},
options: config,
}
}

View File

@@ -1,7 +1,7 @@
/**
* <div style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
* <span>Built-in <b>VK</b> integration.</span>
* <a href="www.vk.com/">
* <a href="https://vk.com/">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/vk.svg" height="48" />
* </a>
* </div>

View File

@@ -1,7 +1,7 @@
/**
* <div style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
* <span>Built-in <b> Wikimedia</b> integration.</span>
* <a href="www.mediawiki.org/">
* <a href="https://mediawiki.org/">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/wikimedia.svg" height="48" />
* </a>
* </div>

View File

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

View File

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

168
pnpm-lock.yaml generated
View File

@@ -27,7 +27,7 @@ importers:
eslint-plugin-svelte3: ^4.0.0
prettier: 2.8.1
prettier-plugin-svelte: ^2.8.1
turbo: ^1.10.3
turbo: ^1.10.12
typescript: 4.9.4
devDependencies:
'@actions/core': 1.10.0
@@ -48,7 +48,7 @@ importers:
eslint-plugin-svelte3: 4.0.0_eslint@8.30.0
prettier: 2.8.1
prettier-plugin-svelte: 2.8.1_prettier@2.8.1
turbo: 1.10.11
turbo: 1.10.12
typescript: 4.9.4
apps/dev/nextjs:
@@ -318,6 +318,31 @@ importers:
firebase-tools: 11.16.1
jest: 29.3.1
packages/adapter-kysely:
specifiers:
'@auth/core': workspace:*
'@next-auth/adapter-test': workspace:*
'@next-auth/tsconfig': workspace:*
'@types/better-sqlite3': ^7.6.3
'@types/pg': ^8.6.5
better-sqlite3: ^8.2.0
jest: ^27.4.3
kysely: ^0.24.2
mysql2: ^3.2.0
pg: ^8.10.0
dependencies:
'@auth/core': link:../core
devDependencies:
'@next-auth/adapter-test': link:../adapter-test
'@next-auth/tsconfig': link:../tsconfig
'@types/better-sqlite3': 7.6.4
'@types/pg': 8.10.2
better-sqlite3: 8.5.0
jest: 27.5.1
kysely: 0.24.2
mysql2: 3.6.0
pg: 8.11.2
packages/adapter-mikro-orm:
specifiers:
'@auth/core': workspace:*
@@ -10139,6 +10164,14 @@ packages:
resolution: {integrity: sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==}
dev: true
/@types/pg/8.10.2:
resolution: {integrity: sha512-MKFs9P6nJ+LAeHLU3V0cODEOgyThJ3OAnmOlsZsxux6sfQs3HRXR5bBn7xG5DjckEFhTAxsXi7k7cd0pCMxpJw==}
dependencies:
'@types/node': 20.4.8
pg-protocol: 1.5.0
pg-types: 4.0.1
dev: true
/@types/phoenix/1.5.4:
resolution: {integrity: sha512-L5eZmzw89eXBKkiqVBcJfU1QGx9y+wurRIEgt0cuLH0hwNtVUxtx+6cu0R2STwWj468sjXyBYPYDtGclUd1kjQ==}
@@ -21590,6 +21623,11 @@ packages:
resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==}
dev: true
/kysely/0.24.2:
resolution: {integrity: sha512-+7eaTJNUYm2yRq1x+lEOZc+78TO35dTZ9b0dh49+Z9CTt2byMSbMiOKpwPlOyCAaHD4kILkAYWYZNywFlmBwRA==}
engines: {node: '>=14.0.0'}
dev: true
/latest-version/5.1.0:
resolution: {integrity: sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==}
engines: {node: '>=8'}
@@ -23801,15 +23839,30 @@ packages:
resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
dev: true
/pg-cloudflare/1.1.1:
resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==}
requiresBuild: true
dev: true
optional: true
/pg-connection-string/2.5.0:
resolution: {integrity: sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==}
dev: true
/pg-connection-string/2.6.2:
resolution: {integrity: sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==}
dev: true
/pg-int8/1.0.1:
resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
engines: {node: '>=4.0.0'}
dev: true
/pg-numeric/1.0.2:
resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==}
engines: {node: '>=4'}
dev: true
/pg-pool/3.5.1_pg@8.7.3:
resolution: {integrity: sha512-6iCR0wVrro6OOHFsyavV+i6KYL4lVNyYAB9RD18w66xSzN+d8b66HiwuP30Gp1SH5O9T82fckkzsRjlrhD0ioQ==}
peerDependencies:
@@ -23818,10 +23871,22 @@ packages:
pg: 8.7.3
dev: true
/pg-pool/3.6.1_pg@8.11.2:
resolution: {integrity: sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==}
peerDependencies:
pg: '>=8.0'
dependencies:
pg: 8.11.2
dev: true
/pg-protocol/1.5.0:
resolution: {integrity: sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==}
dev: true
/pg-protocol/1.6.0:
resolution: {integrity: sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==}
dev: true
/pg-types/2.2.0:
resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==}
engines: {node: '>=4'}
@@ -23833,6 +23898,39 @@ packages:
postgres-interval: 1.2.0
dev: true
/pg-types/4.0.1:
resolution: {integrity: sha512-hRCSDuLII9/LE3smys1hRHcu5QGcLs9ggT7I/TCs0IE+2Eesxi9+9RWAAwZ0yaGjxoWICF/YHLOEjydGujoJ+g==}
engines: {node: '>=10'}
dependencies:
pg-int8: 1.0.1
pg-numeric: 1.0.2
postgres-array: 3.0.2
postgres-bytea: 3.0.0
postgres-date: 2.0.1
postgres-interval: 3.0.0
postgres-range: 1.1.3
dev: true
/pg/8.11.2:
resolution: {integrity: sha512-l4rmVeV8qTIrrPrIR3kZQqBgSN93331s9i6wiUiLOSk0Q7PmUxZD/m1rQI622l3NfqBby9Ar5PABfS/SulfieQ==}
engines: {node: '>= 8.0.0'}
peerDependencies:
pg-native: '>=3.0.1'
peerDependenciesMeta:
pg-native:
optional: true
dependencies:
buffer-writer: 2.0.0
packet-reader: 1.0.0
pg-connection-string: 2.6.2
pg-pool: 3.6.1_pg@8.11.2
pg-protocol: 1.6.0
pg-types: 2.2.0
pgpass: 1.0.5
optionalDependencies:
pg-cloudflare: 1.1.1
dev: true
/pg/8.7.3:
resolution: {integrity: sha512-HPmH4GH4H3AOprDJOazoIcpI49XFsHCe8xlrjHkWiapdbHK+HLtbm/GQzXYAZwmPju/kzKhjaSfMACG+8cgJcw==}
engines: {node: '>= 8.0.0'}
@@ -25032,16 +25130,33 @@ packages:
engines: {node: '>=4'}
dev: true
/postgres-array/3.0.2:
resolution: {integrity: sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==}
engines: {node: '>=12'}
dev: true
/postgres-bytea/1.0.0:
resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==}
engines: {node: '>=0.10.0'}
dev: true
/postgres-bytea/3.0.0:
resolution: {integrity: sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==}
engines: {node: '>= 6'}
dependencies:
obuf: 1.1.2
dev: true
/postgres-date/1.0.7:
resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==}
engines: {node: '>=0.10.0'}
dev: true
/postgres-date/2.0.1:
resolution: {integrity: sha512-YtMKdsDt5Ojv1wQRvUhnyDJNSr2dGIC96mQVKz7xufp07nfuFONzdaowrMHjlAzY6GDLd4f+LUHHAAM1h4MdUw==}
engines: {node: '>=12'}
dev: true
/postgres-interval/1.2.0:
resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==}
engines: {node: '>=0.10.0'}
@@ -25049,6 +25164,15 @@ packages:
xtend: 4.0.2
dev: true
/postgres-interval/3.0.0:
resolution: {integrity: sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==}
engines: {node: '>=12'}
dev: true
/postgres-range/1.1.3:
resolution: {integrity: sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g==}
dev: true
/postgres/3.3.5:
resolution: {integrity: sha512-+JD93VELV9gHkqpV5gdL5/70HdGtEw4/XE1S4BC8f1mcPmdib3K5XsKVbnR1XcAyC41zOnifJ+9YRKxdIsXiUw==}
dev: true
@@ -28576,65 +28700,65 @@ packages:
engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'}
dev: true
/turbo-darwin-64/1.10.11:
resolution: {integrity: sha512-pHNz6D5XUVB+bgZMKXIOegvH9GzPXucwgiHFatQcRoscAW1te1Zvn3fAWYo/mJ550AqPWQLmALZZel3z3lllLA==}
/turbo-darwin-64/1.10.12:
resolution: {integrity: sha512-vmDfGVPl5/aFenAbOj3eOx3ePNcWVUyZwYr7taRl0ZBbmv2TzjRiFotO4vrKCiTVnbqjQqAFQWY2ugbqCI1kOQ==}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/turbo-darwin-arm64/1.10.11:
resolution: {integrity: sha512-j3yGAvkBu0BqR+5nb9LiRs8UZsUQDOqpdP4S9OW3+W5jorJIxUxLawwk3XqoYVGhmPh84LWWOOrMgFQ/Y/3WSg==}
/turbo-darwin-arm64/1.10.12:
resolution: {integrity: sha512-3JliEESLNX2s7g54SOBqqkqJ7UhcOGkS0ywMr5SNuvF6kWVTbuUq7uBU/sVbGq8RwvK1ONlhPvJne5MUqBCTCQ==}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/turbo-linux-64/1.10.11:
resolution: {integrity: sha512-FZ+/VT3Yt188VvPuvqIwIyvosYALzu7e8ewxpl8yiYDwQbLwxMOEt2UKACsL+D7wzNtIMPRDxNmnhNvTbx9Afw==}
/turbo-linux-64/1.10.12:
resolution: {integrity: sha512-siYhgeX0DidIfHSgCR95b8xPee9enKSOjCzx7EjTLmPqPaCiVebRYvbOIYdQWRqiaKh9yfhUtFmtMOMScUf1gg==}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/turbo-linux-arm64/1.10.11:
resolution: {integrity: sha512-IfxO8S1FiikunmUnlul1sd5piPlunU1QlnNNGFfhKJkMidkJ0rXsSbh2epn/pXO8RRPBnFRxYkp6gJz/FTUUTg==}
/turbo-linux-arm64/1.10.12:
resolution: {integrity: sha512-K/ZhvD9l4SslclaMkTiIrnfcACgos79YcAo4kwc8bnMQaKuUeRpM15sxLpZp3xDjDg8EY93vsKyjaOhdFG2UbA==}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/turbo-windows-64/1.10.11:
resolution: {integrity: sha512-5qwTEk27duxYIsDycgZdpti1b41Xu2D3W+WRlg++sylwqhAgcPhfcppXMGd70h/SScgIh7IeLjzgTK7+YPE77g==}
/turbo-windows-64/1.10.12:
resolution: {integrity: sha512-7FSgSwvktWDNOqV65l9AbZwcoueAILeE4L7JvjauNASAjjbuzXGCEq5uN8AQU3U5BOFj4TdXrVmO2dX+lLu8Zg==}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/turbo-windows-arm64/1.10.11:
resolution: {integrity: sha512-FGvWCWvii4PZqy+4VBoanKaMkqeRD146iHL67YpY5sp8z5H/Gkywtu8xxBbkgP14lBr6fAsyRarHBuR+c52cDg==}
/turbo-windows-arm64/1.10.12:
resolution: {integrity: sha512-gCNXF52dwom1HLY9ry/cneBPOKTBHhzpqhMylcyvJP0vp9zeMQQkt6yjYv+6QdnmELC92CtKNp2FsNZo+z0pyw==}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/turbo/1.10.11:
resolution: {integrity: sha512-6GzYbsG5Ro6dK62dJuBjA53RdpMr1PWVwN6ZZRSMgYgkvFmNDMwxzJUKuCSi+jfDSt6avwT7koNlwRPfgTFuOw==}
/turbo/1.10.12:
resolution: {integrity: sha512-WM3+jTfQWnB9W208pmP4oeehZcC6JQNlydb/ZHMRrhmQa+htGhWLCzd6Q9rLe0MwZLPpSPFV2/bN5egCLyoKjQ==}
hasBin: true
requiresBuild: true
optionalDependencies:
turbo-darwin-64: 1.10.11
turbo-darwin-arm64: 1.10.11
turbo-linux-64: 1.10.11
turbo-linux-arm64: 1.10.11
turbo-windows-64: 1.10.11
turbo-windows-arm64: 1.10.11
turbo-darwin-64: 1.10.12
turbo-darwin-arm64: 1.10.12
turbo-linux-64: 1.10.12
turbo-linux-arm64: 1.10.12
turbo-windows-64: 1.10.12
turbo-windows-arm64: 1.10.12
dev: true
/tweetnacl/0.14.5:

View File

@@ -1,13 +1,23 @@
{
"$schema": "https://turborepo.org/schema.json",
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**/*", "*.js", "*.d.ts", "*.d.ts.map"],
"dependsOn": [
"^build"
],
"outputs": [
"dist/**/*",
"lib/**/*",
"*.js",
"*.d.ts",
"*.d.ts.map"
],
"outputMode": "new-only"
},
"next-auth#build": {
"dependsOn": ["^build"],
"dependsOn": [
"^build"
],
"outputs": [
"client/**",
"core/**",
@@ -22,7 +32,9 @@
"outputMode": "new-only"
},
"@auth/core#build": {
"dependsOn": ["^build"],
"dependsOn": [
"^build"
],
"outputs": [
"lib/**",
"providers/**",
@@ -35,8 +47,14 @@
"outputMode": "new-only"
},
"@auth/sveltekit#build": {
"dependsOn": ["^build"],
"outputs": [".svelte-kit/**", "client.*", "index.*"],
"dependsOn": [
"^build"
],
"outputs": [
".svelte-kit/**",
"client.*",
"index.*"
],
"outputMode": "new-only"
},
"clean": {
@@ -49,20 +67,28 @@
"outputMode": "new-only"
},
"e2e": {
"outputs": ["playwright-report/**"]
"outputs": [
"playwright-report/**"
]
},
"@auth/upstash-redis-adapter#test": {
"env": ["UPSTASH_REDIS_KEY", "UPSTASH_REDIS_URL"]
"env": [
"UPSTASH_REDIS_KEY",
"UPSTASH_REDIS_URL"
]
},
"docs#dev": {
"dependsOn": [
"@auth/core#build",
"@auth/prisma-adapter#build",
"@auth/solid-start#build",
"@auth/sveltekit#build",
"@auth/dgraph-adapter#build",
"@auth/drizzle-adapter#build",
"@auth/dynamodb-adapter#build",
"@auth/fauna-adapter#build",
"@auth/firebase-adapter#build",
"@auth/kysely-adapter#build",
"@auth/mikro-orm-adapter#build",
"@auth/mongodb-adapter#build",
"@auth/neo4j-adapter#build",
@@ -72,8 +98,7 @@
"@auth/typeorm-adapter#build",
"@auth/upstash-redis-adapter#build",
"@auth/xata-adapter#build",
"^build",
"next-auth#build"
"^build"
],
"cache": false
},
@@ -81,11 +106,14 @@
"dependsOn": [
"@auth/core#build",
"@auth/prisma-adapter#build",
"@auth/solid-start#build",
"@auth/sveltekit#build",
"@auth/dgraph-adapter#build",
"@auth/drizzle-adapter#build",
"@auth/dynamodb-adapter#build",
"@auth/fauna-adapter#build",
"@auth/firebase-adapter#build",
"@auth/kysely-adapter#build",
"@auth/mikro-orm-adapter#build",
"@auth/mongodb-adapter#build",
"@auth/neo4j-adapter#build",
@@ -95,8 +123,7 @@
"@auth/typeorm-adapter#build",
"@auth/upstash-redis-adapter#build",
"@auth/xata-adapter#build",
"^build",
"next-auth#build"
"^build"
],
"outputs": [
".docusaurus/**/*",
@@ -107,4 +134,4 @@
]
}
}
}
}