Compare commits

...

37 Commits

Author SHA1 Message Date
GitHub Actions
6061bbcde1 chore(release): bump package version(s) [skip ci] 2023-09-22 08:16:24 +00:00
Thang Vu
09c5fe29ba chore: update d1 adapter 2023-09-22 15:08:07 +07:00
Thang Vu
6780ed7fee feat: add pg adapter (#8679)
* Add vanilla Postgres adapter for NextAuth using the pg package.

All tests are passing.

* Fix typo in readme

* Add basic tsconfig

* Replace this.getUser with functio body as this is undef

* Change name from @next-auth/postgres-adapter to @next-auth/pg-adapter

* Fix package folder to be same as name

* Add package to issue template, labeller and PR labeller

* Commit pnpm lock change.

* Set next-auth version to ^4

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

* Return user id when updating user. Fixes #4897.

* chore: format PR

* chore: format PR

* chore: formatting

* fix: tests

* fix

---------

Co-authored-by: Jake Coppinger <jake@jakecoppinger.com>
Co-authored-by: Balázs Orbán <info@balazsorban.com>
2023-09-22 14:49:01 +07:00
Kyle Roberts
5a7c1bb2bb docs: Remove asterisk that causes build error in oauth-tutorial.mdx (#8666)
Update oauth-tutorial.mdx

Remove * from example that's causing an unexpected character error
2023-09-20 19:47:40 +01:00
Kyle Roberts
6448a7b76e docs: Update SvelteKitAuth example default import to named import (#8603)
Update SvelteKitAuth example import to current non-default export
2023-09-20 11:15:28 +02:00
Balázs Orbán
db8fcc3c82 chore: Update invalid-reproduction.md 2023-09-19 16:04:13 +02:00
Bob Ippolito
9aeca63013 fix: Build account object with tokens spread as lowest priority (#8599) 2023-09-19 15:57:41 +02:00
Balázs Orbán
fee85f3138 chore: Rename help.yml to questions.yml 2023-09-19 15:56:34 +02:00
Balázs Orbán
e5bf8ec9c5 chore: move to nissuer for triaging (#8646) 2023-09-19 15:55:16 +02:00
Hamir Mahal
e3ec32812e chore: remove unnecessary comment (#8605)
Co-authored-by: Balázs Orbán <info@balazsorban.com>
2023-09-19 15:54:27 +02:00
k8pai
c776435268 fix(docs): correct import name (#8606) 2023-09-19 11:55:01 +02:00
GitHub Actions
d0cc046e2d chore(release): bump package version(s) [skip ci] 2023-09-16 13:48:09 +00:00
Thang Vu
c818d028aa fix: missing closing tag in adapters doc 2023-09-16 20:42:14 +07:00
Thang Vu
3ba8a0e40a chore: fix deps edgeDB 2023-09-16 18:23:50 +07:00
Josh Schlesser
770d3565f8 feat(adapter): add new Cloudflare D1 Adapter (#6953)
* cleaned everything up

* Added documentation

* fixed up d1 logo in docs

* cleaning out unused file

* changed to esm module

* formatting the PR

* update the deps

* update the deps

* ignore test

* ignore test

* fix deps

* add contributor

---------

Co-authored-by: Nico Domino <yo@ndo.dev>
Co-authored-by: Thang Vu <hi@thvu.dev>
2023-09-16 18:17:02 +07:00
Hamir Mahal
ed32236712 docs: don't copy $ in commands (#8609)
Co-authored-by: Thang Vu <hi@thvu.dev>
2023-09-16 09:56:57 +02:00
Thang Vu
307f7b5eb9 chore: Add EdgeDB in sidebar 2023-09-16 12:06:12 +07:00
Thang Vu
120d7a29ee chore: update email 2023-09-16 12:05:57 +07:00
GitHub Actions
ea65134a84 chore(release): bump package version(s) [skip ci] 2023-09-16 04:30:21 +00:00
Thang Vu
016b22833c chore(docs): add EdgeDB in typedoc 2023-09-16 11:21:48 +07:00
Bruno Crosier
67dbbb2cdf feat(adapters): new EdgeDB Adapter (#5781)
* init adapter folder

* add schema

* run migration

* run `npx @edgedb/generate edgeql-js`

* fix `generate` script

* add `EdgeDBAdapter`

* add tests

* add README

* docs

* dev app

* lockfile

* remove generated files

* ignore `dbschema/edgeql-js`

* add `postinstall` generate script

* update docs

* move `nonrecursive_access_policies` to default schema

* newlines

* remove js generation

* rewrite in edgeql

* make `userId` computed

* fix `createUser` image param

* code review comments

* update lockfile

* code review

* comments

* delete

* types

* revert settings.json

* delete migrations

* deps

* clean up

* Restore packages/adapter-test/index.ts

* chore: formatting & fix logic of useVerificationToken

* chore: delete .md file

* fix: test

* chore: logo

* chore: formatting

* chore: add edgedb to doc

---------

Co-authored-by: Nico Domino <yo@ndo.dev>
Co-authored-by: Thang Vu <hi@thvu.dev>
2023-09-16 11:03:48 +07:00
Dewin Umana
f87e66f0b3 feat(providers): TikTok provider (#8131)
* feat(providers): TikTok provider

* Linting

* Move files under the correct paths

* Added Tiktok Provider

* Update Bug provider template
2023-09-15 12:14:44 +07:00
k8pai
b1c46809f5 fix(docs): remove typesafety statements as string (#8566)
Removed typesafety statement `as string` from Configuring Auth.js of /adapter-dynamodb
2023-09-12 14:42:25 +02:00
Thang Vu
b9e9722b74 feat(providers): Dribbble provider (#8531)
* feat(providers): add Dribbble provider

* update Dribbble

Co-Authored-By: Pavel Fomchenkov <hello@pavel.codes>

---------

Co-authored-by: Pavel Fomchenkov <hello@pavel.codes>
2023-09-09 14:07:24 +07:00
GitHub Actions
57f75c7839 chore(release): bump package version(s) [skip ci] 2023-09-07 15:59:48 +00:00
Jonas Strassel
e20eb5b583 feat: bump mongodb to v6 (#8492)
BREAKING CHANGE:

The required minimum version of `mongodb` has been bumped to v6, make sure to upgrade it in your project via `npm i mongodb@latest` or the equivalent
2023-09-07 17:54:16 +02:00
Thang Vu
fb7c5f9ef6 feat: Mastodon Provider (#8516)
Co-authored-by: Leif Arriens <30775450+leifarriens@users.noreply.github.com>
2023-09-07 22:18:11 +07:00
Anthony Shew
e986369906 docs: Add more detail to Credentials provider. (#8482)
* Soften messaging on Credentials provider documentation.

* Get rid of the swap.

* Switch to using authorize().
2023-09-06 00:24:27 +02:00
Antonio Basile
f3c64a85c9 feat(providers): Click up provider (#8489)
* feat: click up provider created

* docs: ClickUp documentation

* Format

---------

Co-authored-by: Antonio Basile <antoniobasile2@eng.it>
Co-authored-by: Thang Vu <hi@thvu.dev>
2023-09-05 22:13:56 +07:00
GitHub Actions
4f3241f8dd chore(release): bump package version(s) [skip ci] 2023-09-04 23:02:35 +00:00
Jason
65043ba471 fix: return Session from deleteSession() (#8484)
Co-authored-by: Balázs Orbán <info@balazsorban.com>
2023-09-04 16:17:48 +02:00
Balázs Orbán
46c5a97a0e chore: add passage issuer 2023-09-03 13:24:03 +02:00
Balázs Orbán
9f99066b19 chore: update NextAuth.js example to use App Router (#8477)
* move initiaization to App Router

* add more providers

* don't use default export

* update tsconfig

* move under api

* add more providers

* add issuer

* add issuer
2023-09-03 11:59:58 +01:00
Menushka Weeratunga
e266001c28 docs: Add SvelteKit OAuth tutorial docs (#8311)
Co-authored-by: Thang Vu <hi@thvu.dev>
2023-09-02 10:11:31 +07:00
Thang Vu
f1eb45f3c1 docs: Add callbacks example for Credential Provider 2023-09-02 09:40:31 +07:00
jonek
f621627914 fix(sveltekit): support custom base path properly (#8231)
* fix(sveltekit): support custom base path

* update server

---------

Co-authored-by: Thang Vu <hi@thvu.dev>
2023-09-02 00:03:22 +07:00
dependabot[bot]
2b6ad02bba chore(deps-dev): bump mongodb from 4.7.0 to 4.17.0 (#8461)
Bumps [mongodb](https://github.com/mongodb/node-mongodb-native) from 4.7.0 to 4.17.0.
- [Release notes](https://github.com/mongodb/node-mongodb-native/releases)
- [Changelog](https://github.com/mongodb/node-mongodb-native/blob/v4.17.0/HISTORY.md)
- [Commits](https://github.com/mongodb/node-mongodb-native/compare/v4.7.0...v4.17.0)

---
updated-dependencies:
- dependency-name: mongodb
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-01 13:14:49 +01:00
94 changed files with 5510 additions and 3251 deletions

33
.github/DISCUSSION_TEMPLATE/ideas.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
body:
- type: textarea
attributes:
label: Goals
description: Short list of what the feature request aims to address?
value: |
1.
2.
3.
validations:
required: true
- type: textarea
attributes:
label: Non-Goals
description: Short list of what the feature request _does not_ aim to address?
value: |
1.
2.
3.
validations:
required: false
- type: textarea
attributes:
label: Background
description: Discuss prior art, why do you think this feature is needed? Are there current alternatives?
validations:
required: true
- type: textarea
attributes:
label: Proposal
description: How should this feature be implemented? Are you interested in contributing?
validations:
required: true

View File

@@ -0,0 +1,20 @@
body:
- type: textarea
attributes:
label: Summary
description: What do you need help with?
validations:
required: true
- type: textarea
attributes:
label: Additional information
description: Any code snippets, error messages, or dependency details that may be related?
render: js
validations:
required: false
- type: input
attributes:
label: Example
description: A link to a minimal reproduction is helpful for collaborative debugging!
validations:
required: false

View File

@@ -36,10 +36,12 @@ body:
- "Beyond Identity"
- "Box"
- "Bungie"
- "ClickUp"
- "Cognito"
- "Coinbase"
- "Descope"
- "Discord"
- "Dribbble"
- "Dropbox"
- "EVE Online"
- "Facebook"
@@ -58,6 +60,7 @@ body:
- "LinkedIn"
- "Mailchimp"
- "Mail.ru"
- "Mastodon"
- "Medium"
- "Naver"
- "Netlify"
@@ -73,6 +76,7 @@ body:
- "Slack"
- "Spotify"
- "Strava"
- "Tiktok"
- "Todoist"
- "Trakt"
- "Twitch"

View File

@@ -30,6 +30,7 @@ body:
- "@auth/mikro-orm-adapter"
- "@auth/mongodb-adapter"
- "@auth/neo4j-adapter"
- "@auth/pg-adapter"
- "@auth/pouchdb-adapter"
- "@auth/prisma-adapter"
- "@auth/sequelize-adapter"

View File

@@ -1,72 +0,0 @@
name: Feature Request
description: Suggest an idea for NextAuth.js
labels: [triage, enhancement]
# note: markdown sections will NOT appear as part of the issue as per documentation, rather they provide context to the user
# https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#markdown
body:
- type: markdown
attributes:
value: |
**NOTE:** Issues that are potentially security related should be reported to us by following the [Security guidelines](https://next-auth.js.org/security) rather than on GitHub.
Thank you very much for reaching out to us regarding the awesome feature that you believe should be included in the NextAuth.js library.
_NOTE: Feature requests are converted to [discussions (Ideas 💡)](https://github.com/nextauthjs/next-auth/discussions/categories/ideas). Make sure your idea hasn't been asked yet, and upvote the existing one before opening a new instead._
### Important :exclamation:
Please proceed by providing the following information:
- type: textarea
id: description
attributes:
label: Description 📓
description: Please provide a more in-depth description of the feature proposed.
validations:
required: true
- type: markdown
attributes:
value: |
Make sure you provide plenty of [links]() to external documentation and inline code examples like so:
```js
function myAwesomeNextAuthFeature() {
return 💚
}
```
Take time thinking about what you want to say and help us understand your proposal making sure that this description contains:
- **purpose of the feature**
- **potential problems**
- **potential alternatives**
- type: textarea
id: reproduction
attributes:
label: How to reproduce ☕️
description: If you have a CodeSandbox playground or some code snippets to help us visualize your idea better, please provide it here.
validations:
required: true
- type: markdown
attributes:
value: |
You can use one of the templates set up on **CodeSandbox** to better illustrate your idea:
- [`next-auth-example`](https://codesandbox.io/s/next-auth-example-1kktb)
- type: dropdown
id: pr
attributes:
label: Contributing 🙌🏽
multiple: false
options:
- "Yes, I am willing to help implement this feature in a PR"
- "No, I am afraid I cannot help regarding this"
validations:
required: true
- type: markdown
attributes:
value: |
It takes a lot of work 🏋🏻‍♀️ maintaining a library like `next-auth`; any contribution is more than welcome 💚

View File

@@ -1,57 +0,0 @@
name: TypeScript
description: Ask a question about NextAuth.js TypeScript integration
labels: [question, TypeScript]
assignees: [lluia, balazsorban44]
# note: markdown sections will NOT appear as part of the issue as per documentation, rather they provide context to the user
# https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#markdown
body:
- type: textarea
id: question
attributes:
label: Question 💬
description: Please provide an in-depth description of the question you have when using NextAuth.js on a Typescript project or when consuming the built-in types for `next-auth`.
validations:
required: true
- type: markdown
attributes:
value: |
**NOTE:** Issues that are potentially security related should be reported to us by following the [Security guidelines](https://next-auth.js.org/security) rather than on GitHub.
Make sure you [link]() to external documentation if necessary and provide inline code examples like so:
```js
function myAwesomeNextAuthFeature() {
return 💚
}
```
**NOTE:** Questions will be converted to Discussions. You can find them [here](https://github.com/nextauthjs/next-auth/discussions)!
- type: textarea
id: codesandbox
attributes:
label: How to reproduce ☕️
description: Please provide a link to a minimal reproduction or code snippets that represents your question
validations:
required: true
- type: markdown
attributes:
value: |
We encourage you to use the template set-up on **CodeSandbox** as a playground to represent your question or doubt:
- [`next-auth-example`](https://codesandbox.io/s/next-auth-example-1kktb)
- type: dropdown
id: pr
attributes:
label: Contributing 🙌🏽
multiple: false
options:
- "Yes, I am willing to help answer this question in a PR"
- "No, I am afraid I cannot help regarding this"
validations:
required: true
- type: markdown
attributes:
value: |
It takes a lot of work 🏋🏻‍♀️ maintaining a library like `next-auth`; any contribution is more than welcome 💚

View File

@@ -1,61 +0,0 @@
name: Question
description: Ask a question about NextAuth.js or for help using it
labels: [question]
# note: markdown sections will NOT appear as part of the issue as per documentation, rather they provide context to the user
# https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#markdown
body:
- type: markdown
attributes:
value: |
**NOTE:** Issues that are potentially security related should be reported to us by following the [Security guidelines](https://next-auth.js.org/security) rather than on GitHub.
We are glad that you have a question about this library. Please provide the following information:
- type: textarea
id: question
attributes:
label: Question 💬
description: Please provide an in-depth description of the question you have.
validations:
required: true
- type: markdown
attributes:
value: |
Make sure you [link]() to external documentation if necessary and provide inline code examples like so:
```js
function myAwesomeNextAuthFeature() {
return 💚
}
```
**NOTE:** Questions will be converted to Discussions. You can find them [here](https://github.com/nextauthjs/next-auth/discussions)!
- type: textarea
id: reproduction
attributes:
label: How to reproduce ☕️
description: Please provide a link to a minimal reproduction or code snippets that represents your question
validations:
required: true
- type: markdown
attributes:
value: |
We encourage you to use the template set-up on **CodeSandbox** as a playground to represent your question or doubt:
- [`next-auth-example`](https://codesandbox.io/s/next-auth-example-1kktb)
- type: dropdown
id: pr
attributes:
label: Contributing 🙌🏽
multiple: false
options:
- "Yes, I am willing to help answer this question in a PR"
- "No, I am afraid I cannot help regarding this"
validations:
required: true
- type: markdown
attributes:
value: |
It takes a lot of work 🏋🏻‍♀️ maintaining a library like `next-auth`; any contribution is more than welcome 💚

View File

@@ -1 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Ask a question
url: https://github.com/nextauthjs/next-auth/discussions/new?category=questions
about: Ask questions and discuss with other community members
- name: Feature request
url: https://github.com/nextauthjs/next-auth/discussions/new?category=ideas
about: Feature requests should be opened as discussions

View File

@@ -1,2 +0,0 @@
!dist
!package-lock.json

File diff suppressed because one or more lines are too long

View File

@@ -1,653 +0,0 @@
@actions/core
MIT
The MIT License (MIT)
Copyright 2019 GitHub
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@actions/github
MIT
The MIT License (MIT)
Copyright 2019 GitHub
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@actions/http-client
MIT
Actions Http Client for Node.js
Copyright (c) GitHub, Inc.
All rights reserved.
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@octokit/auth-token
MIT
The MIT License
Copyright (c) 2019 Octokit contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@octokit/core
MIT
The MIT License
Copyright (c) 2019 Octokit contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@octokit/endpoint
MIT
The MIT License
Copyright (c) 2018 Octokit contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@octokit/graphql
MIT
The MIT License
Copyright (c) 2018 Octokit contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@octokit/plugin-paginate-rest
MIT
MIT License Copyright (c) 2019 Octokit contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@octokit/plugin-rest-endpoint-methods
MIT
MIT License Copyright (c) 2019 Octokit contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@octokit/request
MIT
The MIT License
Copyright (c) 2018 Octokit contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@octokit/request-error
MIT
The MIT License
Copyright (c) 2019 Octokit contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@vercel/ncc
MIT
Copyright 2018 ZEIT, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
before-after-hook
Apache-2.0
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2018 Gregor Martynus and other contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
deprecation
ISC
The ISC License
Copyright (c) Gregor Martynus and contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
is-plain-object
MIT
The MIT License (MIT)
Copyright (c) 2014-2017, Jon Schlinkert.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
node-fetch
MIT
The MIT License (MIT)
Copyright (c) 2016 David Frank
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
once
ISC
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
root
ISC License
Copyright (c) 2022-2023, Balázs Orbán
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
tr46
MIT
tunnel
MIT
The MIT License (MIT)
Copyright (c) 2012 Koichi Kobayashi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
universal-user-agent
ISC
# [ISC License](https://spdx.org/licenses/ISC)
Copyright (c) 2018, Gregor Martynus (https://github.com/gr2m)
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
uuid
MIT
The MIT License (MIT)
Copyright (c) 2010-2020 Robert Kieffer and other contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
webidl-conversions
BSD-2-Clause
# The BSD 2-Clause License
Copyright (c) 2014, Domenic Denicola
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
whatwg-url
MIT
The MIT License (MIT)
Copyright (c) 20152016 Sebastian Mayr
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
wrappy
ISC
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@@ -1,445 +0,0 @@
{
"name": "issue-validator",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"dependencies": {
"@actions/core": "1.10.0",
"@actions/github": "5.1.1"
},
"devDependencies": {
"@vercel/ncc": "0.34.0"
}
},
"node_modules/@actions/core": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz",
"integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==",
"dependencies": {
"@actions/http-client": "^2.0.1",
"uuid": "^8.3.2"
}
},
"node_modules/@actions/github": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.1.tgz",
"integrity": "sha512-Nk59rMDoJaV+mHCOJPXuvB1zIbomlKS0dmSIqPGxd0enAXBnOfn4VWF+CGtRCwXZG9Epa54tZA7VIRlJDS8A6g==",
"dependencies": {
"@actions/http-client": "^2.0.1",
"@octokit/core": "^3.6.0",
"@octokit/plugin-paginate-rest": "^2.17.0",
"@octokit/plugin-rest-endpoint-methods": "^5.13.0"
}
},
"node_modules/@actions/http-client": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
"dependencies": {
"tunnel": "^0.0.6"
}
},
"node_modules/@octokit/auth-token": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz",
"integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==",
"dependencies": {
"@octokit/types": "^6.0.3"
}
},
"node_modules/@octokit/core": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz",
"integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==",
"dependencies": {
"@octokit/auth-token": "^2.4.4",
"@octokit/graphql": "^4.5.8",
"@octokit/request": "^5.6.3",
"@octokit/request-error": "^2.0.5",
"@octokit/types": "^6.0.3",
"before-after-hook": "^2.2.0",
"universal-user-agent": "^6.0.0"
}
},
"node_modules/@octokit/endpoint": {
"version": "6.0.12",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz",
"integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==",
"dependencies": {
"@octokit/types": "^6.0.3",
"is-plain-object": "^5.0.0",
"universal-user-agent": "^6.0.0"
}
},
"node_modules/@octokit/graphql": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz",
"integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==",
"dependencies": {
"@octokit/request": "^5.6.0",
"@octokit/types": "^6.0.3",
"universal-user-agent": "^6.0.0"
}
},
"node_modules/@octokit/openapi-types": {
"version": "12.10.1",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.10.1.tgz",
"integrity": "sha512-P+SukKanjFY0ZhsK6wSVnQmxTP2eVPPE8OPSNuxaMYtgVzwJZgfGdwlYjf4RlRU4vLEw4ts2fsE2icG4nZ5ddQ=="
},
"node_modules/@octokit/plugin-paginate-rest": {
"version": "2.21.3",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz",
"integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==",
"dependencies": {
"@octokit/types": "^6.40.0"
},
"peerDependencies": {
"@octokit/core": ">=2"
}
},
"node_modules/@octokit/plugin-rest-endpoint-methods": {
"version": "5.16.2",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz",
"integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==",
"dependencies": {
"@octokit/types": "^6.39.0",
"deprecation": "^2.3.1"
},
"peerDependencies": {
"@octokit/core": ">=3"
}
},
"node_modules/@octokit/request": {
"version": "5.6.3",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz",
"integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==",
"dependencies": {
"@octokit/endpoint": "^6.0.1",
"@octokit/request-error": "^2.1.0",
"@octokit/types": "^6.16.1",
"is-plain-object": "^5.0.0",
"node-fetch": "^2.6.7",
"universal-user-agent": "^6.0.0"
}
},
"node_modules/@octokit/request-error": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz",
"integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==",
"dependencies": {
"@octokit/types": "^6.0.3",
"deprecation": "^2.0.0",
"once": "^1.4.0"
}
},
"node_modules/@octokit/types": {
"version": "6.40.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.40.0.tgz",
"integrity": "sha512-MFZOU5r8SwgJWDMhrLUSvyJPtVsqA6VnbVI3TNbsmw+Jnvrktzvq2fYES/6RiJA/5Ykdwq4mJmtlYUfW7CGjmw==",
"dependencies": {
"@octokit/openapi-types": "^12.10.0"
}
},
"node_modules/@vercel/ncc": {
"version": "0.34.0",
"resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.34.0.tgz",
"integrity": "sha512-G9h5ZLBJ/V57Ou9vz5hI8pda/YQX5HQszCs3AmIus3XzsmRn/0Ptic5otD3xVST8QLKk7AMk7AqpsyQGN7MZ9A==",
"dev": true,
"bin": {
"ncc": "dist/ncc/cli.js"
}
},
"node_modules/before-after-hook": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz",
"integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ=="
},
"node_modules/deprecation": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
"integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
},
"node_modules/is-plain-object": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/node-fetch": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"node_modules/tunnel": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
"engines": {
"node": ">=0.6.11 <=0.7.0 || >=0.7.3"
}
},
"node_modules/universal-user-agent": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
"integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
},
"node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
}
},
"dependencies": {
"@actions/core": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz",
"integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==",
"requires": {
"@actions/http-client": "^2.0.1",
"uuid": "^8.3.2"
}
},
"@actions/github": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.1.tgz",
"integrity": "sha512-Nk59rMDoJaV+mHCOJPXuvB1zIbomlKS0dmSIqPGxd0enAXBnOfn4VWF+CGtRCwXZG9Epa54tZA7VIRlJDS8A6g==",
"requires": {
"@actions/http-client": "^2.0.1",
"@octokit/core": "^3.6.0",
"@octokit/plugin-paginate-rest": "^2.17.0",
"@octokit/plugin-rest-endpoint-methods": "^5.13.0"
}
},
"@actions/http-client": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
"requires": {
"tunnel": "^0.0.6"
}
},
"@octokit/auth-token": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz",
"integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==",
"requires": {
"@octokit/types": "^6.0.3"
}
},
"@octokit/core": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz",
"integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==",
"requires": {
"@octokit/auth-token": "^2.4.4",
"@octokit/graphql": "^4.5.8",
"@octokit/request": "^5.6.3",
"@octokit/request-error": "^2.0.5",
"@octokit/types": "^6.0.3",
"before-after-hook": "^2.2.0",
"universal-user-agent": "^6.0.0"
}
},
"@octokit/endpoint": {
"version": "6.0.12",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz",
"integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==",
"requires": {
"@octokit/types": "^6.0.3",
"is-plain-object": "^5.0.0",
"universal-user-agent": "^6.0.0"
}
},
"@octokit/graphql": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz",
"integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==",
"requires": {
"@octokit/request": "^5.6.0",
"@octokit/types": "^6.0.3",
"universal-user-agent": "^6.0.0"
}
},
"@octokit/openapi-types": {
"version": "12.10.1",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.10.1.tgz",
"integrity": "sha512-P+SukKanjFY0ZhsK6wSVnQmxTP2eVPPE8OPSNuxaMYtgVzwJZgfGdwlYjf4RlRU4vLEw4ts2fsE2icG4nZ5ddQ=="
},
"@octokit/plugin-paginate-rest": {
"version": "2.21.3",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz",
"integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==",
"requires": {
"@octokit/types": "^6.40.0"
}
},
"@octokit/plugin-rest-endpoint-methods": {
"version": "5.16.2",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz",
"integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==",
"requires": {
"@octokit/types": "^6.39.0",
"deprecation": "^2.3.1"
}
},
"@octokit/request": {
"version": "5.6.3",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz",
"integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==",
"requires": {
"@octokit/endpoint": "^6.0.1",
"@octokit/request-error": "^2.1.0",
"@octokit/types": "^6.16.1",
"is-plain-object": "^5.0.0",
"node-fetch": "^2.6.7",
"universal-user-agent": "^6.0.0"
}
},
"@octokit/request-error": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz",
"integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==",
"requires": {
"@octokit/types": "^6.0.3",
"deprecation": "^2.0.0",
"once": "^1.4.0"
}
},
"@octokit/types": {
"version": "6.40.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.40.0.tgz",
"integrity": "sha512-MFZOU5r8SwgJWDMhrLUSvyJPtVsqA6VnbVI3TNbsmw+Jnvrktzvq2fYES/6RiJA/5Ykdwq4mJmtlYUfW7CGjmw==",
"requires": {
"@octokit/openapi-types": "^12.10.0"
}
},
"@vercel/ncc": {
"version": "0.34.0",
"resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.34.0.tgz",
"integrity": "sha512-G9h5ZLBJ/V57Ou9vz5hI8pda/YQX5HQszCs3AmIus3XzsmRn/0Ptic5otD3xVST8QLKk7AMk7AqpsyQGN7MZ9A==",
"dev": true
},
"before-after-hook": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz",
"integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ=="
},
"deprecation": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
"integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
},
"is-plain-object": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
},
"node-fetch": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"requires": {
"whatwg-url": "^5.0.0"
}
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"requires": {
"wrappy": "1"
}
},
"tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"tunnel": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
},
"universal-user-agent": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
"integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
},
"uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
},
"webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"requires": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
}
}
}

View File

@@ -1,14 +0,0 @@
{
"private": true,
"exports": "./index.mjs",
"scripts": {
"build": "ncc -m -o . build src/index.mjs --license licenses.txt"
},
"devDependencies": {
"@vercel/ncc": "0.34.0"
},
"dependencies": {
"@actions/core": "1.10.0",
"@actions/github": "5.1.1"
}
}

View File

@@ -1,37 +0,0 @@
We cannot recreate the issue with the provided information. **Please add a reproduction in order for us to be able to investigate.**
### **Why was this issue marked with the `incomplete` label?**
To be able to investigate, we need access to a reproduction to identify what triggered the issue. We prefer a link to a public GitHub repository ([template](https://github.com/nextauthjs/next-auth-example)), but you can also use a tool like [CodeSandbox](https://codesandbox.io/s/github/nextauthjs/next-auth-example/tree/main) or [StackBlitz](https://stackblitz.com/fork/github/nextauthjs/next-auth-example).
To make sure the issue is resolved as quickly as possible, please make sure that the reproduction is as **minimal** as possible. This means that you should **remove unnecessary code, files, and dependencies** that do not contribute to the issue.
Please test your reproduction against the latest version of NextAuth.js (`next-auth@latest`) to make sure your issue has not already been fixed.
### **I added a link, why was it still marked?**
Ensure the link is pointing to a codebase that is accessible (e.g. not a private repository). "[example.com](http://example.com/)", "n/a", "will add later", etc. are not acceptable links -- we need to see a public codebase. See the above section for accepted links.
### **What happens if I don't provide a sufficient minimal reproduction?**
Issues with the `incomplete` label that receives no meaningful activity (e.g. new comments with a reproduction link) are closed after 7 days.
If your issue has _not_ been resolved in that time and it has been closed/locked, please open a new issue with the required reproduction. (It's less likely that we check back on already closed issues.)
### **I did not open this issue, but it is relevant to me, what can I do to help?**
Anyone experiencing the same issue is welcome to provide a minimal reproduction following the above steps. Furthermore, you can upvote the issue using the :+1: reaction on the topmost comment (please **do not** comment "I have the same issue" without repro steps). Then, we can sort issues by votes to prioritize.
### **I think my reproduction is good enough, why aren't you looking into it quicker?**
We look into every NextAuth.js issue and constantly monitor open issues for new comments.
However, sometimes we might miss one or two. We apologize, and kindly ask you to refrain from tagging core maintainers, as that will usually not result in increased priority.
Upvoting issues to show your interest will help us prioritize and address them as quickly as possible. That said, every issue is important to us, and if an issue gets closed by accident, we encourage you to open a new one linking to the old issue and we will look into it.
### **Useful Resources**
- [How to create a Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve)
- [Reporting a NextAuth.js bug](https://github.com/nextauthjs/next-auth/blob/main/.github/ISSUE_TEMPLATE/1_bug_framework.yml)
- [How to Contribute to Open Source (Next.js)](https://www.youtube.com/watch?v=cuoNzXFLitc)

View File

@@ -1,91 +0,0 @@
// @ts-check
// @ts-expect-error
import * as github from "@actions/github"
// @ts-expect-error
import * as core from "@actions/core"
import { readFileSync } from "node:fs"
import { join } from "node:path"
const addReproductionLabel = "incomplete"
/**
* @typedef {{
* id :number
* node_id :string
* url :string
* name :string
* description :string
* color :string
* default :boolean
* }} Label
*
* @typedef {{
* pull_request: any
* issue?: {body: string, number: number, labels: Label[]}
* label: Label
* }} Payload
*
* @typedef {{
* payload: Payload
* repo: any
* }} Context
*/
async function run() {
try {
/** @type {Context} */
const { payload, repo } = github.context
const {
issue,
pull_request,
label: { name: newLabel },
} = payload
if (pull_request || !issue?.body || !process.env.GITHUB_TOKEN) return
const labels = issue.labels.map((l) => l.name)
// const isBugReport =
// labels.includes(bugLabel) || newLabel === bugLabel || !labels.length
if (
// !(isBugReport && issue.number > 43554) &&
![addReproductionLabel].includes(newLabel) &&
!labels.includes(addReproductionLabel)
) {
return core.info(
"Not a bug report or not manually labeled or already labeled."
)
}
const client = github.getOctokit(process.env.GITHUB_TOKEN).rest
const issueCommon = { ...repo, issue_number: issue.number }
if (
newLabel === addReproductionLabel
// || !hasValidRepro
) {
await Promise.all([
client.issues.addLabels({
...issueCommon,
labels: [addReproductionLabel],
}),
client.issues.createComment({
...issueCommon,
body: readFileSync(
join(
"/home/runner/work/next-auth/next-auth/.github/actions/issue-validator/repro.md"
),
"utf8"
),
}),
])
return core.info(
"Commented on issue, because it did not have a sufficient reproduction."
)
}
} catch (error) {
core.setFailed(error.message)
}
}
run()

51
.github/invalid-reproduction.md vendored Normal file
View File

@@ -0,0 +1,51 @@
We could not detect a valid reproduction link. **Make sure to follow the bug report template carefully.**
### Why was this issue closed?
To be able to investigate, we need access to a reproduction to identify what triggered the issue. We need a link to a **public** GitHub repository. Example: ([NextAuth.js example repository](https://github.com/nextauthjs/next-auth-example)).
The bug template that you filled out has a section called "Reproduction URL", which is where you should provide the link to the reproduction.
- If you did not provide a link or the link you provided is not valid, we will close the issue.
- If you provide a link to a private repository, we will close the issue.
- If you provide a link to a repository but not in the correct section, we will close the issue.
### What should I do?
Depending on the reason the issue was closed, you can do the following:
- If you did not provide a link, please open a new issue with a link to a reproduction.
- If you provided a link to a private repository, please open a new issue with a link to a public repository.
- If you provided a link to a repository but not in the correct section, please open a new issue with a link to a reproduction in the correct section.
**In general, assume that we should not go through a lengthy onboarding process at your company code only to be able to verify an issue.**
### My repository is private and cannot make it public
In most cases, a private repo will not be a sufficient **minimal reproduction**, as this codebase might contain a lot of unrelated parts that would make our investigation take longer. Please do **not** make it public. Instead, create a new repository using the templates above, adding the relevant code to reproduce the issue. Common things to look out for:
- Remove any code that is not related to the issue. (pages, API Routes, components, etc.)
- Remove any dependencies that are not related to the issue.
- Remove any third-party service that would require us to sign up for an account to reproduce the issue.
- Remove any environment variables that are not related to the issue.
- Remove private packages that we do not have access to.
- If the issue is not related to a monorepo specifically, try to reproduce the issue without a complex monorepo setup
### I did not open this issue, but it is relevant to me, what can I do to help?
Anyone experiencing the same issue is welcome to provide a minimal reproduction following the above steps by opening a new issue.
### I think my reproduction is good enough, why aren't you looking into it quickly?
We look into every issue and monitor open issues for new comments.
However, sometimes we might miss a few due to the popularity/high traffic of the repository. We apologize, and kindly ask you to refrain from tagging core maintainers, as that will usually not result in increased priority.
Upvoting issues to show your interest will help us prioritize and address them as quickly as possible. That said, every issue is important to us, and if an issue gets closed by accident, we encourage you to open a new one linking to the old issue and we will look into it.
### Useful Resources
- [How to create a Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve)
- [Bug report: Framework](https://github.com/nextauthjs/next-auth/issues/new?assignees=&labels=triage&projects=&template=1_bug_framework.yml)
- [Bug report: Provider](https://github.com/nextauthjs/next-auth/issues/new?assignees=&labels=triage%2Cproviders&projects=&template=2_bug_provider.yml)
- [Bug report: Adapter](https://github.com/nextauthjs/next-auth/issues/new?assignees=&labels=triage%2Cadapters&projects=&template=3_bug_adapter.yml)

View File

@@ -1,49 +0,0 @@
# https://github.com/github/issue-labeler#basic-examples
dgraph:
- "@auth/dgraph-adapter"
drizzle:
- "@auth/drizzle-adapter"
dynamodb:
- "@auth/dynamodb-adapter"
fauna:
- "@auth/fauna-adapter"
firebase:
- "@auth/firebase-adapter"
kysely:
- "@auth/kysely-adapter"
mikro-orm:
- "@auth/mikro-orm-adapter"
mongodb:
- "@auth/mongodb-adapter"
neo4j:
- "@auth/neo4j-adapter"
pouchdb:
- "@auth/pouchdb-adapter"
prisma:
- "@auth/prisma-adapter"
sequelize:
- "@auth/sequelize-adapter"
supabase:
- "@auth/supabase-adapter"
typeorm:
- "@auth/typeorm-adapter"
upstash-redis:
- "@auth/upstash-redis-adapter"
xata:
- "@auth/xata-adapter"

View File

@@ -12,6 +12,7 @@ legacy: ["packages/next-auth/**/*"]
mikro-orm: ["packages/adapter-mikro-orm/**/*"]
mongodb: ["packages/adapter-mongodb/**/*"]
neo4j: ["packages/adapter-neo4j/**/*"]
pg: ["packages/adapter-pg/**/*"]
playgrounds: ["apps/playgrounds/**/*"]
pouchdb: ["packages/adapter-pouchdb/**/*"]
prisma: ["packages/adapter-prisma/**/*"]

View File

@@ -1,18 +0,0 @@
# https://github.com/github/issue-labeler#create-workflow
name: Label issues
on:
issues:
types: [opened]
jobs:
triage:
name: Triage
runs-on: ubuntu-latest
steps:
- uses: github/issue-labeler@v2.5
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
configuration-path: ".github/issue-labeler.yml"
enable-versioned-regex: 0

View File

@@ -1,17 +0,0 @@
name: Validate issue
on:
issues:
types: [labeled]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Run issue validator
run: node /home/runner/work/next-auth/next-auth/.github/actions/issue-validator/index.mjs
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

23
.github/workflows/triage.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
name: Triage issue
on:
issues:
types: [labeled, opened]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
permissions:
issues: write
jobs:
triage:
runs-on: ubuntu-latest
steps:
- name: Nissuer
uses: balazsorban44/nissuer@1.3.5
with:
label-area-prefix: ""
label-area-section: "[Provider|Adapter] type(.*)### Environment"
label-comments: '{ "incomplete": ".github/invalid-reproduction.md" }'
reproduction-link-section: "### Reproduction URL(.*)### Describe the issue"
reproduction-invalid-label: "invalid reproduction"

View File

@@ -59,6 +59,10 @@ WIKIMEDIA_SECRET=
YANDEX_ID=
YANDEX_SECRET=
# ClickUp OAuth. https://clickup.com/api/
CLICK_UP_ID=
CLICK_UP_SECRET=
# Example configuration for a Gmail account (will need SMTP enabled)
EMAIL_SERVER=smtps://user@gmail.com:password@smtp.gmail.com:465
EMAIL_FROM=user@gmail.com

View File

@@ -2,3 +2,4 @@ node_modules/
/test-results/
/playwright-report/
/playwright/.cache/
dbschema/edgeql-js

View File

@@ -0,0 +1,71 @@
module default {
type User {
property name -> str;
required property email -> str {
constraint exclusive;
}
property emailVerified -> datetime;
property image -> str;
multi link accounts := .<user[is Account];
multi link sessions := .<user[is Session];
property createdAt -> datetime {
default := datetime_current();
};
}
type Account {
required property userId := .user.id;
required property type -> str;
required property provider -> str;
required property providerAccountId -> str {
constraint exclusive;
};
property refresh_token -> str;
property access_token -> str;
property expires_at -> int64;
property token_type -> str;
property scope -> str;
property id_token -> str;
property session_state -> str;
required link user -> User {
on target delete delete source;
};
property createdAt -> datetime {
default := datetime_current();
};
constraint exclusive on ((.provider, .providerAccountId))
}
type Session {
required property sessionToken -> str {
constraint exclusive;
}
required property userId := .user.id;
required property expires -> datetime;
required link user -> User {
on target delete delete source;
};
property createdAt -> datetime {
default := datetime_current();
};
}
type VerificationToken {
required property identifier -> str;
required property token -> str {
constraint exclusive;
}
required property expires -> datetime;
property createdAt -> datetime {
default := datetime_current();
};
constraint exclusive on ((.identifier, .token))
}
}
# Disable the application of access policies within access policies
# themselves. This behavior will become the default in EdgeDB 3.0.
# See: https://www.edgedb.com/docs/reference/ddl/access_policies#nonrecursive
using future nonrecursive_access_policies;

View File

@@ -0,0 +1,2 @@
[edgedb]
server-version = "2.6"

View File

@@ -15,11 +15,13 @@
"license": "ISC",
"dependencies": {
"@auth/core": "workspace:*",
"@auth/edgedb-adapter": "workspace:*",
"@auth/fauna-adapter": "workspace:*",
"@auth/prisma-adapter": "workspace:*",
"@auth/supabase-adapter": "workspace:*",
"@auth/typeorm-adapter": "workspace:*",
"@prisma/client": "^3",
"edgedb": "^1.0.1",
"@supabase/supabase-js": "^2.0.5",
"faunadb": "^4",
"next": "13.4.0",
@@ -29,6 +31,7 @@
"react-dom": "^18"
},
"devDependencies": {
"@edgedb/generate": "^0.0.4",
"@playwright/test": "1.29.2",
"@types/jsonwebtoken": "^8.5.5",
"@types/react": "18.0.37",

View File

@@ -39,7 +39,7 @@ import Yandex from "@auth/core/providers/yandex"
import Vk from "@auth/core/providers/vk"
import Wikimedia from "@auth/core/providers/wikimedia"
import WorkOS from "@auth/core/providers/workos"
import ClickUp from '@auth/core/providers/click-up'
// // Prisma
// import { PrismaClient } from "@prisma/client"
// import { PrismaAdapter } from "@auth/prisma-adapter"
@@ -71,6 +71,12 @@ import WorkOS from "@auth/core/providers/workos"
// secret: process.env.SUPABASE_SERVICE_ROLE_KEY,
// })
// // EdgeDB
// import { EdgeDBAdapter } from "@auth/edgedb-adapter"
// import { createHttpClient } from "edgedb"
// const client = createHttpClient()
// const adapter = EdgeDBAdapter(client)
export const authConfig: AuthConfig = {
// adapter,
debug: process.env.NODE_ENV !== "production",
@@ -131,6 +137,7 @@ export const authConfig: AuthConfig = {
Vk({ clientId: process.env.VK_ID, clientSecret: process.env.VK_SECRET }),
Wikimedia({ clientId: process.env.WIKIMEDIA_ID, clientSecret: process.env.WIKIMEDIA_SECRET }),
WorkOS({ clientId: process.env.WORKOS_ID, clientSecret: process.env.WORKOS_SECRET }),
ClickUp({ clientId: process.env.CLICK_UP_ID, clientSecret: process.env.CLICK_UP_SECRET })
],
// debug: process.env.NODE_ENV !== "production",
}

View File

@@ -0,0 +1,5 @@
import NextAuth from "next-auth/next"
import { config } from "auth"
const handler = NextAuth(config)
export { handler as GET, handler as POST }

View File

@@ -0,0 +1,293 @@
import type { GetServerSidePropsContext, NextApiRequest, NextApiResponse } from "next"
import type { NextAuthOptions as NextAuthConfig } from "next-auth"
import { getServerSession } from "next-auth"
import Apple from "next-auth/providers/apple"
import Atlassian from "next-auth/providers/atlassian"
import Auth0 from "next-auth/providers/auth0"
import Authentik from "next-auth/providers/authentik"
import AzureAD from "next-auth/providers/azure-ad"
import AzureB2C from "next-auth/providers/azure-ad-b2c"
import Battlenet from "next-auth/providers/battlenet"
import Box from "next-auth/providers/box"
import BoxyHQSAML from "next-auth/providers/boxyhq-saml"
import Bungie from "next-auth/providers/bungie"
import Cognito from "next-auth/providers/cognito"
import Coinbase from "next-auth/providers/coinbase"
import Discord from "next-auth/providers/discord"
import Dropbox from "next-auth/providers/dropbox"
import DuendeIDS6 from "next-auth/providers/duende-identity-server6"
import Eveonline from "next-auth/providers/eveonline"
import Facebook from "next-auth/providers/facebook"
import Faceit from "next-auth/providers/faceit"
import FortyTwoSchool from "next-auth/providers/42-school"
import Foursquare from "next-auth/providers/foursquare"
import Freshbooks from "next-auth/providers/freshbooks"
import Fusionauth from "next-auth/providers/fusionauth"
import GitHub from "next-auth/providers/github"
import Gitlab from "next-auth/providers/gitlab"
import Google from "next-auth/providers/google"
import Hubspot from "next-auth/providers/hubspot"
import Instagram from "next-auth/providers/instagram"
import Kakao from "next-auth/providers/kakao"
import Keycloak from "next-auth/providers/keycloak"
import Line from "next-auth/providers/line"
import LinkedIn from "next-auth/providers/linkedin"
import Mailchimp from "next-auth/providers/mailchimp"
import Mailru from "next-auth/providers/mailru"
import Medium from "next-auth/providers/medium"
import Naver from "next-auth/providers/naver"
import Netlify from "next-auth/providers/netlify"
import Okta from "next-auth/providers/okta"
import Onelogin from "next-auth/providers/onelogin"
import Osso from "next-auth/providers/osso"
import Osu from "next-auth/providers/osu"
import Passage from "next-auth/providers/passage"
import Patreon from "next-auth/providers/patreon"
import Pinterest from "next-auth/providers/pinterest"
import Pipedrive from "next-auth/providers/pipedrive"
import Reddit from "next-auth/providers/reddit"
import Salesforce from "next-auth/providers/salesforce"
import Slack from "next-auth/providers/slack"
import Spotify from "next-auth/providers/spotify"
import Strava from "next-auth/providers/strava"
import Todoist from "next-auth/providers/todoist"
import Trakt from "next-auth/providers/trakt"
import Twitch from "next-auth/providers/twitch"
import Twitter from "next-auth/providers/twitter"
import UnitedEffects from "next-auth/providers/united-effects"
import Vk from "next-auth/providers/vk"
import Wikimedia from "next-auth/providers/wikimedia"
import Wordpress from "next-auth/providers/wordpress"
import WorkOS from "next-auth/providers/workos"
import Yandex from "next-auth/providers/yandex"
import Zitadel from "next-auth/providers/zitadel"
import Zoho from "next-auth/providers/zoho"
import Zoom from "next-auth/providers/zoom"
// Read more at: https://next-auth.js.org/getting-started/typescript#module-augmentation
declare module "next-auth/jwt" {
interface JWT {
/** The user's role. */
userRole?: "admin"
}
}
export const config = {
providers: [
Apple({ clientId: process.env.AUTH_APPLE_ID, clientSecret: process.env.AUTH_APPLE_SECRET }),
Atlassian({ clientId: process.env.AUTH_ATLASSIAN_ID, clientSecret: process.env.AUTH_ATLASSIAN_SECRET }),
Auth0({ clientId: process.env.AUTH_AUTH0_ID, clientSecret: process.env.AUTH_AUTH0_SECRET, issuer: process.env.AUTH_AUTH0_ISSUER }),
Authentik({ clientId: process.env.AUTH_AUTHENTIK_ID, clientSecret: process.env.AUTH_AUTHENTIK_SECRET }),
AzureAD({ clientId: process.env.AUTH_AZUREAD_ID, clientSecret: process.env.AUTH_AZUREAD_SECRET }),
AzureB2C({ clientId: process.env.AUTH_AZUREB2C_ID, clientSecret: process.env.AUTH_AZUREB2C_SECRET }),
Battlenet({ clientId: process.env.AUTH_BN_ID, clientSecret: process.env.AUTH_BN_SECRET, issuer: process.env.AUTH_BN_ISSUER }),
Box({ clientId: process.env.AUTH_BOX_ID, clientSecret: process.env.AUTH_BOX_SECRET }),
BoxyHQSAML({ clientId: process.env.AUTH_BOXYHQ_ID, clientSecret: process.env.AUTH_BOXYHQ_SECRET, issuer: process.env.AUTH_BOXYHQ_ISSUER }),
Bungie({ clientId: process.env.AUTH_BUNGIE_ID, clientSecret: process.env.AUTH_BUNGIE_SECRET }),
Cognito({ clientId: process.env.AUTH_COGNITO_ID, clientSecret: process.env.AUTH_COGNITO_SECRET }),
Coinbase({ clientId: process.env.AUTH_COINBASE_ID, clientSecret: process.env.AUTH_COINBASE_SECRET }),
Discord({ clientId: process.env.AUTH_DISCORD_ID, clientSecret: process.env.AUTH_DISCORD_SECRET }),
Dropbox({ clientId: process.env.AUTH_DROPBOX_ID, clientSecret: process.env.AUTH_DROPBOX_SECRET }),
DuendeIDS6({ clientId: process.env.AUTH_DUENDEIDS6_ID, clientSecret: process.env.AUTH_DUENDEIDS6_SECRET }),
Eveonline({ clientId: process.env.AUTH_EVEONLINE_ID, clientSecret: process.env.AUTH_EVEONLINE_SECRET }),
Facebook({ clientId: process.env.AUTH_FACEBOOK_ID, clientSecret: process.env.AUTH_FACEBOOK_SECRET }),
Faceit({ clientId: process.env.AUTH_FACEIT_ID, clientSecret: process.env.AUTH_FACEIT_SECRET }),
FortyTwoSchool({ clientId: process.env.AUTH_FORTYTWOSCHOOL_ID, clientSecret: process.env.AUTH_FORTYTWOSCHOOL_SECRET }),
Foursquare({ clientId: process.env.AUTH_FOURSQUARE_ID, clientSecret: process.env.AUTH_FOURSQUARE_SECRET }),
Freshbooks({ clientId: process.env.AUTH_FRESHBOOKS_ID, clientSecret: process.env.AUTH_FRESHBOOKS_SECRET }),
Fusionauth({ clientId: process.env.AUTH_FUSIONAUTH_ID, clientSecret: process.env.AUTH_FUSIONAUTH_SECRET }),
GitHub({ clientId: process.env.AUTH_GITHUB_ID, clientSecret: process.env.AUTH_GITHUB_SECRET }),
Gitlab({ clientId: process.env.AUTH_GITLAB_ID, clientSecret: process.env.AUTH_GITLAB_SECRET }),
Google({ clientId: process.env.AUTH_GOOGLE_ID, clientSecret: process.env.AUTH_GOOGLE_SECRET }),
Hubspot({ clientId: process.env.AUTH_HUBSPOT_ID, clientSecret: process.env.AUTH_HUBSPOT_SECRET }),
Instagram({ clientId: process.env.AUTH_INSTAGRAM_ID, clientSecret: process.env.AUTH_INSTAGRAM_SECRET }),
Kakao({ clientId: process.env.AUTH_KAKAO_ID, clientSecret: process.env.AUTH_KAKAO_SECRET }),
Keycloak({ clientId: process.env.AUTH_KEYCLOAK_ID, clientSecret: process.env.AUTH_KEYCLOAK_SECRET }),
Line({ clientId: process.env.AUTH_LINE_ID, clientSecret: process.env.AUTH_LINE_SECRET }),
LinkedIn({ clientId: process.env.AUTH_LINKEDIN_ID, clientSecret: process.env.AUTH_LINKEDIN_SECRET }),
Mailchimp({ clientId: process.env.AUTH_MAILCHIMP_ID, clientSecret: process.env.AUTH_MAILCHIMP_SECRET }),
Mailru({ clientId: process.env.AUTH_MAILRU_ID, clientSecret: process.env.AUTH_MAILRU_SECRET }),
Medium({ clientId: process.env.AUTH_MEDIUM_ID, clientSecret: process.env.AUTH_MEDIUM_SECRET }),
Naver({ clientId: process.env.AUTH_NAVER_ID, clientSecret: process.env.AUTH_NAVER_SECRET }),
Netlify({ clientId: process.env.AUTH_NETLIFY_ID, clientSecret: process.env.AUTH_NETLIFY_SECRET }),
Okta({ clientId: process.env.AUTH_OKTA_ID, clientSecret: process.env.AUTH_OKTA_SECRET }),
Onelogin({ clientId: process.env.AUTH_ONELOGIN_ID, clientSecret: process.env.AUTH_ONELOGIN_SECRET }),
Osso({ clientId: process.env.AUTH_OSSO_ID, clientSecret: process.env.AUTH_OSSO_SECRET, issuer: process.env.AUTH_OSSO_ISSUER }),
Osu({ clientId: process.env.AUTH_OSU_ID, clientSecret: process.env.AUTH_OSU_SECRET }),
Passage({ clientId: process.env.AUTH_PASSAGE_ID, clientSecret: process.env.AUTH_PASSAGE_SECRET, issuer: process.env.AUTH_PASSAGE_ISSUER }),
Patreon({ clientId: process.env.AUTH_PATREON_ID, clientSecret: process.env.AUTH_PATREON_SECRET }),
Pinterest({ clientId: process.env.AUTH_PINTEREST_ID, clientSecret: process.env.AUTH_PINTEREST_SECRET }),
Pipedrive({ clientId: process.env.AUTH_PIPEDRIVE_ID, clientSecret: process.env.AUTH_PIPEDRIVE_SECRET }),
Reddit({ clientId: process.env.AUTH_REDDIT_ID, clientSecret: process.env.AUTH_REDDIT_SECRET }),
Salesforce({ clientId: process.env.AUTH_SALESFORCE_ID, clientSecret: process.env.AUTH_SALESFORCE_SECRET }),
Slack({ clientId: process.env.AUTH_SLACK_ID, clientSecret: process.env.AUTH_SLACK_SECRET }),
Spotify({ clientId: process.env.AUTH_SPOTIFY_ID, clientSecret: process.env.AUTH_SPOTIFY_SECRET }),
Strava({ clientId: process.env.AUTH_STRAVA_ID, clientSecret: process.env.AUTH_STRAVA_SECRET }),
Todoist({ clientId: process.env.AUTH_TODOIST_ID, clientSecret: process.env.AUTH_TODOIST_SECRET }),
Trakt({ clientId: process.env.AUTH_TRAKT_ID, clientSecret: process.env.AUTH_TRAKT_SECRET }),
Twitch({ clientId: process.env.AUTH_TWITCH_ID, clientSecret: process.env.AUTH_TWITCH_SECRET }),
Twitter({ clientId: process.env.AUTH_TWITTER_ID, clientSecret: process.env.AUTH_TWITTER_SECRET, version: "2.0" }),
UnitedEffects({ clientId: process.env.AUTH_UE_ID, clientSecret: process.env.AUTH_UE_SECRET, issuer: process.env.AUTH_UE_ISSUER }),
Vk({ clientId: process.env.AUTH_VK_ID, clientSecret: process.env.AUTH_VK_SECRET }),
Wikimedia({ clientId: process.env.AUTH_WIKIMEDIA_ID, clientSecret: process.env.AUTH_WIKIMEDIA_SECRET }),
Wordpress({ clientId: process.env.AUTH_WORDPRESS_ID, clientSecret: process.env.AUTH_WORDPRESS_SECRET }),
WorkOS({ clientId: process.env.AUTH_WORKOS_ID, clientSecret: process.env.AUTH_WORKOS_SECRET }),
Yandex({ clientId: process.env.AUTH_YANDEX_ID, clientSecret: process.env.AUTH_YANDEX_SECRET }),
Zitadel({ clientId: process.env.AUTH_ZITADEL_ID, clientSecret: process.env.AUTH_ZITADEL_SECRET }),
Zoho({ clientId: process.env.AUTH_ZOHO_ID, clientSecret: process.env.AUTH_ZOHO_SECRET }),
Zoom({ clientId: process.env.AUTH_ZOOM_ID, clientSecret: process.env.AUTH_ZOOM_SECRET }),
],
callbacks: {
async jwt({ token }) {
token.userRole = "admin"
return token
},
},
} satisfies NextAuthConfig
// Helper function to get session without passing config every time
// https://next-auth.js.org/configuration/nextjs#getserversession
export function auth(...args: [GetServerSidePropsContext["req"], GetServerSidePropsContext["res"]] | [NextApiRequest, NextApiResponse] | []) {
return getServerSession(...args, config)
}
// We recommend doing your own environment variable validation
declare global {
namespace NodeJS {
export interface ProcessEnv {
NEXTAUTH_SECRET: string
AUTH_APPLE_ID: string
AUTH_APPLE_SECRET: string
AUTH_ATLASSIAN_ID: string
AUTH_ATLASSIAN_SECRET: string
AUTH_AUTH0_ID: string
AUTH_AUTH0_ISSUER: string
AUTH_AUTH0_SECRET: string
AUTH_AUTHENTIK_ID: string
AUTH_AUTHENTIK_SECRET: string
AUTH_AZUREAD_ID: string
AUTH_AZUREAD_SECRET: string
AUTH_AZUREB2C_ID: string
AUTH_AZUREB2C_SECRET: string
AUTH_BN_ID: string
AUTH_BN_ISSUER: any
AUTH_BN_SECRET: string
AUTH_BOX_ID: string
AUTH_BOX_SECRET: string
AUTH_BOXYHQ_ID: string
AUTH_BOXYHQ_ISSUER: string
AUTH_BOXYHQ_SECRET: string
AUTH_BUNGIE_ID: string
AUTH_BUNGIE_SECRET: string
AUTH_COGNITO_ID: string
AUTH_COGNITO_SECRET: string
AUTH_COINBASE_ID: string
AUTH_COINBASE_SECRET: string
AUTH_DISCORD_ID: string
AUTH_DISCORD_SECRET: string
AUTH_DROPBOX_ID: string
AUTH_DROPBOX_SECRET: string
AUTH_DUENDEIDS6_ID: string
AUTH_DUENDEIDS6_SECRET: string
AUTH_EVEONLINE_ID: string
AUTH_EVEONLINE_SECRET: string
AUTH_FACEBOOK_ID: string
AUTH_FACEBOOK_SECRET: string
AUTH_FACEIT_ID: string
AUTH_FACEIT_SECRET: string
AUTH_FORTYTWOSCHOOL_ID: string
AUTH_FORTYTWOSCHOOL_SECRET: string
AUTH_FOURSQUARE_ID: string
AUTH_FOURSQUARE_SECRET: string
AUTH_FRESHBOOKS_ID: string
AUTH_FRESHBOOKS_SECRET: string
AUTH_FUSIONAUTH_ID: string
AUTH_FUSIONAUTH_SECRET: string
AUTH_GITHUB_ID: string
AUTH_GITHUB_SECRET: string
AUTH_GITLAB_ID: string
AUTH_GITLAB_SECRET: string
AUTH_GOOGLE_ID: string
AUTH_GOOGLE_SECRET: string
AUTH_HUBSPOT_ID: string
AUTH_HUBSPOT_SECRET: string
AUTH_INSTAGRAM_ID: string
AUTH_INSTAGRAM_SECRET: string
AUTH_KAKAO_ID: string
AUTH_KAKAO_SECRET: string
AUTH_KEYCLOAK_ID: string
AUTH_KEYCLOAK_SECRET: string
AUTH_LINE_ID: string
AUTH_LINE_SECRET: string
AUTH_LINKEDIN_ID: string
AUTH_LINKEDIN_SECRET: string
AUTH_MAILCHIMP_ID: string
AUTH_MAILCHIMP_SECRET: string
AUTH_MAILRU_ID: string
AUTH_MAILRU_SECRET: string
AUTH_MEDIUM_ID: string
AUTH_MEDIUM_SECRET: string
AUTH_NAVER_ID: string
AUTH_NAVER_SECRET: string
AUTH_NETLIFY_ID: string
AUTH_NETLIFY_SECRET: string
AUTH_OKTA_ID: string
AUTH_OKTA_SECRET: string
AUTH_ONELOGIN_ID: string
AUTH_ONELOGIN_SECRET: string
AUTH_OSSO_ID: string
AUTH_OSSO_ISSUER: string
AUTH_OSSO_SECRET: string
AUTH_OSU_ID: string
AUTH_OSU_SECRET: string
AUTH_PASSAGE_ID: string
AUTH_PASSAGE_ISSUER: string
AUTH_PASSAGE_SECRET: string
AUTH_PATREON_ID: string
AUTH_PATREON_SECRET: string
AUTH_PINTEREST_ID: string
AUTH_PINTEREST_SECRET: string
AUTH_PIPEDRIVE_ID: string
AUTH_PIPEDRIVE_SECRET: string
AUTH_REDDIT_ID: string
AUTH_REDDIT_SECRET: string
AUTH_SALESFORCE_ID: string
AUTH_SALESFORCE_SECRET: string
AUTH_SLACK_ID: string
AUTH_SLACK_SECRET: string
AUTH_SPOTIFY_ID: string
AUTH_SPOTIFY_SECRET: string
AUTH_STRAVA_ID: string
AUTH_STRAVA_SECRET: string
AUTH_TODOIST_ID: string
AUTH_TODOIST_SECRET: string
AUTH_TRAKT_ID: string
AUTH_TRAKT_SECRET: string
AUTH_TWITCH_ID: string
AUTH_TWITCH_SECRET: string
AUTH_TWITTER_ID: string
AUTH_TWITTER_SECRET: string
AUTH_UE_ID: string
AUTH_UE_ISSUER: string
AUTH_UE_SECRET: string
AUTH_VK_ID: string
AUTH_VK_SECRET: string
AUTH_WIKIMEDIA_ID: string
AUTH_WIKIMEDIA_SECRET: string
AUTH_WORDPRESS_ID: string
AUTH_WORDPRESS_SECRET: string
AUTH_WORKOS_ID: string
AUTH_WORKOS_SECRET: string
AUTH_YANDEX_ID: string
AUTH_YANDEX_SECRET: string
AUTH_ZITADEL_ID: string
AUTH_ZITADEL_SECRET: string
AUTH_ZOHO_ID: string
AUTH_ZOHO_SECRET: string
AUTH_ZOOM_ID: string
AUTH_ZOOM_SECRET: string
}
}
}

View File

@@ -1,10 +0,0 @@
import "next-auth/jwt"
// Read more at: https://next-auth.js.org/getting-started/typescript#module-augmentation
declare module "next-auth/jwt" {
interface JWT {
/** The user's role. */
userRole?: "admin"
}
}

View File

@@ -1,44 +0,0 @@
import NextAuth, { NextAuthOptions } from "next-auth"
import GoogleProvider from "next-auth/providers/google"
import FacebookProvider from "next-auth/providers/facebook"
import GithubProvider from "next-auth/providers/github"
import TwitterProvider from "next-auth/providers/twitter"
import Auth0Provider from "next-auth/providers/auth0"
// For more information on each option (and a full list of options) go to
// https://next-auth.js.org/configuration/options
export const authOptions: NextAuthOptions = {
// https://next-auth.js.org/configuration/providers/oauth
providers: [
Auth0Provider({
clientId: process.env.AUTH0_ID,
clientSecret: process.env.AUTH0_SECRET,
issuer: process.env.AUTH0_ISSUER,
}),
FacebookProvider({
clientId: process.env.FACEBOOK_ID,
clientSecret: process.env.FACEBOOK_SECRET,
}),
GithubProvider({
clientId: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_SECRET,
}),
GoogleProvider({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
}),
TwitterProvider({
clientId: process.env.TWITTER_ID,
clientSecret: process.env.TWITTER_SECRET,
version: "2.0",
}),
],
callbacks: {
async jwt({ token }) {
token.userRole = "admin"
return token
},
},
}
export default NextAuth(authOptions)

View File

@@ -1,14 +1,13 @@
// This is an example of to protect an API route
import { getServerSession } from "next-auth/next"
import { authOptions } from "../auth/[...nextauth]"
import { auth } from "auth"
import type { NextApiRequest, NextApiResponse } from "next"
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const session = await getServerSession(req, res, authOptions)
const session = await auth(req, res)
if (session) {
return res.send({

View File

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

View File

@@ -1,5 +1,4 @@
import { getServerSession } from "next-auth/next"
import { authOptions } from "./api/auth/[...nextauth]"
import { auth } from "auth"
import Layout from "../components/layout"
import type { GetServerSidePropsContext } from "next"
@@ -36,9 +35,5 @@ export default function ServerSidePage() {
// Export the `session` prop to use sessions with Server Side Rendering
export async function getServerSideProps(context: GetServerSidePropsContext) {
return {
props: {
session: await getServerSession(context.req, context.res, authOptions),
},
}
return { props: { session: await auth(context.req, context.res) } }
}

View File

@@ -1,18 +0,0 @@
declare namespace NodeJS {
export interface ProcessEnv {
NEXTAUTH_URL: string
NEXTAUTH_SECRET: string
GITHUB_ID: string
GITHUB_SECRET: string
FACEBOOK_ID: string
FACEBOOK_SECRET: string
TWITTER_ID: string
TWITTER_SECRET: string
GOOGLE_ID: string
GOOGLE_SECRET: string
AUTH0_ID: string
AUTH0_SECRET: string
DESCOPE_ID: string
DESCOPE_SECRET: string
}
}

View File

@@ -1,7 +1,11 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@@ -13,14 +17,22 @@
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true
"incremental": true,
"baseUrl": ".",
"plugins": [
{
"name": "next"
}
]
},
"include": [
"process.d.ts",
"next-env.d.ts",
"next-auth.d.ts",
"**/*.ts",
"**/*.tsx"
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": ["node_modules"]
"exclude": [
"node_modules"
]
}

View File

@@ -61,7 +61,7 @@ pnpm install
4. Start the development server
```bash
$ pnpm dev:docs
pnpm dev:docs
```
And thats all! Now you should have a local copy of this docs site running at [localhost:3000](http://localhost:3000)!

View File

@@ -89,7 +89,7 @@ NEXTAUTH_SECRET="This is an example"
`NEXTAUTH_SECRET` is a random string used by the library to encrypt tokens and email verification hashes, and **it's mandatory to keep things secure**! 🔥 🔐 . You can use:
```
$ openssl rand -base64 32
openssl rand -base64 32
```
or https://generate-secret.vercel.app/32 to generate a random value for it.
@@ -182,7 +182,127 @@ export default async function listMovies(req, res) {
</TabItem>
<TabItem value="sveltekit" label="SvelteKit">
TODO: SvelteKit
:::warning
`@auth/sveltekit` is currently experimental. The API _will_ change in the future.
:::
### Prerequisites
This tutorial assumes you have a SvelteKit application set up. If you don't, you can follow the [SvelteKit tutorial](https://kit.svelte.dev/docs/creating-a-project) to get started.
### Installing Auth.js
```bash npm2yarn
npm install @auth/core @auth/sveltekit
```
### Create server hook
Create the following [Server hook](https://kit.svelte.dev/docs/hooks) file. This route contains the necessary configuration for Auth.js, as well as the dynamic route handler:
```ts title="src/hooks.server.ts"
import { SvelteKitAuth } from "@auth/sveltekit"
import GitHub from "@auth/core/providers/github"
import { GITHUB_ID, GITHUB_SECRET } from "$env/static/private"
export const handle = SvelteKitAuth({
providers: [GitHub({ clientId: GITHUB_ID, clientSecret: GITHUB_SECRET })],
})
```
:::info
Behind the scenes, this creates all the relevant OAuth API routes within `/api/auth/*` so that auth API requests to:
- [GET `/api/auth/signin`](https://authjs.dev/reference/rest-api#get--apiauthsignin)
- [POST `/api/auth/signin/:provider`](https://authjs.dev/reference/rest-api#post--apiauthsigninprovider)
- [GET/POST `/api/auth/callback/:provider`](https://authjs.dev/reference/rest-api#get--post--apiauthcallbackprovider)
- [GET `/api/auth/signout`](https://authjs.dev/reference/rest-api#get--apiauthsignout)
- [POST `/api/auth/signout`](https://authjs.dev/reference/rest-api#post--apiauthsignout)
- [GET `/api/auth/session`](https://authjs.dev/reference/rest-api#get--apiauthsession)
- [GET `/api/auth/csrf`](https://authjs.dev/reference/rest-api#get--apiauthcsrf)
- [GET `/api/auth/providers`](https://authjs.dev/reference/rest-api#get--apiauthproviders)
can be handled by Auth.js. In this way, Auth.js stays in charge of the whole application's authentication request/response flow.
Auth.js is fully customizable - [our guides section](/guides/overview) teaches you how to set it up to handle auth in different ways. All the possible configuration options are [listed here](/reference/configuration/auth-config).
:::
### Adding environment variables
You may notice we are using environment variables in the code example above. We take the value of `GITHUB_ID` and `GITHUB_SECRET` from the GitHub Developer OAuth Portal. See [Configuring OAuth Provider](/getting-started/oauth-tutorial#2-configuring-oauth-provider) section on how to get those.
In your project root, create a `.env.local` file and add the `AUTH_SECRET` environment variable:
```title=".env.local"
AUTH_SECRET="This is an example"
```
`AUTH_SECRET` is a random string used by the library to encrypt tokens and email verification hashes, and **it's mandatory to keep things secure**! 🔥 🔐 . You can use:
```
openssl rand -base64 32
```
or https://generate-secret.vercel.app/32 to generate a random value for it.
### Exposing the session via page store
Auth.js provides us a getSession, function to access the session data and status, to call from the `event.locals` variable. We can now just call it and add it to our `$page` store.
```ts
import type { LayoutServerLoad } from './$types';
*
export const load: LayoutServerLoad = async (event) => {
return {
session: await event.locals.getSession()
};
};
```
### Consuming the session via page store
You can use the `$page.data.session` variable from anywhere on your page. Learn more about SvelteKit's page store in the [SvelteKit docs](https://learn.svelte.dev/tutorial/page-store).
```ts title="route/+page.svelte"
<script>
import { signIn, signOut } from '@auth/sveltekit/client'
import { page } from '$app/stores'
</script>
{#if $page.data.session?.user}
<p>Signed in as {$page.data.session.user.email}</p>
<button on:click={signOut}>Sign out</button>
<img src="https://cdn.pixabay.com/photo/2017/08/11/19/36/vw-2632486_1280.png" />
{:else}
<p>Not signed in.</p>
<button on:click={() => signIn('github')}>Sign in</button>
{/if}
```
### Protecting API Routes
To protect your API Routes (blocking unauthorized access to resources), you can use `locals.getSessions()` just like in the layouts file to know whether a session exists or not:
```ts title="routes/api/movies/+server.ts"
import { json, error } from "@sveltejs/kit";
import type { RequestEvent } from "./$types";
export async function GET({ locals }: RequestEvent) {
const session = await locals.getSession()
if (!session?.user) {
throw error(401, "You must sign in to view movies.");
}
return json({
movies: [
{ title: "Alien vs Predator", id: 1 },
{ title: "Reservoir Dogs", id: 2 },
]
})
}
```
</TabItem>
<TabItem value="solidstart" label="SolidStart">
TODO: SolidStart
@@ -309,12 +429,29 @@ export default NextAuth({
Great! We're now ready to run our application locally. Start the Next.js app by running on your terminal the following command and navigating to [`http://localhost:3000`](http://localhost:3000):
```
$ npm run next dev
npm run next dev
```
</TabItem>
<TabItem value="sveltekit" label="SvelteKit">
TODO SvelteKit
```ts title="src/hooks.server.ts"
import { SvelteKitAuth } from "@auth/sveltekit"
import GitHub from "@auth/core/providers/github"
import { GITHUB_ID, GITHUB_SECRET } from "$env/static/private"
*
export const handle = SvelteKitAuth({
providers: [GitHub({ clientId: GITHUB_ID, clientSecret: GITHUB_SECRET })],
})
```
Great! We're now ready to run our application locally. Start the Svelte app by running on your terminal the following command and navigating to [`http://localhost:5173`](http://localhost:5173):
```
npm run vite dev
```
</TabItem>
<TabItem value="solidstart" label="SolidStart">
TODO SolidStart

View File

@@ -580,7 +580,7 @@ Auth.js used to generate a secret for convenience, when the user did not define
You can generate a secret to be placed in the `secret` configuration option via the following command:
```bash
$ openssl rand -base64 32
openssl rand -base64 32
```
Therefore, your Auth.js config should look something like this:

View File

@@ -7,11 +7,7 @@ The Credentials provider allows you to handle signing in with arbitrary credenti
It is intended to support use cases where you have an existing system you need to authenticate users against.
It comes with the constraint that users authenticated in this manner are not persisted in the database, and consequently that the Credentials provider can only be used if JSON Web Tokens are enabled for sessions.
:::warning
The functionality provided for credentials-based authentication is intentionally limited to discourage the use of passwords due to the inherent security risks associated with them and the additional complexity associated with supporting usernames and passwords.
:::
It comes with the constraint that users authenticated in this manner are not persisted in the database by default, and consequently, that the Credentials provider can only be used if JSON Web Tokens are enabled for sessions.
## Options
@@ -23,17 +19,31 @@ You can override any of the options to suit your own use case.
## Example - Username / Password
The Credentials provider is specified like other providers, except that you need to define a handler for `authorize()` that accepts credentials submitted via HTTP POST as input and returns either:
:::caution
The functionality provided for credentials-based authentication is intentionally limited to discourage the use of passwords due to the inherent security risks of the username-password model.
1. A `user` object, which indicates the credentials are valid.
OAuth providers spend significant amounts of money, time, and engineering effort to build:
If you return an object it will be persisted to the JSON Web Token and the user will be signed in, unless a custom `signIn()` callback is configured that subsequently rejects it.
- bot-protection
- rate-limiting
- password management
- data security
2. If you return `null` then an error will be displayed advising the user to check their details.
and much more for authentication solutions. It is likely that your application would benefit from leveraging these battle-tested solutions rather than try to rebuild them from scratch.
3. If you throw an Error, the user will be sent to the error page with the error message as a query parameter.
If you'd still like to build password-based authentication for your application despite these risks, Auth.js gives you full control to do so.
The Credentials provider's `authorize()` method also provides the request object as the second parameter (see the example below).
:::
The Credentials provider is specified like other providers, except that you need to define a handler for `authorize()` that accepts credentials submitted via HTTP POST as input and returns a `user` object, which indicates the credentials are valid.
If you return an object it will be persisted to the JSON Web Token and the user will be signed in (unless a custom `signIn()` callback is configured that subsequently rejects it).
- If you return `null` then an error will be displayed advising the user to check their details.
- If you throw an Error, the user will be sent to the error page with the error message as a query parameter.
The Credentials provider's `authorize()` method also provides the request object as the second parameter. Here's an example that handles these concerns.
```js title="auth.js"
import CredentialsProvider from "next-auth/providers/credentials";
@@ -69,7 +79,50 @@ providers: [
...
```
See the [callbacks documentation](/reference/configuration/auth-config#callbacks) for more information on how to interact with the token.
See the [callbacks documentation](/reference/configuration/auth-config#callbacks) for more information on how to interact with the token. For example, you can add additional information to the token by returning an object from the `jwt()` callback:
```js
callbacks: {
async jwt(token, user, account, profile, isNewUser) {
if (user) {
token.id = user.id
}
return token
}
}
```
### Using a database
You can also use the `authorize()` callback to interact with your database to regain some of the functionality from [more powerful providers](reference/core/providers):
```js
...
providers: [
CredentialsProvider({
...
async authorize(credentials, req) {
let user = null
const saltedPasswordToCheck = passwordToSalt(credentials.password)
user = await getUserFromDb(credentials.username, credentials.password)
if (!user) {
const saltedPassword = passwordToSalt(credentials.password)
user = await addUserToDb(credentials.username, saltedPassword)
}
if (!user) {
throw new Error("User was not found and could not be created.")
}
return user
}
})
]
...
```
## Example - Web3 / Signin With Ethereum
@@ -78,8 +131,8 @@ The credentials provider can also be used to integrate with a service like [Sign
For more information, check out the links below:
- [Tutorial](https://docs.login.xyz/integrations/Auth.js)
- [Example App Repo](https://github.com/spruceid/siwe-next-auth-example).
- [Example App Demo](https://siwe-next-auth-example2.vercel.app/).
- [Example App Repo](https://github.com/spruceid/siwe-next-auth-example)
- [Example App Demo](https://siwe-next-auth-example2.vercel.app/)
## Multiple providers

View File

@@ -5,6 +5,14 @@ title: Overview
Using an Auth.js / NextAuth.js adapter you can connect to any database service or even several different services at the same time. The following listed official adapters are created and maintained by the community:
<div class="adapter-card-list">
<a href="/reference/adapter/d1" class="adapter-card">
<img src="/img/adapters/d1.svg" width="40" />
<h4 class="adapter-card__title">D1 Adapter</h4>
</a>
<a href="/reference/adapter/edgedb" class="adapter-card">
<img src="/img/adapters/edgedb.svg" width="30" />
<h4 class="adapter-card__title">EdgeDB Adapter</h4>
</a>
<a href="/reference/adapter/dgraph" class="adapter-card">
<img src="/img/adapters/dgraph.png" width="30" />
<h4 class="adapter-card__title">Dgraph Adapter</h4>
@@ -41,6 +49,10 @@ Using an Auth.js / NextAuth.js adapter you can connect to any database service o
<img src="/img/adapters/neo4j.svg" width="50" />
<h4 class="adapter-card__title">Neo4j Adapter</h4>
</a>
<a href="/reference/adapter/pg" class="adapter-card">
<img src="/img/adapters/pg.png" width="20" />
<h4 class="adapter-card__title">Postgres Adapter</h4>
</a>
<a href="/reference/adapter/pouchdb" class="adapter-card">
<img src="/img/adapters/pouchdb.svg" width="20" />
<h4 class="adapter-card__title">PouchDB Adapter</h4>

View File

@@ -282,6 +282,8 @@ const docusaurusConfig = {
...(process.env.TYPEDOC_SKIP_ADAPTERS
? []
: [
typedocAdapter("D1"),
typedocAdapter("EdgeDb"),
typedocAdapter("Dgraph"),
typedocAdapter("Drizzle"),
typedocAdapter("DynamoDB"),
@@ -291,6 +293,7 @@ const docusaurusConfig = {
typedocAdapter("Mikro ORM"),
typedocAdapter("MongoDB"),
typedocAdapter("Neo4j"),
typedocAdapter("PG"),
typedocAdapter("PouchDB"),
typedocAdapter("Prisma"),
typedocAdapter("TypeORM"),

View File

@@ -46,6 +46,7 @@ module.exports = {
label: "Database Adapters",
link: { type: "doc", id: "reference/adapters/index" },
items: [
{ type: "doc", id: "reference/adapter/edgedb/index" },
{ type: "doc", id: "reference/adapter/dgraph/index" },
{ type: "doc", id: "reference/adapter/drizzle/index" },
{ type: "doc", id: "reference/adapter/dynamodb/index" },
@@ -55,6 +56,7 @@ module.exports = {
{ type: "doc", id: "reference/adapter/mikro-orm/index" },
{ type: "doc", id: "reference/adapter/mongodb/index" },
{ type: "doc", id: "reference/adapter/neo4j/index" },
{ type: "doc", id: "reference/adapter/pg/index" },
{ type: "doc", id: "reference/adapter/pouchdb/index" },
{ type: "doc", id: "reference/adapter/prisma/index" },
{ type: "doc", id: "reference/adapter/sequelize/index" },

View File

@@ -269,7 +269,7 @@ export default function Home() {
}
const svelteKitCode = `
import SvelteKitAuth from "@auth/sveltekit"
import { SvelteKitAuth } from "@auth/sveltekit"
import GitHub from '@auth/core/providers/github'
import Facebook from '@auth/core/providers/facebook'
import Google from '@auth/core/providers/google'

5
docs/static/img/adapters/d1.svg vendored Normal file
View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="49" viewBox="0 0 48 49">
<path d="m18.63 37.418-9.645-12.9 9.592-12.533-1.852-2.527L5.917 23.595l-.015 1.808 10.86 14.542 1.868-2.527z" fill="rgb(243, 128, 32)"></path>
<path d="M21.997 6.503h-3.712l13.387 18.3-13.072 17.7h3.735L35.4 24.81 21.997 6.503z" fill="rgb(243, 128, 32)"></path>
<path d="M29.175 6.503h-3.758l13.598 18.082-13.598 17.918h3.765l12.908-17.01v-1.808L29.175 6.503z" fill="rgb(243, 128, 32)"></path>
</svg>

After

Width:  |  Height:  |  Size: 497 B

4
docs/static/img/adapters/edgedb.svg vendored Normal file
View File

@@ -0,0 +1,4 @@
<svg style="color:#1F8AED" width="140" height="114" viewBox="0 0 140 114" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M118.58 56.966C118.58 66.6274 114.706 68.3592 110.377 68.3592H101.171V45.5728H110.377C114.706 45.5728 118.58 47.3046 118.58 56.966ZM113.887 56.966C113.887 50.2212 111.836 49.9022 108.874 49.9022H106.003V64.0298H108.874C111.836 64.0298 113.887 63.7108 113.887 56.966ZM65.1234 68.3592V45.5728H79.6155V49.9022H69.9541V54.5507H77.2458V58.8345H69.9541V64.0298H79.6155V68.3592H65.1234ZM87.7282 113.932H92.5589V0H87.7282V113.932ZM128.242 58.242V64.0298H132.252C134.758 64.0298 135.396 62.3892 135.396 61.1587C135.396 60.2017 134.941 58.242 131.523 58.242H128.242ZM128.242 49.9022V54.2317H131.523C133.391 54.2317 134.485 53.4113 134.485 52.0442C134.485 50.677 133.391 49.9022 131.523 49.9022H128.242ZM123.411 45.5728H132.708C137.585 45.5728 139.043 48.9908 139.043 51.4517C139.043 53.7304 137.585 55.371 136.582 55.8267C139.499 57.2395 140 60.1105 140 61.5689C140 63.4829 139.043 68.3592 132.708 68.3592H123.411V45.5728ZM37.3702 56.966C37.3702 66.6274 33.4966 68.3592 29.1671 68.3592H19.9614V45.5728H29.1671C33.4966 45.5728 37.3702 47.3046 37.3702 56.966ZM51.5885 64.2121C54.0495 64.2121 55.3255 63.3918 55.7812 62.8449V60.3384H51.862V56.4191H59.6549V65.4881C58.9713 66.5363 55.2344 68.5871 51.8164 68.5871C46.211 68.5871 41.4714 66.3996 41.4714 56.7381C41.4714 47.0767 46.2565 45.345 50.5859 45.345C57.3763 45.345 59.0625 48.8996 59.6094 52.0442L55.5989 52.9556C55.3711 51.4973 54.095 49.6744 51.1784 49.6744C48.2162 49.6744 46.1654 49.9934 46.1654 56.7381C46.1654 63.4829 48.3073 64.2121 51.5885 64.2121ZM32.6756 56.966C32.6756 50.2212 30.6248 49.9022 27.6626 49.9022H24.7915V64.0298H27.6626C30.6248 64.0298 32.6756 63.7108 32.6756 56.966ZM0 68.3592V45.5728H14.4921V49.9022H4.83072V54.5507H12.1224V58.8345H4.83072V64.0298H14.4921V68.3592H0Z" fill="currentColor">
</path>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
docs/static/img/adapters/pg.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

27
docs/static/img/providers/click-up.svg vendored Normal file
View File

@@ -0,0 +1,27 @@
<svg width="185" height="185" viewBox="0 0 185 185" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d)">
<rect x="30" y="20" width="125" height="125" rx="62.5" fill="white"/>
<rect x="30" y="20" width="125" height="125" rx="62.5" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M55.8789 105.714L69.3974 95.3593C76.5762 104.732 84.1998 109.051 92.6948 109.051C101.143 109.051 108.557 104.781 115.414 95.4832L129.119 105.59C119.232 118.996 106.932 126.079 92.6948 126.079C78.5049 126.079 66.0907 119.046 55.8789 105.714Z" fill="url(#paint0_linear)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M92.6491 60.7078L68.5883 81.4406L57.4727 68.5407L92.6969 38.1885L127.647 68.5644L116.477 81.417L92.6491 60.7078Z" fill="url(#paint1_linear)"/>
</g>
<defs>
<filter id="filter0_d" x="0" y="0" width="185" height="185" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
<feOffset dy="10"/>
<feGaussianBlur stdDeviation="15"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.0627451 0 0 0 0 0.117647 0 0 0 0 0.211765 0 0 0 0.1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
</filter>
<linearGradient id="paint0_linear" x1="55.8789" y1="116.251" x2="129.119" y2="116.251" gradientUnits="userSpaceOnUse">
<stop stop-color="#8930FD"/>
<stop offset="1" stop-color="#49CCF9"/>
</linearGradient>
<linearGradient id="paint1_linear" x1="57.4727" y1="67.6025" x2="127.647" y2="67.6025" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF02F0"/>
<stop offset="1" stop-color="#FFC800"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M16 0C7.16704 0 0 7.16704 0 16C0 24.833 7.16704 32 16 32C24.8156 32 32 24.833 32 16C32 7.16704 24.8156 0 16 0ZM26.5683 7.37526C28.4772 9.70064 29.6226 12.6681 29.6573 15.8785C29.2061 15.7917 24.6941 14.872 20.1475 15.4447C20.0434 15.2191 19.9566 14.9761 19.8525 14.7332C19.5748 14.0738 19.2625 13.397 18.9501 12.7549C23.9827 10.7072 26.2733 7.75706 26.5683 7.37526ZM16 2.36009C19.4707 2.36009 22.6464 3.6616 25.0586 5.7961C24.8156 6.14317 22.7505 8.9024 17.8916 10.7245C15.6529 6.61171 13.1714 3.24512 12.7896 2.72451C13.8134 2.48156 14.8894 2.36009 16 2.36009ZM10.1866 3.64426C10.551 4.13014 12.9805 7.51411 15.2538 11.5401C8.86768 13.2408 3.22778 13.2061 2.62039 13.2061C3.50541 8.97181 6.36877 5.44902 10.1866 3.64426ZM2.32538 16.0173C2.32538 15.8785 2.32538 15.7397 2.32538 15.6009C2.9154 15.6182 9.54448 15.705 16.3644 13.6573C16.7636 14.4208 17.128 15.2017 17.475 15.9827C17.3015 16.0347 17.1106 16.0868 16.9371 16.1388C9.89155 18.4122 6.14317 24.6247 5.83082 25.1453C3.6616 22.7332 2.32538 19.5228 2.32538 16.0173ZM16 29.6746C12.8417 29.6746 9.92624 28.5987 7.61821 26.7939C7.86118 26.2907 10.6378 20.9458 18.3427 18.256C18.3774 18.2386 18.3948 18.2386 18.4295 18.2212C20.3557 23.2017 21.1367 27.3839 21.3449 28.5813C19.6963 29.2928 17.8916 29.6746 16 29.6746ZM23.6182 27.3319C23.4794 26.4989 22.7506 22.5076 20.9631 17.5965C25.2495 16.9197 28.9978 18.0304 29.4664 18.1866C28.8764 21.987 26.6898 25.2668 23.6182 27.3319Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M16 0C7.16704 0 0 7.16704 0 16C0 24.833 7.16704 32 16 32C24.8156 32 32 24.833 32 16C32 7.16704 24.8156 0 16 0ZM26.5683 7.37526C28.4772 9.70064 29.6226 12.6681 29.6573 15.8785C29.2061 15.7917 24.6941 14.872 20.1475 15.4447C20.0434 15.2191 19.9566 14.9761 19.8525 14.7332C19.5748 14.0738 19.2625 13.397 18.9501 12.7549C23.9827 10.7072 26.2733 7.75706 26.5683 7.37526ZM16 2.36009C19.4707 2.36009 22.6464 3.6616 25.0586 5.7961C24.8156 6.14317 22.7505 8.9024 17.8916 10.7245C15.6529 6.61171 13.1714 3.24512 12.7896 2.72451C13.8134 2.48156 14.8894 2.36009 16 2.36009ZM10.1866 3.64426C10.551 4.13014 12.9805 7.51411 15.2538 11.5401C8.86768 13.2408 3.22778 13.2061 2.62039 13.2061C3.50541 8.97181 6.36877 5.44902 10.1866 3.64426ZM2.32538 16.0173C2.32538 15.8785 2.32538 15.7397 2.32538 15.6009C2.9154 15.6182 9.54448 15.705 16.3644 13.6573C16.7636 14.4208 17.128 15.2017 17.475 15.9827C17.3015 16.0347 17.1106 16.0868 16.9371 16.1388C9.89155 18.4122 6.14317 24.6247 5.83082 25.1453C3.6616 22.7332 2.32538 19.5228 2.32538 16.0173ZM16 29.6746C12.8417 29.6746 9.92624 28.5987 7.61821 26.7939C7.86118 26.2907 10.6378 20.9458 18.3427 18.256C18.3774 18.2386 18.3948 18.2386 18.4295 18.2212C20.3557 23.2017 21.1367 27.3839 21.3449 28.5813C19.6963 29.2928 17.8916 29.6746 16 29.6746ZM23.6182 27.3319C23.4794 26.4989 22.7506 22.5076 20.9631 17.5965C25.2495 16.9197 28.9978 18.0304 29.4664 18.1866C28.8764 21.987 26.6898 25.2668 23.6182 27.3319Z" fill="#EA4C89"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

10
docs/static/img/providers/mastodon.svg vendored Normal file
View File

@@ -0,0 +1,10 @@
<svg width="75" height="79" viewBox="0 0 75 79" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M73.8393 17.4898C72.6973 9.00165 65.2994 2.31235 56.5296 1.01614C55.05 0.797115 49.4441 0 36.4582 0H36.3612C23.3717 0 20.585 0.797115 19.1054 1.01614C10.5798 2.27644 2.79399 8.28712 0.904997 16.8758C-0.00358524 21.1056 -0.100549 25.7949 0.0682394 30.0965C0.308852 36.2651 0.355538 42.423 0.91577 48.5665C1.30307 52.6474 1.97872 56.6957 2.93763 60.6812C4.73325 68.042 12.0019 74.1676 19.1233 76.6666C26.7478 79.2728 34.9474 79.7055 42.8039 77.9162C43.6682 77.7151 44.5217 77.4817 45.3645 77.216C47.275 76.6092 49.5123 75.9305 51.1571 74.7385C51.1797 74.7217 51.1982 74.7001 51.2112 74.6753C51.2243 74.6504 51.2316 74.6229 51.2325 74.5948V68.6416C51.2321 68.6154 51.2259 68.5896 51.2142 68.5661C51.2025 68.5426 51.1858 68.522 51.1651 68.5058C51.1444 68.4896 51.1204 68.4783 51.0948 68.4726C51.0692 68.4669 51.0426 68.467 51.0171 68.4729C45.9835 69.675 40.8254 70.2777 35.6502 70.2682C26.7439 70.2682 24.3486 66.042 23.6626 64.2826C23.1113 62.762 22.7612 61.1759 22.6212 59.5646C22.6197 59.5375 22.6247 59.5105 22.6357 59.4857C22.6466 59.4609 22.6633 59.4391 22.6843 59.422C22.7053 59.4048 22.73 59.3929 22.7565 59.3871C22.783 59.3813 22.8104 59.3818 22.8367 59.3886C27.7864 60.5826 32.8604 61.1853 37.9522 61.1839C39.1768 61.1839 40.3978 61.1839 41.6224 61.1516C46.7435 61.008 52.1411 60.7459 57.1796 59.7621C57.3053 59.7369 57.431 59.7154 57.5387 59.6831C65.4861 58.157 73.0493 53.3672 73.8178 41.2381C73.8465 40.7606 73.9184 36.2364 73.9184 35.7409C73.9219 34.0569 74.4606 23.7949 73.8393 17.4898Z" fill="url(#paint0_linear_549_34)"/>
<path d="M61.2484 27.0263V48.114H52.8916V27.6475C52.8916 23.3388 51.096 21.1413 47.4437 21.1413C43.4287 21.1413 41.4177 23.7409 41.4177 28.8755V40.0782H33.1111V28.8755C33.1111 23.7409 31.0965 21.1413 27.0815 21.1413C23.4507 21.1413 21.6371 23.3388 21.6371 27.6475V48.114H13.2839V27.0263C13.2839 22.7176 14.384 19.2946 16.5843 16.7572C18.8539 14.2258 21.8311 12.926 25.5264 12.926C29.8036 12.926 33.0357 14.5705 35.1905 17.8559L37.2698 21.346L39.3527 17.8559C41.5074 14.5705 44.7395 12.926 49.0095 12.926C52.7013 12.926 55.6784 14.2258 57.9553 16.7572C60.1531 19.2922 61.2508 22.7152 61.2484 27.0263Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear_549_34" x1="37.0692" y1="0" x2="37.0692" y2="79" gradientUnits="userSpaceOnUse">
<stop stop-color="#6364FF"/>
<stop offset="1" stop-color="#563ACC"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -0,0 +1,7 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg fill="#ffffff" width="64px" height="64px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" stroke="#ffffff">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>

After

Width:  |  Height:  |  Size: 1.6 KiB

14
docs/static/img/providers/tiktok.svg vendored Normal file
View File

@@ -0,0 +1,14 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-label="TikTok" role="img" viewBox="0 0 512.00 512.00" width="64px" height="64px" fill="#000000" stroke="#000000" stroke-width="0.00512">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round" stroke="#CCCCCC" stroke-width="1.024"/>
<g id="SVGRepo_iconCarrier">
<rect rx="15%" height="512" width="512" fill="#ffffff"/>
<defs>

After

Width:  |  Height:  |  Size: 924 B

View File

@@ -249,6 +249,7 @@
{
"files": [
"apps/dev/nextjs/pages/api/auth/[...nextauth].ts",
"apps/examples/nextjs/auth.ts",
"docs/{sidebars,docusaurus.config}.js"
],
"options": {

View File

@@ -0,0 +1,28 @@
<p align="center">
<br/>
<a href="https://authjs.dev" target="_blank">
<img height="64px" src="https://authjs.dev/img/logo/logo-sm.png" />
</a>
<a href="https://developers.cloudflare.com/d1/" target="_blank">
<img height="64px" src="https://authjs.dev/img/adapters/d1.svg"/>
</a>
<h3 align="center"><b>Cloudflare D1 Adapter</b> - NextAuth.js / Auth.js</a></h3>
<p align="center" style="align: center;">
<a href="https://npm.im/@auth/drizzle-adapter">
<img src="https://img.shields.io/badge/TypeScript-blue?style=flat-square" alt="TypeScript" />
</a>
<a href="https://npm.im/@auth/d1-adapter">
<img alt="npm" src="https://img.shields.io/npm/v/@auth/d1-adapter?color=green&label=@auth/d1-adapter&style=flat-square">
</a>
<a href="https://www.npmtrends.com/@auth/d1-adapter">
<img src="https://img.shields.io/npm/dm/@auth/d1-adapter?label=%20downloads&style=flat-square" alt="Downloads" />
</a>
<a href="https://github.com/nextauthjs/next-auth/stargazers">
<img src="https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square" alt="Github Stars" />
</a>
</p>
</p>
---
Check out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/d1).

View File

@@ -0,0 +1,58 @@
{
"name": "@auth/d1-adapter",
"version": "0.2.0",
"description": "A Cloudflare D1 adapter for Auth.js",
"homepage": "https://authjs.dev",
"repository": "https://github.com/nextauthjs/next-auth",
"bugs": {
"url": "https://github.com/nextauthjs/next-auth/issues"
},
"author": "Josh Schlesser <josh@schlesser.dev>",
"contributors": [
"Thang Huu Vu <hi@thvu.dev>"
],
"license": "ISC",
"keywords": [
"next-auth",
"@auth",
"Auth.js",
"next.js",
"oauth",
"d1"
],
"type": "module",
"types": "./index.d.ts",
"exports": {
".": {
"types": "./index.d.ts",
"import": "./index.js"
}
},
"files": [
"*.d.ts*",
"*.js",
"src"
],
"private": false,
"publishConfig": {
"access": "public"
},
"scripts": {
"build": "tsc",
"test": "jest"
},
"dependencies": {
"@auth/core": "workspace:*"
},
"devDependencies": {
"@auth/adapter-test": "workspace:*",
"@auth/tsconfig": "workspace:*",
"@cloudflare/workers-types": "^4.20230321.0",
"@miniflare/d1": "^2.12.2",
"better-sqlite3": "^7.0.0",
"jest": "^29.3.0"
},
"jest": {
"preset": "@auth/adapter-test/jest"
}
}

View File

@@ -0,0 +1,432 @@
/**
* <div style={{display: "flex", justifyContent: "space-between", alignItems: "center", padding: 16}}>
* <p style={{fontWeight: "normal"}}>An official <a href="https://developers.cloudflare.com/d1/">Cloudflare D1</a> adapter for Auth.js / NextAuth.js.</p>
* <a href="https://developers.cloudflare.com/d1/">
* <img style={{display: "block"}} src="/img/adapters/d1.svg" width="48" />
* </a>
* </div>
*
* ## Warning
* This adapter is not developed or maintained by Clouflare and they haven't declared the D1 api stable. The author will make an effort to keep this adapter up to date.
* The adapter is compatible with the D1 api as of March 22, 2023.
*
* ## Installation
*
* ```bash npm2yarn2pnpm
* npm install next-auth @auth/d1-adapter
* ```
*
* @module @auth/d1-adapter
*/
import type { D1Database as WorkerDatabase } from "@cloudflare/workers-types"
import type { D1Database as MiniflareD1Database } from "@miniflare/d1"
import type {
Adapter,
AdapterSession,
AdapterUser,
AdapterAccount,
VerificationToken as AdapterVerificationToken,
} from "@auth/core/adapters"
export { up } from "./migrations"
/**
* @type @cloudflare/workers-types.D1Database | @miniflare/d1.D1Database
*/
export type D1Database = WorkerDatabase | MiniflareD1Database
// all the sqls
// USER
export const CREATE_USER_SQL = `INSERT INTO users (id, name, email, emailVerified, image) VALUES (?, ?, ?, ?, ?)`
export const GET_USER_BY_ID_SQL = `SELECT * FROM users WHERE id = ?`
export const GET_USER_BY_EMAIL_SQL = `SELECT * FROM users WHERE email = ?`
export const GET_USER_BY_ACCOUNTL_SQL = `
SELECT u.*
FROM users u JOIN accounts a ON a.userId = u.id
WHERE a.providerAccountId = ? AND a.provider = ?`
export const UPDATE_USER_BY_ID_SQL = `
UPDATE users
SET name = ?, email = ?, emailVerified = ?, image = ?
WHERE id = ? `
export const DELETE_USER_SQL = `DELETE FROM users WHERE id = ?`
// SESSION
export const CREATE_SESSION_SQL =
"INSERT INTO sessions (id, sessionToken, userId, expires) VALUES (?,?,?,?)"
export const GET_SESSION_BY_TOKEN_SQL = `
SELECT id, sessionToken, userId, expires
FROM sessions
WHERE sessionToken = ?`
export const UPDATE_SESSION_BY_SESSION_TOKEN_SQL = `UPDATE sessions SET expires = ? WHERE sessionToken = ?`
export const DELETE_SESSION_SQL = `DELETE FROM sessions WHERE sessionToken = ?`
export const DELETE_SESSION_BY_USER_ID_SQL = `DELETE FROM sessions WHERE userId = ?`
// ACCOUNT
export const CREATE_ACCOUNT_SQL = `
INSERT INTO accounts (
id, userId, type, provider,
providerAccountId, refresh_token, access_token,
expires_at, token_type, scope, id_token, session_state,
oauth_token, oauth_token_secret
)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)`
export const GET_ACCOUNT_BY_ID_SQL = `SELECT * FROM accounts WHERE id = ? `
export const GET_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL = `SELECT * FROM accounts WHERE provider = ? AND providerAccountId = ?`
export const DELETE_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL = `DELETE FROM accounts WHERE provider = ? AND providerAccountId = ?`
export const DELETE_ACCOUNT_BY_USER_ID_SQL = `DELETE FROM accounts WHERE userId = ?`
// VERIFICATION_TOKEN
export const GET_VERIFICATION_TOKEN_BY_IDENTIFIER_AND_TOKEN_SQL = `SELECT * FROM verification_tokens WHERE identifier = ? AND token = ?`
export const CREATE_VERIFICATION_TOKEN_SQL = `INSERT INTO verification_tokens (identifier, expires, token) VALUES (?,?,?)`
export const DELETE_VERIFICATION_TOKEN_SQL = `DELETE FROM verification_tokens WHERE identifier = ? and token = ?`
// helper functions
// isDate is borrowed from the supabase adapter, graciously
// depending on error messages ("Invalid Date") is always precarious, but probably fine for a built in native like Date
function isDate(date: any) {
return (
new Date(date).toString() !== "Invalid Date" && !isNaN(Date.parse(date))
)
}
// format is borrowed from the supabase adapter, graciously
function format<T>(obj: Record<string, any>): T {
for (const [key, value] of Object.entries(obj)) {
if (value === null) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete obj[key]
}
if (isDate(value)) {
obj[key] = new Date(value)
}
}
return obj as T
}
// D1 doesnt like undefined, it wants null when calling bind
function cleanBindings(bindings: any[]) {
return bindings.map((e) => (e === undefined ? null : e))
}
export async function createRecord<RecordType>(
db: D1Database,
CREATE_SQL: string,
bindings: any[],
GET_SQL: string,
getBindings: any[]
) {
try {
bindings = cleanBindings(bindings)
await db
.prepare(CREATE_SQL)
.bind(...bindings)
.run()
return await getRecord<RecordType>(db, GET_SQL, getBindings)
} catch (e: any) {
console.error(e.message, e.cause?.message)
throw e
}
}
export async function getRecord<RecordType>(
db: D1Database,
SQL: string,
bindings: any[]
): Promise<RecordType | null> {
try {
bindings = cleanBindings(bindings)
const res: any = await db
.prepare(SQL)
.bind(...bindings)
.first()
if (res) {
return format<RecordType>(res)
} else {
return null
}
} catch (e: any) {
console.error(e.message, e.cause?.message)
throw e
}
}
export async function updateRecord(
db: D1Database,
SQL: string,
bindings: any[]
) {
try {
bindings = cleanBindings(bindings)
return await db
.prepare(SQL)
.bind(...bindings)
.run()
} catch (e: any) {
console.error(e.message, e.cause?.message)
throw e
}
}
export async function deleteRecord(
db: D1Database,
SQL: string,
bindings: any[]
) {
// eslint-disable-next-line no-useless-catch
try {
bindings = cleanBindings(bindings)
await db
.prepare(SQL)
.bind(...bindings)
.run()
} catch (e: any) {
console.error(e.message, e.cause?.message)
throw e
}
}
/**
*
* ## Setup
*
* This is the D1 Adapter for [`next-auth`](https://authjs.dev). This package can only be used in conjunction with the primary `next-auth` package. It is not a standalone package.
*
* ### Configure Auth.js
*
* ```javascript title="pages/api/auth/[...nextauth].js"
* import NextAuth from "next-auth"
* import { D1Adapter, up } from "@auth/d1-adapter"
*
*
* // For more information on each option (and a full list of options) go to
* // https://authjs.dev/reference/configuration/auth-options
* export default NextAuth({
* // https://authjs.dev/reference/providers/
* providers: [],
* adapter: D1Adapter(env.db)
* ...
* })
* ```
*
* ### Migrations
*
* Somewhere in the initialization of your application you need to run the `up(env.db)` function to create the tables in D1.
* It will create 4 tables if they don't already exist:
* `accounts`, `sessions`, `users`, `verification_tokens`.
*
* The table prefix "" is not configurable at this time.
*
* You can use something like the following to attempt the migration once each time your worker starts up. Running migrations more than once will not erase your existing tables.
* ```javascript
* import { up } from "@auth/d1-adapter"
*
* let migrated = false;
* async function migrationHandle({event, resolve}) {
* if(!migrated) {
* try {
* await up(event.platform.env.db)
* migrated = true
* } catch(e) {
* console.log(e.cause.message, e.message)
* }
* }
* return resolve(event)
* }
* ```
*
*
* You can also initialize your tables manually. Look in [init.ts](https://github.com/nextauthjs/next-auth/packages/adapter-d1/src/migrations/init.ts) for the relevant sql.
* Paste and run the SQL into your D1 dashboard query tool.
*
**/
export function D1Adapter(db: D1Database): Adapter {
// we need to run migrations if we dont have the right tables
return {
async createUser(user) {
const id: string = crypto.randomUUID()
const createBindings = [
id,
user.name,
user.email,
user.emailVerified?.toISOString(),
user.image,
]
const getBindings = [id]
const newUser = await createRecord<AdapterUser>(
db,
CREATE_USER_SQL,
createBindings,
GET_USER_BY_ID_SQL,
getBindings
)
if (newUser) return newUser
throw new Error("Error creating user: Cannot get user after creation.")
},
async getUser(id) {
return await getRecord<AdapterUser>(db, GET_USER_BY_ID_SQL, [id])
},
async getUserByEmail(email) {
return await getRecord<AdapterUser>(db, GET_USER_BY_EMAIL_SQL, [email])
},
async getUserByAccount({ providerAccountId, provider }) {
return await getRecord<AdapterUser>(db, GET_USER_BY_ACCOUNTL_SQL, [
providerAccountId,
provider,
])
},
async updateUser(user) {
const params = await getRecord<AdapterUser>(db, GET_USER_BY_ID_SQL, [
user.id,
])
if (params) {
// copy any properties not in the update into the existing one and use that for bind params
// covers the scenario where the user arg doesnt have all of the current users properties
Object.assign(params, user)
const res = await updateRecord(db, UPDATE_USER_BY_ID_SQL, [
params.name,
params.email,
params.emailVerified?.toISOString(),
params.image,
params.id,
])
if (res.success) {
// we could probably just return
const user = await getRecord<AdapterUser>(db, GET_USER_BY_ID_SQL, [
params.id,
])
if (user) return user
throw new Error(
"Error updating user: Cannot get user after updating."
)
}
}
throw new Error("Error updating user: Failed to run the update SQL.")
},
async deleteUser(userId) {
// this should probably be in a db.batch but batch has problems right now in miniflare
// no multi line sql statements
await deleteRecord(db, DELETE_ACCOUNT_BY_USER_ID_SQL, [userId])
await deleteRecord(db, DELETE_SESSION_BY_USER_ID_SQL, [userId])
await deleteRecord(db, DELETE_USER_SQL, [userId])
return null
},
async linkAccount(a) {
// convert user_id to userId and provider_account_id to providerAccountId
const id = crypto.randomUUID()
const createBindings = [
id,
a.userId,
a.type,
a.provider,
a.providerAccountId,
a.refresh_token,
a.access_token,
a.expires_at,
a.token_type,
a.scope,
a.id_token,
a.session_state,
a.oauth_token ?? null,
a.oauth_token_secret ?? null,
]
const getBindings = [id]
return await createRecord<AdapterAccount>(
db,
CREATE_ACCOUNT_SQL,
createBindings,
GET_ACCOUNT_BY_ID_SQL,
getBindings
)
},
async unlinkAccount({ providerAccountId, provider }) {
await deleteRecord(
db,
DELETE_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL,
[provider, providerAccountId]
)
},
async createSession({ sessionToken, userId, expires }) {
const id = crypto.randomUUID()
const createBindings = [id, sessionToken, userId, expires.toISOString()]
const getBindings = [sessionToken]
const session = await createRecord<AdapterSession>(
db,
CREATE_SESSION_SQL,
createBindings,
GET_SESSION_BY_TOKEN_SQL,
getBindings
)
if (session) return session
throw new Error(`Couldn't create session`)
},
async getSessionAndUser(sessionToken) {
const session: any = await getRecord<AdapterSession>(
db,
GET_SESSION_BY_TOKEN_SQL,
[sessionToken]
)
// no session? no user!
if (session === null) return null
// this shouldnt happen, but just in case
const user = await getRecord<AdapterUser>(db, GET_USER_BY_ID_SQL, [
session.userId,
])
if (user === null) return null
return { session, user }
},
async updateSession({ sessionToken, expires }) {
// kinda strange that we have to deal with an undefined expires,
// we dont have any policy to enforce, lets just expire it now.
if (expires === undefined) {
await deleteRecord(db, DELETE_SESSION_SQL, [sessionToken])
return null
}
const session = await getRecord<AdapterSession>(
db,
GET_SESSION_BY_TOKEN_SQL,
[sessionToken]
)
if (!session) return null
session.expires = expires
await updateRecord(db, UPDATE_SESSION_BY_SESSION_TOKEN_SQL, [
expires?.toISOString(),
sessionToken,
])
return await db
.prepare(UPDATE_SESSION_BY_SESSION_TOKEN_SQL)
.bind(expires?.toISOString(), sessionToken)
.first()
},
async deleteSession(sessionToken) {
await deleteRecord(db, DELETE_SESSION_SQL, [sessionToken])
return null
},
async createVerificationToken({ identifier, expires, token }) {
return await createRecord(
db,
CREATE_VERIFICATION_TOKEN_SQL,
[identifier, expires.toISOString(), token],
GET_VERIFICATION_TOKEN_BY_IDENTIFIER_AND_TOKEN_SQL,
[identifier, token]
)
},
async useVerificationToken({ identifier, token }) {
const verificationToken = await getRecord<AdapterVerificationToken>(
db,
GET_VERIFICATION_TOKEN_BY_IDENTIFIER_AND_TOKEN_SQL,
[identifier, token]
)
if (!verificationToken) return null
await deleteRecord(db, DELETE_VERIFICATION_TOKEN_SQL, [identifier, token])
return verificationToken
},
}
}

View File

@@ -0,0 +1,66 @@
import type { D1Database } from "."
export const upSQLStatements = [
`CREATE TABLE IF NOT EXISTS "accounts" (
"id" text NOT NULL,
"userId" text NOT NULL DEFAULT NULL,
"type" text NOT NULL DEFAULT NULL,
"provider" text NOT NULL DEFAULT NULL,
"providerAccountId" text NOT NULL DEFAULT NULL,
"refresh_token" text DEFAULT NULL,
"access_token" text DEFAULT NULL,
"expires_at" number DEFAULT NULL,
"token_type" text DEFAULT NULL,
"scope" text DEFAULT NULL,
"id_token" text DEFAULT NULL,
"session_state" text DEFAULT NULL,
"oauth_token_secret" text DEFAULT NULL,
"oauth_token" text DEFAULT NULL,
PRIMARY KEY (id)
);`,
`CREATE TABLE IF NOT EXISTS "sessions" (
"id" text NOT NULL,
"sessionToken" text NOT NULL,
"userId" text NOT NULL DEFAULT NULL,
"expires" datetime NOT NULL DEFAULT NULL,
PRIMARY KEY (sessionToken)
);`,
`CREATE TABLE IF NOT EXISTS "users" (
"id" text NOT NULL DEFAULT '',
"name" text DEFAULT NULL,
"email" text DEFAULT NULL,
"emailVerified" datetime DEFAULT NULL,
"image" text DEFAULT NULL,
PRIMARY KEY (id)
);`,
`CREATE TABLE IF NOT EXISTS "verification_tokens" (
"identifier" text NOT NULL,
"token" text NOT NULL DEFAULT NULL,
"expires" datetime NOT NULL DEFAULT NULL,
PRIMARY KEY (token)
);`,
]
export const down = [
`DROP TABLE IF EXISTS "accounts";`,
`DROP TABLE IF EXISTS "sessions";`,
`DROP TABLE IF EXISTS "users";`,
`DROP TABLE IF EXISTS "verification_token";`,
]
/**
*
* @param db
*/
async function up(db: D1Database) {
// run the migration
upSQLStatements.forEach(async (sql) => {
try {
const res = await db.prepare(sql).run()
} catch (e: any) {
console.error(e.cause?.message, e.message)
}
})
}
export { up }

View File

@@ -0,0 +1,56 @@
import {
D1Adapter,
up,
getRecord,
GET_USER_BY_ID_SQL,
GET_SESSION_BY_TOKEN_SQL,
GET_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL,
GET_VERIFICATION_TOKEN_BY_IDENTIFIER_AND_TOKEN_SQL,
} from "../src"
import {
AdapterSession,
AdapterUser,
AdapterAccount,
} from "@auth/core/adapters"
import { D1Database, D1DatabaseAPI } from "@miniflare/d1"
import { runBasicTests } from "@auth/adapter-test"
import Database from "better-sqlite3"
globalThis.crypto ??= require("node:crypto").webcrypto
if (process.env.CI) {
// TODO: Fix this
test('Skipping D1Adapter tests in CI because of "Error: Must use import to load ES Module: next-auth/node_modules/.pnpm/undici@5.20.0/node_modules/undici/lib/llhttp/llhttp.wasm" errors. Should revisit', () => {
expect(true).toBe(true)
})
process.exit(0)
}
const sqliteDB = new Database(":memory:")
let db = new D1Database(new D1DatabaseAPI(sqliteDB as any))
let adapter = D1Adapter(db)
// put stuff here if we need some async init
beforeAll(async () => await up(db))
runBasicTests({
adapter,
db: {
user: async (id) =>
await getRecord<AdapterUser>(db, GET_USER_BY_ID_SQL, [id]),
session: async (sessionToken) =>
await getRecord<AdapterSession>(db, GET_SESSION_BY_TOKEN_SQL, [
sessionToken,
]),
account: async ({ provider, providerAccountId }) =>
await getRecord<AdapterAccount>(
db,
GET_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL,
[provider, providerAccountId]
),
verificationToken: async ({ identifier, token }) =>
await getRecord(db, GET_VERIFICATION_TOKEN_BY_IDENTIFIER_AND_TOKEN_SQL, [
identifier,
token,
]),
},
})

View File

@@ -0,0 +1,20 @@
{
"extends": "@auth/tsconfig/tsconfig.base.json",
"compilerOptions": {
"allowJs": true,
"baseUrl": ".",
"isolatedModules": true,
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "node",
"outDir": ".",
"rootDir": "src",
"skipDefaultLibCheck": true,
"strictNullChecks": true,
"stripInternal": true,
"declarationMap": true,
"declaration": true
},
"include": ["src/**/*"],
"exclude": ["*.js", "*.d.ts"]
}

View File

@@ -1,7 +1,7 @@
{
"name": "@auth/dynamodb-adapter",
"repository": "https://github.com/nextauthjs/next-auth",
"version": "1.0.1",
"version": "1.0.2",
"description": "AWS DynamoDB adapter for next-auth.",
"keywords": [
"next-auth",
@@ -57,4 +57,4 @@
"dependencies": {
"@auth/core": "workspace:*"
}
}
}

View File

@@ -56,8 +56,8 @@ export interface DynamoDBAdapterOptions {
*
* const config: DynamoDBClientConfig = {
* credentials: {
* accessKeyId: process.env.NEXT_AUTH_AWS_ACCESS_KEY as string,
* secretAccessKey: process.env.NEXT_AUTH_AWS_SECRET_KEY as string,
* accessKeyId: process.env.NEXT_AUTH_AWS_ACCESS_KEY,
* secretAccessKey: process.env.NEXT_AUTH_AWS_SECRET_KEY,
* },
* region: process.env.NEXT_AUTH_AWS_REGION,
* };

View File

@@ -0,0 +1,28 @@
<p align="center">
<br/>
<a href="https://authjs.dev" target="_blank">
<img height="64px" src="https://authjs.dev/img/logo/logo-sm.png" />
</a>
<a href="https://www.edgedb.com/" target="_blank">
<img height="64px" src="https://authjs.dev/img/adapters/edgedb.svg"/>
</a>
<h3 align="center"><b>Edge DB Adapter</b> - NextAuth.js / Auth.js</a></h3>
<p align="center" style="align: center;">
<a href="https://npm.im/@auth/edgedb-adapter">
<img src="https://img.shields.io/badge/TypeScript-blue?style=flat-square" alt="TypeScript" />
</a>
<a href="https://npm.im/@auth/edgedb-adapter">
<img alt="npm" src="https://img.shields.io/npm/v/@auth/edgedb-adapter?color=green&label=@auth/edgedb-adapter&style=flat-square">
</a>
<a href="https://www.npmtrends.com/@auth/edgedb-adapter">
<img src="https://img.shields.io/npm/dm/@auth/edgedb-adapter?label=%20downloads&style=flat-square" alt="Downloads" />
</a>
<a href="https://github.com/nextauthjs/next-auth/stargazers">
<img src="https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square" alt="Github Stars" />
</a>
</p>
</p>
---
Check out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/edgedb).

View File

@@ -0,0 +1,4 @@
<svg style="color:#1F8AED" width="140" height="114" viewBox="0 0 140 114" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M118.58 56.966C118.58 66.6274 114.706 68.3592 110.377 68.3592H101.171V45.5728H110.377C114.706 45.5728 118.58 47.3046 118.58 56.966ZM113.887 56.966C113.887 50.2212 111.836 49.9022 108.874 49.9022H106.003V64.0298H108.874C111.836 64.0298 113.887 63.7108 113.887 56.966ZM65.1234 68.3592V45.5728H79.6155V49.9022H69.9541V54.5507H77.2458V58.8345H69.9541V64.0298H79.6155V68.3592H65.1234ZM87.7282 113.932H92.5589V0H87.7282V113.932ZM128.242 58.242V64.0298H132.252C134.758 64.0298 135.396 62.3892 135.396 61.1587C135.396 60.2017 134.941 58.242 131.523 58.242H128.242ZM128.242 49.9022V54.2317H131.523C133.391 54.2317 134.485 53.4113 134.485 52.0442C134.485 50.677 133.391 49.9022 131.523 49.9022H128.242ZM123.411 45.5728H132.708C137.585 45.5728 139.043 48.9908 139.043 51.4517C139.043 53.7304 137.585 55.371 136.582 55.8267C139.499 57.2395 140 60.1105 140 61.5689C140 63.4829 139.043 68.3592 132.708 68.3592H123.411V45.5728ZM37.3702 56.966C37.3702 66.6274 33.4966 68.3592 29.1671 68.3592H19.9614V45.5728H29.1671C33.4966 45.5728 37.3702 47.3046 37.3702 56.966ZM51.5885 64.2121C54.0495 64.2121 55.3255 63.3918 55.7812 62.8449V60.3384H51.862V56.4191H59.6549V65.4881C58.9713 66.5363 55.2344 68.5871 51.8164 68.5871C46.211 68.5871 41.4714 66.3996 41.4714 56.7381C41.4714 47.0767 46.2565 45.345 50.5859 45.345C57.3763 45.345 59.0625 48.8996 59.6094 52.0442L55.5989 52.9556C55.3711 51.4973 54.095 49.6744 51.1784 49.6744C48.2162 49.6744 46.1654 49.9934 46.1654 56.7381C46.1654 63.4829 48.3073 64.2121 51.5885 64.2121ZM32.6756 56.966C32.6756 50.2212 30.6248 49.9022 27.6626 49.9022H24.7915V64.0298H27.6626C30.6248 64.0298 32.6756 63.7108 32.6756 56.966ZM0 68.3592V45.5728H14.4921V49.9022H4.83072V54.5507H12.1224V58.8345H4.83072V64.0298H14.4921V68.3592H0Z" fill="currentColor">
</path>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,60 @@
{
"name": "@auth/edgedb-adapter",
"version": "0.2.0",
"description": "EdgeDB adapter for next-auth.",
"homepage": "https://authjs.dev",
"repository": "https://github.com/nextauthjs/next-auth",
"bugs": {
"url": "https://github.com/nextauthjs/next-auth/issues"
},
"author": "Bruno Crosier",
"contributors": [
"Thang Huu Vu <hi@thvu.dev>"
],
"type": "module",
"types": "./index.d.ts",
"files": [
"*.d.ts*",
"*.js",
"lib",
"src"
],
"exports": {
".": {
"types": "./index.d.ts",
"import": "./index.js"
}
},
"license": "ISC",
"keywords": [
"next-auth",
"next.js",
"oauth",
"edgedb"
],
"private": false,
"publishConfig": {
"access": "public"
},
"scripts": {
"build": "tsc",
"test": "jest"
},
"dependencies": {
"@auth/core": "workspace:*"
},
"peerDependencies": {
"edgedb": "^1.0.1"
},
"devDependencies": {
"@auth/adapter-test": "workspace:^0.0.0",
"@auth/tsconfig": "workspace:^0.0.0",
"jest": "^27.4.3",
"@auth/core": "workspace:*",
"typescript": "^4.7.4",
"edgedb": "^1.0.1"
},
"jest": {
"preset": "@auth/adapter-test/jest"
}
}

View File

@@ -0,0 +1,615 @@
/**
* <div style={{display: "flex", justifyContent: "space-between", alignItems: "center", padding: 16}}>
* <p style={{fontWeight: "normal"}}>Official <a href="https://www.edgedb.com/">Edge DB</a> adapter for Auth.js / NextAuth.js.</p>
* <a href="https://www.edgedb.com/">
* <img style={{display: "block"}} src="/img/adapters/edgedb.svg" width="38" />
* </a>
* </div>
*
* ## Installation
*
* ```bash npm2yarn2pnpm
* npm install edgedb @auth/edgedb-adapter
* npm install @edgedb/generate --save-dev
* ```
*
* @module @auth/edgedb-adapter
*/
import type {
Adapter,
AdapterSession,
AdapterUser,
VerificationToken,
} from "@auth/core/adapters"
import type { Client } from "edgedb"
/**
*
* To use this Adapter, you need to install `edgedb`, `@edgedb/generate`, and the separate `@auth/edgedb-adapter` package:
*
* ```bash npm2yarn2pnpm
* npm install edgedb @auth/edgedb-adapter
* npm install @edgedb/generate --save-dev
* ```
*
* ## Installation
*
* First, ensure you have the EdgeDB CLI installed.
*
* Follow the instructions below, or read the [EdgeDB quickstart](https://www.edgedb.com/docs/intro/quickstart) to install the EdgeDB CLI and initialize a project
*
* ### Linux or macOS
* ```bash
* curl --proto '=https' --tlsv1.2 -sSf https://sh.edgedb.com | sh
* ```
*
* ### Windows
* ```powershell
* iwr https://ps1.edgedb.com -useb | iex
* ```
*
* Check that the CLI is available with the `edgedb --version` command. If you get a `Command not found` error, you may need to open a new terminal window before the `edgedb` command is available.
*
* Once the CLI is installed, initialize a project from the applications root directory. Youll be presented with a series of prompts.
*
* ```bash
* edgedb project init
* ```
*
* This process will spin up an EdgeDB instance and [“link”](https://www.edgedb.com/docs/cli/edgedb_instance/edgedb_instance_link#edgedb-instance-link) it with your current directory. As long as youre inside that directory, CLI commands and client libraries will be able to connect to the linked instance automatically, without additional configuration.
*
* ## Setup
*
* ### NextAuth.js configuration
*
* Configure your NextAuth.js to use the EdgeDB Adapter:
*
* ```javascript title="pages/api/auth/[...nextauth].js"
* import NextAuth from "next-auth"
* import GoogleProvider from "next-auth/providers/google"
* import { EdgeDBAdapter } from "@auth/edgedb-adapter"
* import { createClient } from "edgedb"
*
* const client = createClient()
*
* export default NextAuth({
* adapter: EdgeDBAdapter(client),
* providers: [
* GoogleProvider({
* clientId: process.env.GOOGLE_CLIENT_ID,
* clientSecret: process.env.GOOGLE_CLIENT_SECRET,
* }),
* ],
* })
* ```
*
* ### Create the EdgeDB schema
*
* Replace the contents of the auto-generated file in `dbschema/default.esdl` with the following:
*
* > This schema is adapted for use in EdgeDB and based upon our main [schema](/adapters/models)
*
* ```json title="default.esdl"
* module default {
* type User {
* property name -> str;
* required property email -> str {
* constraint exclusive;
* }
* property emailVerified -> datetime;
* property image -> str;
* multi link accounts := .<user[is Account];
* multi link sessions := .<user[is Session];
* property createdAt -> datetime {
* default := datetime_current();
* };
* }
*
* type Account {
* required property userId := .user.id;
* required property type -> str;
* required property provider -> str;
* required property providerAccountId -> str {
* constraint exclusive;
* };
* property refresh_token -> str;
* property access_token -> str;
* property expires_at -> int64;
* property token_type -> str;
* property scope -> str;
* property id_token -> str;
* property session_state -> str;
* required link user -> User {
* on target delete delete source;
* };
* property createdAt -> datetime {
* default := datetime_current();
* };
*
* constraint exclusive on ((.provider, .providerAccountId))
* }
*
* type Session {
* required property sessionToken -> str {
* constraint exclusive;
* }
* required property userId := .user.id;
* required property expires -> datetime;
* required link user -> User {
* on target delete delete source;
* };
* property createdAt -> datetime {
* default := datetime_current();
* };
* }
*
* type VerificationToken {
* required property identifier -> str;
* required property token -> str {
* constraint exclusive;
* }
* required property expires -> datetime;
* property createdAt -> datetime {
* default := datetime_current();
* };
*
* constraint exclusive on ((.identifier, .token))
* }
* }
*
* # Disable the application of access policies within access policies
* # themselves. This behavior will become the default in EdgeDB 3.0.
* # See: https://www.edgedb.com/docs/reference/ddl/access_policies#nonrecursive
* using future nonrecursive_access_policies;
* ```
*
* ### Migrate the database schema
*
* Create a migration
*
* ```
* edgedb migration create
* ```
*
* Apply the migration
*
* ```
* edgedb migrate
* ```
*
* To learn more about [EdgeDB migrations](https://www.edgedb.com/docs/intro/migrations#generate-a-migration), check out the [Migrations docs](https://www.edgedb.com/docs/intro/migrations).
*
* ### Generate the query builder
*
* ```npm2yarn2pnpm
* npx @edgedb/generate edgeql-js
* ```
*
* This will generate the [query builder](https://www.edgedb.com/docs/clients/js/querybuilder) so that you can write fully typed EdgeQL queries with TypeScript in a code-first way.
*
* For example
*
* ```ts
* const query = e.select(e.User, () => ({
* id: true,
* email: true,
* emailVerified: true,
* name: true,
* image: true,
* filter_single: { email: 'johndoe@example.com' },
* }));
*
* return await query.run(client);
*
* // Return type:
* // {
* // id: string;
* // email: string;
* // emailVerified: Date | null;
* // image: string | null;
* // name: string | null;
* // } | null
*
* ```
*
*
* ## Deploying
*
* ### Deploy EdgeDB
*
* First deploy an EdgeDB instance on your preferred cloud provider:
*
* [AWS](https://www.edgedb.com/docs/guides/deployment/aws_aurora_ecs)
*
* [Google Cloud](https://www.edgedb.com/docs/guides/deployment/gcp)
*
* [Azure](https://www.edgedb.com/docs/guides/deployment/azure_flexibleserver)
*
* [DigitalOcean](https://www.edgedb.com/docs/guides/deployment/digitalocean)
*
* [Fly.io](https://www.edgedb.com/docs/guides/deployment/fly_io)
*
* [Docker](https://www.edgedb.com/docs/guides/deployment/docker) (cloud-agnostic)
*
* ### Find your instances DSN
*
* The DSN is also known as a connection string. It will have the format `edgedb://username:password@hostname:port`. The exact instructions for this depend on which cloud you are deploying to.
*
* ### Set an environment variable
*
* ```env title=".env"
* EDGEDB_DSN=edgedb://johndoe:supersecure@myhost.com:420
* ```
*
* ### Update the client
*
* ```diff title="pages/api/auth/[...nextauth].js"
* import NextAuth from "next-auth"
* import GoogleProvider from "next-auth/providers/google"
* import { EdgeDBAdapter } from "@auth/edgedb-adapter"
* import { createClient } from "edgedb"
*
* - const client = createClient()
* + const client = createClient({ dsn: process.env.EDGEDB_DSN })
*
* export default NextAuth({
* adapter: EdgeDBAdapter(client),
* providers: [
* GoogleProvider({
* clientId: process.env.GOOGLE_CLIENT_ID,
* clientSecret: process.env.GOOGLE_CLIENT_SECRET,
* }),
* ],
* })
* ```
*
*
*
* ### Apply migrations
*
* Use the DSN to apply migrations against your remote instance.
*
* ```bash
* edgedb migrate --dsn <your-instance-dsn>
* ```
*
* ### Set up a `prebuild` script
*
* Add the following `prebuild` script to your `package.json`. When your hosting provider initializes the build, it will trigger this script which will generate the query builder. The `npx @edgedb/generate edgeql-js` command will read the value of the `EDGEDB_DSN` environment variable, connect to the database, and generate the query builder before your hosting provider starts building the project.
*
* ```diff title="package.json"
* "scripts": {
* "dev": "next dev",
* "build": "next build",
* "start": "next start",
* "lint": "next lint",
* + "prebuild": "npx @edgedb/generate edgeql-js"
* },
* ```
*
*/
export function EdgeDBAdapter(client: Client): Adapter {
return {
async createUser({ email, emailVerified, name, image }) {
return await client.queryRequiredSingle(
`
with
image := <optional str>$image,
name := <optional str>$name,
emailVerified := <optional str>$emailVerified
select (
insert User {
email:= <str>$email,
emailVerified:= <datetime>emailVerified,
name:= name,
image:= image,
}
) {
id,
email,
emailVerified,
name,
image
}
`,
{
email,
emailVerified: emailVerified && new Date(emailVerified).toISOString(),
name,
image,
}
)
},
async getUser(id) {
return await client.querySingle<AdapterUser>(
`
select User {
id,
email,
emailVerified,
name,
image
} filter .id = <uuid>$id;
`,
{ id }
)
},
async getUserByEmail(email) {
return await client.querySingle<AdapterUser>(
`
select User {
id,
email,
emailVerified,
name,
image
} filter .email = <str>$email;
`,
{ email }
)
},
async getUserByAccount({ providerAccountId, provider }) {
return await client.querySingle<AdapterUser>(
`
with account := (
select Account
filter .providerAccountId = <str>$providerAccountId
and .provider = <str>$provider
)
select account.user {
id,
email,
image,
name,
emailVerified
}
`,
{ providerAccountId, provider }
)
},
async updateUser({ email, emailVerified, id, image, name }) {
return await client.queryRequiredSingle<AdapterUser>(
`
with
email := <optional str>$email,
emailVerified := <optional str>$emailVerified,
image := <optional str>$image,
name := <optional str>$name
select (
update User
filter .id = <uuid>$id
set {
email := email ?? .email,
emailVerified := <datetime>emailVerified ?? .emailVerified,
image := image ?? .image,
name := name ?? .name,
}
) {
id,
email,
emailVerified,
image,
name
}
`,
{
email,
emailVerified: emailVerified && new Date(emailVerified).toISOString(),
id,
image,
name,
}
)
},
async deleteUser(id) {
await client.execute(`delete User filter .id = <uuid>$id;`, { id })
},
async linkAccount({
userId,
type,
provider,
providerAccountId,
refresh_token,
access_token,
expires_at,
token_type,
scope,
id_token,
session_state,
}) {
await client.execute(
`
with
userId := <optional str>$userId,
refresh_token := <optional str>$refresh_token,
access_token := <optional str>$access_token,
expires_at := <optional str>$expires_at,
token_type := <optional str>$token_type,
scope := <optional str>$scope,
id_token := <optional str>$id_token,
session_state := <optional str>$session_state
insert Account {
type := <str>$type,
provider := <str>$provider,
providerAccountId := <str>$providerAccountId,
refresh_token := refresh_token,
access_token := access_token,
expires_at := <int64>expires_at,
token_type := token_type,
scope := scope,
id_token := id_token,
session_state := session_state,
user := (
select User filter .id = <uuid>userId
)
}
`,
{
userId,
type,
provider,
providerAccountId,
refresh_token,
access_token,
expires_at: expires_at && String(expires_at),
token_type,
scope,
id_token,
session_state,
}
)
},
async unlinkAccount({ providerAccountId, provider }) {
await client.execute(
`
delete Account filter
.providerAccountId = <str>$providerAccountId
and
.provider = <str>$provider
`,
{ providerAccountId, provider }
)
},
async createSession({ expires, sessionToken, userId }) {
return await client.queryRequiredSingle<AdapterSession>(
`
select (
insert Session {
expires := <datetime>$expires,
sessionToken := <str>$sessionToken,
user := (
select User filter .id = <uuid>$userId
)
}
) {
expires,
sessionToken,
userId
};
`,
{ expires, sessionToken, userId }
)
},
async getSessionAndUser(sessionToken) {
const sessionAndUser = await client.querySingle<
AdapterSession & { user: AdapterUser }
>(
`
select Session {
userId,
id,
expires,
sessionToken,
user: {
id,
email,
emailVerified,
image,
name
}
} filter .sessionToken = <str>$sessionToken;
`,
{ sessionToken }
)
if (!sessionAndUser) {
return null
}
const { user, ...session } = sessionAndUser
if (!user || !session) {
return null
}
return {
user,
session,
}
},
async updateSession({ sessionToken, expires, userId }) {
return await client.querySingle<AdapterSession>(
`
with
sessionToken := <optional str>$sessionToken,
expires := <optional str>$expires,
userId := <optional str>$userId,
user := (
select User filter .id = <uuid>userId
)
select (
update Session
filter .sessionToken = <str>$sessionToken
set {
sessionToken := sessionToken ?? .sessionToken,
expires := <datetime>expires ?? .expires,
user := user ?? .user
}
) {
sessionToken,
userId,
expires
}
`,
{
sessionToken,
expires: expires && new Date(expires).toISOString(),
userId,
}
)
},
async deleteSession(sessionToken) {
await client.query(
`delete Session filter .sessionToken = <str>$sessionToken`,
{ sessionToken }
)
},
async createVerificationToken({ identifier, expires, token }) {
const createdVerificationToken =
await client.querySingle<VerificationToken>(
`
select (
insert VerificationToken {
identifier := <str>$identifier,
expires := <datetime>$expires,
token := <str>$token,
}
) {
identifier,
expires,
token
}
`,
{ identifier, expires, token }
)
return createdVerificationToken
},
async useVerificationToken({ token, identifier }) {
const verificationToken = await client.querySingle<VerificationToken>(
`
select (
delete VerificationToken filter .token = <str>$token
and
.identifier = <str>$identifier
) {
identifier,
expires,
token
}
`,
{ token, identifier }
)
if (verificationToken && "id" in verificationToken) {
delete verificationToken.id
}
return verificationToken
},
}
}

View File

@@ -0,0 +1,91 @@
import { runBasicTests } from "@auth/adapter-test"
import { EdgeDBAdapter } from "../src"
import { createClient } from "edgedb"
if (process.env.CI) {
// TODO: Fix this
test('Skipping EdgeDBAdapter tests in CI because of "Request failed" errors. Should revisit', () => {
expect(true).toBe(true)
})
process.exit(0)
}
const client = createClient();
runBasicTests({
adapter: EdgeDBAdapter(client),
db: {
connect: async () => {
await client.query(`
delete User;
delete Account;
delete Session;
delete VerificationToken;
`)
},
disconnect: async () => {
await client.query(`
delete User;
delete Account;
delete Session;
delete VerificationToken;
`)
},
user: async (id) => {
return await client.querySingle(`
select User {
id,
email,
emailVerified,
name,
image
} filter .id = <uuid>$id
`, { id })
},
account: async ({ providerAccountId, provider }) => {
return await client.querySingle(`
select Account {
provider,
providerAccountId,
type,
access_token,
expires_at,
id_token,
refresh_token,
token_type,
scope,
session_state,
id,
userId
}
filter
.providerAccountId = <str>$providerAccountId
and
.provider = <str>$provider
`, { providerAccountId, provider })
},
session: async (sessionToken) => {
return await client.querySingle(`
select Session {
userId,
id,
expires,
sessionToken,
}
filter .sessionToken = <str>$sessionToken
`, { sessionToken })
},
async verificationToken({ token, identifier }) {
return await client.querySingle(`
select VerificationToken {
identifier,
expires,
token,
}
filter .token = <str>$token
and
.identifier = <str>$identifier
`, { token, identifier })
},
},
})

View File

@@ -0,0 +1,20 @@
{
"extends": "@auth/tsconfig/tsconfig.base.json",
"compilerOptions": {
"allowJs": true,
"baseUrl": ".",
"isolatedModules": true,
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "node",
"outDir": ".",
"rootDir": "src",
"skipDefaultLibCheck": true,
"strictNullChecks": true,
"stripInternal": true,
"declarationMap": true,
"declaration": true
},
"include": ["src/**/*"],
"exclude": ["*.js", "*.d.ts"]
}

View File

@@ -1,6 +1,6 @@
{
"name": "@auth/mongodb-adapter",
"version": "1.0.0",
"version": "2.0.0",
"description": "MongoDB adapter for Auth.js",
"homepage": "https://authjs.dev",
"repository": "https://github.com/nextauthjs/next-auth",
@@ -42,15 +42,15 @@
"@auth/core": "workspace:*"
},
"peerDependencies": {
"mongodb": "^5 || ^4"
"mongodb": "^6"
},
"devDependencies": {
"@auth/adapter-test": "workspace:*",
"@auth/tsconfig": "workspace:*",
"jest": "^27.4.3",
"mongodb": "^5.1.0"
"mongodb": "^6.0.0"
},
"jest": {
"preset": "@auth/adapter-test/jest"
}
}
}

View File

@@ -193,7 +193,7 @@ export function MongoDBAdapter(
await db
).U.findOneAndUpdate({ _id }, { $set: user }, { returnDocument: "after" })
return from<AdapterUser>(result.value!)
return from<AdapterUser>(result!)
},
async deleteUser(id) {
const userId = _id(id)
@@ -210,7 +210,7 @@ export function MongoDBAdapter(
return account
},
async unlinkAccount(provider_providerAccountId) {
const { value: account } = await (
const account = await (
await db
).A.findOneAndDelete(provider_providerAccountId)
return from<AdapterAccount>(account!)
@@ -235,17 +235,17 @@ export function MongoDBAdapter(
async updateSession(data) {
const { _id, ...session } = to<AdapterSession>(data)
const result = await (
const updatedSession = await (
await db
).S.findOneAndUpdate(
{ sessionToken: session.sessionToken },
{ $set: session },
{ returnDocument: "after" }
)
return from<AdapterSession>(result.value!)
return from<AdapterSession>(updatedSession!)
},
async deleteSession(sessionToken) {
const { value: session } = await (
const session = await (
await db
).S.findOneAndDelete({
sessionToken,
@@ -257,14 +257,13 @@ export function MongoDBAdapter(
return data
},
async useVerificationToken(identifier_token) {
const { value: verificationToken } = await (
const verificationToken = await (
await db
).V.findOneAndDelete(identifier_token)
if (!verificationToken) return null
// @ts-expect-error
delete verificationToken._id
return verificationToken
const { _id, ...rest } = verificationToken;
return rest;
},
}
}

View File

@@ -0,0 +1,28 @@
<p align="center">
<br/>
<a href="https://authjs.dev" target="_blank">
<img height="64px" src="https://authjs.dev/img/logo/logo-sm.png" />
</a>
<a href="https://www.postgresql.org/" target="_blank">
<img height="64px" src="https://authjs.dev/img/adapters/pg.png"/>
</a>
<h3 align="center"><b>Postgres Adapter</b> - NextAuth.js / Auth.js</a></h3>
<p align="center" style="align: center;">
<a href="https://npm.im/@auth/pg-adapter">
<img src="https://img.shields.io/badge/TypeScript-blue?style=flat-square" alt="TypeScript" />
</a>
<a href="https://npm.im/@auth/pg-adapter">
<img alt="npm" src="https://img.shields.io/npm/v/@auth/pg-adapter?color=green&label=@auth/pg-adapter&style=flat-square">
</a>
<a href="https://www.npmtrends.com/@auth/pg-adapter">
<img src="https://img.shields.io/npm/dm/@auth/pg-adapter?label=%20downloads&style=flat-square" alt="Downloads" />
</a>
<a href="https://github.com/nextauthjs/next-auth/stargazers">
<img src="https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square" alt="Github Stars" />
</a>
</p>
</p>
---
Check out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/pg).

View File

@@ -0,0 +1,13 @@
version: "3"
services:
postgres:
image: postgres:latest
container_name: pg-adapter
environment:
POSTGRES_DB: adapter-postgres-test
POSTGRES_USER: pg
POSTGRES_PASSWORD: pg
volumes:
- ./example-schema.sql:/docker-entrypoint-initdb.d/example-schema.sql
ports:
- 5432:5432

View File

@@ -0,0 +1,49 @@
\set ON_ERROR_STOP true
CREATE TABLE verification_token
(
identifier TEXT NOT NULL,
expires TIMESTAMPTZ NOT NULL,
token TEXT NOT NULL,
PRIMARY KEY (identifier, token)
);
CREATE TABLE accounts
(
id SERIAL,
"userId" INTEGER NOT NULL,
type VARCHAR(255) NOT NULL,
provider VARCHAR(255) NOT NULL,
"providerAccountId" VARCHAR(255) NOT NULL,
refresh_token TEXT,
access_token TEXT,
expires_at BIGINT,
id_token TEXT,
scope TEXT,
session_state TEXT,
token_type TEXT,
PRIMARY KEY (id)
);
CREATE TABLE sessions
(
id SERIAL,
"userId" INTEGER NOT NULL,
expires TIMESTAMPTZ NOT NULL,
"sessionToken" VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE users
(
id SERIAL,
name VARCHAR(255),
email VARCHAR(255),
"emailVerified" TIMESTAMPTZ,
image TEXT,
PRIMARY KEY (id)
);

View File

@@ -0,0 +1,59 @@
{
"name": "@auth/pg-adapter",
"version": "0.2.0",
"description": "Postgres adapter for next-auth.",
"homepage": "https://authjs.dev",
"repository": "https://github.com/nextauthjs/next-auth",
"bugs": {
"url": "https://github.com/nextauthjs/next-auth/issues"
},
"author": "Jake Coppinger",
"contributors": [
"Thang Huu Vu <hi@thvu.dev>"
],
"license": "ISC",
"keywords": [
"next-auth",
"@auth",
"Auth.js",
"next.js",
"oauth",
"postgres"
],
"type": "module",
"exports": {
".": {
"types": "./index.d.ts",
"import": "./index.js"
}
},
"files": [
"*.d.ts*",
"*.js",
"src"
],
"private": false,
"publishConfig": {
"access": "public"
},
"scripts": {
"test": "./tests/test.sh",
"build": "tsc"
},
"dependencies": {
"@auth/core": "workspace:*"
},
"peerDependencies": {
"pg": "^8"
},
"devDependencies": {
"@auth/adapter-test": "workspace:*",
"@auth/tsconfig": "workspace:*",
"@types/pg": "^8.6.5",
"jest": "^27.4.3",
"pg": "^8.7.1"
},
"jest": {
"preset": "@auth/adapter-test/jest"
}
}

View File

@@ -0,0 +1,346 @@
/**
* <div style={{display: "flex", justifyContent: "space-between", alignItems: "center", padding: 16}}>
* <p style={{fontWeight: "normal"}}>An official <a href="https://www.postgresql.org/">PostgreSQL</a> adapter for Auth.js / NextAuth.js.</p>
* <a href="https://www.postgresql.org/">
* <img style={{display: "block"}} src="/img/adapters/pg.svg" width="48" />
* </a>
* </div>
*
* ## Installation
*
* ```bash npm2yarn2pnpm
* npm install next-auth @auth/pg-adapter pg
* ```
*
* @module @auth/pg-adapter
*/
import type {
Adapter,
AdapterUser,
VerificationToken,
AdapterSession,
} from "@auth/core/adapters"
import type { Pool } from "pg"
export function mapExpiresAt(account: any): any {
const expires_at: number = parseInt(account.expires_at)
return {
...account,
expires_at,
}
}
/**
* ## Setup
*
* The SQL schema for the tables used by this adapter is as follows. Learn more about the models at our doc page on [Database Models](http://localhost:3000/reference/adapters#models).
* ```sql
* CREATE TABLE verification_token
* (
* identifier TEXT NOT NULL,
* expires TIMESTAMPTZ NOT NULL,
* token TEXT NOT NULL,
*
* PRIMARY KEY (identifier, token)
* );
*
* CREATE TABLE accounts
* (
* id SERIAL,
* "userId" INTEGER NOT NULL,
* type VARCHAR(255) NOT NULL,
* provider VARCHAR(255) NOT NULL,
* "providerAccountId" VARCHAR(255) NOT NULL,
* refresh_token TEXT,
* access_token TEXT,
* expires_at BIGINT,
* id_token TEXT,
* scope TEXT,
* session_state TEXT,
* token_type TEXT,
*
* PRIMARY KEY (id)
* );
*
* CREATE TABLE sessions
* (
* id SERIAL,
* "userId" INTEGER NOT NULL,
* expires TIMESTAMPTZ NOT NULL,
* "sessionToken" VARCHAR(255) NOT NULL,
*
* PRIMARY KEY (id)
* );
*
* CREATE TABLE users
* (
* id SERIAL,
* name VARCHAR(255),
* email VARCHAR(255),
* "emailVerified" TIMESTAMPTZ,
* image TEXT,
*
* PRIMARY KEY (id)
* );
*
* ```
*
* ```bash npm2yarn2pnpm
* npm install pg @auth/pg-adapter next-auth
* ```
*
* ```typescript title="auth.ts"
* import NextAuth from "next-auth"
* import GoogleProvider from "next-auth/providers/google"
* import { PostgresAdapter } from "@auth/pg-adapter"
* import { Pool } from 'pg'
*
* const pool = new Pool({
* host: 'localhost',
* user: 'database-user',
* max: 20,
* idleTimeoutMillis: 30000,
* connectionTimeoutMillis: 2000,
* })
*
* export default NextAuth({
* adapter: PostgresAdapter(pool),
* providers: [
* GoogleProvider({
* clientId: process.env.GOOGLE_CLIENT_ID,
* clientSecret: process.env.GOOGLE_CLIENT_SECRET,
* }),
* ],
* })
* ```
*
*/
export default function PostgresAdapter(client: Pool): Adapter {
return {
async createVerificationToken(
verificationToken: VerificationToken
): Promise<VerificationToken> {
const { identifier, expires, token } = verificationToken
const sql = `
INSERT INTO verification_token ( identifier, expires, token )
VALUES ($1, $2, $3)
`
await client.query(sql, [identifier, expires, token])
return verificationToken
},
async useVerificationToken({
identifier,
token,
}: {
identifier: string
token: string
}): Promise<VerificationToken> {
const sql = `delete from verification_token
where identifier = $1 and token = $2
RETURNING identifier, expires, token `
const result = await client.query(sql, [identifier, token])
return result.rowCount !== 0 ? result.rows[0] : null
},
async createUser(user: Omit<AdapterUser, "id">) {
const { name, email, emailVerified, image } = user
const sql = `
INSERT INTO users (name, email, "emailVerified", image)
VALUES ($1, $2, $3, $4)
RETURNING id, name, email, "emailVerified", image`
const result = await client.query(sql, [
name,
email,
emailVerified,
image,
])
return result.rows[0]
},
async getUser(id) {
const sql = `select * from users where id = $1`
try {
const result = await client.query(sql, [id])
return result.rowCount === 0 ? null : result.rows[0]
} catch (e) {
return null
}
},
async getUserByEmail(email) {
const sql = `select * from users where email = $1`
const result = await client.query(sql, [email])
return result.rowCount !== 0 ? result.rows[0] : null
},
async getUserByAccount({
providerAccountId,
provider,
}): Promise<AdapterUser | null> {
const sql = `
select u.* from users u join accounts a on u.id = a."userId"
where
a.provider = $1
and
a."providerAccountId" = $2`
const result = await client.query(sql, [provider, providerAccountId])
return result.rowCount !== 0 ? result.rows[0] : null
},
async updateUser(user: Partial<AdapterUser>): Promise<AdapterUser> {
const fetchSql = `select * from users where id = $1`
const query1 = await client.query(fetchSql, [user.id])
const oldUser = query1.rows[0]
const newUser = {
...oldUser,
...user,
}
const { id, name, email, emailVerified, image } = newUser
const updateSql = `
UPDATE users set
name = $2, email = $3, "emailVerified" = $4, image = $5
where id = $1
RETURNING name, id, email, "emailVerified", image
`
const query2 = await client.query(updateSql, [
id,
name,
email,
emailVerified,
image,
])
return query2.rows[0]
},
async linkAccount(account) {
const sql = `
insert into accounts
(
"userId",
provider,
type,
"providerAccountId",
access_token,
expires_at,
refresh_token,
id_token,
scope,
session_state,
token_type
)
values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
returning
id,
"userId",
provider,
type,
"providerAccountId",
access_token,
expires_at,
refresh_token,
id_token,
scope,
session_state,
token_type
`
const params = [
account.userId,
account.provider,
account.type,
account.providerAccountId,
account.access_token,
account.expires_at,
account.refresh_token,
account.id_token,
account.scope,
account.session_state,
account.token_type,
]
const result = await client.query(sql, params)
return mapExpiresAt(result.rows[0])
},
async createSession({ sessionToken, userId, expires }) {
if (userId === undefined) {
throw Error(`userId is undef in createSession`)
}
const sql = `insert into sessions ("userId", expires, "sessionToken")
values ($1, $2, $3)
RETURNING id, "sessionToken", "userId", expires`
const result = await client.query(sql, [userId, expires, sessionToken])
return result.rows[0]
},
async getSessionAndUser(sessionToken: string | undefined): Promise<{
session: AdapterSession
user: AdapterUser
} | null> {
if (sessionToken === undefined) {
return null
}
const result1 = await client.query(
`select * from sessions where "sessionToken" = $1`,
[sessionToken]
)
if (result1.rowCount === 0) {
return null
}
let session: AdapterSession = result1.rows[0]
const result2 = await client.query("select * from users where id = $1", [
session.userId,
])
if (result2.rowCount === 0) {
return null
}
const user = result2.rows[0]
return {
session,
user,
}
},
async updateSession(
session: Partial<AdapterSession> & Pick<AdapterSession, "sessionToken">
): Promise<AdapterSession | null | undefined> {
const { sessionToken } = session
const result1 = await client.query(
`select * from sessions where "sessionToken" = $1`,
[sessionToken]
)
if (result1.rowCount === 0) {
return null
}
const originalSession: AdapterSession = result1.rows[0]
const newSession: AdapterSession = {
...originalSession,
...session,
}
const sql = `
UPDATE sessions set
expires = $2
where "sessionToken" = $1
`
const result = await client.query(sql, [
newSession.sessionToken,
newSession.expires,
])
return result.rows[0]
},
async deleteSession(sessionToken) {
const sql = `delete from sessions where "sessionToken" = $1`
await client.query(sql, [sessionToken])
},
async unlinkAccount(partialAccount) {
const { provider, providerAccountId } = partialAccount
const sql = `delete from accounts where "providerAccountId" = $1 and provider = $2`
await client.query(sql, [providerAccountId, provider])
},
async deleteUser(userId: string) {
await client.query(`delete from users where id = $1`, [userId])
await client.query(`delete from sessions where "userId" = $1`, [userId])
await client.query(`delete from accounts where "userId" = $1`, [userId])
},
}
}

View File

@@ -0,0 +1,50 @@
import { runBasicTests } from "@auth/adapter-test"
import PostgresAdapter, { mapExpiresAt } from "../src"
import { Pool } from "pg"
const POOL_SIZE = 20
const client = new Pool({
host: "127.0.0.1",
database: "adapter-postgres-test",
user: "pg",
password: "pg",
port: 5432,
max: POOL_SIZE,
})
runBasicTests({
adapter: PostgresAdapter(client),
db: {
disconnect: async () => {
await client.end()
},
user: async (id: string) => {
const sql = `select * from users where id = $1`
const result = await client.query(sql, [id])
return result.rowCount !== 0 ? result.rows[0] : null
},
account: async (account) => {
const sql = `
select * from accounts where "providerAccountId" = $1`
const result = await client.query(sql, [account.providerAccountId])
return result.rowCount !== 0 ? mapExpiresAt(result.rows[0]) : null
},
session: async (sessionToken) => {
const result1 = await client.query(
`select * from sessions where "sessionToken" = $1`,
[sessionToken]
)
return result1.rowCount !== 0 ? result1.rows[0] : null
},
async verificationToken(identifier_token) {
const { identifier, token } = identifier_token
const sql = `
select * from verification_token where identifier = $1 and token = $2`
const result = await client.query(sql, [identifier, token])
return result.rowCount !== 0 ? result.rows[0] : null
},
},
})

View File

@@ -0,0 +1,13 @@
#!/usr/bin/env bash
docker-compose up -d
echo "waiting 10 seconds for databases to start..."
sleep 10
# Always stop container, but exit with 1 when tests are failing
if npx jest tests; then
docker stop pg-adapter
else
docker stop pg-adapter && exit 1
fi

View File

@@ -0,0 +1,20 @@
{
"extends": "@auth/tsconfig/tsconfig.base.json",
"compilerOptions": {
"allowJs": true,
"baseUrl": ".",
"isolatedModules": true,
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "node",
"outDir": ".",
"rootDir": "src",
"skipDefaultLibCheck": true,
"strictNullChecks": true,
"stripInternal": true,
"declarationMap": true,
"declaration": true
},
"include": ["src/**/*"],
"exclude": ["*.js", "*.d.ts"]
}

View File

@@ -58,7 +58,7 @@
"@auth/tsconfig": "workspace:*",
"@prisma/client": "^5.2.0",
"jest": "^27.4.3",
"mongodb": "^4.4.0",
"mongodb": "^4.17.0",
"prisma": "^5.2.0"
},
"jest": {

View File

@@ -1,6 +1,6 @@
{
"name": "@auth/sequelize-adapter",
"version": "1.0.1",
"version": "1.0.2",
"description": "Sequelize adapter for Auth.js",
"homepage": "https://authjs.dev",
"repository": "https://github.com/nextauthjs/next-auth",
@@ -51,4 +51,4 @@
"jest": {
"preset": "@auth/adapter-test/jest"
}
}
}

View File

@@ -301,7 +301,9 @@ export default function SequelizeAdapter(
async deleteSession(sessionToken) {
await sync()
const session = await Session.findOne({ where: { sessionToken } })
await Session.destroy({ where: { sessionToken } })
return session?.get({ plain: true })
},
async createVerificationToken(token) {
await sync()

View File

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

View File

@@ -202,10 +202,10 @@ async function getUserAndAccount(
return {
user,
account: {
...tokens,
provider: provider.id,
type: provider.type,
providerAccountId: user.id.toString(),
...tokens,
},
}
} catch (e) {

View File

@@ -0,0 +1,103 @@
/**
* <div style={{backgroundColor: "#24292f", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
* <span>Built-in <b>ClickUp</b> integration.</span>
* <a href="https://clickup.com">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/click-up.svg" height="48" width="48"/>
* </a>
* </div>
*
* @module providers/ClickUp
*/
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
/** @see [Get the authenticated user](https://clickup.com/api/clickupreference/operation/GetAuthorizedUser/)*/
export interface ClickUpProfile {
user: {
id: number
username: string
color: string
profilePicture: string
}
}
/**
* Add ClickUp login to your page and make requests to [ClickUp APIs](https://clickup.com/api/).
*
* ### Setup
*
* #### Callback URL
* ```
* https://example.com/api/auth/callback/clickup
* ```
*
* #### Configuration
* ```ts
* import { Auth } from "@auth/core"
* import ClickUp from "@auth/core/providers/click-up"
*
* const request = new Request(origin)
* const response = await Auth(request, {
* providers: [ClickUp({ clientId: CLICKUP_CLIENT_ID, clientSecret: CLICKUP_CLIENT_SECRET })],
* })
* ```
*
* ### Resources
*
* - [ClickUp - Authorizing OAuth Apps](https://clickup.com/api/developer-portal/authentication#oauth-flow)
* - [Source code](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/click-up.ts)
*
* ### Notes
*
* By default, Auth.js assumes that the ClickUp provider is
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
*
* :::tip
*
* The ClickUp provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/click-up.ts).
* To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
*
* :::
*
* :::info **Disclaimer**
*
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
*
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
*
* :::
*/
export default function ClickUp(
config: OAuthUserConfig<ClickUpProfile>
): OAuthConfig<ClickUpProfile> {
return {
id: "clickup",
name: "ClickUp",
type: "oauth",
authorization: "https://app.clickup.com/api",
token: "https://api.clickup.com/api/v2/oauth/token",
userinfo: "https://api.clickup.com/api/v2/user",
clientId: config.clientId,
clientSecret: config.clientSecret,
checks: ["state"],
profile: (profile: ClickUpProfile) => {
return {
id: profile.user.id.toString(),
name: profile.user.username,
profilePicture: profile.user.profilePicture,
color: profile.user.color,
}
},
style: {
logo: "/click-up.svg",
logoDark: "/click-up.svg",
bg: "#fff",
bgDark: "#24292f",
text: "#000",
textDark: "#fff",
},
options: config,
}
}

View File

@@ -77,6 +77,19 @@ export type CredentialsProviderType = "Credentials"
* with supporting usernames and passwords.
*
* :::
*
* See the [callbacks documentation](/reference/configuration/auth-config#callbacks) for more information on how to interact with the token. For example, you can add additional information to the token by returning an object from the `jwt()` callback:
*
* ```js
* callbacks: {
* async jwt(token, user, account, profile, isNewUser) {
* if (user) {
* token.id = user.id
* }
* return token
* }
* }
* ```
*
* @example
* ```js

View File

@@ -0,0 +1,123 @@
/**
* <div style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
* <span>Built-in <b>Dribbble</b> integration.</span>
* <a href="https://dribbble.com">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/dribbble.svg" height="48" width="48"/>
* </a>
* </div>
*
* @module providers/dribbble
*/
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
export interface DribbbleProfile extends Record<string, any> {
id: number
name: string
email: string
avatar_url: string
}
/**
*
* Add Dribbble login to your page.
*
* ### Setup
*
* #### Callback URL
* ```
* https://example.com/api/auth/callback/dribbble
* ```
*
* #### Configuration
*```js
* import Auth from "@auth/core"
* import Dribbble from "@auth/core/providers/dribbble"
*
* const request = new Request(origin)
* const response = await Auth(request, {
* providers: [Dribbble({ clientId: DRIBBBLE_CLIENT_ID, clientSecret: DRIBBBLE_CLIENT_SECRET })],
* })
* ```
*
* ### Resources
*
* - [Dribbble API](https://developer.dribbble.com)
* - [Dribbble OAuth](https://developer.dribbble.com/v2/oauth/)
* - [Dribbble Applications](https://dribbble.com/account/applications/new)
*
* ### Notes
*
* By default, Auth.js assumes that the GitHub provider is
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
*
* :::tip
*
* The Dribbble provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/dribbble.ts).
* To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
*
* :::
*
* :::info **Disclaimer**
*
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
*
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
*
* :::
*
* :::tip
* You can optionally set the scope to `public upload` for more advanced scenarios. If omitted, the default `public` scope will be used for authentication purposes.
* :::
*/
export default function Dribbble<P extends DribbbleProfile>(
options: OAuthUserConfig<P> & {
/**
* Reference: https://developer.dribbble.com/v2/oauth/#scopes
*
* For the purposes of NextAuth.js `upload`-only scope makes no sense,
* therefore it is excluded from suggested values. Treated by Dribbble as `public` when omitted.
*
* @default public
*/
scope?: "public" | "public upload"
}
): OAuthConfig<P> {
return {
id: "dribbble",
name: "Dribbble",
type: "oauth",
authorization: {
url: "https://dribbble.com/oauth/authorize",
params: { scope: options.scope },
},
token: "https://dribbble.com/oauth/token",
userinfo: "https://api.dribbble.com/v2/user",
profile(profile) {
return {
id: profile.id.toString(),
name: profile.name,
email: profile.email,
image: profile.avatar_url,
}
},
style: {
// Light mode
logo: "/dribbble.svg",
text: "#ea4c89",
bg: "#fff",
// Dark mode
logoDark: "/dribbble-dark.svg",
textDark: "#fff",
bgDark: "#000",
},
options,
}
}

View File

@@ -0,0 +1,106 @@
/**
* <div style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
* <span>Built-in <b>Mastodon</b> integration.</span>
* <a href="https://mastodon.social">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/mastodon.svg" height="48" width="48"/>
* </a>
* </div>
*
* @module providers/mastodon
*/
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
export interface MastodonProfile extends Record<string, any> {
id: string
username: string
acct: string
display_name: string
locked: boolean
bot: boolean
created_at: string
note: string
url: string
avatar: string
avatar_static: string
header: string
header_static: string
followers_count: number
following_count: number
statuses_count: number
last_status_at: string | null
}
/**
* Add Mastodon login to your page.
*
* ### Setup
*
* #### Callback URL
* ```
* https://example.com/api/auth/callback/mastodon
* ```
*
* #### Configuration
*```js
* import Auth from "@auth/core"
* import Mastodon from "@auth/core/providers/mastodon"
*
* const request = new Request(origin)
* const response = await Auth(request, {
* providers: [Mastodon({ clientId: MASTODON_CLIENT_ID, clientSecret: MASTODON_CLIENT_SECRET, issuer: MASTODON_ISSUER })],
* })
* ```
*
* ### Resources
*
* - [Mastodon OAuth documentation](https://docs.joinmastodon.org/client/token/)
* - [Mastodon OAuth Configuration](https://mastodon.social/settings/applications)
*
* ### Notes
*
* By default, Auth.js assumes that the Mastodon provider is
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
*
* Due to Mastodons infrastructure beeing a Fediverse you have to define the `issuer` you want to connect to.
*
* :::tip
*
* The Mastodon provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/mastodon.ts).
* To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
*
* :::
*
* :::info **Disclaimer**
*
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
*
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
*
* :::
*/
export default function Mastodon<P extends MastodonProfile>(
options: OAuthUserConfig<P> & {
issuer: string
}
): OAuthConfig<P> {
return {
id: "mastodon",
name: "Mastodon",
type: "oauth",
authorization: `${options.issuer}/oauth/authorize?scope=read`,
token: `${options.issuer}/oauth/token`,
userinfo: `${options.issuer}/api/v1/accounts/verify_credentials`,
profile(profile) {
return {
id: profile.id,
name: profile.username,
image: profile.avatar_static,
email: null,
}
},
options,
}
}

View File

@@ -0,0 +1,269 @@
/**
* <div style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
* <span>Built-in <b>Tiktok</b> integration.</span>
* <a href="https://www.tiktok.com/">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/tiktok.svg" height="48" />
* </a>
* </div>
*
* @module providers/tiktok
*/
import { TokenSet } from "src/types.js"
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
/**
* [More info](https://developers.tiktok.com/doc/tiktok-api-v2-get-user-info/)
*/
export interface TiktokProfile extends Record<string, any> {
data: {
user: {
/**
* The unique identification of the user in the current application.Open id
* for the client.
*
* To return this field, add `fields=open_id` in the user profile request's query parameter.
*/
open_id: string
/**
* The unique identification of the user across different apps for the same developer.
* For example, if a partner has X number of clients,
* it will get X number of open_id for the same TikTok user,
* but one persistent union_id for the particular user.
*
* To return this field, add `fields=union_id` in the user profile request's query parameter.
*/
union_id?: string
/**
* User's profile image.
*
* To return this field, add `fields=avatar_url` in the user profile request's query parameter.
*/
avatar_url: string
/**
* User`s profile image in 100x100 size.
*
* To return this field, add `fields=avatar_url_100` in the user profile request's query parameter.
*/
avatar_url_100?: string
/**
* User's profile image with higher resolution
*
* To return this field, add `fields=avatar_url_100` in the user profile request's query parameter.
*/
avatar_large_url?: string
/**
* User's profile name
*
* To return this field, add `fields=display_name` in the user profile request's query parameter.
*/
display_name: string
/**
* User's username.
*
* To return this field, add `fields=username` in the user profile request's query parameter.
*/
username: string
/** @note Email is currently unsupported by Tiktok */
email?: string
/**
* User's bio description if there is a valid one.
*
* To return this field, add `fields=bio_description` in the user profile request's query parameter.
*/
bio_description?: string
/**
* The link to user's TikTok profile page.
*
* To return this field, add `fields=profile_deep_link` in the user profile request's query parameter.
*/
profile_deep_link?: string
/**
* Whether TikTok has provided a verified badge to the account after confirming
* that it belongs to the user it represents.
*
* To return this field, add `fields=is_verified` in the user profile request's query parameter.
*/
is_verified?: boolean
/**
* User's followers count.
*
* To return this field, add `fields=follower_count` in the user profile request's query parameter.
*/
follower_count?: number
/**
* The number of accounts that the user is following.
*
* To return this field, add `fields=following_count` in the user profile request's query parameter.
*/
following_count?: number
/**
* The total number of likes received by the user across all of their videos.
*
* To return this field, add `fields=likes_count` in the user profile request's query parameter.
*/
likes_count?: number
/**
* The total number of publicly posted videos by the user.
*
* To return this field, add `fields=video_count` in the user profile request's query parameter.
*/
video_count?: number
}
}
error: {
/**
* The error category in string.
*/
code: string
/**
* The error message in string.
*/
message: string
/**
* The error message in string.
*/
log_id: string
}
}
/**
* Add Tiktok login to your page.
*
* ### Setup
*
* #### Callback URL
* ```
* https://example.com/api/auth/callback/tiktok
* ```
*
* #### Configuration
*```js
* import Auth from "@auth/core"
* import Tiktok from "@auth/core/providers/tiktok"
*
* const request = new Request(origin)
* const response = await Auth(request, {
* providers: [Tiktok({ clientId: TIKTOK_CLIENT_KEY, clientSecret: TIKTOK_CLIENT_SECRET })],
* })
* ```
*
* ### Resources
* - [Tiktok app console](https://developers.tiktok.com/)
* - [Tiktok login kit documentation](https://developers.tiktok.com/doc/login-kit-web/)
* - [Avaliable Scopes](https://developers.tiktok.com/doc/tiktok-api-scopes/)
*
*
* ### Notes
*
* :::tip
*
* Production applications cannot use localhost URLs to sign in with Tiktok. You need add the domain and Callback/Redirect url's to your Tiktok app and have them review and approved by the Tiktok Team.
*
* :::
*
* :::tip
*
* Email address is not supported by Tiktok.
*
* :::
*
* :::tip
*
* Client_ID will be the Client Key in the Tiktok Application
*
* :::
*
* By default, Auth.js assumes that the Tiktok provider is
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
*
* :::tip
*
* The Tiktok provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/tiktok.ts).
* To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
*
* :::
*
* :::info **Disclaimer**
*
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
*
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
*
* :::
*/
export default function Tiktok<P extends TiktokProfile>(
options: OAuthUserConfig<P>
): OAuthConfig<P> {
return {
id: "tiktok",
name: "TikTok",
type: "oauth",
authorization: {
url: "https://www.tiktok.com/v2/auth/authorize",
params: {
client_key: options.clientId,
scope: "user.info.profile",
response_type: "code",
},
},
token: {
async request({ params, provider }) {
const res = await fetch(`https://open.tiktokapis.com/v2/oauth/token/`, {
method: "POST",
headers: {
"Cache-Control": "no-cache",
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
client_key: provider.clientId!,
client_secret: provider.clientSecret!,
code: params.code!,
grant_type: "authorization_code",
redirect_uri: provider.callbackUrl!,
}),
}).then((res) => res.json())
const tokens: TokenSet = {
access_token: res.access_token,
expires_at: res.expires_in,
refresh_token: res.refresh_token,
scope: res.scope,
id_token: res.open_id,
token_type: res.token_type,
session_state: res.open_id,
}
return {
tokens,
}
},
},
userinfo: {
url: "https://open.tiktokapis.com/v2/user/info/?fields=open_id,avatar_url,display_name,username",
async request({ tokens, provider }) {
return await fetch(provider.userinfo?.url as URL, {
headers: { Authorization: `Bearer ${tokens.access_token}` },
}).then(async (res) => await res.json())
},
},
profile(profile) {
return {
id: profile.data.user.open_id,
name: profile.data.user.display_name,
image: profile.data.user.avatar_url,
email: profile.data.user.email || null,
}
},
style: {
logo: "/tiktok.svg",
logoDark: "/tiktok-dark.svg",
bg: "#fff",
bgDark: "#000",
text: "#000",
textDark: "#fff",
},
options,
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@auth/sveltekit",
"version": "0.3.6",
"version": "0.3.7",
"description": "Authentication for SvelteKit.",
"keywords": [
"authentication",
@@ -69,4 +69,4 @@
},
"./package.json": "./package.json"
}
}
}

View File

@@ -8,6 +8,7 @@ import type {
BuiltInProviderType,
RedirectableProviderType,
} from "@auth/core/providers"
import { base } from "$app/paths";
/**
* Client-side method to initiate a signin flow
@@ -34,16 +35,15 @@ export async function signIn<
const isEmail = providerId === "email"
const isSupportingReturn = isCredentials || isEmail
// TODO: Handle custom base path
const signInUrl = `/auth/${
const basePath = base ?? ""
const signInUrl = `${basePath}/auth/${
isCredentials ? "callback" : "signin"
}/${providerId}`
const _signInUrl = `${signInUrl}?${new URLSearchParams(authorizationParams)}`
// TODO: Handle custom base path
// TODO: Remove this since Sveltekit offers the CSRF protection via origin check
const csrfTokenResponse = await fetch("/auth/csrf")
const csrfTokenResponse = await fetch(`${basePath}/auth/csrf`)
const { csrfToken } = await csrfTokenResponse.json()
const res = await fetch(_signInUrl, {
@@ -81,11 +81,11 @@ export async function signIn<
*/
export async function signOut(options?: SignOutParams) {
const { callbackUrl = window.location.href } = options ?? {}
// TODO: Custom base path
const basePath = base ?? ""
// TODO: Remove this since Sveltekit offers the CSRF protection via origin check
const csrfTokenResponse = await fetch("/auth/csrf")
const csrfTokenResponse = await fetch(`${basePath}/auth/csrf`)
const { csrfToken } = await csrfTokenResponse.json()
const res = await fetch(`/auth/signout`, {
const res = await fetch(`${basePath}/auth/signout`, {
method: "post",
headers: {
"Content-Type": "application/x-www-form-urlencoded",

View File

@@ -204,6 +204,7 @@
import type { Handle, RequestEvent } from "@sveltejs/kit"
import { dev } from "$app/environment"
import { base } from "$app/paths"
import { env } from "$env/dynamic/private"
import { Auth } from "@auth/core"
@@ -216,7 +217,7 @@ export async function getSession(
config.secret ??= env.AUTH_SECRET
config.trustHost ??= true
const prefix = config.prefix ?? "/auth"
const prefix = config.prefix ?? `${base}/auth`
const url = new URL(prefix + "/session", req.url)
const request = new Request(url, { headers: req.headers })
const response = await Auth(request, config)
@@ -236,7 +237,7 @@ export interface SvelteKitAuthConfig extends AuthConfig {
* If you change the default value,
* you must also update the callback URL used by the [providers](https://authjs.dev/reference/core/providers).
*
* @default "/auth"
* @default `${base}/auth` - `base` is the base path of your SvelteKit app, configured in `svelte.config.js`.
*/
prefix?: string
}
@@ -260,7 +261,7 @@ function AuthHandle(svelteKitAuthOptions: SvelteKitAuthConfig | DynamicSvelteKit
typeof svelteKitAuthOptions === "object"
? svelteKitAuthOptions
: await svelteKitAuthOptions(event)
const { prefix = "/auth" } = authOptions
const { prefix = `${base}/auth` } = authOptions
const { url, request } = event
event.locals.getSession ??= () => getSession(request, authOptions)
@@ -285,6 +286,7 @@ export function SvelteKitAuth(options: SvelteKitAuthConfig | DynamicSvelteKitAut
if (typeof options === "object") {
options.secret ??= env.AUTH_SECRET
options.trustHost ??= !!(env.AUTH_TRUST_HOST ?? env.VERCEL ?? dev)
options.prefix ??= `${base}/auth`
}
return AuthHandle(options)
}

View File

@@ -9,7 +9,7 @@
"Balázs Orbán <info@balazsorban.com>",
"Nico Domino <yo@ndo.dev>",
"Lluis Agusti <hi@llu.lu>",
"Thang Huu Vu <thvu@hey.com>"
"Thang Huu Vu <hi@thvu.dev>"
],
"main": "index.js",
"module": "index.js",

3487
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,22 +2,12 @@
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"dependsOn": [
"^build"
],
"outputs": [
"dist/**/*",
"lib/**/*",
"*.js",
"*.d.ts",
"*.d.ts.map"
],
"dependsOn": ["^build"],
"outputs": ["dist/**/*", "lib/**/*", "*.js", "*.d.ts", "*.d.ts.map"],
"outputMode": "new-only"
},
"next-auth#build": {
"dependsOn": [
"^build"
],
"dependsOn": ["^build"],
"outputs": [
"client/**",
"core/**",
@@ -32,9 +22,7 @@
"outputMode": "new-only"
},
"@auth/core#build": {
"dependsOn": [
"^build"
],
"dependsOn": ["^build"],
"outputs": [
"lib/**",
"providers/**",
@@ -47,14 +35,8 @@
"outputMode": "new-only"
},
"@auth/sveltekit#build": {
"dependsOn": [
"^build"
],
"outputs": [
".svelte-kit/**",
"client.*",
"index.*"
],
"dependsOn": ["^build"],
"outputs": [".svelte-kit/**", "client.*", "index.*"],
"outputMode": "new-only"
},
"clean": {
@@ -67,15 +49,10 @@
"outputMode": "new-only"
},
"e2e": {
"outputs": [
"playwright-report/**"
]
"outputs": ["playwright-report/**"]
},
"@auth/upstash-redis-adapter#test": {
"env": [
"UPSTASH_REDIS_KEY",
"UPSTASH_REDIS_URL"
]
"env": ["UPSTASH_REDIS_KEY", "UPSTASH_REDIS_URL"]
},
"docs#dev": {
"dependsOn": [
@@ -83,15 +60,18 @@
"@auth/prisma-adapter#build",
"@auth/solid-start#build",
"@auth/sveltekit#build",
"@auth/d1-adapter#build",
"@auth/dgraph-adapter#build",
"@auth/drizzle-adapter#build",
"@auth/dynamodb-adapter#build",
"@auth/edgedb-adapter#build",
"@auth/fauna-adapter#build",
"@auth/firebase-adapter#build",
"@auth/kysely-adapter#build",
"@auth/mikro-orm-adapter#build",
"@auth/mongodb-adapter#build",
"@auth/neo4j-adapter#build",
"@auth/pg-adapter#build",
"@auth/pouchdb-adapter#build",
"@auth/sequelize-adapter#build",
"@auth/supabase-adapter#build",
@@ -134,4 +114,4 @@
]
}
}
}
}