Compare commits

...

75 Commits

Author SHA1 Message Date
Balázs Orbán
70ffa6592f chore(release): bump package version(s) [skip ci] 2022-07-13 14:16:44 +02:00
mshd
3666e438a3 refactor(providers): convert GitHub provider to TypeScript (#4908)
* rewrite github provider in typescript

* rename md js endings

* Update packages/next-auth/src/providers/github.ts

Co-authored-by: Balázs Orbán <info@balazsorban.com>

* add eslint disable next line

Co-authored-by: Balázs Orbán <info@balazsorban.com>
2022-07-13 14:07:24 +02:00
Aliaksei Tayanouski
cdf467eba1 fix(providers): fix VK provider and convert to TS (#3709)
* renamed vk provider file extension to .ts

* lint issue fix

* vk provider fix

* formatting fix

* remove user_id from tokens

* Update src/providers/vk.ts

Co-authored-by: Balázs Orbán <info@balazsorban.com>

* Update src/providers/vk.ts

Co-authored-by: Balázs Orbán <info@balazsorban.com>

* user interface additions

Co-authored-by: Balázs Orbán <info@balazsorban.com>
2022-07-13 14:04:55 +02:00
Balázs Orbán
374dc30f9f chore: bump pnpm version requirement 2022-07-13 14:01:25 +02:00
Balázs Orbán
d9534d807d chore: tweak release script 2022-07-13 13:59:45 +02:00
Balázs Orbán
f4c7401a5d chore: fix release script text 2022-07-13 13:09:08 +02:00
Balázs Orbán
2baa0c30c1 chore: bump turbo 2022-07-13 13:08:58 +02:00
Sagar Gajare
839b9108ea docs: provider link fixed (#4913) 2022-07-13 01:39:13 +02:00
Juho Tapio
0bf955a63d docs: a few typos in CONTRIBUTING.md (#4910)
* docs: fixed a typo

* docs: update link to scripts index

* docks: more typos

* docs: added a comma
2022-07-12 17:58:54 +02:00
Balázs Orbán
83a974d455 docs: update Middleware docs
closes #4867
2022-07-12 17:19:23 +02:00
Nico Domino
8f54b8f729 docs: upgrade docusaurus-plugin-npm2yarn2pnpm (#4909) 2022-07-12 16:52:21 +02:00
nkg
1b91282402 fix(ts): use correct type for nodemailer config in the EmailProvider (#4097)
Co-authored-by: Thang Vu <31528554+ThangHuuVu@users.noreply.github.com>
2022-07-12 02:50:09 +02:00
Ryan Barr
c2a9ab3023 fix(providers): convert Discord Provider to TypeScript (#4170)
* chore(providers): Add typings for Discord Provider

* Update DiscordProfile interface to extend Record.
2022-07-12 02:43:56 +02:00
Chancellor Clark
5bd00f6ff1 feat(adapters): update Firebase adapter to use new API (#3873)
Ports and refactors `@next-auth/firebase-adapter` to use the new Adapter API. Ported from this PR: nextauthjs/adapters#183

BREAKING CHANGE:

- Renames `FirebaseAdapter` export to `FirestoreAdpater`
- This adapter now requires firebase v9+
2022-07-12 01:45:06 +02:00
William Crutchfield
af3c2dd33d fix(ts): typo in Azure Active Directory Provider (#4895) 2022-07-11 18:24:09 +02:00
John Brunton
709edc5153 fix(adapters): use correct peer dependency in Sequelize adapter (#4880) 2022-07-11 18:23:04 +02:00
Balázs Orbán
fa3ea37ebc chore: comment out adapter in dev app 2022-07-11 18:20:53 +02:00
Yoann Fleury
6a364f0353 docs: update CONTRIBUTING for pnpm instead of yarn (#4888)
* docs: update CONTRIBUTING for pnpm instead of yarn

* dev:app -> dev

Co-authored-by: Thang Vu <31528554+ThangHuuVu@users.noreply.github.com>
2022-07-11 10:38:30 +07:00
mshd
c22d613774 feat(providers): Add Wikimedia Oauth Provider (#4813)
* Add Wikimedia Oauth Provider

* add docs

* Update wikimedia.md

* Update wikimedia.md

* Update wikimedia.md

Co-authored-by: Nico Domino <yo@ndo.dev>
2022-07-10 19:38:01 +02:00
Nico Domino
9efafcd36c chore: fix dev app URL parse error (#4887) 2022-07-10 14:30:10 +02:00
Misha Kaletsky
e317b16cd2 docs: getToken link (#4884)
fix(docs): getToken link

without `/` it's considered a relative link so goes to https://next-auth.js.org/configuration/tutorials/securing-pages-and-api-routes#using-gettoken which doesn't exist.
2022-07-09 22:46:38 +02:00
Rudy Boutte
2edc79ed2b fix(docs): options page link (#4876) 2022-07-09 12:56:20 +02:00
Balázs Orbán
637dda9966 chore: tweak release script 2022-07-08 19:55:08 +02:00
Balázs Orbán
10bb32c479 chore(adapters): fix MikroORM adapter dependencies 2022-07-08 19:36:24 +02:00
Balázs Orbán
89e25568b1 chore: accept --dry-run flag in release script 2022-07-08 19:31:04 +02:00
Balázs Orbán
88ad25a16b feat(adapters): update typeorm support (#4844)
We haven't kept up with the recent TypeORM changes, and since they are still <1, it's likely that users kept upgrading, even if there were breaking changes.

BREAKING CHANGE:

[`typeorm`](https://github.com/typeorm/typeorm) is still in active development and has not yet published a stable release. Because of this, you can expect breaking changes in minor versions. This release of the adapter expects `typeorm@0.3.7` and is not validated against previous or future releases.

Run `npm i typeorm@latest` and make sure to read the [release notes](https://github.com/typeorm/typeorm/releases) for breaking changes in TypeORM
2022-07-08 19:26:59 +02:00
Luiz Bett
c1f7ce3436 fix(ts): make colorScheme optional (#4868)
Documentation says if it is not passed it will be "auto", but it is mandatory on this type.
2022-07-08 19:24:30 +02:00
sean-nicholas
c59a4e04d1 fix(middleware): allow secret as option in Middleware (#4846)
*  provide secret via config

* 🐛 make secret optional

* 📝 docs for middleware and env var

* 📝  recommendation at the end of paragraph

Co-authored-by: Balázs Orbán <info@balazsorban.com>
2022-07-07 14:14:21 +02:00
Joshua Grant
3c210d961b feat(providers): add Duende IdentityServer 6 (#4850)
* add duende identity server 6 provider

* Update docs/versioned_docs/version-v3/providers/duende-identity-server6.md

Co-authored-by: Balázs Orbán <info@balazsorban.com>

* Update docs/versioned_docs/version-v3/providers/duende-identity-server6.md

Co-authored-by: Balázs Orbán <info@balazsorban.com>

* Update packages/next-auth/src/providers/duende-identity-server6.ts

Co-authored-by: Balázs Orbán <info@balazsorban.com>

* Update packages/next-auth/src/providers/duende-identity-server6.ts

Co-authored-by: Balázs Orbán <info@balazsorban.com>

* Update docs/versioned_docs/version-v3/providers/duende-identity-server6.md

Co-authored-by: Balázs Orbán <info@balazsorban.com>

* Update apps/dev/pages/api/auth/[...nextauth].ts

Co-authored-by: Balázs Orbán <info@balazsorban.com>

* Update docs/versioned_docs/version-v3/providers/duende-identity-server6.md

Co-authored-by: Balázs Orbán <info@balazsorban.com>

* Update docs/versioned_docs/version-v3/providers/duende-identity-server6.md

Co-authored-by: Balázs Orbán <info@balazsorban.com>

* Update packages/next-auth/src/providers/duende-identity-server6.ts

Co-authored-by: Balázs Orbán <info@balazsorban.com>

Co-authored-by: Joshua <joshua.grant@tempcover.com>
Co-authored-by: Balázs Orbán <info@balazsorban.com>
2022-07-07 14:12:35 +02:00
Balázs Orbán
9457593038 chore: update Next.js example, bump dependencies 2022-07-07 11:31:17 +02:00
dependabot[bot]
5081d25f5c chore(deps): bump next-auth in /apps/playground-sveltekit (#4859)
Bumps [next-auth](https://github.com/nextauthjs/next-auth) from 4.5.0 to 4.9.0.
- [Release notes](https://github.com/nextauthjs/next-auth/releases)
- [Changelog](https://github.com/nextauthjs/next-auth/blob/main/CHANGELOG.md)
- [Commits](https://github.com/nextauthjs/next-auth/compare/next-auth@v4.5.0...next-auth@v4.9.0)

---
updated-dependencies:
- dependency-name: next-auth
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-07 00:34:27 +02:00
Rudy Boutte
384edbab3b docs: Added documentation on how to use a custom base path in NextAuth (#4858) 2022-07-07 00:34:12 +02:00
Balázs Orbán
2adfadefdc chore: bump version 2022-07-06 11:51:37 +02:00
Balázs Orbán
32fa01f939 chore: re-add GITHUB_TOKEN 2022-07-06 11:44:37 +02:00
Balázs Orbán
ae834f1e08 feat(providers): allow styling e-mail through theme option (#4841)
* fix(core): move email handling

* fix: don' use `replaceAll`

* feat(providers): re-use `theme` for e-mail

* docs: mention `theme` option for email

* fix: don't render user e-mail in the email HTML body

* docs: add missing comma

* refactor: fix lint

* refactor: fix lint
2022-07-05 16:02:04 +02:00
Nico Domino
4d4c276627 docs: replace npm2yarn with npm2yarn2pnpm docusaurus plugin (#4805)
* feat: rm npm2yarn add npm2yarn2pnpm plugin

* fix: pnpm-lock.yaml

* chore: test change

* fix: update npm2yarn2pnpm usage

* fix: package.json mistake packages added

* fix: pnpm-lock.yaml

* fix: test debug output

* fix: named import npm2yarn2pnpm

* chore: rm debug:true

* fix: change require statement

* Update pnpm-lock.yaml

* Update pnpm-lock.yaml

* Update pnpm-lock.yaml

* Update pnpm-lock.yaml

* Delete pnpm-lock.yaml

* add pnpm-lock.yaml

Co-authored-by: Balázs Orbán <info@balazsorban.com>
2022-07-05 11:56:44 +02:00
Junichi Sato
f4c0d5ab5d docs: Correct grammatical error (#4836) 2022-07-05 00:25:46 -04:00
Nico Domino
01cd6b0f7b docs: fix unstable_getServerSession arguments (#4815)
* chore(docs): fix unstable_getServerSession arguments

* chore: add authOptions import
2022-07-03 23:27:37 +02:00
Nico Domino
993c0f46b0 fix: show experimental api warning only in dev and only once (#4816)
Co-authored-by: Lluis Agusti <hi@llu.lu>
2022-07-02 21:00:11 +02:00
Balázs Orbán
163d8c66e2 chore: bump version 2022-07-01 13:53:57 +02:00
Balázs Orbán
5319dca583 fix(ts): fall back to empty string when parsing cookie 2022-07-01 12:14:48 +02:00
Balázs Orbán
cd6ccfde89 fix(core): handle invalid email 2022-07-01 12:09:57 +02:00
Nico Domino
89d91ea282 chore: update docs regarding new server session API (#4776)
* chore: update docs regarding new server session API

* fix: add note about unstable_ API prefix

* Apply suggestions from code review

Co-authored-by: Balázs Orbán <info@balazsorban.com>
2022-06-30 05:18:52 -04:00
Balázs Orbán
ca3165bd5a Update README.md 2022-06-29 15:55:54 +02:00
Balázs Orbán
aa527b37bf Update README.md 2022-06-29 15:54:49 +02:00
Richard
f3233641d0 docs: not -> now (#4783)
I assume this is meant to say "is now secured" ?
2022-06-28 19:12:48 +02:00
Balázs Orbán
4bee970775 chore: bump version 2022-06-28 18:28:53 +02:00
Balázs Orbán
80a4f50be2 chore: upgrade Docusaurus (#4782) 2022-06-28 12:55:43 +02:00
Ofek Asido
1f4ffbaefe docs: wrong semicolon placement (#4781) 2022-06-28 12:45:48 +02:00
Balázs Orbán
a911b4a40b chore(deps): upgrade dependencies 2022-06-28 12:22:11 +02:00
Balázs Orbán
cb0f3e1ae2 chore: rename GITHUB_TOKEN to RELEASE_TOKEN 2022-06-28 12:21:58 +02:00
Balázs Orbán
c194261617 fix(core): respect NEXTAUTH_SECRET in unstable_getServerSession (#4774)
* fix(core): respect `NEXTAUTH_SECRET` in `unstable_getServerSession`

* add `secret` tests

* add `@types/jest`

* fix tests
2022-06-27 17:00:08 +02:00
Nico Domino
5fdd8483d8 chore: add security headers to docs vercel.json (#4766) 2022-06-24 14:30:48 +02:00
Balázs Orbán
99f5b9616f fix: update Middleware (#4757)
* merge `main`, fix workspace dependencies

* chore(dev): use `matcher` in Middleware

* fix(middleware): support `cookies` as `Map`

* simplify

* chore(example): use new Middleware API

* chore(example): use `next-auth@latest`

* docs(middleware): document new Middleware API

* docs(ts): update inline example

* fix(ts): make cookies optional

* remove non-null assertion
2022-06-24 14:11:39 +02:00
Michael Hays
d8d9ab94cb feat(core): pass profile to linkAccount event (#4242)
Co-authored-by: Nico Domino <yo@ndo.dev>
2022-06-24 12:03:20 +02:00
Nico Domino
e8827cbf45 chore(docs): update securing pages tutorial (#3982)
Co-authored-by: Lluis Agusti <hi@llu.lu>
Co-authored-by: Thang Vu <31528554+ThangHuuVu@users.noreply.github.com>
2022-06-24 10:02:26 +02:00
Balázs Orbán
37c4a813e3 chore: bump version 2022-06-23 16:48:10 +02:00
Balázs Orbán
6a23ff7126 fix(build): include utils in package (#4760)
* fix: include `utils` in package

* fix: import `InternalUrl` as type

* `"emitDeclarationOnly": false`

* don't ignore `types.ts` in build
2022-06-23 16:41:01 +02:00
Balázs Orbán
23db0e68dd chore: bump version 2022-06-23 12:30:56 +02:00
Arthur Pedroti
e03e234b86 fix(ts): infer provider type in signIn (#4679)
* fix: signIn infer provider type

The "P" type it's not passed in any props, so the result type doesn't understand and return the false type always, Adding the "P" at provider type props.

* fix: P possibly undefined

Co-authored-by: arthurpedroti@gmail.com <arthurpedroti@LAPTOP-MVAK9RM5.localdomain>
2022-06-23 12:18:32 +02:00
Thang Vu
66fb914a31 feat: introduce experimental unstable_getServerSession API (#4116)
* refactor: improve `getServerSession` API

* Apply review comment

Co-authored-by: Balázs Orbán <info@balazsorban.com>

* Apply review comment

Co-authored-by: Balázs Orbán <info@balazsorban.com>

* Apply review comment

Co-authored-by: Balázs Orbán <info@balazsorban.com>

* Apply review comment

Co-authored-by: Balázs Orbán <info@balazsorban.com>

* Apply review comment

Co-authored-by: Balázs Orbán <info@balazsorban.com>

* Apply review comment

Co-authored-by: Balázs Orbán <info@balazsorban.com>

* Address docs review

* Fix a typo

* Update lint file location

* Address review comments

* getServerSession -> unstable_getServerSession

* Apply suggestions from code review

* Apply suggestions from code review

Co-authored-by: Dragate <spidfair@gmail.com>

* Update packages/next-auth/package.json

Co-authored-by: Balázs Orbán <info@balazsorban.com>
Co-authored-by: Dragate <spidfair@gmail.com>
2022-06-23 12:15:15 +02:00
Balázs Orbán
8ce728197f chore: update to use release token 2022-06-23 12:10:05 +02:00
ml4den
87d1a7af6d docs(providers): Change env names (#4753)
Proposing to match the env names to what is in the example [...nextauth].ts
2022-06-22 22:23:04 +02:00
Matthew Francis Brunetti
172813f987 docs(adapters): fix references to deprecated adapters repo (#4737) 2022-06-22 18:12:07 +02:00
dependabot[bot]
cc934fceec chore(deps): bump next-auth in /apps/playground-sveltekit (#4746)
Bumps [next-auth](https://github.com/nextauthjs/next-auth) from 4.3.3 to 4.5.0.
- [Release notes](https://github.com/nextauthjs/next-auth/releases)
- [Changelog](https://github.com/nextauthjs/next-auth/blob/main/CHANGELOG.md)
- [Commits](https://github.com/nextauthjs/next-auth/compare/next-auth@v4.3.3...next-auth@v4.5.0)

---
updated-dependencies:
- dependency-name: next-auth
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-22 18:07:17 +02:00
Guillermo Villegas Gray
46e467a7cb docs(providers): typo in slack.md (#4733)
typo in slack.md, thanks for the warning though!
2022-06-22 18:06:59 +02:00
Gal Schlezinger
73d489beac fix(edge): support request.cookies as a map (#4745)
in next Next.js versions, NextRequest.cookies will be an instance of NextCookies which is
some kind of a Map, instead of a plain object.

This commit checks whether there's a `get` function in req.cookies, and acts accordingly,
to make sure we will support newer Next.js versions with Edge Functions/Middleware
2022-06-21 20:20:48 +02:00
Balázs Orbán
e498483b23 test: add test for invalid callbackUrl handling 2022-06-20 10:38:21 +02:00
Cody Ogden
7cf49566a6 docs(providers/workos.md): Update reference link. (#4721)
The file extension changed to `.ts`.
2022-06-16 18:38:34 +02:00
Balázs Orbán
2469e44572 feat: allow standard Request in NextAuthHandler (#4704)
* chore: upgrade dev dependencies

* chore: use SWC for test transforms

* feat: allow standard `Request` as `NextAuthHandler` argument

* test: add initial core tests

* chore: ignore `tests` in build

* chore: fix lint

* chore: move `lib` to `utils`

* add body parsing, simplify

* fix tests

* chore: use `NPM_TOKEN_PKG` for experimental release
2022-06-14 12:25:07 +02:00
Danny Luedke
408b6b175f chore(sveltekit): cookie.parse type error for null (#4580) 2022-06-14 00:32:53 +02:00
ike
92dfc3c8b0 fix(providers): use client_secret_post auth method for Instagram (#4705) 2022-06-14 00:23:23 +02:00
Balázs Orbán
8c5d9faad6 chore: bump versions
[skip ci]
2022-06-14 00:10:16 +02:00
Balázs Orbán
49a8d51f79 fix: don't show error on relative callbackUrl
fixes #4700
2022-06-12 14:37:04 +02:00
Balázs Orbán
c0d251731d chore: bump version 2022-06-10 14:52:05 +02:00
174 changed files with 15906 additions and 19268 deletions

View File

@@ -21,7 +21,7 @@ jobs:
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@v2.2.1 uses: pnpm/action-setup@v2.2.1
with: with:
version: 6.32.8 version: 7.5.1
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
@@ -55,7 +55,7 @@ jobs:
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@v2.2.1 uses: pnpm/action-setup@v2.2.1
with: with:
version: 6.32.8 version: 7.5.1
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
@@ -69,7 +69,8 @@ jobs:
git config --global user.name "Balázs Orbán" git config --global user.name "Balázs Orbán"
pnpm release pnpm release
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
NPM_TOKEN_PKG: ${{ secrets.NPM_TOKEN_PKG }} NPM_TOKEN_PKG: ${{ secrets.NPM_TOKEN_PKG }}
NPM_TOKEN_ORG: ${{ secrets.NPM_TOKEN_ORG }} NPM_TOKEN_ORG: ${{ secrets.NPM_TOKEN_ORG }}
release-pr: release-pr:
@@ -84,7 +85,7 @@ jobs:
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@v2.2.1 uses: pnpm/action-setup@v2.2.1
with: with:
version: 6.32.8 version: 7.5.1
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
@@ -103,7 +104,7 @@ jobs:
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> .npmrc echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> .npmrc
pnpm publish --no-git-checks --access public --tag experimental pnpm publish --no-git-checks --access public --tag experimental
env: env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN_PKG }}
- name: Comment version on PR - name: Comment version on PR
uses: NejcZdovc/comment-pr@v1 uses: NejcZdovc/comment-pr@v1
with: with:

3
.gitignore vendored
View File

@@ -30,7 +30,7 @@ packages/next-auth/providers
packages/next-auth/src/providers/oauth-types.ts packages/next-auth/src/providers/oauth-types.ts
packages/next-auth/client packages/next-auth/client
packages/next-auth/css packages/next-auth/css
packages/next-auth/lib packages/next-auth/utils
packages/next-auth/core packages/next-auth/core
packages/next-auth/jwt packages/next-auth/jwt
packages/next-auth/react packages/next-auth/react
@@ -44,6 +44,7 @@ packages/next-auth/middleware.js
# Development app # Development app
apps/dev/src/css apps/dev/src/css
apps/dev/prisma/migrations apps/dev/prisma/migrations
apps/dev/typeorm
# VS # VS
/.vs/slnx.sqlite-journal /.vs/slnx.sqlite-journal

View File

@@ -17,7 +17,7 @@ Anyone can be a contributor. Either you found a typo, or you have an awesome fea
- The latest changes are always in `main`, so please make your Pull Request against that branch. - The latest changes are always in `main`, so please make your Pull Request against that branch.
- Pull Requests should be raised for any change - Pull Requests should be raised for any change
- Pull Requests need approval of a [core contributor](https://next-auth.js.org/contributors#core-team) before merging - Pull Requests need approval of a [core contributor](https://next-auth.js.org/contributors#core-team) before merging
- We use ESLint/Prettier for linting/formatting, so please run `yarn lint:fix` before committing to make resolving conflicts easier (VSCode users, check out [this ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) and [this Prettier extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) to fix lint and formatting issues in development) - We use ESLint/Prettier for linting/formatting, so please run `pnpm lint:fix` before committing to make resolving conflicts easier (VSCode users, check out [this ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) and [this Prettier extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) to fix lint and formatting issues in development)
- We encourage you to test your changes, and if you have the opportunity, please make those tests part of the Pull Request - We encourage you to test your changes, and if you have the opportunity, please make those tests part of the Pull Request
- If you add new functionality, please provide the corresponding documentation as well and make it part of the Pull Request - If you add new functionality, please provide the corresponding documentation as well and make it part of the Pull Request
@@ -37,7 +37,7 @@ cd next-auth
1. Install packages. Developing requires Node.js v16: 1. Install packages. Developing requires Node.js v16:
```sh ```sh
yarn pnpm install
``` ```
3. Populate `.env.local`: 3. Populate `.env.local`:
@@ -55,7 +55,7 @@ cp .env.local.example .env.local
4. Start the developer application/server: 4. Start the developer application/server:
```sh ```sh
yarn dev:app pnpm dev
``` ```
Your developer application will be available on `http://localhost:3000` Your developer application will be available on `http://localhost:3000`
@@ -65,7 +65,7 @@ If you need an example project to link to, you can use [next-auth-example](https
#### Hot reloading #### Hot reloading
When running `yarn dev:app`, you start a Next.js developer server on `http://localhost:3000`, which includes hot reloading out of the box. Make changes on any of the files in `src` and see the changes immediately. When running `pnpm dev`, you start a Next.js developer server on `http://localhost:3000`, which includes hot reloading out-of-the-box. Make changes on any of the files in `src` and see the changes immediately.
> NOTE: When working on CSS, you will have to manually refresh the page after changes. The reason for this is our pages using CSS are server-side rendered (using API routes). (Improving this through a PR is very welcome!) > NOTE: When working on CSS, you will have to manually refresh the page after changes. The reason for this is our pages using CSS are server-side rendered (using API routes). (Improving this through a PR is very welcome!)
@@ -75,7 +75,7 @@ When running `yarn dev:app`, you start a Next.js developer server on `http://loc
If you think your custom provider might be useful to others, we encourage you to open a PR and add it to the built-in list so others can discover it much more easily! You only need to add two changes: If you think your custom provider might be useful to others, we encourage you to open a PR and add it to the built-in list so others can discover it much more easily! You only need to add two changes:
1. Add your config: [`src/providers/{provider}.js`](https://github.com/nextauthjs/next-auth/tree/main/src/providers) (Make sure you use a named default export, like `export default function YourProvider`!) 1. Add your config: [`src/providers/{provider}.js`](https://github.com/nextauthjs/next-auth/tree/main/packages/next-auth/src/providers) (Make sure you use a named default export, like `export default function YourProvider`!)
2. Add provider documentation: [`www/docs/providers/{provider}.md`](https://github.com/nextauthjs/next-auth/tree/main/www/docs/providers) 2. Add provider documentation: [`www/docs/providers/{provider}.md`](https://github.com/nextauthjs/next-auth/tree/main/www/docs/providers)
That's it! 🎉 Others will be able to discover this provider much more easily now! That's it! 🎉 Others will be able to discover this provider much more easily now!
@@ -88,13 +88,13 @@ If you would like to contribute to an existing database adapter or help create a
#### Testing #### Testing
Tests can be run with `yarn test`. Tests can be run with `pnpm test`.
Automated tests are currently crude and limited in functionality, but improvements are in development. Automated tests are currently crude and limited in functionality, but improvements are in development.
## For maintainers ## For maintainers
We use [a custom script](https://github.com/nextauthjs/next-auth/tree/main/scripts/index.ts) together with [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0) to automate releases. This makes the maintenance process easier and less error-prone. Please study the "Conventional Commits" site to understand how to write a good commit message. We use [a custom script](https://github.com/nextauthjs/next-auth/blob/main/scripts/release/index.ts) together with [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0) to automate releases. This makes the maintenance process easier and less error-prone. Please study the "Conventional Commits" site to understand how to write a good commit message.
When accepting Pull Requests, make sure the following: When accepting Pull Requests, make sure the following:
@@ -103,9 +103,9 @@ When accepting Pull Requests, make sure the following:
- Rewrite the commit message to conform to the `Conventional Commits` style. - Rewrite the commit message to conform to the `Conventional Commits` style.
- Using `fix` releases a patch (x.x.1) - Using `fix` releases a patch (x.x.1)
- Using `feat` releases a minor (x.1.x) - Using `feat` releases a minor (x.1.x)
- Using `feat` when `BREAKING CHANGE` is present in the commit messgae releases a major (1.x.x) - Using `feat` when `BREAKING CHANGE` is present in the commit message releases a major (1.x.x)
- Optionally link issues the PR will resolve (You can add "close" in front of the issue numbers to close the issues automatically, when the PR is merged. `semantic-release` will also comment back to connected issues and PRs, notifying the users that a feature is added/bug fixed, etc.) - Optionally link issues the PR will resolve (You can add "close" in front of the issue numbers to close the issues automatically, when the PR is merged. `semantic-release` will also comment back to connected issues and PRs, notifying the users that a feature is added/bug fixed, etc.)
### Skipping a release ### Skipping a release
If a commit contains `[skip release]` in their message will be excluded from the commit analysis and won't participate in the release type determination. This is useful, if the PR being merged should not trigger a new `npm` release. If a commit contains `[skip release]` in their message, it will be excluded from the commit analysis and won't participate in the release type determination. This is useful, if the PR being merged should not trigger a new `npm` release.

View File

@@ -50,3 +50,6 @@ DATABASE_URL=
BOXYHQSAML_ISSUER="https://jackson-demo.boxyhq.com" BOXYHQSAML_ISSUER="https://jackson-demo.boxyhq.com"
BOXYHQSAML_ID="tenant=boxyhq.com&product=saml-demo.boxyhq.com" BOXYHQSAML_ID="tenant=boxyhq.com&product=saml-demo.boxyhq.com"
BOXYHQSAML_SECRET="dummy" BOXYHQSAML_SECRET="dummy"
WIKIMEDIA_ID=
WIKIMEDIA_SECRET=

View File

@@ -1,5 +1,7 @@
export { default } from "next-auth/middleware" export { default } from "next-auth/middleware"
export const config = { matcher: ["/middleware-protected"] }
// Other ways to use this middleware // Other ways to use this middleware
// import withAuth from "next-auth/middleware" // import withAuth from "next-auth/middleware"

View File

@@ -16,21 +16,25 @@
}, },
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@next-auth/fauna-adapter": "^1.0.1", "@next-auth/fauna-adapter": "workspace:*",
"@next-auth/prisma-adapter": "^1.0.1", "@next-auth/prisma-adapter": "workspace:*",
"@prisma/client": "^3.10.0", "@next-auth/typeorm-legacy-adapter": "workspace:*",
"cpx": "^1.5.0", "@prisma/client": "^3",
"fake-smtp-server": "^0.8.0", "faunadb": "^4",
"faunadb": "^4.4.1", "next": "12.2.0",
"next": "^12.1.0", "nodemailer": "^6",
"nodemailer": "^6.7.2", "react": "^18",
"react": "^17.0.2", "react-dom": "^18"
"react-dom": "^17.0.2"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "^17.0.37", "@types/react": "^18.0.15",
"@types/react-dom": "^17.0.11", "@types/react-dom": "^18.0.6",
"concurrently": "^7.1.0", "concurrently": "^7",
"prisma": "^3.10.0" "cpx": "^1.5.0",
"fake-smtp-server": "^0.8.0",
"pg": "^8.7.3",
"prisma": "^3",
"sqlite3": "^5.0.8",
"typeorm": "0.3.7"
} }
} }

View File

@@ -4,10 +4,11 @@ import GitHubProvider from "next-auth/providers/github"
import Auth0Provider from "next-auth/providers/auth0" import Auth0Provider from "next-auth/providers/auth0"
import KeycloakProvider from "next-auth/providers/keycloak" import KeycloakProvider from "next-auth/providers/keycloak"
import TwitterProvider, { import TwitterProvider, {
TwitterLegacy as TwitterLegacyProvider, // TwitterLegacy as TwitterLegacyProvider,
} from "next-auth/providers/twitter" } from "next-auth/providers/twitter"
import CredentialsProvider from "next-auth/providers/credentials" import CredentialsProvider from "next-auth/providers/credentials"
import IDS4Provider from "next-auth/providers/identity-server4" import IDS4Provider from "next-auth/providers/identity-server4"
import DuendeIDS6Provider from "next-auth/providers/duende-identity-server6"
import Twitch from "next-auth/providers/twitch" import Twitch from "next-auth/providers/twitch"
import GoogleProvider from "next-auth/providers/google" import GoogleProvider from "next-auth/providers/google"
import FacebookProvider from "next-auth/providers/facebook" import FacebookProvider from "next-auth/providers/facebook"
@@ -31,20 +32,39 @@ import PatreonProvider from "next-auth/providers/patreon"
import TraktProvider from "next-auth/providers/trakt" import TraktProvider from "next-auth/providers/trakt"
import WorkOSProvider from "next-auth/providers/workos" import WorkOSProvider from "next-auth/providers/workos"
import BoxyHQSAMLProvider from "next-auth/providers/boxyhq-saml" import BoxyHQSAMLProvider from "next-auth/providers/boxyhq-saml"
import WikimediaProvider from "next-auth/providers/wikimedia"
import VkProvider from "next-auth/providers/vk"
// TypeORM
// import { TypeORMLegacyAdapter } from "@next-auth/typeorm-legacy-adapter"
// const adapter = TypeORMLegacyAdapter({
// type: "sqlite",
// name: "next-auth-test-memory",
// database: "./typeorm/dev.db",
// synchronize: true,
// })
// // Prisma
// import { PrismaAdapter } from "@next-auth/prisma-adapter" // import { PrismaAdapter } from "@next-auth/prisma-adapter"
// import { PrismaClient } from "@prisma/client" // import { PrismaClient } from "@prisma/client"
// const prisma = new PrismaClient() // const prisma = new PrismaClient()
// const adapter = PrismaAdapter(prisma) // const adapter = PrismaAdapter(prisma)
// // Fauna
// import { Client as FaunaClient } from "faunadb" // import { Client as FaunaClient } from "faunadb"
// import { FaunaAdapter } from "@next-auth/fauna-adapter" // import { FaunaAdapter } from "@next-auth/fauna-adapter"
// const client = new FaunaClient({ // const client = new FaunaClient({
// secret: process.env.FAUNA_SECRET, // secret: process.env.FAUNA_SECRET,
// domain: process.env.FAUNA_DOMAIN, // domain: process.env.FAUNA_DOMAIN,
// }) // })
// const adapter = FaunaAdapter(client) // const adapter = FaunaAdapter(client)
// // Dummy
// const adapter: any = {
// getUserByEmail: (email) => ({ id: "1", email, emailVerified: null }),
// createVerificationToken: (token) => token,
// }
export const authOptions: NextAuthOptions = { export const authOptions: NextAuthOptions = {
// adapter, // adapter,
providers: [ providers: [
@@ -147,6 +167,11 @@ export const authOptions: NextAuthOptions = {
clientSecret: process.env.IDS4_SECRET, clientSecret: process.env.IDS4_SECRET,
issuer: process.env.IDS4_ISSUER, issuer: process.env.IDS4_ISSUER,
}), }),
DuendeIDS6Provider({
clientId: "interactive.confidential",
clientSecret: "secret",
issuer: "https://demo.duendesoftware.com",
}),
DiscordProvider({ DiscordProvider({
clientId: process.env.DISCORD_ID, clientId: process.env.DISCORD_ID,
clientSecret: process.env.DISCORD_SECRET, clientSecret: process.env.DISCORD_SECRET,
@@ -202,10 +227,18 @@ export const authOptions: NextAuthOptions = {
clientSecret: process.env.WORKOS_SECRET, clientSecret: process.env.WORKOS_SECRET,
}), }),
BoxyHQSAMLProvider({ BoxyHQSAMLProvider({
issuer: process.env.BOXYHQSAML_ISSUER, issuer: process.env.BOXYHQSAML_ISSUER ?? "https://example.com",
clientId: process.env.BOXYHQSAML_ID, clientId: process.env.BOXYHQSAML_ID,
clientSecret: process.env.BOXYHQSAML_SECRET, clientSecret: process.env.BOXYHQSAML_SECRET,
}), }),
WikimediaProvider({
clientId: process.env.WIKIMEDIA_ID,
clientSecret: process.env.WIKIMEDIA_SECRET,
}),
VkProvider({
clientId: process.env.VK_ID,
clientSecret: process.env.VK_SECRET
}),
], ],
debug: true, debug: true,
theme: { theme: {

View File

@@ -1,8 +1,8 @@
// This is an example of to protect an API route // This is an example of to protect an API route
import { getSession } from "next-auth/react" import { unstable_getServerSession } from "next-auth/next"
export default async (req, res) => { export default async (req, res) => {
const session = await getSession({ req }) const session = await unstable_getServerSession(req, res, options)
if (session) { if (session) {
res.send({ res.send({

View File

@@ -1,7 +1,7 @@
// This is an example of how to access a session from an API route // This is an example of how to access a session from an API route
import { getSession } from "next-auth/react" import { unstable_getServerSession } from "next-auth/next"
export default async (req, res) => { export default async (req, res) => {
const session = await getSession({ req }) const session = await unstable_getServerSession(req, res, authOptions)
res.send(JSON.stringify(session, null, 2)) res.send(JSON.stringify(session, null, 2))
} }

View File

@@ -1,5 +1,5 @@
// This is an example of how to protect content using server rendering // This is an example of how to protect content using server rendering
import { getServerSession } from "next-auth/next" import { unstable_getServerSession } from "next-auth/next"
import { authOptions } from "./api/auth/[...nextauth]" import { authOptions } from "./api/auth/[...nextauth]"
import Layout from "../components/layout" import Layout from "../components/layout"
import AccessDenied from "../components/access-denied" import AccessDenied from "../components/access-denied"
@@ -26,7 +26,11 @@ export default function Page({ content, session }) {
} }
export async function getServerSideProps(context) { export async function getServerSideProps(context) {
const session = await getServerSession(context, authOptions) const session = await unstable_getServerSession(
context.req,
context.res,
authOptions
)
let content = null let content = null
if (session) { if (session) {

View File

@@ -1,4 +1,4 @@
import { getSession } from "next-auth/react" import { unstable_getServerSession } from "next-auth/next"
import Layout from "../components/layout" import Layout from "../components/layout"
export default function Page() { export default function Page() {
@@ -11,13 +11,17 @@ export default function Page() {
<Layout> <Layout>
<h1>Server Side Rendering</h1> <h1>Server Side Rendering</h1>
<p> <p>
This page uses the universal <strong>getSession()</strong> method in{" "} This page uses the <strong>unstable_getServerSession()</strong> method
<strong>getServerSideProps()</strong>. in <strong>getServerSideProps()</strong>.
</p> </p>
<p> <p>
Using <strong>getSession()</strong> in{" "} Using <strong>unstable_getServerSession()</strong> in{" "}
<strong>getServerSideProps()</strong> is the recommended approach if you <strong>getServerSideProps()</strong> is currently the recommended
need to support Server Side Rendering with authentication. approach, although the API may still change, if you need to support
Server Side Rendering with authentication.
</p>
<p>
Using <strong>getSession()</strong> is still recommended on the client.
</p> </p>
<p> <p>
The advantage of Server Side Rendering is this page does not require The advantage of Server Side Rendering is this page does not require
@@ -35,7 +39,11 @@ export default function Page() {
export async function getServerSideProps(context) { export async function getServerSideProps(context) {
return { return {
props: { props: {
session: await getSession(context), session: await unstable_getServerSession(
contex.req,
contex.res,
authOptions
),
}, },
} }
} }

View File

@@ -65,7 +65,6 @@ You **can** skip configuring a database and come back to it later if you want.
For more information about setting up a database, please check out the following links: For more information about setting up a database, please check out the following links:
* Docs: [next-auth.js.org/adapters/overview](https://next-auth.js.org/adapters/overview) * Docs: [next-auth.js.org/adapters/overview](https://next-auth.js.org/adapters/overview)
* Adapters Repo: [nextauthjs/adapters](https://github.com/nextauthjs/adapters)
### 3. Configure Authentication Providers ### 3. Configure Authentication Providers

View File

@@ -12,9 +12,9 @@
"dependencies": { "dependencies": {
"dotenv": "^16.0.0", "dotenv": "^16.0.0",
"gatsby": "next", "gatsby": "next",
"next-auth": "^4.2.1", "next-auth": "latest",
"react": "^17.0.2", "react": "^18",
"react-dom": "^17.0.2" "react-dom": "^18"
}, },
"devDependencies": { "devDependencies": {
"vercel": "^23.1.2" "vercel": "^23.1.2"

View File

@@ -68,7 +68,6 @@ You **can** skip configuring a database and come back to it later if you want.
For more information about setting up a database, please check out the following links: For more information about setting up a database, please check out the following links:
* Docs: [next-auth.js.org/adapters/overview](https://next-auth.js.org/adapters/overview) * Docs: [next-auth.js.org/adapters/overview](https://next-auth.js.org/adapters/overview)
* Adapters Repo: [nextauthjs/adapters](https://github.com/nextauthjs/adapters)
### 3. Configure Authentication Providers ### 3. Configure Authentication Providers

View File

@@ -0,0 +1,12 @@
import { withAuth } from "next-auth/middleware"
// More on how NextAuth.js middleware works: https://next-auth.js.org/configuration/nextjs#middleware
export default withAuth({
callbacks: {
authorized: ({ req, token }) =>
// /admin requires admin role, but /me only requires the user to be logged in.
req.nextUrl.pathname !== "/admin" || token?.userRole === "admin",
},
})
export const config = { matcher: ["/admin", "/me"] }

View File

@@ -1,19 +1,15 @@
{ {
"name": "next-auth-example",
"version": "0.0.0",
"private": true, "private": true,
"description": "An example project for NextAuth.js", "description": "An example project for NextAuth.js with Next.js",
"repository": "https://github.com/nextauthjs/next-auth-example.git", "repository": "https://github.com/nextauthjs/next-auth-example.git",
"bugs": { "bugs": {
"url": "https://github.com/nextauthjs/next-auth/issues" "url": "https://github.com/nextauthjs/next-auth/issues"
}, },
"homepage": "https://next-auth-example.vercel.app", "homepage": "https://next-auth-example.vercel.app",
"main": "",
"scripts": { "scripts": {
"dev": "next", "dev": "next",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start"
"types": "tsc --noEmit"
}, },
"author": "Iain Collins <me@iaincollins.com>", "author": "Iain Collins <me@iaincollins.com>",
"contributors": [ "contributors": [
@@ -21,20 +17,16 @@
"Nico Domino <yo@ndo.dev>", "Nico Domino <yo@ndo.dev>",
"Lluis Agusti <hi@llu.lu>" "Lluis Agusti <hi@llu.lu>"
], ],
"license": "ISC",
"dependencies": { "dependencies": {
"next": "^12.0.11-canary.4", "next": "latest",
"next-auth": "latest", "next-auth": "latest",
"nodemailer": "^6.6.3", "nodemailer": "^6",
"react": "^17.0.2", "react": "^18.2.0",
"react-dom": "^17.0.2" "react-dom": "^18.2.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^17.0.14", "@types/node": "^17",
"@types/react": "^17.0.39", "@types/react": "^18.0.15",
"typescript": "^4.5.5" "typescript": "^4"
},
"prettier": {
"semi": false
} }
} }

View File

@@ -1,7 +1,8 @@
import { SessionProvider } from "next-auth/react" import { SessionProvider } from "next-auth/react"
import type { AppProps } from "next/app"
import "./styles.css" import "./styles.css"
import type { AppProps } from "next/app"
// Use of the <SessionProvider> is mandatory to allow components that call // Use of the <SessionProvider> is mandatory to allow components that call
// `useSession()` anywhere in your application to access the `session` object. // `useSession()` anywhere in your application to access the `session` object.
export default function App({ Component, pageProps }: AppProps) { export default function App({ Component, pageProps }: AppProps) {

View File

@@ -1,4 +1,4 @@
import Layout from "../../components/layout" import Layout from "../components/layout"
export default function Page() { export default function Page() {
return ( return (

View File

@@ -1,8 +0,0 @@
import { withAuth } from "next-auth/middleware"
// More on how NextAuth.js middleware works: https://next-auth.js.org/configuration/nextjs#middleware
export default withAuth({
callbacks: {
authorized: ({ token }) => token?.userRole === "admin",
},
})

View File

@@ -1,4 +1,4 @@
import NextAuth from "next-auth" import NextAuth, { NextAuthOptions } from "next-auth"
import GoogleProvider from "next-auth/providers/google" import GoogleProvider from "next-auth/providers/google"
import FacebookProvider from "next-auth/providers/facebook" import FacebookProvider from "next-auth/providers/facebook"
import GithubProvider from "next-auth/providers/github" import GithubProvider from "next-auth/providers/github"
@@ -9,7 +9,7 @@ import Auth0Provider from "next-auth/providers/auth0"
// For more information on each option (and a full list of options) go to // For more information on each option (and a full list of options) go to
// https://next-auth.js.org/configuration/options // https://next-auth.js.org/configuration/options
export default NextAuth({ export const authOptions: NextAuthOptions = {
// https://next-auth.js.org/configuration/providers/oauth // https://next-auth.js.org/configuration/providers/oauth
providers: [ providers: [
/* EmailProvider({ /* EmailProvider({
@@ -60,4 +60,6 @@ export default NextAuth({
return token return token
}, },
}, },
}) }
export default NextAuth(authOptions)

View File

@@ -1,10 +1,14 @@
// This is an example of how to read a JSON Web Token from an API route // This is an example of how to read a JSON Web Token from an API route
import { getToken } from "next-auth/jwt" import { getToken } from "next-auth/jwt"
import type { NextApiRequest, NextApiResponse } from "next" import type { NextApiRequest, NextApiResponse } from "next"
const secret = process.env.NEXTAUTH_SECRET const secret = process.env.NEXTAUTH_SECRET
export default async (req: NextApiRequest, res: NextApiResponse) => { export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const token = await getToken({ req, secret }) const token = await getToken({ req, secret })
res.send(JSON.stringify(token, null, 2)) res.send(JSON.stringify(token, null, 2))
} }

View File

@@ -1,18 +1,23 @@
// This is an example of to protect an API route // This is an example of to protect an API route
import { getSession } from "next-auth/react" import { unstable_getServerSession } from "next-auth/next"
import { authOptions } from "../auth/[...nextauth]"
import type { NextApiRequest, NextApiResponse } from "next" import type { NextApiRequest, NextApiResponse } from "next"
export default async (req: NextApiRequest, res: NextApiResponse) => { export default async function handler(
const session = await getSession({ req }) req: NextApiRequest,
res: NextApiResponse
) {
const session = await unstable_getServerSession(req, res, authOptions)
if (session) { if (session) {
res.send({ return res.send({
content: content:
"This is protected content. You can access this content because you are signed in.", "This is protected content. You can access this content because you are signed in.",
}) })
} else { }
res.send({ res.send({
error: "You must be signed in to view the protected content on this page.", error: "You must be signed in to view the protected content on this page.",
}) })
} }
}

View File

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

View File

@@ -1,5 +1,5 @@
import { useSession } from "next-auth/react" import { useSession } from "next-auth/react"
import Layout from "../../components/layout" import Layout from "../components/layout"
export default function MePage() { export default function MePage() {
const { data } = useSession() const { data } = useSession()

View File

@@ -1,2 +0,0 @@
// More on how NextAuth.js middleware works: https://next-auth.js.org/configuration/nextjs#middleware
export { default } from "next-auth/middleware"

View File

@@ -1,26 +1,25 @@
import { useSession, getSession } from "next-auth/react" import { unstable_getServerSession } from "next-auth/next"
import { authOptions } from "./api/auth/[...nextauth]"
import Layout from "../components/layout" import Layout from "../components/layout"
import type { NextPageContext } from "next"
export default function ServerSidePage() { import type { GetServerSidePropsContext } from "next"
import type { Session } from "next-auth"
export default function ServerSidePage({ session }: { session: Session }) {
// As this page uses Server Side Rendering, the `session` will be already // As this page uses Server Side Rendering, the `session` will be already
// populated on render without needing to go through a loading stage. // populated on render without needing to go through a loading stage.
// This is possible because of the shared context configured in `_app.js` that
// is used by `useSession()`.
const { data: session, status } = useSession()
const loading = status === "loading"
return ( return (
<Layout> <Layout>
<h1>Server Side Rendering</h1> <h1>Server Side Rendering</h1>
<p> <p>
This page uses the universal <strong>getSession()</strong> method in{" "} This page uses the <strong>unstable_getServerSession()</strong> method
<strong>getServerSideProps()</strong>. in <strong>unstable_getServerSideProps()</strong>.
</p> </p>
<p> <p>
Using <strong>getSession()</strong> in{" "} Using <strong>unstable_getServerSession()</strong> in{" "}
<strong>getServerSideProps()</strong> is the recommended approach if you <strong>unstable_getServerSideProps()</strong> is the recommended
need to support Server Side Rendering with authentication. approach if you need to support Server Side Rendering with
authentication.
</p> </p>
<p> <p>
The advantage of Server Side Rendering is this page does not require The advantage of Server Side Rendering is this page does not require
@@ -30,15 +29,20 @@ export default function ServerSidePage() {
The disadvantage of Server Side Rendering is that this page is slower to The disadvantage of Server Side Rendering is that this page is slower to
render. render.
</p> </p>
<pre>{JSON.stringify(session, null, 2)}</pre>
</Layout> </Layout>
) )
} }
// Export the `session` prop to use sessions with Server Side Rendering // Export the `session` prop to use sessions with Server Side Rendering
export async function getServerSideProps(context: NextPageContext) { export async function getServerSideProps(context: GetServerSidePropsContext) {
return { return {
props: { props: {
session: await getSession(context), session: await unstable_getServerSession(
context.req,
context.res,
authOptions
),
}, },
} }
} }

View File

@@ -30,7 +30,7 @@
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"cookie": "0.4.1", "cookie": "0.4.1",
"next-auth": "^4.3.3" "next-auth": "workspace:*"
}, },
"prettier": { "prettier": {
"semi": false, "semi": false,

View File

@@ -65,7 +65,7 @@ async function SKNextAuthHandler(
query: Object.fromEntries(url.searchParams), query: Object.fromEntries(url.searchParams),
headers: request.headers, headers: request.headers,
method: request.method, method: request.method,
cookies: cookie.parse(request.headers.get("cookie")), cookies: cookie.parse(request.headers.get("cookie") ?? ""),
action: nextauth[0] as NextAuthAction, action: nextauth[0] as NextAuthAction,
providerId: nextauth[1], providerId: nextauth[1],
error: nextauth[1], error: nextauth[1],
@@ -91,7 +91,7 @@ export async function getServerSession(
host: import.meta.env.VITE_NEXTAUTH_URL, host: import.meta.env.VITE_NEXTAUTH_URL,
action: "session", action: "session",
method: "GET", method: "GET",
cookies: cookie.parse(request.headers.get("cookie")), cookies: cookie.parse(request.headers.get("cookie") ?? ""),
headers: request.headers, headers: request.headers,
}, },
options, options,

View File

@@ -1232,10 +1232,10 @@ natural-compare@^1.4.0:
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
next-auth@^4.3.3: "next-auth@workspace:*":
version "4.3.3" version "4.9.0"
resolved "https://registry.yarnpkg.com/next-auth/-/next-auth-4.3.3.tgz#5ff892e73648a0f33c2af0e9d7cafda729f63ae7" resolved "https://registry.yarnpkg.com/next-auth/-/next-auth-4.9.0.tgz#0d8cabcb22a976744131a2e68d5f08756f322593"
integrity sha512-bUs+oOOPT18Pq/+4v9q4PA/DGoVoAX6jwY7RTfE/akFXwlny+y/mNS6lPSUwpqcHjljqBaq34PQA3+01SdOOPw== integrity sha512-/4S5dFeyNg2nXlD7g/Sh5A4WZWnUMDpEf8x/x+gzmAf5cAY2SjDM6sLk9u4XRmsndsxQpIMWDw03sUTAD+Yzog==
dependencies: dependencies:
"@babel/runtime" "^7.16.3" "@babel/runtime" "^7.16.3"
"@panva/hkdf" "^1.0.1" "@panva/hkdf" "^1.0.1"

View File

@@ -11,7 +11,7 @@ This is the Dgraph Adapter for [`next-auth`](https://next-auth.js.org).
1. Install the necessary packages 1. Install the necessary packages
```bash npm2yarn ```bash npm2yarn2pnpm
npm install next-auth @next-auth/dgraph-adapter npm install next-auth @next-auth/dgraph-adapter
``` ```
@@ -226,22 +226,22 @@ database you must customize next-auth `encode` and `decode` functions, as the de
further customize the jwt with roles if you want to implement [`RBAC logic`](https://dgraph.io/docs/graphql/authorization/directive/#role-based-access-control). further customize the jwt with roles if you want to implement [`RBAC logic`](https://dgraph.io/docs/graphql/authorization/directive/#role-based-access-control).
```js ```js
import * as jwt from "jsonwebtoken"; import * as jwt from "jsonwebtoken"
export default NextAuth({ export default NextAuth({
session: { session: {
strategy: "jwt" strategy: "jwt",
}, },
jwt: { jwt: {
secret: process.env.SECRET, secret: process.env.SECRET,
encode: async ({ secret, token }) => { encode: async ({ secret, token }) => {
return jwt.sign({ ...token, userId: token.id }, secret, { return jwt.sign({ ...token, userId: token.id }, secret, {
algorithm: "HS256", algorithm: "HS256",
expiresIn: 30 * 24 * 60 * 60; // 30 days expiresIn: 30 * 24 * 60 * 60, // 30 days
}); })
}, },
decode: async ({ secret, token }) => { decode: async ({ secret, token }) => {
return jwt.verify(token, secret, { algorithms: ["HS256"] }); return jwt.verify(token, secret, { algorithms: ["HS256"] })
} },
}, },
}) })
``` ```

View File

@@ -15,7 +15,7 @@ You can find the full schema in the table structure section below.
1. Install `next-auth` and `@next-auth/dynamodb-adapter` 1. Install `next-auth` and `@next-auth/dynamodb-adapter`
```bash npm2yarn ```bash npm2yarn2pnpm
npm install next-auth @next-auth/dynamodb-adapter npm install next-auth @next-auth/dynamodb-adapter
``` ```

View File

@@ -13,7 +13,7 @@ You can find the Fauna schema and seed information in the docs at [next-auth.js.
1. Install the necessary packages 1. Install the necessary packages
```bash npm2yarn ```bash npm2yarn2pnpm
npm install next-auth @next-auth/fauna-adapter faunadb npm install next-auth @next-auth/fauna-adapter faunadb
``` ```

View File

@@ -5,18 +5,14 @@ title: Firebase
# Firebase # Firebase
:::warning This is the Firebase (Firestore) Adapter for [`next-auth`](https://next-auth.js.org). This package can only be used in conjunction with the primary `next-auth` package. It is not a standalone package.
This adapter is still experimental and does not work with NextAuth.js 4 or newer. If you would like to help out upgrading it, please visit [this PR](https://github.com/nextauthjs/next-auth/pull/3873)
:::
This is the Firebase Adapter for [`next-auth`](https://next-auth.js.org). This package can only be used in conjunction with the primary `next-auth` package. It is not a standalone package.
## Getting Started ## Getting Started
1. Install the necessary packages 1. Install the necessary packages
```bash npm2yarn ```bash npm2yarn2pnpm
npm install next-auth @next-auth/firebase-adapter@experimental npm install next-auth @next-auth/firebase-adapter
``` ```
2. Add this adapter to your `pages/api/auth/[...nextauth].js` next-auth configuration object. 2. Add this adapter to your `pages/api/auth/[...nextauth].js` next-auth configuration object.
@@ -24,14 +20,7 @@ npm install next-auth @next-auth/firebase-adapter@experimental
```javascript title="pages/api/auth/[...nextauth].js" ```javascript title="pages/api/auth/[...nextauth].js"
import NextAuth from "next-auth" import NextAuth from "next-auth"
import GoogleProvider from "next-auth/providers/google" import GoogleProvider from "next-auth/providers/google"
import { FirebaseAdapter } from "@next-auth/firebase-adapter" import { FirestoreAdapter } from "@next-auth/firebase-adapter"
import firebase from "firebase/app"
import "firebase/firestore"
const firestore = (
firebase.apps[0] ?? firebase.initializeApp(/* your config */)
).firestore()
// For more information on each option (and a full list of options) go to // For more information on each option (and a full list of options) go to
// https://next-auth.js.org/configuration/options // https://next-auth.js.org/configuration/options
@@ -43,9 +32,19 @@ export default NextAuth({
clientSecret: process.env.GOOGLE_SECRET, clientSecret: process.env.GOOGLE_SECRET,
}), }),
], ],
adapter: FirebaseAdapter(firestore), adapter: FirestoreAdapter({
... apiKey: process.env.FIREBASE_API_KEY,
}) appId: process.env.FIREBASE_APP_ID,
authDomain: process.env.FIREBASE_AUTH_DOMAIN,
databaseURL: process.env.FIREBASE_DATABASE_URL,
projectId: process.env.FIREBASE_PROJECT_ID,
storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID,
// Optional emulator config (see below for options)
emulator: {},
}),
// ...
});
``` ```
## Options ## Options
@@ -69,6 +68,21 @@ const firebaseConfig = {
See [firebase.google.com/docs/web/setup](https://firebase.google.com/docs/web/setup) for more details. See [firebase.google.com/docs/web/setup](https://firebase.google.com/docs/web/setup) for more details.
You can optionally pass in emulator options to automatically connect to your local Firebase emulator.
```js
FirestoreAdapter({
// ...
// Passing in an enable object will enable the emulator
emulator: {
// Optional host, defaults to `localhost`
host: 'localhost',
// Optional port, defaults to `3001`
port: 3001,
},
}),
```
:::tip **From Firebase** :::tip **From Firebase**
**Caution**: We do not recommend manually modifying an app's Firebase config file or object. If you initialize an app with invalid or missing values for any of these required "Firebase options", then your end users may experience serious issues. **Caution**: We do not recommend manually modifying an app's Firebase config file or object. If you initialize an app with invalid or missing values for any of these required "Firebase options", then your end users may experience serious issues.

View File

@@ -5,7 +5,7 @@ title: MikroORM
To use this Adapter, you need to install Mikro ORM, the driver that suits your database, and the separate `@next-auth/mikro-orm-adapter` package: To use this Adapter, you need to install Mikro ORM, the driver that suits your database, and the separate `@next-auth/mikro-orm-adapter` package:
```bash npm2yarn ```bash npm2yarn2pnpm
npm install next-auth @next-auth/mikro-orm-adapter @mikro-orm/core @mikro-orm/[YOUR DRIVER] npm install next-auth @next-auth/mikro-orm-adapter @mikro-orm/core @mikro-orm/[YOUR DRIVER]
``` ```

View File

@@ -11,7 +11,7 @@ The MongoDB adapter does not handle connections automatically, so you will have
1. Install the necessary packages 1. Install the necessary packages
```bash npm2yarn ```bash npm2yarn2pnpm
npm install next-auth @next-auth/mongodb-adapter mongodb npm install next-auth @next-auth/mongodb-adapter mongodb
``` ```

View File

@@ -11,7 +11,7 @@ This is the Neo4j Adapter for [`next-auth`](https://next-auth.js.org). This pack
1. Install the necessary packages 1. Install the necessary packages
```bash npm2yarn ```bash npm2yarn2pnpm
npm install next-auth @next-auth/neo4j-adapter neo4j-driver npm install next-auth @next-auth/neo4j-adapter neo4j-driver
``` ```

View File

@@ -19,7 +19,7 @@ Depending on your architecture you can use PouchDB's http adapter to reach any d
1. Install `next-auth` and `@next-auth/pouchdb-adapter` 1. Install `next-auth` and `@next-auth/pouchdb-adapter`
```bash npm2yarn ```bash npm2yarn2pnpm
npm install next-auth @next-auth/pouchdb-adapter npm install next-auth @next-auth/pouchdb-adapter
``` ```

View File

@@ -7,7 +7,7 @@ title: Prisma
To use this Adapter, you need to install Prisma Client, Prisma CLI, and the separate `@next-auth/prisma-adapter` package: To use this Adapter, you need to install Prisma Client, Prisma CLI, and the separate `@next-auth/prisma-adapter` package:
```bash npm2yarn ```bash npm2yarn2pnpm
npm install next-auth @prisma/client @next-auth/prisma-adapter npm install next-auth @prisma/client @next-auth/prisma-adapter
npm install prisma --save-dev npm install prisma --save-dev
``` ```

View File

@@ -11,7 +11,7 @@ This is the Sequelize Adapter for [`next-auth`](https://next-auth.js.org).
1. Install the necessary packages 1. Install the necessary packages
```bash npm2yarn ```bash npm2yarn2pnpm
npm install next-auth @next-auth/sequelize-adapter sequelize npm install next-auth @next-auth/sequelize-adapter sequelize
``` ```

View File

@@ -5,21 +5,25 @@ title: TypeORM
# TypeORM # TypeORM
This Adapter is used to support SQL-flavored databases (like SQLite, MySQL, MSSQL, MariaDB, CockroachDB, etc.) through [TypeORM](https://typeorm.io), and mostly kept around for legacy reasons. (See the warning below.) This Adapter is used to support SQL-flavored databases (like SQLite, MySQL, MSSQL, MariaDB, CockroachDB, etc.) through [TypeORM](https://typeorm.io).
:::note :::note
If you previously used this Adapter with MongoDB, check out the [MongoDB Adapter](/adapters/mongodb) instead. If you previously used this Adapter with MongoDB, check out the [MongoDB Adapter](/adapters/mongodb) instead.
::: :::
:::warning :::note
In the future, we might split up this adapter to support single flavors of SQL for easier maintenance and reduced bundle size. In the future, we might split up this adapter to support single flavors of SQL for easier maintenance and reduced bundle size.
::: :::
## Usage ## Usage
:::warning
[`typeorm`](https://github.com/typeorm/typeorm) is still in active development and has not yet published a stable release. Because of this, you can expect breaking changes in minor versions. This adapter expects `typeorm@0.3.7` and is not validated against previous or future releases.
:::
To use this Adapter, you need to install the following packages: To use this Adapter, you need to install the following packages:
```bash npm2yarn ```bash npm2yarn2pnpm
npm install next-auth @next-auth/typeorm-legacy-adapter typeorm npm install next-auth @next-auth/typeorm-legacy-adapter typeorm
``` ```
@@ -36,7 +40,7 @@ export default NextAuth({
}) })
``` ```
`TypeORMLegacyAdapter` takes either a connection string, or a [`ConnectionOptions`](https://github.com/typeorm/typeorm/blob/master/docs/connection-options.md) object as its first parameter. `TypeORMLegacyAdapter` takes either a connection string, or a [`DataSourceOptions`](https://github.com/typeorm/typeorm/blob/master/docs/data-source-options.md) object as its first parameter.
## Custom models ## Custom models
@@ -217,9 +221,9 @@ For example, you can add the naming convention option to the connection object i
import NextAuth from "next-auth" import NextAuth from "next-auth"
import { TypeORMLegacyAdapter } from "@next-auth/typeorm-legacy-adapter" import { TypeORMLegacyAdapter } from "@next-auth/typeorm-legacy-adapter"
import { SnakeNamingStrategy } from 'typeorm-naming-strategies' import { SnakeNamingStrategy } from 'typeorm-naming-strategies'
import { ConnectionOptions } from "typeorm"
const connection: ConnectionOptions = { export default NextAuth({
adapter: TypeORMLegacyAdapter({
type: "mysql", type: "mysql",
host: "localhost", host: "localhost",
port: 3306, port: 3306,
@@ -227,10 +231,7 @@ const connection: ConnectionOptions = {
password: "test", password: "test",
database: "test", database: "test",
namingStrategy: new SnakeNamingStrategy() namingStrategy: new SnakeNamingStrategy()
} }),
export default NextAuth({
adapter: TypeORMLegacyAdapter(connection),
... ...
}) })
``` ```

View File

@@ -7,7 +7,7 @@ title: Upstash Redis
To use this Adapter, you need to install `@upstash/redis` and `@next-auth/upstash-redis-adapter` package: To use this Adapter, you need to install `@upstash/redis` and `@next-auth/upstash-redis-adapter` package:
```bash npm2yarn ```bash npm2yarn2pnpm
npm install @upstash/redis @next-auth/upstash-redis-adapter npm install @upstash/redis @next-auth/upstash-redis-adapter
``` ```

View File

@@ -107,7 +107,7 @@ The redirect callback may be invoked more than once in the same flow.
This callback is called whenever a JSON Web Token is created (i.e. at sign This callback is called whenever a JSON Web Token is created (i.e. at sign
in) or updated (i.e whenever a session is accessed in the client). The returned value will be [encrypted](/configuration/options#jwt), and it is stored in a cookie. in) or updated (i.e whenever a session is accessed in the client). The returned value will be [encrypted](/configuration/options#jwt), and it is stored in a cookie.
Requests to `/api/auth/signin`, `/api/auth/session` and calls to `getSession()`, `useSession()` will invoke this function, but only if you are using a [JWT session](/configuration/options#session). This method is not invoked when you persist sessions in a database. Requests to `/api/auth/signin`, `/api/auth/session` and calls to `getSession()`, `unstable_getServerSession()`, `useSession()` will invoke this function, but only if you are using a [JWT session](/configuration/options#session). This method is not invoked when you persist sessions in a database.
- As with database persisted session expiry times, token expiry time is extended whenever a session is active. - As with database persisted session expiry times, token expiry time is extended whenever a session is active.
- The arguments _user_, _account_, _profile_ and _isNewUser_ are only passed the first time this callback is called on a new session, after the user signs in. In subsequent calls, only `token` will be available. - The arguments _user_, _account_, _profile_ and _isNewUser_ are only passed the first time this callback is called on a new session, after the user signs in. In subsequent calls, only `token` will be available.

View File

@@ -53,6 +53,7 @@ The message object will contain:
- `user`: The user object from your adapter. - `user`: The user object from your adapter.
- `account`: The object returned from the provider. - `account`: The object returned from the provider.
- `profile`: The object returned from the `profile` callback of the OAuth provider.
### session ### session

View File

@@ -1,5 +1,73 @@
# Next.js # Next.js
## `unstable_getServerSession`
:::warning
This feature is experimental and may be removed or changed in the future.
:::
When calling from server-side i.e. in API routes or in `getServerSideProps`, we recommend using this function instead of `getSession` to retrieve the `session` object. This method is especially useful when you are using NextAuth.js with a database. This method can _drastically_ reduce response time when used over `getSession` server-side, due to avoiding an extra `fetch` to an API Route (this is generally [not recommended in Next.js](https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props#getserversideprops-or-api-routes)). In addition, `unstable_getServerSession` will correctly update the cookie expiry time and update the session content if `callbacks.jwt` or `callbacks.session` changed something.
Otherwise, if you only want to get the session token, see [`getToken`](/tutorials/securing-pages-and-api-routes#using-gettoken).
`unstable_getServerSession` requires passing the same object you would pass to `NextAuth` when initializing NextAuth.js. To do so, you can export your NextAuth.js options in the following way:
In `[...nextauth.js]`:
```ts
import { NextAuth } from 'next-auth'
import type { NextAuthOptions } from 'next-auth'
export const authOptions: NextAuthOptions = {
// your configs
}
export default NextAuth(authOptions);
```
In `getServerSideProps`:
```js
import { authOptions } from 'pages/api/[...nextauth]'
import { unstable_getServerSession } from "next-auth/next"
export async function getServerSideProps(context) {
const session = await unstable_getServerSession(context.req, context.res, authOptions)
if (!session) {
return {
redirect: {
destination: '/',
permanent: false,
},
}
}
return {
props: {
session,
},
}
}
```
In API routes:
```js
import { authOptions } from 'pages/api/[...nextauth]'
import { unstable_getServerSession } from "next-auth/next"
export async function handler(req, res) {
const session = await unstable_getServerSession(req, res, authOptions)
if (!session) {
res.status(401).json({ message: "You must be logged in." });
return;
}
return res.json({
message: 'Success',
})
}
```
## Middleware ## Middleware
You can use a Next.js Middleware with NextAuth.js to protect your site. You can use a Next.js Middleware with NextAuth.js to protect your site.
@@ -12,56 +80,34 @@ You can get the `withAuth` middleware function from `next-auth/middleware` eithe
### Prerequisites ### Prerequisites
You must set the [`NEXTAUTH_SECRET`](/configuration/options#nextauth_secret) environment variable when using this middleware. If you are using the [`secret` option](/configuration/options#secret) this value must match. You must set the same secret in the middleware that you use in NextAuth. The easiest way is to set the [`NEXTAUTH_SECRET`](/configuration/options#nextauth_secret) environment variable. It will be picked up by both the [NextAuth config](/configuration/options#options), as well as the middleware config.
**We strongly recommend** replacing the `secret` value completely with this `NEXTAUTH_SECRET` environment variable. This environment variable will be picked up by both the [NextAuth config](/configuration/options#options), as well as the middleware config. Alternatively, you can provide the secret using the [`secret`](#secret) option in the middleware config.
**We strongly recommend** replacing the `secret` value completely with this `NEXTAUTH_SECRET` environment variable.
### Basic usage ### Basic usage
The most simple usage is when you want to require authentication for your entire site. You can add a `middleware.js` file with the following:
```js ```js
import withAuth from "next-auth/middleware" export { default } from "next-auth/middleware"
// or
import { withAuth } from "next-auth/middleware"
``` ```
### Custom JWT decode method That's it! Your application is now secured. 🎉
If you have custom jwt decode method set in `[...nextauth].ts`, you must also pass the same `decode` method to `withAuth` in order to read the custom-signed JWT correctly. You may want to extract the encode/decode logic to a separate function for consistency. If you only want to secure certain pages, export a `config` object with a `matcher`:
`[...nextauth].ts` ```js
```ts export { default } from "next-auth/middleware"
import jwt from "jsonwebtoken";
export default NextAuth({ export const config = { matcher: ["/dashboard"] }
providers: [...],
secret: /* Please use `process.env.NEXTAUTH_SECRET` */,
jwt: {
encode: async ({ secret, token }) => {
return jwt.sign(token as any, secret);
},
decode: async ({ secret, token }) => {
return jwt.verify(token as string, secret) as any;
},
},
})
``` ```
Any `_middleware.ts` Now you will still be able to visit every page, but only `/dashboard` will require authentication.
```ts
import withAuth from "next-auth/middleware" If a user is not logged in, the default behavior is to redirect them to the sign-in page.
import jwt from "jsonwebtoken";
export default withAuth({
jwt: {
decode: async ({ secret, token }) => {
return jwt.verify(token, secret) as any;
},
},
callbacks: {
authorized: ({ token }) => !!token,
},
})
```
--- ---
### `callbacks` ### `callbacks`
@@ -104,46 +150,40 @@ See the documentation for the [pages option](/configuration/pages) for more info
--- ---
### Examples ### `secret`
`withAuth` is very flexible, there are multiple ways to use it. - **Required**: _No_
#### Description
The same `secret` used in the [NextAuth config](/configuration/options#options).
#### Example (default value)
```js
secret: process.env.NEXTAUTH_SECRET
```
---
### Advanced usage
NextAuth.js Middleware is very flexible, there are multiple ways to use it.
:::note :::note
If you do not define the options, NextAuth.js will use the default values for the omitted options. If you do not define the options, NextAuth.js will use the default values for the omitted options.
::: :::
#### default re-export
```js title="pages/_middleware.js"
export { default } from "next-auth/middleware"
```
With this one line, when someone tries to load any of your pages, they will have to be logged-in first. Otherwise, they are redirected to the login page. It will assume that you are using the `NEXTAUTH_SECRET` environment variable.
#### default `withAuth` export
```js title="pages/admin/_middleware.js"
import { withAuth } from "next-auth/middleware"
export default withAuth({
callbacks: {
authorized: ({ token }) => token?.role === "admin",
},
})
```
With the above code, you just made sure that only user's with the `admin` role can access any of the pages under the `/admin` route. (Including nested routes as well, like `/admin/settings` etc.).
#### wrap middleware #### wrap middleware
```ts title="pages/admin/_middleware.ts" ```ts title="middleware.ts"
import type { NextRequest } from "next/server" import type { NextRequest } from "next/server"
import type { JWT } from "next-auth/jwt" import type { JWT } from "next-auth/jwt"
import { withAuth } from "next-auth/middleware" import { withAuth } from "next-auth/middleware"
export default withAuth( export default withAuth(
function middleware(req: NextRequest & { nextauth: { token: JWT } }) { // `withAuth` can augment your Request with the user's token.
function middleware(req: NextRequest & { nextauth: { token: JWT | null } }) {
console.log(req.nextauth.token) console.log(req.nextauth.token)
}, },
{ {
@@ -152,12 +192,53 @@ export default withAuth(
}, },
} }
) )
export const config = { matcher: ["/admin"] }
``` ```
The `middleware` function will only be invoked if the `authorized` callback returns `true`. The `middleware` function will only be invoked if the `authorized` callback returns `true`.
--- ---
#### Custom JWT decode method
If you have a custom jwt decode method set in `[...nextauth].ts`, you must also pass the same `decode` method to `withAuth` in order to read the custom-signed JWT correctly. You may want to extract the encode/decode logic to a separate function for consistency.
``
```ts title="/api/auth/[...nextauth].ts"
import type { NextAuthOptions } from "next-auth"
import NextAuth from "next-auth"
import jwt from "jsonwebtoken"
export const authOptions: NextAuthOptions = {
providers: [...],
jwt: {
async encode({ secret, token }) {
return jwt.sign(token, secret)
},
async decode({ secret, token }) {
return jwt.verify(token, secret)
},
},
}
export default NextAuth(authOptions)
```
And:
```ts title="middleware.ts"
import withAuth from "next-auth/middleware"
import { authOptions } from "pages/api/auth/[...nextauth]";
export default withAuth({
jwt: { decode: authOptions.jwt },
callbacks: {
authorized: ({ token }) => !!token,
},
})
```
### Caveats ### Caveats
- Currently only supports session verification, as parts of the sign-in code need to run in a Node.js environment. In the future, we would like to make sure that NextAuth.js can fully run at the [Edge](https://nextjs.org/docs/api-reference/edge-runtime) - Currently only supports session verification, as parts of the sign-in code need to run in a Node.js environment. In the future, we would like to make sure that NextAuth.js can fully run at the [Edge](https://nextjs.org/docs/api-reference/edge-runtime)

View File

@@ -13,19 +13,22 @@ When deploying to production, set the `NEXTAUTH_URL` environment variable to the
NEXTAUTH_URL=https://example.com NEXTAUTH_URL=https://example.com
``` ```
If your Next.js application uses a custom base path, specify the route to the API endpoint in full. If your Next.js application uses a custom base path, specify the route to the API endpoint in full. More informations about the usage of custom base path [here](/getting-started/client#custom-base-path).
_e.g. `NEXTAUTH_URL=https://example.com/custom-route/api/auth`_ _e.g. `NEXTAUTH_URL=https://example.com/custom-route/api/auth`_
:::tip
When you're using a custom base path, you will need to pass the `basePath` page prop to the `<SessionProvider>`. More informations [here](/getting-started/client#custom-base-path).
:::
:::note :::note
Using [System Environment Variables](https://vercel.com/docs/concepts/projects/environment-variables#system-environment-variables) we automatically detect when you deploy to [Vercel](https://vercel.com) so you don't have to define this variable. Make sure **Automatically expose System Environment Variables** is checked in your Project Settings. Using [System Environment Variables](https://vercel.com/docs/concepts/projects/environment-variables#system-environment-variables) we automatically detect when you deploy to [Vercel](https://vercel.com) so you don't have to define this variable. Make sure **Automatically expose System Environment Variables** is checked in your Project Settings.
::: :::
### NEXTAUTH_SECRET ### NEXTAUTH_SECRET
Used to encrypt the NextAuth.js JWT, and to hash [email verification tokens](/adapters/models#verification-token). This is the default value for the [`secret`](/configuration/options#secret) option. The `secret` option might be removed in the future in favor of this. Used to encrypt the NextAuth.js JWT, and to hash [email verification tokens](/adapters/models#verification-token). This is the default value for the `secret` option in [NextAuth](/configuration/options#secret) and [Middleware](/configuration/nextjs#secret).
If you are using [Middleware](/configuration/nextjs#prerequisites) this environment variable must be set.
### NEXTAUTH_URL_INTERNAL ### NEXTAUTH_URL_INTERNAL
@@ -366,11 +369,14 @@ Changes the color scheme theme of [pages](/configuration/pages) as well as allow
In addition, you can define a logo URL in `theme.logo` which will be rendered above the main card in the default signin/signout/error/verify-request pages, as well as a `theme.brandColor` which will affect the accent color of these pages. In addition, you can define a logo URL in `theme.logo` which will be rendered above the main card in the default signin/signout/error/verify-request pages, as well as a `theme.brandColor` which will affect the accent color of these pages.
The sign-in button's background color will match the `brandColor` and defaults to `"#346df1"`. The text color is `#fff` by default, but if your brand color gives a weak contrast, correct it with the `buttonText` color option.
```js ```js
theme: { theme: {
colorScheme: "auto", // "auto" | "dark" | "light" colorScheme: "auto", // "auto" | "dark" | "light"
brandColor: "", // Hex color code brandColor: "", // Hex color code
logo: "" // Absolute URL to image logo: "", // Absolute URL to image
buttonText: "" // Hex color code
} }
``` ```

View File

@@ -350,7 +350,7 @@ providers: [
## Built-in providers ## Built-in providers
NextAuth.js comes with a set of built-in providers. You can find them [here](https://github.com/nextauthjs/next-auth/tree/main/src/providers). Each built-in provider has its own documentation page: NextAuth.js comes with a set of built-in providers. You can find them [here](https://github.com/nextauthjs/next-auth/tree/main/packages/next-auth/src/providers). Each built-in provider has its own documentation page:
<div className="provider-name-list"> <div className="provider-name-list">
{Object.entries(require("../../../providers.json")) {Object.entries(require("../../../providers.json"))

View File

@@ -126,7 +126,7 @@ function Auth({ children }) {
// if `{ required: true }` is supplied, `status` can only be "loading" or "authenticated" // if `{ required: true }` is supplied, `status` can only be "loading" or "authenticated"
const { status } = useSession({ required: true }) const { status } = useSession({ required: true })
if (status === 'loading') { if (status === "loading") {
return <div>Loading...</div> return <div>Loading...</div>
} }
@@ -161,13 +161,19 @@ See repository [`README`](https://github.com/nextauthjs/react-query) for more de
## getSession() ## getSession()
- Client Side: **Yes** - Client Side: **Yes**
- Server Side: **Yes** - Server Side: **No** (See: [`unstable_getServerSession()`](/configuration/nextjs#unstable_getserversession)
NextAuth.js provides a `getSession()` method which can be called client or server side to return a session. NextAuth.js provides a `getSession()` helper which should be called **client side only** to return the current active session.
It calls `/api/auth/session` and returns a promise with a session object, or null if no session exists. On the server side, **this is still available to use**, however, we recommend using `unstable_getServerSession` going forward. The idea behind this is to avoid an additional unnecessary `fetch` call on the server side. For more information, please check out [this issue](https://github.com/nextauthjs/next-auth/issues/1535).
#### Client Side Example :::note
The `unstable_getServerSession` only has the prefix `unstable_` at the moment, because the API may change in the future. There are no known bugs at the moment and it is safe to use. If you discover any issues, please do report them as a [GitHub Issue](https://github.com/nextauthjs/next-auth/issues) and we will patch them as soon as possible.
:::
This helper is helpful in case you want to read the session outside of the context of React.
When called, `getSession()` will send a request to `/api/auth/session` and returns a promise with a [session object](https://github.com/nextauthjs/next-auth/blob/main/packages/next-auth/src/core/types.ts#L407-L425), or `null` if no session exists.
```js ```js
async function myFunction() { async function myFunction() {
@@ -176,23 +182,7 @@ async function myFunction() {
} }
``` ```
#### Server Side Example Read the tutorial [securing pages and API routes](/tutorials/securing-pages-and-api-routes) to know how to fetch the session in server side calls using `unstable_getServerSession()`.
```js
import { getSession } from "next-auth/react"
export default async (req, res) => {
const session = await getSession({ req })
/* ... */
res.end()
}
```
:::note
When calling `getSession()` server side, you need to pass `{req}` or `context` object.
:::
The tutorial [securing pages and API routes](/tutorials/securing-pages-and-api-routes) shows how to use `getSession()` in server side calls.
--- ---
@@ -254,7 +244,7 @@ export default async (req, res) => {
``` ```
:::note :::note
Unlike `getSession()` and `getCsrfToken()`, when calling `getProviders()` server side, you don't need to pass anything, just as calling it client side. Unlike and `getCsrfToken()`, when calling `getProviders()` server side, you don't need to pass anything, just as calling it client side.
::: :::
--- ---
@@ -436,14 +426,15 @@ If you pass the `session` page prop to the `<SessionProvider>` as in the exa
This only works on pages where you provide the correct `pageProps`, however. This is normally done in `getInitialProps` or `getServerSideProps` on an individual page basis like so: This only works on pages where you provide the correct `pageProps`, however. This is normally done in `getInitialProps` or `getServerSideProps` on an individual page basis like so:
```js title="pages/index.js" ```js title="pages/index.js"
import { getSession } from "next-auth/react" import { unstable_getServerSession } from "next-auth/next"
import { authOptions } from './api/auth/[...nextauth]'
... ...
export async function getServerSideProps(ctx) { export async function getServerSideProps({ req, res }) {
return { return {
props: { props: {
session: await getSession(ctx) session: await unstable_getServerSession(req, res, authOptions)
} }
} }
} }
@@ -455,7 +446,7 @@ If every one of your pages needs to be protected, you can do this in `getInitial
The session state is automatically synchronized across all open tabs/windows and they are all updated whenever they gain or lose focus or the state changes (e.g. a user signs in or out) when `refetchOnWindowFocus` is `true`. The session state is automatically synchronized across all open tabs/windows and they are all updated whenever they gain or lose focus or the state changes (e.g. a user signs in or out) when `refetchOnWindowFocus` is `true`.
If you have session expiry times of 30 days (the default) or more then you probably don't need to change any of the default options in the Provider. If you need to, you can trigger an update of the session object across all tabs/windows by calling `getSession()` from a client side function. If you have session expiry times of 30 days (the default) or more then you probably don't need to change any of the default options in the Provider. If you need to, you can trigger an update of the session object across all tabs/windows by calling [`getSession()`](/getting-started/client#getsession) from a client side function.
However, if you need to customize the session behavior and/or are using short session expiry times, you can pass options to the provider to customize the behavior of the `useSession()` hook. However, if you need to customize the session behavior and/or are using short session expiry times, you can pass options to the provider to customize the behavior of the `useSession()` hook.
@@ -515,3 +506,29 @@ However, if it was set to `false`, it stops re-fetching the session and the comp
:::note :::note
See [**the Next.js documentation**](https://nextjs.org/docs/advanced-features/custom-app) for more information on **\_app.js** in Next.js applications. See [**the Next.js documentation**](https://nextjs.org/docs/advanced-features/custom-app) for more information on **\_app.js** in Next.js applications.
::: :::
### Custom base path
When your Next.js application uses a custom base path, set the `NEXTAUTH_URL` environment variable to the route to the API endpoint in full - as in the example below and as explained [here](/configuration/options#nextauth_url).
Also, make sure to pass the `basePath` page prop to the `<SessionProvider>` as in the example below so your custom base path is fully configured and used by NextAuth.js.
#### Example
In this example, the custom base path used is `/custom-route`.
```
NEXTAUTH_URL=https://example.com/custom-route/api/auth
```
```jsx title="pages/_app.js"
import { SessionProvider } from "next-auth/react"
export default function App({
Component,
pageProps: { session, ...pageProps },
}) {
return (
<SessionProvider session={session} basePath="/custom-route/api/auth">
<Component {...pageProps} />
</SessionProvider>
)
}
```

View File

@@ -93,13 +93,14 @@ You can use the `useSession` hook from anywhere in your application (e.g. in a h
### Backend - API Route ### Backend - API Route
To protect an API Route, you can use the [`getSession()`](/getting-started/client#getsession) method in the NextAuth.js client. To protect an API Route, you can use the [`unstable_getServerSession()`](/configuration/nextjs#unstable_getserversession) method.
```javascript title="pages/api/restricted.js" showLineNumbers ```javascript title="pages/api/restricted.js" showLineNumbers
import { getSession } from "next-auth/react" import { unstable_getServerSession } from "next-auth/next"
import { authOptions } from "./api/auth/[...nextauth]"
export default async (req, res) => { export default async (req, res) => {
const session = await getSession({ req }) const session = await unstable_getServerSession(req, res, authOptions)
if (session) { if (session) {
res.send({ res.send({
@@ -143,7 +144,7 @@ callbacks: {
... ...
``` ```
Now whenever you call `getSession` or `useSession`, the data object which is returned will include the `accessToken` value. Now whenever you call [`getSession`](/getting-started/client#getsession) or [`useSession`](/getting-started/client#usesession), the data object which is returned will include the `accessToken` value.
```jsx title="components/accessToken.jsx" showLineNumbers ```jsx title="components/accessToken.jsx" showLineNumbers
import { useSession, signIn, signOut } from "next-auth/react" import { useSession, signIn, signOut } from "next-auth/react"

View File

@@ -13,7 +13,7 @@ We encourage users to try it out and report any and all issues they come across.
You can upgrade to the new version by running: You can upgrade to the new version by running:
```bash npm2yarn ```bash npm2yarn2pnpm
npm install next-auth npm install next-auth
``` ```

View File

@@ -124,67 +124,74 @@ providers: [
The following code shows the complete source for the built-in `sendVerificationRequest()` method: The following code shows the complete source for the built-in `sendVerificationRequest()` method:
```js ```js
import nodemailer from "nodemailer" import { createTransport } from "nodemailer"
async function sendVerificationRequest({ async function sendVerificationRequest(params) {
identifier: email, const { identifier, url, provider, theme } = params
url,
provider: { server, from },
}) {
const { host } = new URL(url) const { host } = new URL(url)
const transport = nodemailer.createTransport(server) // NOTE: You are not required to use `nodemailer`, use whatever you want.
await transport.sendMail({ const transport = createTransport(provider.server)
to: email, const result = await transport.sendMail({
from, to: identifier,
from: provider.from,
subject: `Sign in to ${host}`, subject: `Sign in to ${host}`,
text: text({ url, host }), text: text({ url, host }),
html: html({ url, host, email }), html: html({ url, host, theme }),
}) })
const failed = result.rejected.concat(result.pending).filter(Boolean)
if (failed.length) {
throw new Error(`Email(s) (${failed.join(", ")}) could not be sent`)
}
} }
// Email HTML body /**
function html({ url, host, email }: Record<"url" | "host" | "email", string>) { * Email HTML body
// Insert invisible space into domains and email address to prevent both the * Insert invisible space into domains from being turned into a hyperlink by email
// email address and the domain from being turned into a hyperlink by email * clients like Outlook and Apple mail, as this is confusing because it seems
// clients like Outlook and Apple mail, as this is confusing because it seems * like they are supposed to click on it to sign in.
// like they are supposed to click on their email address to sign in. *
const escapedEmail = `${email.replace(/\./g, "&#8203;.")}` * @note We don't add the email address to avoid needing to escape it, if you do, remember to sanitize it!
const escapedHost = `${host.replace(/\./g, "&#8203;.")}` */
function html(params: { url: string; host: string; theme: Theme }) {
const { url, host, theme } = params
// Some simple styling options const escapedHost = host.replace(/\./g, "&#8203;.")
const backgroundColor = "#f9f9f9"
const textColor = "#444444" const brandColor = theme.brandColor || "#346df1"
const mainBackgroundColor = "#ffffff" const color = {
const buttonBackgroundColor = "#346df1" background: "#f9f9f9",
const buttonBorderColor = "#346df1" text: "#444",
const buttonTextColor = "#ffffff" mainBackground: "#fff",
buttonBackground: brandColor,
buttonBorder: brandColor,
buttonText: theme.buttonText || "#fff",
}
return ` return `
<body style="background: ${backgroundColor};"> <body style="background: ${color.background};">
<table width="100%" border="0" cellspacing="0" cellpadding="0"> <table width="100%" border="0" cellspacing="20" cellpadding="0"
style="background: ${color.mainBackground}; max-width: 600px; margin: auto; border-radius: 10px;">
<tr> <tr>
<td align="center" style="padding: 10px 0px 20px 0px; font-size: 22px; font-family: Helvetica, Arial, sans-serif; color: ${textColor};"> <td align="center"
<strong>${escapedHost}</strong> style="padding: 10px 0px; font-size: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};">
</td> Sign in to <strong>${escapedHost}</strong>
</tr>
</table>
<table width="100%" border="0" cellspacing="20" cellpadding="0" style="background: ${mainBackgroundColor}; max-width: 600px; margin: auto; border-radius: 10px;">
<tr>
<td align="center" style="padding: 10px 0px 0px 0px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; color: ${textColor};">
Sign in as <strong>${escapedEmail}</strong>
</td> </td>
</tr> </tr>
<tr> <tr>
<td align="center" style="padding: 20px 0;"> <td align="center" style="padding: 20px 0;">
<table border="0" cellspacing="0" cellpadding="0"> <table border="0" cellspacing="0" cellpadding="0">
<tr> <tr>
<td align="center" style="border-radius: 5px;" bgcolor="${buttonBackgroundColor}"><a href="${url}" target="_blank" style="font-size: 18px; font-family: Helvetica, Arial, sans-serif; color: ${buttonTextColor}; text-decoration: none; border-radius: 5px; padding: 10px 20px; border: 1px solid ${buttonBorderColor}; display: inline-block; font-weight: bold;">Sign in</a></td> <td align="center" style="border-radius: 5px;" bgcolor="${color.buttonBackground}"><a href="${url}"
target="_blank"
style="font-size: 18px; font-family: Helvetica, Arial, sans-serif; color: ${color.buttonText}; text-decoration: none; border-radius: 5px; padding: 10px 20px; border: 1px solid ${color.buttonBorder}; display: inline-block; font-weight: bold;">Sign
in</a></td>
</tr> </tr>
</table> </table>
</td> </td>
</tr> </tr>
<tr> <tr>
<td align="center" style="padding: 0px 0px 10px 0px; font-size: 16px; line-height: 22px; font-family: Helvetica, Arial, sans-serif; color: ${textColor};"> <td align="center"
style="padding: 0px 0px 10px 0px; font-size: 16px; line-height: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};">
If you did not request this email you can safely ignore it. If you did not request this email you can safely ignore it.
</td> </td>
</tr> </tr>
@@ -193,8 +200,8 @@ function html({ url, host, email }: Record<"url" | "host" | "email", string>) {
` `
} }
// Email Text body (fallback for email clients that don't render HTML, e.g. feature phones) /** Email Text body (fallback for email clients that don't render HTML, e.g. feature phones) */
function text({ url, host }: Record<"url" | "host", string>) { function text({ url, host }: { url: string; host: string }) {
return `Sign in to ${host}\n${url}\n\n` return `Sign in to ${host}\n${url}\n\n`
} }
``` ```

View File

@@ -19,7 +19,7 @@ https://github.com/settings/apps
The **GitHub Provider** comes with a set of default options: The **GitHub Provider** comes with a set of default options:
- [GitHub Provider options](https://github.com/nextauthjs/next-auth/blob/main/packages/next-auth/src/providers/github.js) - [GitHub Provider options](https://github.com/nextauthjs/next-auth/blob/main/packages/next-auth/src/providers/github.ts)
You can override any of the options to suit your own use case. You can override any of the options to suit your own use case.
@@ -30,8 +30,8 @@ import GitHubProvider from "next-auth/providers/github";
... ...
providers: [ providers: [
GitHubProvider({ GitHubProvider({
clientId: process.env.GITHUB_CLIENT_ID, clientId: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET clientSecret: process.env.GITHUB_SECRET
}) })
] ]
... ...

View File

@@ -5,7 +5,7 @@ title: Overview
Authentication Providers in **NextAuth.js** are services that can be used to sign in a user. Authentication Providers in **NextAuth.js** are services that can be used to sign in a user.
There's four ways a user can be signed in: There are four ways a user can be signed in:
- [Using a built-in OAuth Provider](/configuration/providers/oauth) (e.g Github, Twitter, Google, etc...) - [Using a built-in OAuth Provider](/configuration/providers/oauth) (e.g Github, Twitter, Google, etc...)
- [Using a custom OAuth Provider](/configuration/providers/oauth#using-a-custom-provider) - [Using a custom OAuth Provider](/configuration/providers/oauth#using-a-custom-provider)

View File

@@ -13,7 +13,7 @@ https://api.slack.com/docs/sign-in-with-slack
https://api.slack.com/apps https://api.slack.com/apps
:::warning :::warning
Slack requires you that the redirect URL of your app uses `https`, even for local development. An easy workaround for this is using a service like [`ngrok`](https://ngrok.com) that creates a secure tunnel to your app, using `https`. Remember to set the url as `NEXTAUTH_URL` as well. Slack requires that the redirect URL of your app uses `https`, even for local development. An easy workaround for this is using a service like [`ngrok`](https://ngrok.com) that creates a secure tunnel to your app, using `https`. Remember to set the url as `NEXTAUTH_URL` as well.
::: :::
![](https://i.imgur.com/ydYKTLD.png) ![](https://i.imgur.com/ydYKTLD.png)

View File

@@ -0,0 +1,50 @@
---
id: wikimedia
title: Wikimedia
---
## Documentation
https://www.mediawiki.org/wiki/Extension:OAuth
This provider also supports all Wikimedia projects:
- Wikipedia
- Wikidata
- Wikibooks
- Wiktionary
- etc..
Please be aware that Wikimedia accounts do not have to have an associated email address. So you may want to add check if the user has an email address before allowing them to login.
## Configuration
1. Go to and accept the Consumer Registration doc: https://meta.wikimedia.org/wiki/Special:OAuthConsumerRegistration
2. Request a new OAuth 2.0 consumer to get the `clientId` and `clientSecret`: https://meta.wikimedia.org/wiki/Special:OAuthConsumerRegistration/propose/oauth2
2a. Add the following redirect URL into the console `http://<your-next-app-url>/api/auth/callback/wikimedia`
2b. Do not check the box next to `This consumer is only for [your username]`
2c. Unless you explicitly need a larger scope, feel free to select the radio button labelled `User identity verification only - no ability to read pages or act on the users behalf.`
After registration, you can initally test your application only with your own Wikimedia account. You may have to wait several days for the application to be approved for it to be used by everyone.
## Options
The **Wikimedia Provider** comes with a set of default options:
- [Wikimedia Provider options](https://github.com/nextauthjs/next-auth/blob/main/packages/next-auth/src/providers/wikimedia.ts)
You can override any of the options to suit your own use case.
## Example
```js
import WikimediaProvider from "next-auth/providers/wikimedia";
...
providers: [
WikimediaProvider({
clientId: process.env.WIKIMEDIA_CLIENT_ID,
clientSecret: process.env.WIKIMEDIA_CLIENT_SECRET
})
]
...
```

View File

@@ -15,7 +15,7 @@ https://dashboard.workos.com
The **WorkOS Provider** comes with a set of default options: The **WorkOS Provider** comes with a set of default options:
- [WorkOS Provider options](https://github.com/nextauthjs/next-auth/blob/main/packages/next-auth/src/providers/workos.js) - [WorkOS Provider options](https://github.com/nextauthjs/next-auth/blob/main/packages/next-auth/src/providers/workos.ts)
You can override any of the options to suit your own use case. You can override any of the options to suit your own use case.

View File

@@ -7,7 +7,7 @@ NextAuth.js provides the ability to setup a [custom Credential provider](/config
You will need an additional dependency, `ldapjs`, which you can install by running You will need an additional dependency, `ldapjs`, which you can install by running
```bash npm2yarn ```bash npm2yarn2pnpm
npm install ldapjs npm install ldapjs
``` ```

View File

@@ -40,12 +40,29 @@ export default function Page() {
} }
``` ```
### Next.js (Middleware)
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"
```
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](https://next-auth.js.org/configuration/options#session).
More details can be found [here](https://next-auth.js.org/configuration/nextjs#middleware).
### Server Side ### Server Side
You can protect server side rendered pages using the `getSession()` method. 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`.
```js title="pages/server-side-example.js" ```js title="pages/server-side-example.js"
import { useSession, getSession } from "next-auth/react" import { useSession, unstable_getServerSession } from "next-auth/next"
import { authOptions } from "./api/auth/[...nextauth]"
export default function Page() { export default function Page() {
const { data: session } = useSession() const { data: session } = useSession()
@@ -66,7 +83,11 @@ export default function Page() {
export async function getServerSideProps(context) { export async function getServerSideProps(context) {
return { return {
props: { props: {
session: await getSession(context), session: await unstable_getServerSession(
context.req,
context.res,
authOptions
),
}, },
} }
} }
@@ -94,15 +115,16 @@ export default function App({
## Securing API Routes ## Securing API Routes
### Using getSession() ### Using unstable_getServerSession()
You can protect API routes using the `getSession()` method. You can protect API routes using the `unstable_getServerSession()` method.
```js title="pages/api/get-session-example.js" ```js title="pages/api/get-session-example.js"
import { getSession } from "next-auth/react" import { unstable_getServerSession } from "next-auth/next"
import { authOptions } from "./api/auth/[...nextauth]"
export default async (req, res) => { export default async (req, res) => {
const session = await getSession({ req }) const session = await unstable_getServerSession(req, res, authOptions)
if (session) { if (session) {
// Signed in // Signed in
console.log("Session", JSON.stringify(session, null, 2)) console.log("Session", JSON.stringify(session, null, 2))

View File

@@ -9,7 +9,7 @@ To test an implementation of NextAuth.js, we encourage you to use [Cypress](http
To get started, install the dependencies: To get started, install the dependencies:
```bash npm2yarn ```bash npm2yarn2pnpm
npm install --save-dev cypress cypress-social-logins @testing-library/cypress npm install --save-dev cypress cypress-social-logins @testing-library/cypress
``` ```

View File

@@ -33,6 +33,10 @@ In development, we generate a `secret` based on your configuration for convenien
Twitter OAuth 2.0 is currently in beta as certain changes might still be necessary. This is not covered by semver. See the docs https://next-auth.js.org/providers/twitter#oauth-2 Twitter OAuth 2.0 is currently in beta as certain changes might still be necessary. This is not covered by semver. See the docs https://next-auth.js.org/providers/twitter#oauth-2
#### EXPERIMENTAL_API
Some APIs are still experimental; they may be changed or removed in the future. Use at your own risk.
## Adapter ## Adapter
### ADAPTER_TYPEORM_UPDATING_ENTITIES ### ADAPTER_TYPEORM_UPDATING_ENTITIES

View File

@@ -155,9 +155,9 @@ module.exports = {
showLastUpdateAuthor: true, showLastUpdateAuthor: true,
showLastUpdateTime: true, showLastUpdateTime: true,
remarkPlugins: [ remarkPlugins: [
require("@sapphire/docusaurus-plugin-npm2yarn2pnpm").npm2yarn2pnpm,
require("remark-github"), require("remark-github"),
require("mdx-mermaid"), require("mdx-mermaid"),
[require("@docusaurus/remark-plugin-npm2yarn"), { sync: true }],
], ],
versions: { versions: {
current: { current: {

View File

@@ -19,11 +19,11 @@
"generate-providers": "node ./scripts/generate-providers.js" "generate-providers": "node ./scripts/generate-providers.js"
}, },
"dependencies": { "dependencies": {
"@docusaurus/core": "^2.0.0-beta.20", "@docusaurus/core": "^2.0.0-beta.21",
"@docusaurus/preset-classic": "^2.0.0-beta.20", "@docusaurus/preset-classic": "^2.0.0-beta.21",
"@docusaurus/remark-plugin-npm2yarn": "^2.0.0-beta.20", "@docusaurus/theme-common": "2.0.0-beta.21",
"@docusaurus/theme-common": "2.0.0-beta.20",
"@mdx-js/react": "1.6.22", "@mdx-js/react": "1.6.22",
"@sapphire/docusaurus-plugin-npm2yarn2pnpm": "1.1.3",
"classnames": "^2.3.1", "classnames": "^2.3.1",
"mdx-mermaid": "^1.2.2", "mdx-mermaid": "^1.2.2",
"mermaid": "^9.0.1", "mermaid": "^9.0.1",

View File

@@ -1,4 +1,23 @@
{ {
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "X-Content-Type-Options",
"value": "nosniff"
},
{
"key": "X-Frame-Options",
"value": "DENY"
},
{
"key": "X-XSS-Protection",
"value": "1; mode=block"
}
]
}
],
"redirects": [ "redirects": [
{ {
"source": "/schemas/models", "source": "/schemas/models",

View File

@@ -181,7 +181,7 @@ If you think your custom provider might be useful to others, we encourage you to
You only need to add two changes: You only need to add two changes:
1. Add your config: [`src/providers/{provider}.js`](https://github.com/nextauthjs/next-auth/tree/main/src/providers)<br /> 1. Add your config: [`src/providers/{provider}.js`](https://github.com/nextauthjs/next-auth/tree/main/packages/next-auth/src/providers)<br />
• make sure you use a named default export, like this: `export default function YourProvider` • make sure you use a named default export, like this: `export default function YourProvider`
2. Add provider documentation: [`www/docs/providers/{provider}.md`](https://github.com/nextauthjs/next-auth/tree/ead715219a5d7a6e882a6ba27fa56b03954d062d/www/docs/providers) 2. Add provider documentation: [`www/docs/providers/{provider}.md`](https://github.com/nextauthjs/next-auth/tree/ead715219a5d7a6e882a6ba27fa56b03954d062d/www/docs/providers)
3. Add it to our [provider types](https://github.com/nextauthjs/next-auth/blob/ead715219a5d7a6e882a6ba27fa56b03954d062d/types/providers.d.ts) (for TS projects)<br /> 3. Add it to our [provider types](https://github.com/nextauthjs/next-auth/blob/ead715219a5d7a6e882a6ba27fa56b03954d062d/types/providers.d.ts) (for TS projects)<br />

View File

@@ -0,0 +1,53 @@
---
id: duende-identityserver6
title: DuendeIdentityServer6
---
## Documentation
https://docs.duendesoftware.com/identityserver/v6
## Options
The **DuendeIdentityServer6 Provider** comes with a set of default options:
- [DuendeIdentityServer6 Provider options](https://github.com/nextauthjs/next-auth/tree/main/packages/next-auth/src/providers/duende-identity-server6.ts)
You can override any of the options to suit your own use case.
## Example
```js
import DuendeIDS6Provider from "next-auth/providers/duende-identity-server6"
...
providers: [
DuendeIDS6Provider({
clientId: process.env.DUENDE_IDS6_ID,
clientSecret: process.env.DUENDE_IDS6_SECRET,
issuer: process.env.DUENDE_IDS6_ISSUER,
})
]
...
```
## Demo IdentityServer
The configuration below is for the demo server at https://demo.duendesoftware.com/
If you want to try it out, you can copy and paste the configuration below.
You can sign in to the demo service with either <b>bob/bob</b> or <b>alice/alice</b>.
```js
import DuendeIDS6Provider from "next-auth/providers/duende-identity-server6"
...
providers: [
DuendeIDS6Provider({
clientId: "interactive.confidential",
clientSecret: "secret",
issuer: "https://demo.duendesoftware.com",
})
]
...
```

View File

@@ -15,7 +15,7 @@ https://github.com/settings/apps
The **Github Provider** comes with a set of default options: The **Github Provider** comes with a set of default options:
- [Github Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/github.js) - [Github Provider options](https://github.com/nextauthjs/next-auth/blob/main/src/providers/github.ts)
You can override any of the options to suit your own use case. You can override any of the options to suit your own use case.

View File

@@ -54,6 +54,6 @@ providers: [
clientSecret: "secret", clientSecret: "secret",
protection: "pkce" protection: "pkce"
}) })
} ]
... ...
``` ```

View File

@@ -36,8 +36,9 @@
"pretty-quick": "^3.1.2", "pretty-quick": "^3.1.2",
"semver": "7.3.5", "semver": "7.3.5",
"stream-to-array": "2.3.0", "stream-to-array": "2.3.0",
"ts-node": "10.5.0", "ts-node": "10.8.2",
"turbo": "^1.2.5", "turbo": "1.3.1",
"type-fest": "2.16.0",
"typescript": "^4.5.2" "typescript": "^4.5.2"
}, },
"engines": { "engines": {
@@ -97,7 +98,7 @@
"**/tests", "**/tests",
"**/__tests__" "**/__tests__"
], ],
"packageManager": "pnpm@6.32.8", "packageManager": "pnpm@7.5.1",
"funding": [ "funding": [
{ {
"type": "github", "type": "github",

View File

@@ -6,7 +6,7 @@
Open Source. Full Stack. Own Your Data. Open Source. Full Stack. Own Your Data.
</p> </p>
<!-- <p align="center" style="align: center;"> <!-- <p align="center" style="align: center;">
<img src="https://github.com/nextauthjs/adapters/actions/workflows/release.yml/badge.svg" alt="CI Test" /> <img src="https://github.com/nextauthjs/next-auth/actions/workflows/release.yml/badge.svg?branch=main" alt="CI Test" />
<img src="https://img.shields.io/bundlephobia/minzip/@next-auth/prisma-adapter" alt="Bundle Size"/> <img src="https://img.shields.io/bundlephobia/minzip/@next-auth/prisma-adapter" alt="Bundle Size"/>
<img src="https://img.shields.io/npm/v/@next-auth/prisma-adapter" alt="@next-auth/prisma-adapter Version" /> <img src="https://img.shields.io/npm/v/@next-auth/prisma-adapter" alt="@next-auth/prisma-adapter Version" />
</p> --> </p> -->
@@ -150,7 +150,7 @@ type User
## Contributing ## Contributing
We're open to all community contributions! If you'd like to contribute in any way, please read our [Contributing Guide](https://github.com/nextauthjs/adapters/blob/main/CONTRIBUTING.md). We're open to all community contributions! If you'd like to contribute in any way, please read our [Contributing Guide](https://github.com/nextauthjs/next-auth/blob/main/CONTRIBUTING.md).
## License ## License

View File

@@ -3,9 +3,9 @@
"version": "1.0.3", "version": "1.0.3",
"description": "Dgraph adapter for next-auth.", "description": "Dgraph adapter for next-auth.",
"homepage": "https://next-auth.js.org", "homepage": "https://next-auth.js.org",
"repository": "https://github.com/nextauthjs/adapters", "repository": "https://github.com/nextauthjs/next-auth",
"bugs": { "bugs": {
"url": "https://github.com/nextauthjs/adapters/issues" "url": "https://github.com/nextauthjs/next-auth/issues"
}, },
"author": "Arnaud Derbey <arnaud@derbey.dev>", "author": "Arnaud Derbey <arnaud@derbey.dev>",
"contributors": [], "contributors": [],

View File

@@ -6,7 +6,7 @@
Open Source. Full Stack. Own Your Data. Open Source. Full Stack. Own Your Data.
</p> </p>
<p align="center" style="align: center;"> <p align="center" style="align: center;">
<img src="https://github.com/nextauthjs/adapters/actions/workflows/release.yml/badge.svg" alt="Build Test" /> <img src="https://github.com/nextauthjs/next-auth/actions/workflows/release.yml/badge.svg?branch=main" alt="Build Test" />
<img src="https://img.shields.io/bundlephobia/minzip/@next-auth/dynamodb-adapter/latest" alt="Bundle Size"/> <img src="https://img.shields.io/bundlephobia/minzip/@next-auth/dynamodb-adapter/latest" alt="Bundle Size"/>
<img src="https://img.shields.io/npm/v/@next-auth/dynamodb-adapter" alt="@next-auth/dynamodb-adapter Version" /> <img src="https://img.shields.io/npm/v/@next-auth/dynamodb-adapter" alt="@next-auth/dynamodb-adapter Version" />
</p> </p>
@@ -96,7 +96,7 @@ Here is a schema of the table :
## Contributing ## Contributing
We're open to all community contributions! If you'd like to contribute in any way, please read our [Contributing Guide](https://github.com/nextauthjs/adapters/blob/main/CONTRIBUTING.md). We're open to all community contributions! If you'd like to contribute in any way, please read our [Contributing Guide](https://github.com/nextauthjs/next-auth/blob/main/CONTRIBUTING.md).
## License ## License

View File

@@ -1,6 +1,6 @@
{ {
"name": "@next-auth/dynamodb-adapter", "name": "@next-auth/dynamodb-adapter",
"repository": "https://github.com/nextauthjs/adapters", "repository": "https://github.com/nextauthjs/next-auth",
"version": "1.0.3", "version": "1.0.3",
"description": "AWS DynamoDB adapter for next-auth.", "description": "AWS DynamoDB adapter for next-auth.",
"keywords": [ "keywords": [

View File

@@ -7,7 +7,7 @@
Open Source. Full Stack. Own Your Data. Open Source. Full Stack. Own Your Data.
</p> </p>
<p align="center" style="align: center;"> <p align="center" style="align: center;">
<img src="https://github.com/nextauthjs/adapters/actions/workflows/release.yml/badge.svg" alt="Build Test" /> <img src="https://github.com/nextauthjs/next-auth/actions/workflows/release.yml/badge.svg?branch=main" alt="Build Test" />
<a href="https://www.npmjs.com/package/@next-auth/faunadb-adapter" target="_blank"><img src="https://img.shields.io/bundlephobia/minzip/@next-auth/fauna-adapter/next" alt="Bundle Size"/></a> <a href="https://www.npmjs.com/package/@next-auth/faunadb-adapter" target="_blank"><img src="https://img.shields.io/bundlephobia/minzip/@next-auth/fauna-adapter/next" alt="Bundle Size"/></a>
<a href="https://www.npmjs.com/package/@next-auth/faunadb-adapter" target="_blank"><img src="https://img.shields.io/npm/v/@next-auth/fauna-adapter/next" alt="@next-auth/fauna-adapter Version" /></a> <a href="https://www.npmjs.com/package/@next-auth/faunadb-adapter" target="_blank"><img src="https://img.shields.io/npm/v/@next-auth/fauna-adapter/next" alt="@next-auth/fauna-adapter Version" /></a>
</p> </p>
@@ -53,7 +53,7 @@ export default NextAuth({
## Contributing ## Contributing
We're open to all community contributions! If you'd like to contribute in any way, please read our [Contributing Guide](https://github.com/nextauthjs/adapters/blob/main/CONTRIBUTING.md). We're open to all community contributions! If you'd like to contribute in any way, please read our [Contributing Guide](https://github.com/nextauthjs/next-auth/blob/main/CONTRIBUTING.md).
## License ## License

View File

@@ -3,7 +3,7 @@
"version": "1.0.3", "version": "1.0.3",
"description": "Fauna Adapter for NextAuth", "description": "Fauna Adapter for NextAuth",
"homepage": "https://next-auth.js.org", "homepage": "https://next-auth.js.org",
"repository": "https://github.com/nextauthjs/adapters", "repository": "https://github.com/nextauthjs/next-auth",
"bugs": { "bugs": {
"url": "https://github.com/nextauthjs/next-auth/issues" "url": "https://github.com/nextauthjs/next-auth/issues"
}, },

View File

@@ -7,7 +7,7 @@
Open Source. Full Stack. Own Your Data. Open Source. Full Stack. Own Your Data.
</p> </p>
<p align="center" style="align: center;"> <p align="center" style="align: center;">
<img src="https://github.com/nextauthjs/adapters/actions/workflows/release.yml/badge.svg" alt="Build Test" /> <img src="https://github.com/nextauthjs/next-auth/actions/workflows/release.yml/badge.svg?branch=main" alt="Build Test" />
<img src="https://img.shields.io/bundlephobia/minzip/@next-auth/firebase-adapter/latest" alt="Bundle Size"/> <img src="https://img.shields.io/bundlephobia/minzip/@next-auth/firebase-adapter/latest" alt="Bundle Size"/>
<img src="https://img.shields.io/npm/v/@next-auth/firebase-adapter" alt="@next-auth/firebase-adapter Version" /> <img src="https://img.shields.io/npm/v/@next-auth/firebase-adapter" alt="@next-auth/firebase-adapter Version" />
</p> </p>
@@ -32,14 +32,13 @@ npm install next-auth @next-auth/firebase-adapter
```js ```js
import NextAuth from "next-auth" import NextAuth from "next-auth"
import Providers from "next-auth/providers" import Providers from "next-auth/providers"
import { FirebaseAdapter } from "@next-auth/firebase-adapter" import { FirestoreAdapter } from "@next-auth/firebase-adapter"
import firebase from "firebase/app" import { initializeApp } from "firebase/app";
import "firebase/firestore" import { getFirestore } from "firebase/firestore"
const firestore = ( const app = initializeApp({ projectId: "next-auth-test" });
firebase.apps[0] ?? firebase.initializeApp(/* your config */) const firestore = getFirestore(app);
).firestore()
// For more information on each option (and a full list of options) go to // For more information on each option (and a full list of options) go to
// https://next-auth.js.org/configuration/options // https://next-auth.js.org/configuration/options
@@ -51,7 +50,7 @@ export default NextAuth({
clientSecret: process.env.GOOGLE_SECRET, clientSecret: process.env.GOOGLE_SECRET,
}), }),
], ],
adapter: FirebaseAdapter(firestore), adapter: FirestoreAdapter(firestore),
... ...
}) })
``` ```
@@ -83,7 +82,7 @@ See [firebase.google.com/docs/web/setup](https://firebase.google.com/docs/web/se
## Contributing ## Contributing
We're open to all community contributions! If you'd like to contribute in any way, please read our [Contributing Guide](https://github.com/nextauthjs/adapters/blob/main/CONTRIBUTING.md). We're open to all community contributions! If you'd like to contribute in any way, please read our [Contributing Guide](https://github.com/nextauthjs/next-auth/blob/main/CONTRIBUTING.md).
## License ## License

View File

@@ -1 +1 @@
module.exports = require("@next-auth/adapter-test/jest.config") module.exports = require("@next-auth/adapter-test/jest/jest-preset")

View File

@@ -1,11 +1,11 @@
{ {
"name": "@next-auth/firebase-adapter", "name": "@next-auth/firebase-adapter",
"version": "0.1.3", "version": "1.0.0",
"description": "Firebase adapter for next-auth.", "description": "Firebase adapter for next-auth.",
"homepage": "https://next-auth.js.org", "homepage": "https://next-auth.js.org",
"repository": "https://github.com/nextauthjs/adapters", "repository": "https://github.com/nextauthjs/next-auth",
"bugs": { "bugs": {
"url": "https://github.com/nextauthjs/adapters/issues" "url": "https://github.com/nextauthjs/next-auth/issues"
}, },
"author": "Ron Houben <ron.houben85@gmail.com>", "author": "Ron Houben <ron.houben85@gmail.com>",
"contributors": [ "contributors": [
@@ -28,18 +28,18 @@
"access": "public" "access": "public"
}, },
"scripts": { "scripts": {
"build:wip": "tsc", "build": "tsc",
"test:wip": "FIRESTORE_EMULATOR_HOST=localhost:8080 firebase emulators:exec --only firestore --project next-auth-test jest" "test": "FIRESTORE_EMULATOR_HOST=localhost:8080 firebase emulators:exec --only firestore --project next-auth-test jest"
}, },
"peerDependencies": { "peerDependencies": {
"firebase": "^8.6.2", "firebase": "^9.7.0",
"next-auth": "workspace:*" "next-auth": "workspace:*"
}, },
"devDependencies": { "devDependencies": {
"@next-auth/adapter-test": "workspace:^0.0.0", "@next-auth/adapter-test": "workspace:^0.0.0",
"@next-auth/tsconfig": "workspace:^0.0.0", "@next-auth/tsconfig": "workspace:^0.0.0",
"firebase": "^8.6.2", "firebase": "^9.7.0",
"firebase-tools": "^9.11.0", "firebase-tools": "^10.7.2",
"jest": "^27.4.3", "jest": "^27.4.3",
"next-auth": "workspace:*" "next-auth": "workspace:*"
} }

View File

@@ -0,0 +1,58 @@
import { Timestamp } from "firebase/firestore"
import type {
FirestoreDataConverter,
QueryDocumentSnapshot,
WithFieldValue,
} from "firebase/firestore"
const isTimestamp = (value: unknown): value is Timestamp =>
typeof value === "object" && value !== null && value instanceof Timestamp
interface GetConverterOptions {
excludeId?: boolean
}
export const getConverter = <Document extends Record<string, unknown>>(
options?: GetConverterOptions
): FirestoreDataConverter<Document> => ({
// `PartialWithFieldValue` implicitly types `object` as `any`, so we want to explicitly type it
toFirestore(object: WithFieldValue<Document>) {
const document: Record<string, unknown> = {}
Object.keys(object).forEach((key) => {
if (object[key] !== undefined) {
document[key] = object[key]
}
})
return document
},
// We need to explicitly type `snapshot` since it uses `DocumentData` for generic type
fromFirestore(snapshot: QueryDocumentSnapshot<Document>) {
if (!snapshot.exists()) {
return snapshot
}
let document: Document = snapshot.data()
if (!options?.excludeId) {
document = {
...document,
id: snapshot.id,
}
}
for (const key in document) {
const value = document[key]
if (isTimestamp(value)) {
document = {
...document,
[key]: value.toDate(),
}
}
}
return document
},
})

View File

@@ -1,283 +1,289 @@
import type firebase from "firebase" import { initializeApp } from "firebase/app"
import { createHash, randomBytes } from "crypto" import type { FirebaseOptions } from "firebase/app"
import { Adapter } from "next-auth/adapters"
import { import {
querySnapshotToObject, addDoc,
docSnapshotToObject, collection,
stripUndefined, deleteDoc,
} from "./utils" doc,
import { Profile, Session, User } from "next-auth" getDoc,
getDocs,
getFirestore,
limit,
query,
runTransaction,
setDoc,
where,
connectFirestoreEmulator,
} from "firebase/firestore"
import type { Account } from "next-auth"
import type {
Adapter,
AdapterSession,
AdapterUser,
VerificationToken,
} from "next-auth/adapters"
interface FirebaseVerificationRequest { import { getConverter } from "./converter"
id: string
identifier: string type IndexableObject = Record<string, unknown>
token: string
expires: Date export interface FirestoreAdapterOptions {
emulator?: {
host?: string
port?: number
}
} }
export type FirebaseSession = Session & { export function FirestoreAdapter({
id: string emulator,
expires: Date ...firebaseOptions
} }: FirebaseOptions & FirestoreAdapterOptions): Adapter {
const firebaseApp = initializeApp(firebaseOptions)
const db = getFirestore(firebaseApp)
// @ts-expect-error if (emulator) {
export const FirebaseAdapter: Adapter< connectFirestoreEmulator(
firebase.firestore.Firestore, db,
never, emulator?.host ?? "localhost",
User & { id: string }, emulator?.port ?? 3001
Profile,
FirebaseSession
> = (client) => {
return {
async getAdapter({ session, secret, ...appOptions }) {
const sessionMaxAge = session.maxAge * 1000 // default is 30 days
const sessionUpdateAge = session.updateAge * 1000 // default is 1 day
/**
* @todo Move this to core package
* @todo Use bcrypt or a more secure method
*/
const hashToken = (token: string) =>
createHash("sha256").update(`${token}${secret}`).digest("hex")
return {
displayName: "FIREBASE",
async createUser(profile) {
const userRef = await client.collection("users").add(
stripUndefined({
name: profile.name,
email: profile.email,
image: profile.image,
emailVerified: profile.emailVerified ?? null,
})
) )
const snapshot = await userRef.get() }
const user = docSnapshotToObject(snapshot)
return user
},
const Users = collection(db, "users").withConverter(
getConverter<AdapterUser>()
)
const Sessions = collection(db, "sessions").withConverter(
getConverter<AdapterSession & IndexableObject>()
)
const Accounts = collection(db, "accounts").withConverter(
getConverter<Account>()
)
const VerificationTokens = collection(db, "verificationTokens").withConverter(
getConverter<VerificationToken & IndexableObject>({ excludeId: true })
)
return {
async createUser(newUser) {
const userRef = await addDoc(Users, newUser)
const userSnapshot = await getDoc(userRef)
if (userSnapshot.exists() && Users.converter) {
return Users.converter.fromFirestore(userSnapshot)
}
throw new Error("[createUser] Failed to create user")
},
async getUser(id) { async getUser(id) {
const snapshot = await client.collection("users").doc(id).get() const userSnapshot = await getDoc(doc(Users, id))
const user = docSnapshotToObject(snapshot)
return user
},
if (userSnapshot.exists() && Users.converter) {
return Users.converter.fromFirestore(userSnapshot)
}
return null
},
async getUserByEmail(email) { async getUserByEmail(email) {
if (!email) return null const userQuery = query(Users, where("email", "==", email), limit(1))
const userSnapshots = await getDocs(userQuery)
const userSnpashot = userSnapshots.docs[0]
const snapshot = await client if (userSnpashot?.exists() && Users.converter) {
.collection("users") return Users.converter.fromFirestore(userSnpashot)
.where("email", "==", email) }
.limit(1)
.get()
const user = querySnapshotToObject(snapshot) return null
return user },
async getUserByAccount({ provider, providerAccountId }) {
const accountQuery = query(
Accounts,
where("provider", "==", provider),
where("providerAccountId", "==", providerAccountId),
limit(1)
)
const accountSnapshots = await getDocs(accountQuery)
const accountSnapshot = accountSnapshots.docs[0]
if (accountSnapshot?.exists()) {
const { userId } = accountSnapshot.data()
const userDoc = await getDoc(doc(Users, userId))
if (userDoc.exists() && Users.converter) {
return Users.converter.fromFirestore(userDoc)
}
}
return null
}, },
async getUserByProviderAccountId(providerId, providerAccountId) { async updateUser(partialUser) {
const accountSnapshot = await client const userRef = doc(Users, partialUser.id)
.collection("accounts")
.where("providerId", "==", providerId)
.where("providerAccountId", "==", providerAccountId)
.limit(1)
.get()
if (accountSnapshot.empty) return null await setDoc(userRef, partialUser, { merge: true })
const userId = accountSnapshot.docs[0].data().userId const userSnapshot = await getDoc(userRef)
const userSnapshot = await client if (userSnapshot.exists() && Users.converter) {
.collection("users") return Users.converter.fromFirestore(userSnapshot)
.doc(userId) }
.get()
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion throw new Error("[updateUser] Failed to update user")
return { ...userSnapshot.data(), id: userSnapshot.id } as any
},
async updateUser(user) {
await client
.collection("users")
.doc(user.id)
.update(stripUndefined(user))
return user
}, },
async deleteUser(userId) { async deleteUser(userId) {
await client.collection("users").doc(userId).delete() const userRef = doc(Users, userId)
const accountsQuery = query(Accounts, where("userId", "==", userId))
const sessionsQuery = query(Sessions, where("userId", "==", userId))
// TODO: May be better to use events instead of transactions?
await runTransaction(db, async (transaction) => {
const accounts = await getDocs(accountsQuery)
const sessions = await getDocs(sessionsQuery)
transaction.delete(userRef)
accounts.forEach((account) => transaction.delete(account.ref))
sessions.forEach((session) => transaction.delete(session.ref))
})
}, },
async linkAccount( async linkAccount(account) {
userId, const accountRef = await addDoc(Accounts, account)
providerId, const accountSnapshot = await getDoc(accountRef)
providerType,
providerAccountId, if (accountSnapshot.exists() && Accounts.converter) {
refreshToken, return Accounts.converter.fromFirestore(accountSnapshot)
accessToken, }
accessTokenExpires },
) {
const accountRef = await client.collection("accounts").add( async unlinkAccount({ provider, providerAccountId }) {
stripUndefined({ const accountQuery = query(
userId, Accounts,
providerId, where("provider", "==", provider),
providerType, where("providerAccountId", "==", providerAccountId),
providerAccountId, limit(1)
refreshToken,
accessToken,
accessTokenExpires,
})
) )
const accountSnapshots = await getDocs(accountQuery)
const accountSnapshot = accountSnapshots.docs[0]
const accountSnapshot = await accountRef.get() if (accountSnapshot?.exists()) {
const account = docSnapshotToObject(accountSnapshot) await deleteDoc(accountSnapshot.ref)
return account
},
async unlinkAccount(userId, providerId, providerAccountId) {
const snapshot = await client
.collection("accounts")
.where("userId", "==", userId)
.where("providerId", "==", providerId)
.where("providerAccountId", "==", providerAccountId)
.limit(1)
.get()
const accountId = snapshot.docs[0].id
await client.collection("accounts").doc(accountId).delete()
},
async createSession(user) {
const sessionRef = await client.collection("sessions").add({
userId: user.id,
expires: new Date(Date.now() + sessionMaxAge),
sessionToken: randomBytes(32).toString("hex"),
accessToken: randomBytes(32).toString("hex"),
})
const snapshot = await sessionRef.get()
const session = docSnapshotToObject(snapshot)
return session
},
async getSession(sessionToken) {
const snapshot = await client
.collection("sessions")
.where("sessionToken", "==", sessionToken)
.limit(1)
.get()
const session = querySnapshotToObject<FirebaseSession>(snapshot)
if (!session) return null
// if the session has expired
if (session.expires < new Date()) {
// delete the session
await client.collection("sessions").doc(session.id).delete()
return null
} }
// return already existing session
return session
}, },
async updateSession(session, force) { async createSession(session) {
if ( const sessionRef = await addDoc(Sessions, session)
!force && const sessionSnapshot = await getDoc(sessionRef)
Number(session.expires) - sessionMaxAge + sessionUpdateAge >
Date.now() if (sessionSnapshot.exists() && Sessions.converter) {
) { return Sessions.converter.fromFirestore(sessionSnapshot)
return null
} }
// Update the item in the database throw new Error("[createSession] Failed to create session")
await client },
.collection("sessions")
.doc(session.id) async getSessionAndUser(sessionToken) {
.update({ const sessionQuery = query(
expires: new Date(Date.now() + sessionMaxAge), Sessions,
}) where("sessionToken", "==", sessionToken),
limit(1)
)
const sessionSnapshots = await getDocs(sessionQuery)
const sessionSnapshot = sessionSnapshots.docs[0]
if (sessionSnapshot?.exists() && Sessions.converter) {
const session = Sessions.converter.fromFirestore(sessionSnapshot)
const userDoc = await getDoc(doc(Users, session.userId))
if (userDoc.exists() && Users.converter) {
const user = Users.converter.fromFirestore(userDoc)
return { session, user }
}
}
return null
},
async updateSession(partialSession) {
const sessionQuery = query(
Sessions,
where("sessionToken", "==", partialSession.sessionToken),
limit(1)
)
const sessionSnapshots = await getDocs(sessionQuery)
const sessionSnapshot = sessionSnapshots.docs[0]
if (sessionSnapshot?.exists()) {
await setDoc(sessionSnapshot.ref, partialSession, { merge: true })
const sessionDoc = await getDoc(sessionSnapshot.ref)
if (sessionDoc?.exists() && Sessions.converter) {
const session = Sessions.converter.fromFirestore(sessionDoc)
return session return session
}
}
return null
}, },
async deleteSession(sessionToken) { async deleteSession(sessionToken) {
const snapshot = await client const sessionQuery = query(
.collection("sessions") Sessions,
.where("sessionToken", "==", sessionToken) where("sessionToken", "==", sessionToken),
.limit(1) limit(1)
.get() )
const sessionSnapshots = await getDocs(sessionQuery)
const sessionSnapshot = sessionSnapshots.docs[0]
const session = querySnapshotToObject<FirebaseSession>(snapshot) if (sessionSnapshot?.exists()) {
if (!session) return await deleteDoc(sessionSnapshot.ref)
}
await client.collection("sessions").doc(session.id).delete()
}, },
async createVerificationRequest(identifier, url, token, _, provider) { async createVerificationToken(verificationToken) {
const verificationRequestRef = await client const verificationTokenRef = await addDoc(
.collection("verificationRequests") VerificationTokens,
.add({ verificationToken
identifier, )
token: hashToken(token), const verificationTokenSnapshot = await getDoc(verificationTokenRef)
expires: new Date(Date.now() + provider.maxAge * 1000),
})
// With the verificationCallback on a provider, you can send an email, or queue if (verificationTokenSnapshot.exists() && VerificationTokens.converter) {
// an email to be sent, or perform some other action (e.g. send a text message) const {
await provider.sendVerificationRequest({ id,
identifier, ...verificationToken
url, } = VerificationTokens.converter.fromFirestore(
token, verificationTokenSnapshot
baseUrl: appOptions.baseUrl, )
provider,
})
const snapshot = await verificationRequestRef.get() return verificationToken
return docSnapshotToObject<FirebaseVerificationRequest>(snapshot) }
}, },
async getVerificationRequest(identifier, token) { async useVerificationToken({ identifier, token }) {
const snapshot = await client const verificationTokensQuery = query(
.collection("verificationRequests") VerificationTokens,
.where("token", "==", hashToken(token)) where("identifier", "==", identifier),
.where("identifier", "==", identifier) where("token", "==", token),
.limit(1) limit(1)
.get() )
const verificationTokenSnapshots = await getDocs(verificationTokensQuery)
const verificationTokenSnapshot = verificationTokenSnapshots.docs[0]
const verificationRequest = if (verificationTokenSnapshot?.exists() && VerificationTokens.converter) {
querySnapshotToObject<FirebaseVerificationRequest>(snapshot) await deleteDoc(verificationTokenSnapshot.ref)
if (!verificationRequest) return null
const {
id,
...verificationToken
} = VerificationTokens.converter.fromFirestore(
verificationTokenSnapshot
)
return verificationToken
}
if (verificationRequest.expires < new Date()) {
// Delete verification entry so it cannot be used again
await client
.collection("verificationRequests")
.doc(verificationRequest.id)
.delete()
return null return null
}
return verificationRequest
},
async deleteVerificationRequest(identifier, token) {
const snapshot = await client
.collection("verificationRequests")
.where("token", "==", hashToken(token))
.where("identifier", "==", identifier)
.limit(1)
.get()
const verificationRequest =
querySnapshotToObject<FirebaseVerificationRequest>(snapshot)
if (!verificationRequest) return null
await client
.collection("verificationRequests")
.doc(verificationRequest.id)
.delete()
},
}
}, },
} }
} }

View File

@@ -1,40 +0,0 @@
import type firebase from "firebase"
/**
* Takes in a snapshot and returns all of its `data()`,
* as well as `id` and `createdAt` and `updatedAt` `Date`
*/
export function docSnapshotToObject<T>(
snapshot: firebase.firestore.DocumentSnapshot<firebase.firestore.DocumentData>
): T | null {
if (!snapshot.exists) {
return null
}
const data: any = snapshot.data()
if (data.expires) {
data.expires = data.expires.toDate()
}
return { id: snapshot.id, ...data }
}
export function querySnapshotToObject<T>(
snapshot: firebase.firestore.QuerySnapshot<firebase.firestore.DocumentData>
): T | null {
if (snapshot.empty) {
return null
}
const doc = snapshot.docs[0]
const data: any = doc.data()
if (data.expires) {
data.expires = data.expires.toDate()
}
return { id: doc.id, ...data }
}
/** Firebase does not like `undefined` values */
export function stripUndefined(obj: any) {
return Object.fromEntries(
Object.entries(obj).filter(([, value]) => typeof value !== "undefined")
)
}

View File

@@ -1,67 +1,77 @@
import { runBasicTests } from "@next-auth/adapter-test" import { runBasicTests } from "@next-auth/adapter-test"
import { FirebaseAdapter } from "../src" import { FirestoreAdapter } from "../src"
import { docSnapshotToObject, querySnapshotToObject } from "../src/utils"
import firebase from "firebase/app" import { getFirestore, connectFirestoreEmulator, terminate, collection, query, where, limit, getDocs, getDoc, doc } from "firebase/firestore"
import "firebase/firestore" import { initializeApp } from "firebase/app";
import { getConverter } from "../src/converter";
import type { AdapterSession, AdapterUser, VerificationToken } from "next-auth/adapters";
import type { Account } from "next-auth";
const firestore = ( const app = initializeApp({ projectId: "next-auth-test" });
firebase.apps[0] ?? firebase.initializeApp({ projectId: "next-auth-test" }) const firestore = getFirestore(app);
).firestore()
firestore.useEmulator("localhost", 8080) connectFirestoreEmulator(firestore, 'localhost', 8080);
type IndexableObject = Record<string, unknown>;
const Users = collection(firestore, 'users').withConverter(getConverter<AdapterUser>());
const Sessions = collection(firestore, 'sessions').withConverter(getConverter<AdapterSession & IndexableObject>());
const Accounts = collection(firestore, 'accounts').withConverter(getConverter<Account>());
const VerificationTokens = collection(firestore, 'verificationTokens').withConverter(getConverter<VerificationToken & IndexableObject>({ excludeId: true }));
runBasicTests({ runBasicTests({
adapter: FirebaseAdapter(firestore), adapter: FirestoreAdapter({ projectId: "next-auth-test" }),
db: { db: {
async disconnect() { async disconnect() {
await firestore.terminate() await terminate(firestore);
}, },
async session(sessionToken) { async session(sessionToken) {
const snapshot = await firestore const snapshotQuery = query(Sessions, where("sessionToken", "==", sessionToken), limit(1));
.collection("sessions") const snapshots = await getDocs(snapshotQuery);
.where("sessionToken", "==", sessionToken) const snapshot = snapshots.docs[0];
.limit(1)
.get()
return querySnapshotToObject(snapshot)
},
async expireSession(sessionToken, expires) {
const snapshot = await firestore
.collection("sessions")
.where("sessionToken", "==", sessionToken)
.limit(1)
.get()
if (snapshot.empty) { if (snapshot?.exists() && Sessions.converter) {
console.error(sessionToken, expires) const session = Sessions.converter.fromFirestore(snapshot);
throw new Error("Could not expire session")
return session;
} }
return await firestore return null;
.collection("sessions")
.doc(snapshot.docs[0].id)
.update({ expires })
}, },
async user(id) { async user(id) {
const snapshot = await firestore.collection("users").doc(id).get() const snapshot = await getDoc(doc(Users, id));
return docSnapshotToObject(snapshot)
if (snapshot?.exists() && Users.converter) {
const user = Users.converter.fromFirestore(snapshot);
return user;
}
return null;
}, },
async account(providerId, providerAccountId) { async account({ provider, providerAccountId }) {
const snapshot = await firestore const snapshotQuery = query(Accounts, where("provider", "==", provider), where("providerAccountId", "==", providerAccountId), limit(1));
.collection("accounts") const snapshots = await getDocs(snapshotQuery);
.where("providerId", "==", providerId) const snapshot = snapshots.docs[0];
.where("providerAccountId", "==", providerAccountId)
.limit(1) if (snapshot?.exists() && Accounts.converter) {
.get() const account = Accounts.converter.fromFirestore(snapshot);
return querySnapshotToObject(snapshot)
return account;
}
return null;
}, },
async verificationRequest(identifier, token) { async verificationToken({ identifier, token }) {
const snapshot = await firestore const snapshotQuery = query(VerificationTokens, where("identifier", "==", identifier), where("token", "==", token), limit(1));
.collection("verificationRequests") const snapshots = await getDocs(snapshotQuery);
.where("identifier", "==", identifier) const snapshot = snapshots.docs[0];
.where("token", "==", token)
.limit(1) if (snapshot?.exists() && VerificationTokens.converter) {
.get() const verificationToken = VerificationTokens.converter.fromFirestore(snapshot);
return querySnapshotToObject(snapshot)
return verificationToken;
}
}, },
}, },
}) })

View File

@@ -1,8 +1,10 @@
{ {
"extends": "@next-auth/tsconfig/base.json", "extends": "@next-auth/tsconfig/adapters.json",
"compilerOptions": { "compilerOptions": {
"rootDir": "src", "rootDir": "src",
"outDir": "dist" "outDir": "dist",
"strict": true,
"noUncheckedIndexedAccess": true
}, },
"exclude": ["tests", "dist", "jest.config.js"] "exclude": ["tests", "dist", "jest.config.js"]
} }

View File

@@ -6,7 +6,7 @@
Open Source. Full Stack. Own Your Data. Open Source. Full Stack. Own Your Data.
</p> </p>
<p align="center" style="align: center;"> <p align="center" style="align: center;">
<img src="https://github.com/nextauthjs/adapters/actions/workflows/release.yml/badge.svg" alt="CI Test" /> <img src="https://github.com/nextauthjs/next-auth/actions/workflows/release.yml/badge.svg?branch=main" alt="CI Test" />
<a href="https://www.npmjs.com/package/@next-auth/mikro-orm-adapter" target="_blank"><img src="https://img.shields.io/bundlephobia/minzip/@next-auth/mikro-orm-adapter/next" alt="Bundle Size"/></a> <a href="https://www.npmjs.com/package/@next-auth/mikro-orm-adapter" target="_blank"><img src="https://img.shields.io/bundlephobia/minzip/@next-auth/mikro-orm-adapter/next" alt="Bundle Size"/></a>
<a href="https://www.npmjs.com/package/@next-auth/mikro-orm-adapter" target="_blank"><img src="https://img.shields.io/npm/v/@next-auth/mikro-orm-adapter/next" alt="@next-auth/mikro-orm-adapter Version" /></a> <a href="https://www.npmjs.com/package/@next-auth/mikro-orm-adapter" target="_blank"><img src="https://img.shields.io/npm/v/@next-auth/mikro-orm-adapter/next" alt="@next-auth/mikro-orm-adapter Version" /></a>
</p> </p>
@@ -49,7 +49,7 @@ This is the MikroORM Adapter for [`next-auth`](https://next-auth.js.org). This p
## Contributing ## Contributing
We're open to all community contributions! If you'd like to contribute in any way, please read our [Contributing Guide](https://github.com/nextauthjs/adapters/blob/main/CONTRIBUTING.md). We're open to all community contributions! If you'd like to contribute in any way, please read our [Contributing Guide](https://github.com/nextauthjs/next-auth/blob/main/CONTRIBUTING.md).
## License ## License

View File

@@ -3,7 +3,7 @@
"version": "2.0.1", "version": "2.0.1",
"description": "MikroORM adapter for next-auth.", "description": "MikroORM adapter for next-auth.",
"homepage": "https://next-auth.js.org", "homepage": "https://next-auth.js.org",
"repository": "https://github.com/nextauthjs/adapters", "repository": "https://github.com/nextauthjs/next-auth",
"bugs": { "bugs": {
"url": "https://github.com/nextauthjs/next-auth/issues" "url": "https://github.com/nextauthjs/next-auth/issues"
}, },
@@ -33,17 +33,21 @@
], ],
"peerDependencies": { "peerDependencies": {
"@mikro-orm/core": "^5.0.2", "@mikro-orm/core": "^5.0.2",
"next-auth": "workspace:*" "next-auth": "^4"
}, },
"devDependencies": { "devDependencies": {
"@mikro-orm/core": "^5.0.2", "@mikro-orm/core": "^5.0.2",
"@mikro-orm/sqlite": "^5.0.2", "@mikro-orm/sqlite": "^5.0.2",
"@next-auth/adapter-test": "workspace:^0.0.0", "@next-auth/adapter-test": "workspace:^0.0.0",
"@next-auth/tsconfig": "workspace:^0.0.0", "@next-auth/tsconfig": "workspace:^0.0.0",
"@types/uuid": "^8.3.3",
"jest": "^27.4.3", "jest": "^27.4.3",
"next-auth": "workspace:*" "next-auth": "workspace:*"
}, },
"jest": { "jest": {
"preset": "@next-auth/adapter-test/jest" "preset": "@next-auth/adapter-test/jest"
},
"dependencies": {
"uuid": "^8.3.2"
} }
} }

View File

@@ -6,7 +6,7 @@
Open Source. Full Stack. Own Your Data. Open Source. Full Stack. Own Your Data.
</p> </p>
<p align="center" style="align: center;"> <p align="center" style="align: center;">
<img src="https://github.com/nextauthjs/adapters/actions/workflows/release.yml/badge.svg" alt="CI Test" /> <img src="https://github.com/nextauthjs/next-auth/actions/workflows/release.yml/badge.svg?branch=main" alt="CI Test" />
<a href="https://www.npmjs.com/package/@next-auth/mongodb-adapter" target="_blank"><img src="https://img.shields.io/bundlephobia/minzip/@next-auth/mongodb-adapter" alt="Bundle Size"/></a> <a href="https://www.npmjs.com/package/@next-auth/mongodb-adapter" target="_blank"><img src="https://img.shields.io/bundlephobia/minzip/@next-auth/mongodb-adapter" alt="Bundle Size"/></a>
<a href="https://www.npmjs.com/package/@next-auth/mongodb-adapter" target="_blank"><img src="https://img.shields.io/npm/v/@next-auth/mongodb-adapter" alt="@next-auth/mongodb-adapter Version" /></a> <a href="https://www.npmjs.com/package/@next-auth/mongodb-adapter" target="_blank"><img src="https://img.shields.io/npm/v/@next-auth/mongodb-adapter" alt="@next-auth/mongodb-adapter Version" /></a>
</p> </p>
@@ -79,7 +79,7 @@ export default NextAuth({
## Contributing ## Contributing
We're open to all community contributions! If you'd like to contribute in any way, please read our [Contributing Guide](https://github.com/nextauthjs/adapters/blob/main/CONTRIBUTING.md). We're open to all community contributions! If you'd like to contribute in any way, please read our [Contributing Guide](https://github.com/nextauthjs/next-auth/blob/main/CONTRIBUTING.md).
## License ## License

View File

@@ -3,7 +3,7 @@
"version": "1.0.3", "version": "1.0.3",
"description": "mongoDB adapter for next-auth.", "description": "mongoDB adapter for next-auth.",
"homepage": "https://next-auth.js.org", "homepage": "https://next-auth.js.org",
"repository": "https://github.com/nextauthjs/adapters", "repository": "https://github.com/nextauthjs/next-auth",
"bugs": { "bugs": {
"url": "https://github.com/nextauthjs/next-auth/issues" "url": "https://github.com/nextauthjs/next-auth/issues"
}, },

View File

@@ -6,7 +6,7 @@
Open Source. Full Stack. Own Your Data. Open Source. Full Stack. Own Your Data.
</p> </p>
<p align="center" style="align: center;"> <p align="center" style="align: center;">
<img src="https://github.com/nextauthjs/adapters/actions/workflows/release.yml/badge.svg" alt="Canary CI Test" /> <img src="https://github.com/nextauthjs/next-auth/actions/workflows/release.yml/badge.svg?branch=main" alt="Canary CI Test" />
<img src="https://img.shields.io/bundlephobia/minzip/@next-auth/neo4j-adapter" alt="Bundle Size"/> <img src="https://img.shields.io/bundlephobia/minzip/@next-auth/neo4j-adapter" alt="Bundle Size"/>
<img src="https://img.shields.io/npm/v/@next-auth/neo4j-adapter" alt="@next-auth/neo4j-adapter Version" /> <img src="https://img.shields.io/npm/v/@next-auth/neo4j-adapter" alt="@next-auth/neo4j-adapter Version" />
</p> </p>
@@ -50,7 +50,7 @@ export default NextAuth({
## Contributing ## 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/adapters/blob/canary/CONTRIBUTING.md). 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/next-auth/blob/canary/CONTRIBUTING.md).
## License ## License

View File

@@ -3,7 +3,7 @@
"version": "1.0.3", "version": "1.0.3",
"description": "neo4j adapter for next-auth.", "description": "neo4j adapter for next-auth.",
"homepage": "https://next-auth.js.org", "homepage": "https://next-auth.js.org",
"repository": "https://github.com/nextauthjs/adapters", "repository": "https://github.com/nextauthjs/next-auth",
"bugs": { "bugs": {
"url": "https://github.com/nextauthjs/next-auth/issues" "url": "https://github.com/nextauthjs/next-auth/issues"
}, },

View File

@@ -6,7 +6,7 @@
Open Source. Full Stack. Own Your Data. Open Source. Full Stack. Own Your Data.
</p> </p>
<p align="center" style="align: center;"> <p align="center" style="align: center;">
<img src="https://github.com/nextauthjs/adapters/actions/workflows/release.yml/badge.svg" alt="CI Test" /> <img src="https://github.com/nextauthjs/next-auth/actions/workflows/release.yml/badge.svg?branch=main" alt="CI Test" />
<img src="https://img.shields.io/bundlephobia/minzip/@next-auth/pouchdb-adapter" alt="Bundle Size"/> <img src="https://img.shields.io/bundlephobia/minzip/@next-auth/pouchdb-adapter" alt="Bundle Size"/>
<img src="https://img.shields.io/npm/v/@next-auth/pouchdb-adapter" alt="@next-auth/pouchdb-adapter Version" /> <img src="https://img.shields.io/npm/v/@next-auth/pouchdb-adapter" alt="@next-auth/pouchdb-adapter Version" />
</p> </p>
@@ -71,7 +71,7 @@ For more details, please see https://pouchdb.com/api.html#sync
## Contributing ## Contributing
We're open to all community contributions! If you'd like to contribute in any way, please read our [Contributing Guide](https://github.com/nextauthjs/adapters/blob/main/CONTRIBUTING.md). We're open to all community contributions! If you'd like to contribute in any way, please read our [Contributing Guide](https://github.com/nextauthjs/next-auth/blob/main/CONTRIBUTING.md).
## License ## License

View File

@@ -3,7 +3,7 @@
"version": "0.1.3", "version": "0.1.3",
"description": "PouchDB adapter for next-auth.", "description": "PouchDB adapter for next-auth.",
"homepage": "https://next-auth.js.org", "homepage": "https://next-auth.js.org",
"repository": "https://github.com/nextauthjs/adapters", "repository": "https://github.com/nextauthjs/next-auth",
"bugs": { "bugs": {
"url": "https://github.com/nextauthjs/next-auth/issues" "url": "https://github.com/nextauthjs/next-auth/issues"
}, },

View File

@@ -6,7 +6,7 @@
Open Source. Full Stack. Own Your Data. Open Source. Full Stack. Own Your Data.
</p> </p>
<p align="center" style="align: center;"> <p align="center" style="align: center;">
<img src="https://github.com/nextauthjs/adapters/actions/workflows/release.yml/badge.svg" alt="CI Test" /> <img src="https://github.com/nextauthjs/next-auth/actions/workflows/release.yml/badge.svg?branch=main" alt="CI Test" />
<a href="https://www.npmjs.com/package/@next-auth/prisma-adapter" target="_blank"><img src="https://img.shields.io/bundlephobia/minzip/@next-auth/prisma-adapter/next" alt="Bundle Size"/></a> <a href="https://www.npmjs.com/package/@next-auth/prisma-adapter" target="_blank"><img src="https://img.shields.io/bundlephobia/minzip/@next-auth/prisma-adapter/next" alt="Bundle Size"/></a>
<a href="https://www.npmjs.com/package/@next-auth/prisma-adapter" target="_blank"><img src="https://img.shields.io/npm/v/@next-auth/prisma-adapter/next" alt="@next-auth/prisma-adapter Version" /></a> <a href="https://www.npmjs.com/package/@next-auth/prisma-adapter" target="_blank"><img src="https://img.shields.io/npm/v/@next-auth/prisma-adapter/next" alt="@next-auth/prisma-adapter Version" /></a>
</p> </p>
@@ -48,7 +48,7 @@ export default NextAuth({
## Contributing ## Contributing
We're open to all community contributions! If you'd like to contribute in any way, please read our [Contributing Guide](https://github.com/nextauthjs/adapters/blob/main/CONTRIBUTING.md). We're open to all community contributions! If you'd like to contribute in any way, please read our [Contributing Guide](https://github.com/nextauthjs/next-auth/blob/main/CONTRIBUTING.md).
## License ## License

View File

@@ -3,7 +3,7 @@
"version": "1.0.3", "version": "1.0.3",
"description": "Prisma adapter for next-auth.", "description": "Prisma adapter for next-auth.",
"homepage": "https://next-auth.js.org", "homepage": "https://next-auth.js.org",
"repository": "https://github.com/nextauthjs/adapters", "repository": "https://github.com/nextauthjs/next-auth",
"bugs": { "bugs": {
"url": "https://github.com/nextauthjs/next-auth/issues" "url": "https://github.com/nextauthjs/next-auth/issues"
}, },

View File

@@ -6,7 +6,7 @@
Open Source. Full Stack. Own Your Data. Open Source. Full Stack. Own Your Data.
</p> </p>
<p align="center" style="align: center;"> <p align="center" style="align: center;">
<img src="https://github.com/nextauthjs/adapters/actions/workflows/release.yml/badge.svg" alt="CI Test" /> <img src="https://github.com/nextauthjs/next-auth/actions/workflows/release.yml/badge.svg?branch=main" alt="CI Test" />
<img src="https://img.shields.io/bundlephobia/minzip/@next-auth/sequelize-adapter" alt="Bundle Size"/> <img src="https://img.shields.io/bundlephobia/minzip/@next-auth/sequelize-adapter" alt="Bundle Size"/>
<img src="https://img.shields.io/npm/v/@next-auth/sequelize-adapter" alt="@next-auth/sequelize-adapter Version" /> <img src="https://img.shields.io/npm/v/@next-auth/sequelize-adapter" alt="@next-auth/sequelize-adapter Version" />
</p> </p>
@@ -89,7 +89,7 @@ export default NextAuth({
## Contributing ## Contributing
We're open to all community contributions! If you'd like to contribute in any way, please read our [Contributing Guide](https://github.com/nextauthjs/adapters/blob/main/CONTRIBUTING.md). We're open to all community contributions! If you'd like to contribute in any way, please read our [Contributing Guide](https://github.com/nextauthjs/next-auth/blob/main/CONTRIBUTING.md).
## License ## License

View File

@@ -1,11 +1,11 @@
{ {
"name": "@next-auth/sequelize-adapter", "name": "@next-auth/sequelize-adapter",
"version": "1.0.2", "version": "1.0.5",
"description": "Sequelize adapter for next-auth.", "description": "Sequelize adapter for next-auth.",
"homepage": "https://next-auth.js.org", "homepage": "https://next-auth.js.org",
"repository": "https://github.com/nextauthjs/adapters", "repository": "https://github.com/nextauthjs/next-auth",
"bugs": { "bugs": {
"url": "https://github.com/nextauthjs/adapters/issues" "url": "https://github.com/nextauthjs/next-auth/issues"
}, },
"author": "github.com/luke-j", "author": "github.com/luke-j",
"main": "dist/index.js", "main": "dist/index.js",
@@ -29,7 +29,7 @@
"dist" "dist"
], ],
"peerDependencies": { "peerDependencies": {
"next-auth": "workspace:*", "next-auth": "^4",
"sequelize": "^6.6.5" "sequelize": "^6.6.5"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -1,7 +1,13 @@
const swcConfig = {
jsc: {
parser: { syntax: "typescript", decorators: true },
transform: { legacyDecorator: true, decoratorMetadata: true },
},
}
module.exports = { module.exports = {
transform: { transform: {
".(ts|tsx)$": "ts-jest", ".(ts|tsx)$": ["@swc/jest", swcConfig],
".(js|jsx)$": "babel-jest", // jest's default ".(js|jsx)$": ["@swc/jest", swcConfig],
}, },
transformIgnorePatterns: ["[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$"], transformIgnorePatterns: ["[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$"],
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],

View File

@@ -19,7 +19,6 @@
"@types/nodemailer": "^6.4.4", "@types/nodemailer": "^6.4.4",
"@typescript-eslint/eslint-plugin": "^4.24.0", "@typescript-eslint/eslint-plugin": "^4.24.0",
"@typescript-eslint/parser": "^4.24.0", "@typescript-eslint/parser": "^4.24.0",
"babel-jest": "^27.4.2",
"eslint": "^7.27.0", "eslint": "^7.27.0",
"eslint-config-prettier": "^8.3.0", "eslint-config-prettier": "^8.3.0",
"eslint-config-standard-with-typescript": "^20.0.0", "eslint-config-standard-with-typescript": "^20.0.0",

View File

@@ -6,7 +6,7 @@
Open Source. Full Stack. Own Your Data. Open Source. Full Stack. Own Your Data.
</p> </p>
<p align="center" style="align: center;"> <p align="center" style="align: center;">
<img src="https://github.com/nextauthjs/adapters/actions/workflows/release.yml/badge.svg" alt="Canary CI Test" /> <img src="https://github.com/nextauthjs/next-auth/actions/workflows/release.yml/badge.svg?branch=main" alt="Canary CI Test" />
<img src="https://img.shields.io/bundlephobia/minzip/@next-auth/typeorm-legacy-adapter/canary" alt="Bundle Size"/> <img src="https://img.shields.io/bundlephobia/minzip/@next-auth/typeorm-legacy-adapter/canary" alt="Bundle Size"/>
<img src="https://img.shields.io/npm/v/@next-auth/typeorm-legacy-adapter" alt="@next-auth/typeorm-legacy-adapter Version" /> <img src="https://img.shields.io/npm/v/@next-auth/typeorm-legacy-adapter" alt="@next-auth/typeorm-legacy-adapter Version" />
</p> </p>

View File

@@ -1,11 +1,11 @@
{ {
"name": "@next-auth/typeorm-legacy-adapter", "name": "@next-auth/typeorm-legacy-adapter",
"version": "1.0.3", "version": "2.0.0",
"description": "TypeORM (legacy) adapter for next-auth.", "description": "TypeORM (legacy) adapter for next-auth.",
"homepage": "https://next-auth.js.org", "homepage": "https://next-auth.js.org",
"repository": "https://github.com/nextauthjs/adapters", "repository": "https://github.com/nextauthjs/next-auth",
"bugs": { "bugs": {
"url": "https://github.com/nextauthjs/adapters/issues" "url": "https://github.com/nextauthjs/next-auth/issues"
}, },
"author": "Iain Collins", "author": "Iain Collins",
"contributors": [ "contributors": [
@@ -37,24 +37,25 @@
"sqlite": "tests/sqlite/test.sh" "sqlite": "tests/sqlite/test.sh"
}, },
"devDependencies": { "devDependencies": {
"@next-auth/adapter-test": "workspace:^0.0.0", "@next-auth/adapter-test": "workspace:*",
"@next-auth/tsconfig": "workspace:^0.0.0", "@next-auth/tsconfig": "workspace:*",
"jest": "^27.4.3", "jest": "^27.4.3",
"mssql": "^7.2.1", "mssql": "^7.2.1",
"mysql": "^2.18.1", "mysql": "^2.18.1",
"next-auth": "workspace:*", "next-auth": "workspace:*",
"pg": "^8.7.1", "pg": "^8.7.3",
"sqlite3": "^5.0.2", "sqlite3": "^5.0.8",
"typeorm": "^0.2.37", "typeorm": "0.3.7",
"typeorm-naming-strategies": "^2.0.0" "typeorm-naming-strategies": "^4.1.0",
"typescript": "^4.7.4"
}, },
"peerDependencies": { "peerDependencies": {
"mssql": "^6.2.1 || 7", "mssql": "^6.2.1 || 7",
"mysql": "^2.18.1", "mysql": "^2.18.1",
"next-auth": "workspace:*", "next-auth": "^4",
"pg": "^8.2.1", "pg": "^8.2.1",
"sqlite3": "^5.0.2", "sqlite3": "^5.0.2",
"typeorm": "^0.2.31" "typeorm": "0.3.7"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"mysql": { "mysql": {

View File

@@ -1,13 +1,8 @@
import { Adapter, AdapterSession, AdapterUser } from "next-auth/adapters" import type { Adapter, AdapterSession, AdapterUser } from "next-auth/adapters"
import { import { DataSourceOptions, DataSource, EntityManager } from "typeorm"
Connection, import type { Account } from "next-auth"
ConnectionOptions,
EntityManager,
getConnectionManager,
} from "typeorm"
import { Account } from "next-auth"
import * as defaultEntities from "./entities" import * as defaultEntities from "./entities"
import { parseConnectionConfig, updateConnectionEntities } from "./utils" import { parseDataSourceConfig, updateConnectionEntities } from "./utils"
export const entities = defaultEntities export const entities = defaultEntities
@@ -17,43 +12,40 @@ export interface TypeORMLegacyAdapterOptions {
entities?: Entities entities?: Entities
} }
let _connection: Connection let _dataSource: DataSource | undefined
export async function getManager(options: { export async function getManager(options: {
connection: string | ConnectionOptions dataSource: string | DataSourceOptions
entities: Entities entities: Entities
}): Promise<EntityManager> { }): Promise<EntityManager> {
const { connection, entities } = options const { dataSource, entities } = options
const config = { const config = {
...parseConnectionConfig(connection), ...parseDataSourceConfig(dataSource),
entities: Object.values(entities), entities: Object.values(entities),
} }
const connectionManager = getConnectionManager() if (!_dataSource) _dataSource = new DataSource(config)
if (connectionManager.has(config.name ?? "default")) { const manager = _dataSource?.manager
_connection = connectionManager.get(config.name ?? "default")
if (_connection.isConnected) return _connection.manager if (!manager.connection.isInitialized) {
await manager.connection.initialize()
}
if (process.env.NODE_ENV !== "production") { if (process.env.NODE_ENV !== "production") {
await updateConnectionEntities(_connection, config.entities) await updateConnectionEntities(_dataSource, config.entities)
} }
} else { return manager
_connection = await connectionManager.create(config).connect()
}
return _connection.manager
} }
export function TypeORMLegacyAdapter( export function TypeORMLegacyAdapter(
connection: string | ConnectionOptions, dataSource: string | DataSourceOptions,
options?: TypeORMLegacyAdapterOptions options?: TypeORMLegacyAdapterOptions
): Adapter { ): Adapter {
const entities = options?.entities const entities = options?.entities
const c = { const c = {
connection, dataSource,
entities: { entities: {
UserEntity: entities?.UserEntity ?? defaultEntities.UserEntity, UserEntity: entities?.UserEntity ?? defaultEntities.UserEntity,
SessionEntity: entities?.SessionEntity ?? defaultEntities.SessionEntity, SessionEntity: entities?.SessionEntity ?? defaultEntities.SessionEntity,
@@ -82,14 +74,14 @@ export function TypeORMLegacyAdapter(
// @ts-expect-error // @ts-expect-error
async getUser(id) { async getUser(id) {
const m = await getManager(c) const m = await getManager(c)
const user = await m.findOne("UserEntity", { id }) const user = await m.findOne("UserEntity", { where: { id } })
if (!user) return null if (!user) return null
return { ...user } return { ...user }
}, },
// @ts-expect-error // @ts-expect-error
async getUserByEmail(email) { async getUserByEmail(email) {
const m = await getManager(c) const m = await getManager(c)
const user = await m.findOne("UserEntity", { email }) const user = await m.findOne("UserEntity", { where: { email } })
if (!user) return null if (!user) return null
return { ...user } return { ...user }
}, },
@@ -97,8 +89,7 @@ export function TypeORMLegacyAdapter(
const m = await getManager(c) const m = await getManager(c)
const account = await m.findOne<Account & { user: AdapterUser }>( const account = await m.findOne<Account & { user: AdapterUser }>(
"AccountEntity", "AccountEntity",
provider_providerAccountId, { where: provider_providerAccountId, relations: ["user"] }
{ relations: ["user"] }
) )
if (!account) return null if (!account) return null
return account.user ?? null return account.user ?? null
@@ -136,7 +127,7 @@ export function TypeORMLegacyAdapter(
const m = await getManager(c) const m = await getManager(c)
const sessionAndUser = await m.findOne< const sessionAndUser = await m.findOne<
AdapterSession & { user: AdapterUser } AdapterSession & { user: AdapterUser }
>("SessionEntity", { sessionToken }, { relations: ["user"] }) >("SessionEntity", { where: { sessionToken }, relations: ["user"] })
if (!sessionAndUser) return null if (!sessionAndUser) return null
const { user, ...session } = sessionAndUser const { user, ...session } = sessionAndUser
@@ -162,10 +153,9 @@ export function TypeORMLegacyAdapter(
// @ts-expect-error // @ts-expect-error
async useVerificationToken(identifier_token) { async useVerificationToken(identifier_token) {
const m = await getManager(c) const m = await getManager(c)
const verificationToken = await m.findOne( const verificationToken = await m.findOne("VerificationTokenEntity", {
"VerificationTokenEntity", where: identifier_token,
identifier_token })
)
if (!verificationToken) { if (!verificationToken) {
return null return null
} }

View File

@@ -1,10 +1,10 @@
import { Connection, ConnectionOptions } from "typeorm" import type { DataSource, DataSourceOptions } from "typeorm"
import * as defaultEntities from "./entities" import * as defaultEntities from "./entities"
/** Ensure configOrString is normalized to an object. */ /** Ensure configOrString is normalized to an object. */
export function parseConnectionConfig( export function parseDataSourceConfig(
configOrString: string | ConnectionOptions configOrString: string | DataSourceOptions
): ConnectionOptions { ): DataSourceOptions {
if (typeof configOrString !== "string") { if (typeof configOrString !== "string") {
return { return {
...configOrString, ...configOrString,
@@ -89,22 +89,22 @@ function entitiesChanged(
} }
export async function updateConnectionEntities( export async function updateConnectionEntities(
connection: Connection, dataSource: DataSource,
entities: any[] entities: any[]
) { ) {
if (!entitiesChanged(connection.options.entities, entities)) return if (!entitiesChanged(dataSource.entityMetadatas, entities)) return
// @ts-expect-error // @ts-expect-error
connection.options.entities = entities dataSource.entityMetadatas = entities
// @ts-expect-error // @ts-expect-error
connection.buildMetadatas() dataSource.buildMetadatas()
if (connection.options.synchronize !== false) { if (dataSource.options.synchronize !== false) {
console.warn( console.warn(
"[next-auth][warn][adapter_typeorm_updating_entities]", "[next-auth][warn][adapter_typeorm_updating_entities]",
"\nhttps://next-auth.js.org/warnings#adapter_typeorm_updating_entities" "\nhttps://next-auth.js.org/warnings#adapter_typeorm_updating_entities"
) )
await connection.synchronize() await dataSource.synchronize()
} }
} }

View File

@@ -1,46 +1,49 @@
import { ConnectionManager, ConnectionOptions } from "typeorm" import { DataSource } from "typeorm"
import { TestOptions } from "@next-auth/adapter-test" import type { DataSourceOptions } from "typeorm"
import type { TestOptions } from "@next-auth/adapter-test"
import * as defaultEntities from "../src/entities" import * as defaultEntities from "../src/entities"
import { parseConnectionConfig } from "../src/utils" import { parseDataSourceConfig } from "../src/utils"
export { defaultEntities } export { defaultEntities }
console.warn = jest.fn()
/** Set up Test Database */ /** Set up Test Database */
export function db( export function db(
config: string | ConnectionOptions, config: string | DataSourceOptions,
entities: typeof defaultEntities = defaultEntities entities: typeof defaultEntities = defaultEntities
): TestOptions["db"] { ): TestOptions["db"] {
const connection = new ConnectionManager().create({ const connection = new DataSource({
...parseConnectionConfig(config), ...parseDataSourceConfig(config),
entities: Object.values(entities), entities: Object.values(entities),
}) }).manager.connection
const m = connection.manager const m = connection.manager
return { return {
connect: async () => await connection.connect(), connect: async () => await connection.initialize(),
disconnect: async () => await connection.close(), disconnect: async () => await connection.destroy(),
async user(id) { async user(id) {
const user = await m.findOne(entities.UserEntity, id) const user = await m.findOne(entities.UserEntity, { where: { id } })
return user ?? null return user ?? null
}, },
async account(provider_providerAccountId) { async account(provider_providerAccountId) {
const account = await m.findOne( const account = await m.findOne(entities.AccountEntity, {
entities.AccountEntity, where: provider_providerAccountId,
provider_providerAccountId })
)
return account ?? null return account ?? null
}, },
async session(sessionToken) { async session(sessionToken) {
const session = await m.findOne(entities.SessionEntity, { sessionToken }) const session = await m.findOne(entities.SessionEntity, {
where: { sessionToken },
})
return session ?? null return session ?? null
}, },
async verificationToken(token_identifier) { async verificationToken(token_identifier) {
const verificationToken = await m.findOne( const verificationToken = await m.findOne(
entities.VerificationTokenEntity, entities.VerificationTokenEntity,
token_identifier { where: token_identifier }
) )
if (!verificationToken) return null if (!verificationToken) return null
// @ts-expect-error
const { id: _, ...rest } = verificationToken const { id: _, ...rest } = verificationToken
return rest return rest
}, },

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