Compare commits

...

505 Commits

Author SHA1 Message Date
Balázs Orbán
59d4205be5 fix(ts): use relative imports 2021-04-19 17:14:40 +02:00
Balázs Orbán
7335759b04 fix(ts): extract EmailConfigServerOptions to interface 2021-04-19 17:02:53 +02:00
Balázs Orbán
97c77ff44b refactor(ts): de-duplicate types (#1690)
* refactor(ts): deduplicate internal types

* refactor(ts): ease up providers typings

* test(ts): fix failing TS tests

* test(ts): rename TS property to fix test

* docs(ts): mention TS docs in README.md

* feat(ts): move/update client types

* refactor(TS): rename some types

* test(ts): fix client tests

* docs(ts): move function descriptions to .d.ts

* chore: fix lint error

* refactor(ts): separate internal types

* chore: simplify build-types script

* chore: update type import paths in src

* chore(build): create root files at build

* chore: remove unnecessary .npmignore

* chore: run prettier on types

* fix(ts): clean up jwt types

* fix(ts): make getToken return type depend on raw param

* docs(page): explain page errors, add theming note

* docs(ts): add JSDoc to NextAuthOptions props

* chore(ts): remove unused import

* docs(ts): change JSDOC docs notation

* refactor(build): extract module entries into enum

* chore(ts): move ClientSafeProvider

* chore(ts): simplify GetTokenParams generic

* style(lint): fix linting errors

* chore: re-add generic extension to GetTokenParams
2021-04-19 16:54:03 +02:00
Nico Domino
b6963abda7 docs(lint): update contributing.md (#1760)
Regarding ESLint / Prettier use and link to their VSCode extensions
2021-04-18 13:47:02 +02:00
Nico Domino
bdb12adb28 style: replace ts-standard with eslint/prettier (#1724)
* style: move from ts-standard to eslint/prettier

* fix: install remaining eslint-config-standard peer deps

* fix: add remaining missing dependencies/config

Co-authored-by: Balázs Orbán <info@balazsorban.com>
2021-04-14 20:22:51 +02:00
Balázs Orbán
a1e30507c2 feat(ts): support module augmentation (#1681)
* chore(ts): remove unused imports

* refactor(ts): clean up CallbackOptions

* docs(ts): explain Module Augmentation

* docs(ts): don't use @ in folder name "types"

* test(ts): make jwt params optional

* docs(ts): fix typo (TypeScript -> NextAuth.js)
2021-04-10 23:48:26 +02:00
Balázs Orbán
2c4fce3699 fix(build): fix release 2021-04-09 21:26:00 +02:00
Balázs Orbán
8fa71512d1 fix(built): typo in package.json 2021-04-09 21:20:41 +02:00
Balázs Orbán
d420eeff9d fix(ts): add .d.ts sub-module files to package.json
#1677 seemed to miss this
2021-04-09 21:10:43 +02:00
Lluis Agusti
0d863d38bc fix(ts): correctly export sub-module types (#1677)
* chore(types): build types script

Adds a script that moves the declaration files we have in `./types` to `./dist` relative to the files they intend to type.

This is the first step, we still need to change what we declare in `package.json`, add the script to the CI pipeline if we're happy with it and figure out how to type `next-auth/jwt`.

* refactor(lint): fix build-types script
2021-04-09 20:28:11 +02:00
Lluis Agusti
6f9f42a85b chore(ci): fix typo on types workflow 2021-04-07 17:05:48 +02:00
Lluis Agusti
2160be2a8a feat(ts): expose types from the package (#1665)
* chore(types): move existing types to the repo
* feat(ts): expose types from the main package
* chore(deps): bring back `react-dom` version range
* chore(ts): cleanup deps and comments
* chore(ci): run types tests on a separate workflow
2021-04-07 17:03:17 +02:00
Balázs Orbán
55eb066793 chore: add beta to release flow/GH actions 2021-04-04 22:08:25 +02:00
Jasper Moelker
5bc8f8b986 docs(page): correct getCsrfToken and input types (#1651)
This fixes the a mismatch between the import (`csrfToken`) and the method (`getCsrfToken`) used in `getInitialProps`/`getServerSideProps`.
In addition the form input fields now have their correct type: `email` for email input (for better autocomplete, virtual keyboard support and native validation) and `password` for the password input (to hide password while typing).
2021-04-04 22:01:53 +02:00
hoangbits
136361e1f4 docs: rename command to vercel cli, now cli is deprecated (#1647) 2021-04-04 11:02:38 +02:00
hoangbits
cc9869592c docs: fix typo in providers.md (#1641) 2021-04-02 17:18:40 +02:00
Jay Liew
073da60c3d docs: Update pages.md (#1592)
* Update pages.md

Updated Credentials Sign-In code example to indicate how to use `getServerSideProps` but still also showing the older `getInitialProps` example

* Update www/docs/configuration/pages.md

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

* update documentation to show example using getServerSideProps()

Co-authored-by: Balázs Orbán <info@balazsorban.com>
Co-authored-by: Jay Liew <jay@haute.tech>
2021-03-31 00:31:31 +02:00
ifly7charlie
aacc34bbfd docs(error): Add missing error message and technique to resolve (#1549)
* Add missing error message and technique to resolve

* Update errors.md

Correct with correct error message and more complete suggestions on resolving it
2021-03-26 23:09:21 +01:00
jgollhardt
074688d10e docs(provider): fix wrong param name in sendVerificationRequest example (#1595) 2021-03-26 23:01:25 +01:00
Macarse, Christian Ryan R
b3ffe50c03 docs(provider): removed misleading provider signin link (#1588) 2021-03-25 22:30:46 +01:00
Shubham Shukla
e6d063825d fix(provider): added options in instagram provider (#1570) 2021-03-23 22:28:54 +01:00
Balázs Orbán
985f7b3431 fix(logger): properly end request every time (#1557)
* fix(logger): properly end request every time

* chore: fix linting
2021-03-20 10:08:12 +01:00
Max
237b016378 fix(provider): reject access token if slack login flow was canceled (#1544)
* fix: reject access token if slack login flow was canceled

* style: fix lint errors in oauth client
2021-03-18 14:59:24 +01:00
Joshua Williams
776b9480da feat(provider): add Zoho provider (#1516)
* feat(provider): add zoho

* fix: use LF instead of CRLF

* fix: crlf to lf line endings

Co-authored-by: Balázs Orbán <info@balazsorban.com>
2021-03-16 19:27:11 +01:00
Honman Yau
07a3f76cb3 docs: fix typos in REST API guide (#1528) 2021-03-16 19:25:24 +01:00
tclaude94
3726d68c49 feat(provider): add FACEIT provider (#1469) 2021-03-16 00:00:35 +01:00
dependabot[bot]
e31db1726a chore(deps): bump xmldom from 0.3.0 to 0.5.0 (#1510)
Bumps [xmldom](https://github.com/xmldom/xmldom) from 0.3.0 to 0.5.0.
- [Release notes](https://github.com/xmldom/xmldom/releases)
- [Changelog](https://github.com/xmldom/xmldom/blob/master/CHANGELOG.md)
- [Commits](https://github.com/xmldom/xmldom/compare/0.3.0...0.5.0)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-13 14:19:22 +01:00
James Perkins
a241199c11 docs(tutorials): Adding two more tutorials to the list.
[skip release]
2021-03-13 03:42:00 +00:00
dependabot[bot]
5385ec20a9 chore(deps): bump elliptic from 6.5.3 to 6.5.4 in /www (#1493)
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.3 to 6.5.4.
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.3...v6.5.4)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-10 22:59:10 +01:00
Balázs Orbán
810d02e671 fix(deps): upgrade to latest preact-render-to-string (#1475) 2021-03-08 10:39:13 +01:00
Valentin Hervieu
e5535734f8 fix(client): set useSession loading state correctly (#1468)
This is fixing #1467.

The issue was due to doing the `setLoading(false)` in the finally:  as we can do an early return [here](a7e08e2a32/src/client/index.js (L100-L100)), we would still go to the finally and mark the session as being loaded.

I simply removed the `finally` block to only set the `loading` state to false when:
- the data is ready
- an error occures
2021-03-07 19:11:32 +01:00
Taehwan Noh
ba7aed1057 feat(provider): add Kakao provider (#1459) 2021-03-06 21:52:39 +01:00
Balázs Orbán
a7e08e2a32 fix: make sure useSession populates session correctly (#1462) 2021-03-06 20:02:43 +01:00
mcha
0d13040264 docs(client): fix client.md typos (#1453) 2021-03-06 10:50:29 +01:00
Balázs Orbán
582520f8ef chore: fix typo in feature request template 2021-03-06 00:35:09 +01:00
Sam Bauch
95942519a5 feat(provider): add Osso SAML provider (#1448)
Co-authored-by: @sbauch
2021-03-06 00:21:38 +01:00
Balázs Orbán
f3e64f04cc feat(client): introduce NEXTAUTH_URL_INTERNAL (#1449)
Co-authored-by: @gergelyke
2021-03-06 00:10:36 +01:00
Balázs Orbán
ed5cc4aa65 feat(provider): add Instagram provider (#1447)
Co-authored-by: @PolMrt pol@hey.com
2021-03-05 23:39:50 +01:00
Balázs Orbán
0e20b60229 docs(database): mention CockroachDB
Co-authored-by: @jukbot <jukbot@yellotalk.co>
2021-03-05 22:52:48 +01:00
Balázs Orbán
3aee24b5dc refactor: client improvements (#1428)
* docs(client): add TS definitions to client

* docs(client): add documentation links to public methods

* refactor(client): simplify window sync, simplify logic

* refactor(client): extract repeating logic to _fetchData

* refactor(client): remove clientId

* refactor(client): use session in Provider if passed
2021-03-05 22:47:32 +01:00
Baterka
960ca85907 fix: send only the error message in callback redirects (#1424)
Changed `encodeURIComponent(error)` to `encodeURIComponent(error.message)` to remove prefix (such as `Error: ` and possible stack trace).
Seems like better way of doing it and also safer if server throws some error with sensitive data.
2021-03-05 18:34:11 +01:00
Balázs Orbán
f960cc0f6f chore(docs): upgrade docs dependencies 2021-03-03 20:13:46 +01:00
Balázs Orbán
0f64f3eea7 chore: don't mark bugs as stale
Had a good laugh today 😄:

"This is not stale. Bread goes stale. Bugs don't. They don't just magically go away because time has passed" - Unknown
https://twitter.com/ericclemmons/status/1367000259046604803
2021-03-03 19:54:13 +01:00
yannicktian
71c78e8e24 feat(provider): allow disabling redirection on sign in with email (#1416)
* feat: allow to disable client-side redirect for email provider

* docs(client): mention that redirect can also be disabled for email provider

* feat: only display one email input in email page
2021-03-02 22:38:02 +01:00
dependabot[bot]
d86609a2dc chore(deps): bump prismjs from 1.22.0 to 1.23.0 in /www (#1409)
Bumps [prismjs](https://github.com/PrismJS/prism) from 1.22.0 to 1.23.0.
- [Release notes](https://github.com/PrismJS/prism/releases)
- [Changelog](https://github.com/PrismJS/prism/blob/master/CHANGELOG.md)
- [Commits](https://github.com/PrismJS/prism/compare/v1.22.0...v1.23.0)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-01 22:52:46 +01:00
Joost van Wollingen
d0c3400d30 docs(page): Remove unnecessary context param (#1406)
...when calling providers in the docs
2021-03-01 21:39:03 +01:00
Praneeth
172e79cb04 fix(page): add character encoding and page titles (#1380)
* added character encoding fix

* changed multi-line to inline and added title param to send fn in src/server/pages/index.js

* modified the return object of renderPage in src/server/pages/index.js
2021-03-01 21:17:51 +01:00
Balázs Orbán
46d5c76605 docs: reword callbacks.md
Explain the `jwt()` callback before the `session()` callback, as it comes first in the flow.
2021-02-28 18:05:10 +01:00
Zach White
438efd8a9b docs: reword pages.md (#1386)
language edits
2021-02-27 23:43:45 +01:00
Balázs Orbán
d8d497cc91 feat(provider): call generateVerificationToken async (#1378) 2021-02-27 23:33:26 +01:00
Pop Stefan
6152c8afbb docs: added refresh token tutorial link in faq page (#1385) 2021-02-27 20:24:09 +01:00
Balázs Orbán
5ae6f6118c docs: add missing comma
Thx @followbl 😺
2021-02-25 23:29:33 +01:00
sid
96ff048b59 fix(provider): use correct file type for Discord profile img (#1365) 2021-02-23 21:39:27 +01:00
Ariel Weingarten
e80f6e936d docs(provider): Update twitch.md (#1353)
State what redirect URL to add to the Twitch console.
2021-02-22 20:04:38 +01:00
Lawrence Chen
6b5a215fb2 docs(tutorials): refresh token rotation (#1310)
* docs(tutorials): refresh token rotation

* use simple initialization

* be optimistic

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

* add yarn.lock to .gitignore

Co-authored-by: Balázs Orbán <info@balazsorban.com>
2021-02-21 22:30:01 +01:00
Balázs Orbán
782482b9f4 feat: make tokens available in profile callback (#1329)
* feat: make access_token available in profile callback

* docs(provider): mention access_token param in profile callback

* feat: send all available tokens to provider.profile
2021-02-20 22:58:48 +01:00
Balázs Orbán
2d364f246a docs: tweak release badges 2021-02-17 19:14:45 +01:00
Balázs Orbán
564b342f69 fix(docs): generate providers on docosaurus start 2021-02-16 15:42:27 +01:00
Balázs Orbán
63638d81dc docs: add sponsoring information 2021-02-16 15:42:27 +01:00
Balázs Orbán
28683015f1 docs: add links to README badges 2021-02-16 10:34:54 +01:00
Balázs Orbán
726c49603d chore: make next a prerelease channel 2021-02-16 10:20:46 +01:00
Balázs Orbán
a7113c6d3e chore: trigger release on main branch 2021-02-16 09:50:19 +01:00
Balázs Orbán
910514c6e2 chore: trigger release action on next branch 2021-02-15 21:51:17 +01:00
Balázs Orbán
b7cca484cf docs(provider): mention re-exporting config 2021-02-15 13:28:20 +01:00
Balázs Orbán
e293e786a8 fix(page): fallback to default error when no query param (#1303) 2021-02-11 22:25:09 +01:00
Balázs Orbán
82dd6ba3e4 feat(logger): introduce user configurable logger (#1294) 2021-02-11 14:50:53 +01:00
Balázs Orbán
6e28a07746 fix(client): reload after login/logout when url contains hash (#1298)
Co-authored-by: Thew Dhanat
2021-02-11 12:18:54 +01:00
Balázs Orbán
61047e3c14 chore: add CodeQL code analysis action 2021-02-11 09:58:22 +01:00
Balázs Orbán
dc5f3f481d chore(deps): upgrade test dependencies 2021-02-10 21:04:36 +01:00
Balázs Orbán
0343344802 chore: add new PR labels 2021-02-10 20:56:22 +01:00
Balázs Orbán
134a95a4bd docs(provider): auto-generate providers list (#1295)
* docs: ignore providers.json

* chore: generate providers.json from front-matter

* chore: remove autogenerated file

* docs(provider): rename vk.com to VK

* docs: encourage adding new providers
2021-02-10 20:43:04 +01:00
Joshua Payette
52a4bd97cd docs(provider): Update azure-ad-b2c (#1288)
It seems that the AZURE_TENANT_NAME env var is not required for next-auth.  This update removes it.

Co-authored-by: Balázs Orbán <info@balazsorban.com>
2021-02-10 00:57:43 +01:00
Benjamin Bender
87d43e4038 docs: Update client.md (#1289)
Fix typo on getProviders()
2021-02-10 00:37:27 +01:00
Balázs Orbán
68695af1f3 chore: update release flow 2021-02-09 22:08:52 +01:00
Balázs Orbán
76df2b5e70 chore: disable semantic releases temporarily 2021-02-09 20:52:01 +01:00
Balázs Orbán
8bd9d87633 feat: new release (#1259)
* feat: simplify NextAuth instantiation (#911)

* feat: allow react 17 as a peer dependency (#819)

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

* docs: update for Now to Vercel (#847)

Vercel archived their now packages a while back, so you can use vercel env pull to pull in the .env

* docs: fix discord example code (#850)

* docs: fix typo in callbacks.md (#815)

This is a simple typographical error changed accesed to accessed

* fix: update nodemailer version in response to CVE. (#860)

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-7769 reports a high-severity issue with the current version of nodemailer. This should be merged and released right away if possible.

* fix: ensure Images are produced for discord (#734)

* fix: update Okta routes (#763)

the current routing for the Okta provider does not follow the standard
set by Okta, and as such doesn't allow for custom subdomains. this
update amends the routes to allow for customer subdomains, and also
aligns next-auth with Okta's documentation.

* fix(provider): handle no profile image for Spotify (#914)

* chore(deps): upgrade "standard"

* style(lint): run lint fix

* fix(provider): optional chain Spotify provider profile img

* Merge main into canary (#917)

* chore: use stale label, instead of wontfix

* chore: add link to issue explaining stalebot

* chore: fix typo in stalebot comment

* chore: run build GitHub Action on canary also

* chore: run build GitHub Actions on canary as well

* chore: add reproduction section to questions

* docs: Update default ports for support Databases (#839)

https://next-auth.js.org/configuration/databases

* Fix for Reddit Authentication (#866)

* Fixed Reddit Authentication

* updated fix for build test

* updated buffer to avoid deprecation message

* Updated for passing tests

* WIP: Update Docusaurus + Site dependencies (#802)

* update: deps

* fix: broken link

* fix: search upgrade change

* Include callbackUrl in newUser page (#790)

* Include callbackUrl in newUser page

* Update src/server/routes/callback.js

Co-authored-by: Iain Collins <me@iaincollins.com>

* Update src/server/routes/callback.js

Co-authored-by: Iain Collins <me@iaincollins.com>

Co-authored-by: Iain Collins <me@iaincollins.com>
Co-authored-by: Nico Domino <yo@ndo.dev>

* add(db): Add support for Fauna DB (#708)

* Add support for Fauna DB

* Add integration tests

Co-authored-by: Nico Domino <yo@ndo.dev>

* feat(provider): add netlify (#555)

Co-authored-by: styxlab <cws@DE01WP777.scdom.net>
Co-authored-by: Balázs Orbán <info@balazsorban.com>

* Bump next from 9.5.3 to 9.5.4 in /test/docker/app (#759)

Bumps [next](https://github.com/vercel/next.js) from 9.5.3 to 9.5.4.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v9.5.3...v9.5.4)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Nico Domino <yo@ndo.dev>

* feat(provider): Add Bungie (#589)

* Add Bungie provider

* Use absolute URL for images

* Correct image URL and use consistent formatting

Co-authored-by: Nico Domino <yo@ndo.dev>

* feat: add foursquare (#584)

* feat(provider): Add Azure Active Directory B2C (#921)

* add provider: Microsoft

* documentation

* support no tenant setup

* fix code style

* chore: rename Microsoft provider to AzureADB2C

* chore: alphabetical order in providers/index

* doc: add provider to FAQ

* update(provider): Update Slack provider to use V2 OAuth endpoints (#895)

* Update Slack to v2 authorize urls, option for additional authorize params
* acessTokenGetter + documentation

* refactor(db): update Prisma calls to support 2.12+ (#881)

Co-authored-by: Balázs Orbán <info@balazsorban.com>
Co-authored-by: Nico Domino <yo@ndo.dev>

* chore(dep): Bump highlight.js from 9.18.1 to 9.18.5 (#880)

Bumps [highlight.js](https://github.com/highlightjs/highlight.js) from 9.18.1 to 9.18.5.
- [Release notes](https://github.com/highlightjs/highlight.js/releases)
- [Changelog](https://github.com/highlightjs/highlight.js/blob/9.18.5/CHANGES.md)
- [Commits](https://github.com/highlightjs/highlight.js/compare/9.18.1...9.18.5)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Balázs Orbán <info@balazsorban.com>
Co-authored-by: Nico Domino <yo@ndo.dev>

* chore: disallow issues without template

* chore: add note about conveting questions to discussions

* chore: create PULL_REQUEST_TEMPLATE.md

* chore: reword PR template

* feat: Store user ID in sub claim of default JWT (#784)

This allows us to check if the user is signed in when using JWTs

Part of #625

* docs: fix incorrect references in cypress docs (#932)

* chore: use stale label, instead of wontfix

* chore: add link to issue explaining stalebot

* chore: fix typo in stalebot comment

* chore: run build GitHub Action on canary also

* chore: run build GitHub Actions on canary as well

* chore: add reproduction section to questions

* feat(provider): Add Azure Active Directory B2C (#809)

* add provider: Microsoft

* documentation

* support no tenant setup

* fix code style

* chore: rename Microsoft provider to AzureADB2C

* chore: alphabetical order in providers/index

* Revert "feat(provider): Add Azure Active Directory B2C (#809)" (#919)

This reverts commit 6e6a24a7af.

* chore: add myself to the contributors list 🙈

* docs: fix incorrect references in cypress docs

* chore: add additional docs clarification

Co-authored-by: Balázs Orbán <info@balazsorban.com>
Co-authored-by: Vladimir Evdokimov <evdokimov.vladimir@gmail.com>

* feat: Display error if no [...nextauth].js found (#678)

* Display error if no [...nextauth].js found

fixes #647

* Log the error and describe it inside errors.md

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

* chore(deps): Bump ini from 1.3.5 to 1.3.8 in /www (#953)

Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.8.
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.8)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* docs: fix typo Adapater -> Adapter (#960)

Co-authored-by: Balázs Orbán <info@balazsorban.com>
Co-authored-by: Vladimir Evdokimov <evdokimov.vladimir@gmail.com>

* docs: We have twice the word "side" (#964)

* chore: use stale label, instead of wontfix

* chore: add link to issue explaining stalebot

* chore: fix typo in stalebot comment

* chore: run build GitHub Action on canary also

* chore: run build GitHub Actions on canary as well

* chore: add reproduction section to questions

* feat(provider): Add Azure Active Directory B2C (#809)

* add provider: Microsoft

* documentation

* support no tenant setup

* fix code style

* chore: rename Microsoft provider to AzureADB2C

* chore: alphabetical order in providers/index

* Revert "feat(provider): Add Azure Active Directory B2C (#809)" (#919)

This reverts commit 6e6a24a7af.

* chore: add myself to the contributors list 🙈

* We have twice the word "side"

Co-authored-by: Balázs Orbán <info@balazsorban.com>
Co-authored-by: Vladimir Evdokimov <evdokimov.vladimir@gmail.com>

* docs: Correcting a typo. "available" Line 70 (#965)

* chore: use stale label, instead of wontfix

* chore: add link to issue explaining stalebot

* chore: fix typo in stalebot comment

* chore: run build GitHub Action on canary also

* chore: run build GitHub Actions on canary as well

* chore: add reproduction section to questions

* feat(provider): Add Azure Active Directory B2C (#809)

* add provider: Microsoft

* documentation

* support no tenant setup

* fix code style

* chore: rename Microsoft provider to AzureADB2C

* chore: alphabetical order in providers/index

* Revert "feat(provider): Add Azure Active Directory B2C (#809)" (#919)

This reverts commit 6e6a24a7af.

* chore: add myself to the contributors list 🙈

* Correcting a typo. "available" Line 70

Co-authored-by: Balázs Orbán <info@balazsorban.com>
Co-authored-by: Vladimir Evdokimov <evdokimov.vladimir@gmail.com>

* chore: hide comments from pull request template

* Update README.md

Updated the readme to include the projects logo, fixed some typos, and added license info and contributor image.

* feat: add strava provider (#986)

* Add Strava as a provider

* Add documentation for Strava provider

* Fix lint errors

Co-authored-by: Paul Kenneth Kent <paul@ventureharbour.com>

* Update README.md

* Update README.md

* feat: add semantic-release (#920)

* chore(release): change semantic-release/git to semantic-release/github

* docs(database): add mssql indexes in docs, fix typos (#925)

* added mssql indexes in docs, fixed typo

* docs: fix typo in www/docs/schemas/mssql.md

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

* chore(release): delete old workflow

* chore(release): trigger release on docs type

* fix: treat user.id as optional param (#1010)

* fix(adapter): use findOne for typeorm (#1014)

* Change image to text from varchar (#777)

Co-authored-by: Nico Domino <yo@ndo.dev>

* feat(db): make Fauna DB collections & indexes configurable (#968)

* Add collections & indexes overrides for Fauna DB

* Fix the name of the verification token index

Co-authored-by: Florian Michaut <florian@coding-days.com>

* docs: Remove unnecessary promises (#915)

* feat: allow to return string in signIn callback (#1019)

* docs: small update to sign in/out examples (#1016)

* Update examples in client.md

* Update more examples

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

* docs: update contributing information [skip release] (#1011)

* docs: update CONTRIBUTING.md

* docs:  use db instead of database for more space

* docs: update CONTRIBUTING.md

* docs: update PR template

* docs: add note about skipping a release

* docs: fix typos in CONTRIBUTING.md [skip release]

* refactor: code base improvements (#959)

* chore: fix casing of OAuth

* refacotr: simplify default callbacks lib file

* refactor: use native URL instead of string concats

* refactor: move redirect to res.redirect, done to res.end

* refactor: move options to req

* refactor: improve IntelliSense, name all functions

* fix(lint): fix lint errors

* refactor: remove jwt-decode dependency

* refactor: refactor some callbacks to Promises

* revert: "refactor: use native URL instead of string concats"

Refs: 690c55b04089e4f3157424c816d43ee4cecb77a0

* chore: misc changes

Co-authored-by: Balazs Orban <balazs@nhi.no>

* feat(provider): Add Mail.ru OAuth Service Provider and Callback snippet (#522)

* Update callback.js

- Fix Mail.ru bug (missing request parameter: access_token)

Note: setGetAccessTokenProfileUrl should be added to Mail.ru provider to enable support.

* Add Mail.ru OAuth Service Provider

* Update callbacks.md

- Fix broken callbacks snippet.

* Update callback.js

- Bug fix https://github.com/nextauthjs/next-auth/pull/522#issuecomment-669851914
- Minor refactoring.

* Fix: Code linting.

* Update callback.js

Improve approach for building of URL based review recommendation.

* Feat: Reduce API surface expansion

Make use of provider.id === "mailru" as suggested in review discussion in place of setGetAccessTokenProfileUrl.

* Fix: Code linting

* feat: forward id_token to jwt and signIn callbacks (#1024)

* chore: add auto labeling to PRs [skip release] (#1025)

* chore: add auto labeling to PRs [skip release]

* chore: allow any file type for test label to be added

* chore: rename labeler.yaml to labeler.yml [skip release]

* fix: miscellaneous bugfixes (#1030)

* fix: use named params to fix order

* fix: avoid recursive redirects

* fix: revert to use parsed baseUrl

* fix: avoid recursive res.end calls

* fix: use named params in renderPage

* fix: promisify lib/oauth/callback result

* fix: don't chain on res.end on non-chainable res methods (#1031)

* docs: add powered by vercel logo [skip release]

* chore: run tests on canary [skip release]

* docs: misc improvements [skip release] (#1043)

* refactor: code base improvements 2 (#1045)

* fix: trigger release

* fix: use authorizationUrl correctly

* feat(provider): reduce user facing API (#1023)

Co-authored-by: Balazs Orban <balazs@nhi.no>

* fix: remove async from NextAuth default handler

This function should not return a Promise

* feat(provider): add vk.com provider (#1060)

* feat(provider): add vk.com provider

* refactor(provider): reduce vk.com provider api

* refactor: code base improvements 3 (#1072)

* refactor: extend res.{end,send,json}, redirect

* refactor: chain res methods, remove unnecessary ones

* refactor: simplify oauth callback signature

* refactor: code simplifications

* refactor: re-export everything from routes in one

* refactor: split up main index.js to multiple files

* refactor: simplify passing of provider(s) around

* refactor: extend req with callbackUrl inline

* refactor: simplify page rendering

* refactor: move error page redirects to main file, simplify renderer

* refactor: inline req.options definition

* refactor: simplify error fallbacks

* refactor: remove else branches and unnecessary try..catch

* refactor: add docs, and simplify jwt functions

* refactor: prefer errors object over switch..case in signin page

* feat: log all params sent to logger instead of only first

* refactor: fewer lines input validation

* refactor: remove even more unnecessary else branches

* feat: improve package development experience (#1064)

* chore(deps): add next and react to dev dependencies

* chore: move build configs to avoid crash with next dev

* chore: add next js dev app

* chore: remove .txt extension from LICENSE file

* chore: update CONTRIBUTING.md

* chore: watch css under development

* style(lint): run linter on index.css

* chore: fix some imports for dev server

* refactor: simplify client code

* chore: mention VSCode extension for linting

* docs: reword CONTRIBUTING.md

* chore: ignore linting pages and components

* fix: pass csrfToken to signin renderer

* feat: replace blur/focus event to visibility API for getSession (#1081)

* docs: clarify .env usage in CONTRIBUTING.md [skip release] (#1085)

* docs: improve FAQ docs [skip release]

* chore: update caiuse-lite db

* docs: update  some urls in the docs [skip release]

* feat(pages): add dark theme support (#1088)

* feat(pages): add dark theme support

* docs: document theme option

* chore: remove ts-check from dev app

* style(pages): fix some text colors in dark mode

* feat(provider): add LINE provider (#1091)

* refactor: be explicit about path in jsonconfig [skip release]

* refactor: show signin page in dev app [skip release]

* fix: export getSession [skip release]

somehow the default export does not work in the dev app

* style: make p system theme aware [skip release]

* feat(provider): finish Reddit provider and add documentation (#1094)

* Create reddit.md

* uncommented profile callback

* Update reddit.md

* fix lint issues

* added reddit provider

* added reddit provider

* Add Reddit Provider

For some reason a bunch of providers got deleted in the last commit

* Add Reddit Provider

* Add Reddit Provider

* chore: define providers in single file for docs [skip release]

* chore: Comply to Vercel Open Source sponsorship [skip release] (#1087)

* added banner

* Changed banner image allignment

* changed location of banner again

* added to acknowledgement

* added to acknowledgement 1

* changed image size

* k

* l

* s

* s

* .

* added link to the banner in readme.md

* fixed image redirect

* fixed image allignment

* made changes in readme and index.js

* Changed the source of the banner image

* added banner to the footer of the site

* chore: fix lint issues [skip release]

* feat: add native hkdf (#1124)

* feat: add native hkdf

* feat: import only needed to do hkdf

* feat: tweak digest and arguments

* chore(deps): upgrade typeorm to v0.2.30 (#1145)

* docs: remove v1 documentation (#1142)

* chore(adapters): remove fauna (#1148)

* feat: forward signIn auth params to /authorize (#1149)

* refactor: authorisation -> authorization

* feat: forward authorizationParams from signIn function

* refactor: take auth params as third argument

* docs: document signIn authorizationParams

* fix(adapter): fix ISO Datetime type error in Prisma updateSession (#640)

Co-authored-by: Nico Domino <yo@ndo.dev>
Co-authored-by: Balázs Orbán <info@balazsorban.com>

* feat(provider): add option to generate email verification token (#541)

* Add option to generate email verification token

* chore: remove unused import

* refactor: define default generateVerificationToken in-place

* refactor: define default generateVerificationToken in-place

Co-authored-by: Nico Domino <yo@ndo.dev>
Co-authored-by: Balázs Orbán <info@balazsorban.com>

* docs: update info about TypeScript [skip release]

* feat: add PKCE support (#941)

* chore(deps): upgrade dependencies

* chore(deps): add pkce-challenge

* feat(pkce): initial implementation of PCKE support

* chore: remove URLSearchParams

* chore(deps): upgrade lockfile

* refactor: store code_verifier in a cookie

* refactor: add pkce handlers

* docs: add PKCE documentation

* chore: remove unused param

* chore: revert unnecessary code change

* fix: correct variable names

* fix: correct logger import

* feat(provider): add Salesforce provider (#1027)

* docs(provider): add Salesforce provider

* fix(provider): use authed_user on slack instead of spotify (#1174)

* fix: use startsWith for protocol matching in parseUrl

closes #842

* fix: fix lint issues

* docs: clear things up around using access_token [skip release]

#1078

* docs: fix typo in callbacks.md [skip release]

* chore(provider): remove Mixer (#1178)

"Thank you to our amazing community and Partners.
As of July 22, the Mixer service has closed."

* feat(provider): re-add state, expand protection provider options  (#1184)

* refactor: move OAuthCallbackError to errors file

* refactor: improve pkce handling

* feat(provider): re-introduce state to provider options

* docs(provider): mention protection options "state" and "none"

* docs(provider): document state property deprecation

* fix: only add code_verifier param if protection is pkce

* docs: explain state deprecation better

* chore: unify string

* fix: send /authorize params through url

* fix: Add a null check to the window 'storage' event listener (#1198)

* Add a null check to the window 'storage' event listener

While testing in Cypress it's possible to receive a null value on Storage Events when 'clear' is called and will cause errors as seen in #1125.

* Update index.js

typo

* Update src/client/index.js

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

* formatting

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

* docs(provider): fix typos in providers code snippets [skip release] (#1204)

* docs(adapter): add adapter repo to documentation [skip release] (#1173)

* docs(adapter): add adapter repo to documentation

* docs(adapter): elaborate on custom repo

* fix: forward second argument to fetch body in signIn

fixes #1206

* docs: Fix grammar in "Feature Requests" section of FAQs [skip release] (#1212)

* refactor: provide raw idToken through account object (#1211)

* refactor: provide raw idToken through account object

* docs: clear up accessToken naming

* refactor: provide raw token response to account

* chore: fix grammar in comments

* feat: send all params to logger function (#1214)

* feat(provider): Add Medium (#1213)

* fix: leave accessTokenExpires as null

Forwarding expires_in as is to accessTokenExpires has shown to cause issues with Prisma, and maybe with other flows as well. Setting it back to `null` for now. We still forward `expires_in`, so users can use it if they want to.

Fixes #1216

* docs: more emphasis on req methods [skip release]

* docs: remove announcement bar [skip release]

* fix: make OAuth 1 work after refactoring (#1218)

* chore: add twitter provider to dev app

* feat: bind client instance to overriden methods

* fix: don't add extra params to getOAuthRequestToken

* chore: add twitter to env example, add secret gen instructions

* docs: Update Providers.Credential Example Block [skip release] (#1225)

Closing curly bracket where it should have been a square bracket.

* feat(provider): option to disable client-side redirects (credentials) (#1219)

* chore: add credentials provider to dev app

* feat: add redirect option to signIn, signOut

* feat: set correct status codes for credentials errors

* chore: add credentials page to dev app

* fix: support any provider name for credentials

* feat(ts): preliminary TypeScript support (#1223)

* chore: replace standard with ts-standard

* feat(ts): add some initial types

* feat(ts): import and use types

* chore: allow global fetch through package.json

* chore: upgrade lint scripts to use ts-standard

* chore: run linter on dev app

* chore(ts): satisfy dev Next.js server for TS

* fix: add eslint as dev dependency

* fix(lint): ignore next-env.d.ts from linting

* feat(ts): improve cookies options types

* fix: run linter with fix

* feat(provider): add EVE Online provider (#1227)

* Adding EVEOnline provider

* Adding EVEOnline provider

* Adding EVEOnline provider

* Adding EVEOnline provider

* Adding EVEOnline provider

* Adding EVEOnline provider

* Adding EVEOnline provider

* Adding EVEOnline provider

Co-authored-by: Gerald McNicholl <gerald.mcnicholl@xero.com>

* docs: clarify custom pages usage [skip release] (#1239)

* docs(provider): Update Atlassian docs (#1255)

* docs: Update Atlassian docs [skip release]

* Update atlassian.md

* fix(provider): okta client authentication (#1257)

* fix: okta client authentication

* chore: run lint fix

* Update pages/api/auth/[...nextauth].js

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

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

* chore: don't sync labels with labeler [skip release]

manually added PR labels were constantly removed on new commits/builds, this hopefully fixes that

* fix(provider): add verificationRequest flag to email signIn callback (#1258)

* fix(ui): use color text var for input color (#1260)

Co-authored-by: Archit Khode <archit.khode@gmail.com>

* docs: Minor text error fixed [skip release] (#1263)

* feat(provider): update session when signIn/signOut successful (#1267)

* feat(provider): update session when login/logout successful

* chore: remove manual page reload from dev app

* docs(client): document redirect: false

* fix(page): fix typo in error page

* Merge pull request from GHSA-pg53-56cg-4m8q

* fix(adapter): Verify identifier as well as token in Prisma adapter

* feat(adapter): Improve typeorm adapter

Improve conditional check in TypeORM adapter.

This should have no impact in practice but sets  a good example.

* docs(adapter): Update Prisma docs [skip release] (#1279) (#1283)

Co-authored-by: Iain Collins <me@iaincollins.com>

* docs(provider): Update azure-ad-b2c.md [skip release] (#1280)

* docs(adapter): Update Prisma docs (#1279)

* Update azure-ad-b2c.md

add hint for redirection URL, otherwise difficult to find out

* Update azure-ad-b2c.md

changed .env ro .env.local as per recommendation

* Update azure-ad-b2c.md

* Update azure-ad-b2c.md

* Update azure-ad-b2c.md

* update conf in .env.local 

follow the .env guidelines

* Update azure-ad-b2c.md

* Create azure-ad-b2c.md

* Create azure-ad-b2c.md

* Update azure-ad-b2c.md

Co-authored-by: Iain Collins <me@iaincollins.com>

* docs: Change "docs" to "documentation"

* fix(provider): Fixes for email sign in (#1285)

* fix(adapter): Fix Prisma delete

Must use Prsima deleteMany() instead of delete() with multiple clauses.

* feat: Update example project

Update example project to make it easier to test with database adapters.

* fix(ui): Fix message text in light / auto theme

Info message text is always on the same background (blue) on both themes so should always be white.

* docs: Update example .env [skip release]

* feat: Update Prisma peerOptionalDependencies

* docs: trigger release

Co-authored-by: Luke Lau <luke_lau@icloud.com>
Co-authored-by: James Perkins <jamesperkins@hey.com>
Co-authored-by: Joshua K. Martinez <joshkmartinez@gmail.com>
Co-authored-by: Pauldic <Pauldiconline@yahoo.com>
Co-authored-by: Josh Padnick <josh@gruntwork.io>
Co-authored-by: Daggy1234 <arnav.jindal7@gmail.com>
Co-authored-by: Alan Ray <71240883+ohheyalanray@users.noreply.github.com>
Co-authored-by: Manish Chiniwalar <manishrc@users.noreply.github.com>
Co-authored-by: Aymeric <34040599+afoyer@users.noreply.github.com>
Co-authored-by: Nico Domino <yo@ndo.dev>
Co-authored-by: Fabrizio Ruggeri <ramiel@users.noreply.github.com>
Co-authored-by: Iain Collins <me@iaincollins.com>
Co-authored-by: Joseph Vaughan <Joev-@users.noreply.github.com>
Co-authored-by: Joost Jansky <styxlab@users.noreply.github.com>
Co-authored-by: styxlab <cws@DE01WP777.scdom.net>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: RobertCraigie <robertcraigie30@gmail.com>
Co-authored-by: Joe Bell <joe@joebell.co.uk>
Co-authored-by: Vladimir Evdokimov <evdokimov.vladimir@gmail.com>
Co-authored-by: Cathy Chen <cathykaichen@gmail.com>
Co-authored-by: Kristóf Poduszló <kripod@protonmail.com>
Co-authored-by: Haldun Anil <haldunanil@users.noreply.github.com>
Co-authored-by: Jakub Naskręski <36169811+kleyu@users.noreply.github.com>
Co-authored-by: imgregduh <imgregorywong@gmail.com>
Co-authored-by: pkabore <paulkabore333@gmail.com>
Co-authored-by: Paul Kenneth Kent <pkennethkent@gmail.com>
Co-authored-by: Paul Kenneth Kent <paul@ventureharbour.com>
Co-authored-by: Balazs Orban <balazs@nhi.no>
Co-authored-by: Junior Vidotti <jrvidotti@gmail.com>
Co-authored-by: Yuma Matsune <yuma.matsune@gmail.com>
Co-authored-by: Ben West <Xodarap@users.noreply.github.com>
Co-authored-by: Florian Michaut <florianmichaut@gmail.com>
Co-authored-by: Florian Michaut <florian@coding-days.com>
Co-authored-by: Melanie Seltzer <melleh11@gmail.com>
Co-authored-by: Didi Keke <nyedidikeke@users.noreply.github.com>
Co-authored-by: Evgeniy Boreyko <boreykojenya@yandex.ru>
Co-authored-by: Alex B <lnikell@gmail.com>
Co-authored-by: Ben <5271788+bebax@users.noreply.github.com>
Co-authored-by: suraj10k <63460026+suraj10k@users.noreply.github.com>
Co-authored-by: t.kuriyama <koolii0909@gmail.com>
Co-authored-by: Yuri Gor <YuriGor@users.noreply.github.com>
Co-authored-by: Radhika <56536997+96RadhikaJadhav@users.noreply.github.com>
Co-authored-by: Henrik Wenz <HaNdTriX@users.noreply.github.com>
Co-authored-by: Zhao Lei <firede@firede.com>
Co-authored-by: Mohamed El Mahallawy <mmahalwy@gmail.com>
Co-authored-by: Dillon Mulroy <dillon.mulroy@gmail.com>
Co-authored-by: Carmelo Scandaliato <8927157+cascandaliato@users.noreply.github.com>
Co-authored-by: Aishah <aissshah@outlook.com>
Co-authored-by: Samson Zhang <wwsamson@yahoo.com>
Co-authored-by: Vova <volodimir.partytskyi@gmail.com>
Co-authored-by: Cody Ogden <cody@codyogden.com>
Co-authored-by: geraldm74 <gerald_mcnicholl@yahoo.com>
Co-authored-by: Gerald McNicholl <gerald.mcnicholl@xero.com>
Co-authored-by: Jeremy Caine <jezcaine@gmail.com>
Co-authored-by: Matthew Graser <mdgraser@gmail.com>
Co-authored-by: mgraser <matt.graser@mlb.com>
Co-authored-by: Kristofor Carle <kris@maphubs.com>
Co-authored-by: Archit Khode <arkits@outlook.com>
Co-authored-by: Archit Khode <archit.khode@gmail.com>
Co-authored-by: Daniel Gadd <danielgadd@outlook.com>
Co-authored-by: Robert Hufsky <Robert.Hufsky@gmx.net>
2021-02-09 15:47:47 +01:00
Iain Collins
6af40e3fe2 docs(adapter): Update Prisma docs (#1279) 2021-02-08 19:30:32 +00:00
Cody Ogden
cdc1ac52b2 docs: Update Providers.Credential Example Block [skip release] (#1225)
Closing curly bracket where it should have been a square bracket.
2021-02-01 10:01:10 +01:00
Balázs Orbán
f2a7ee0b34 fix: make OAuth 1 work after refactoring (#1218)
* chore: add twitter provider to dev app

* feat: bind client instance to overriden methods

* fix: don't add extra params to getOAuthRequestToken

* chore: add twitter to env example, add secret gen instructions
2021-02-01 10:01:10 +01:00
Balázs Orbán
d67f1b7718 docs: remove announcement bar [skip release] 2021-02-01 10:01:10 +01:00
Balázs Orbán
396f5d8bbc docs: more emphasis on req methods [skip release] 2021-02-01 10:01:10 +01:00
Balázs Orbán
d7e78d5996 fix: leave accessTokenExpires as null
Forwarding expires_in as is to accessTokenExpires has shown to cause issues with Prisma, and maybe with other flows as well. Setting it back to `null` for now. We still forward `expires_in`, so users can use it if they want to.

Fixes #1216
2021-02-01 10:01:10 +01:00
Vova
ad3b0b6a7d feat(provider): Add Medium (#1213) 2021-02-01 10:01:10 +01:00
Balázs Orbán
f4a954ccbb feat: send all params to logger function (#1214) 2021-02-01 10:01:10 +01:00
Balázs Orbán
93f051ce08 refactor: provide raw idToken through account object (#1211)
* refactor: provide raw idToken through account object

* docs: clear up accessToken naming

* refactor: provide raw token response to account

* chore: fix grammar in comments
2021-02-01 10:01:10 +01:00
Samson Zhang
645b53ee49 docs: Fix grammar in "Feature Requests" section of FAQs [skip release] (#1212) 2021-02-01 10:01:10 +01:00
Balázs Orbán
9c9744f30a fix: forward second argument to fetch body in signIn
fixes #1206
2021-02-01 10:01:10 +01:00
Aishah
9860ad8c8c docs(adapter): add adapter repo to documentation [skip release] (#1173)
* docs(adapter): add adapter repo to documentation

* docs(adapter): elaborate on custom repo
2021-02-01 10:01:10 +01:00
Carmelo Scandaliato
23ada52f97 docs(provider): fix typos in providers code snippets [skip release] (#1204) 2021-02-01 10:01:10 +01:00
Dillon Mulroy
65e4910618 fix: Add a null check to the window 'storage' event listener (#1198)
* Add a null check to the window 'storage' event listener

While testing in Cypress it's possible to receive a null value on Storage Events when 'clear' is called and will cause errors as seen in #1125.

* Update index.js

typo

* Update src/client/index.js

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

* formatting

Co-authored-by: Balázs Orbán <info@balazsorban.com>
2021-02-01 10:01:10 +01:00
Balázs Orbán
30c6d6360f fix: send /authorize params through url 2021-02-01 10:01:10 +01:00
Balázs Orbán
214b22ecbb feat(provider): re-add state, expand protection provider options (#1184)
* refactor: move OAuthCallbackError to errors file

* refactor: improve pkce handling

* feat(provider): re-introduce state to provider options

* docs(provider): mention protection options "state" and "none"

* docs(provider): document state property deprecation

* fix: only add code_verifier param if protection is pkce

* docs: explain state deprecation better

* chore: unify string
2021-02-01 10:01:10 +01:00
Balázs Orbán
f50ac194aa chore(provider): remove Mixer (#1178)
"Thank you to our amazing community and Partners.
As of July 22, the Mixer service has closed."
2021-02-01 10:01:10 +01:00
Balázs Orbán
b40e1441ec docs: fix typo in callbacks.md [skip release] 2021-02-01 10:01:10 +01:00
Balázs Orbán
90a8f7c890 docs: clear things up around using access_token [skip release]
#1078
2021-02-01 10:01:10 +01:00
Balázs Orbán
a7bae0395b fix: fix lint issues 2021-02-01 10:01:10 +01:00
Balázs Orbán
71b3122fd1 fix: use startsWith for protocol matching in parseUrl
closes #842
2021-02-01 10:01:10 +01:00
Luke Lau
1caa9bb813 fix(provider): use authed_user on slack instead of spotify (#1174) 2021-02-01 10:01:10 +01:00
Balázs Orbán
daed68aee2 docs(provider): add Salesforce provider 2021-02-01 10:01:10 +01:00
Mohamed El Mahallawy
2f3ed7507b feat(provider): add Salesforce provider (#1027) 2021-02-01 10:01:10 +01:00
Balázs Orbán
a15bdc191b fix: correct logger import 2021-02-01 10:01:10 +01:00
Balázs Orbán
ea71a1fb2f feat: add PKCE support (#941)
* chore(deps): upgrade dependencies

* chore(deps): add pkce-challenge

* feat(pkce): initial implementation of PCKE support

* chore: remove URLSearchParams

* chore(deps): upgrade lockfile

* refactor: store code_verifier in a cookie

* refactor: add pkce handlers

* docs: add PKCE documentation

* chore: remove unused param

* chore: revert unnecessary code change

* fix: correct variable names
2021-02-01 10:01:10 +01:00
Balázs Orbán
0069095bce docs: update info about TypeScript [skip release] 2021-02-01 10:01:10 +01:00
Zhao Lei
42a822c407 feat(provider): add option to generate email verification token (#541)
* Add option to generate email verification token

* chore: remove unused import

* refactor: define default generateVerificationToken in-place

* refactor: define default generateVerificationToken in-place

Co-authored-by: Nico Domino <yo@ndo.dev>
Co-authored-by: Balázs Orbán <info@balazsorban.com>
2021-02-01 10:01:10 +01:00
Henrik Wenz
2cfe5ad879 fix(adapter): fix ISO Datetime type error in Prisma updateSession (#640)
Co-authored-by: Nico Domino <yo@ndo.dev>
Co-authored-by: Balázs Orbán <info@balazsorban.com>
2021-02-01 10:01:10 +01:00
Balázs Orbán
cba149f4b6 feat: forward signIn auth params to /authorize (#1149)
* refactor: authorisation -> authorization

* feat: forward authorizationParams from signIn function

* refactor: take auth params as third argument

* docs: document signIn authorizationParams
2021-02-01 10:01:10 +01:00
Balázs Orbán
31bb2c342c chore(adapters): remove fauna (#1148) 2021-02-01 10:01:10 +01:00
Radhika
af30be1fa4 docs: remove v1 documentation (#1142) 2021-02-01 10:01:10 +01:00
Yuri Gor
ebaa28f04e chore(deps): upgrade typeorm to v0.2.30 (#1145) 2021-02-01 10:01:10 +01:00
t.kuriyama
e26297b901 feat: add native hkdf (#1124)
* feat: add native hkdf

* feat: import only needed to do hkdf

* feat: tweak digest and arguments
2021-02-01 10:01:10 +01:00
Balázs Orbán
2562b3c5d8 chore: fix lint issues [skip release] 2021-02-01 10:01:10 +01:00
suraj10k
f8e5a79ce1 chore: Comply to Vercel Open Source sponsorship [skip release] (#1087)
* added banner

* Changed banner image allignment

* changed location of banner again

* added to acknowledgement

* added to acknowledgement 1

* changed image size

* k

* l

* s

* s

* .

* added link to the banner in readme.md

* fixed image redirect

* fixed image allignment

* made changes in readme and index.js

* Changed the source of the banner image

* added banner to the footer of the site
2021-02-01 10:01:10 +01:00
Balázs Orbán
be53ef0f71 chore: define providers in single file for docs [skip release] 2021-02-01 10:01:10 +01:00
Aymeric
fbb5a12cbd feat(provider): finish Reddit provider and add documentation (#1094)
* Create reddit.md

* uncommented profile callback

* Update reddit.md

* fix lint issues

* added reddit provider

* added reddit provider

* Add Reddit Provider

For some reason a bunch of providers got deleted in the last commit

* Add Reddit Provider

* Add Reddit Provider
2021-02-01 10:01:10 +01:00
Balázs Orbán
70a186c183 style: make p system theme aware [skip release] 2021-02-01 10:01:10 +01:00
Balázs Orbán
cff9d3e294 fix: export getSession [skip release]
somehow the default export does not work in the dev app
2021-02-01 10:01:10 +01:00
Balázs Orbán
2865b8cc2d refactor: show signin page in dev app [skip release] 2021-02-01 10:01:10 +01:00
Balázs Orbán
b84f1b681c refactor: be explicit about path in jsonconfig [skip release] 2021-02-01 10:01:10 +01:00
Ben
ea1d09bf83 feat(provider): add LINE provider (#1091) 2021-02-01 10:01:10 +01:00
Balázs Orbán
a18ec09307 feat(pages): add dark theme support (#1088)
* feat(pages): add dark theme support

* docs: document theme option

* chore: remove ts-check from dev app

* style(pages): fix some text colors in dark mode
2021-02-01 10:01:10 +01:00
Balázs Orbán
37cb81094f docs: update some urls in the docs [skip release] 2021-02-01 10:01:10 +01:00
Balázs Orbán
040d7c5017 chore: update caiuse-lite db 2021-02-01 10:01:10 +01:00
Balázs Orbán
f53ea6c9a9 docs: improve FAQ docs [skip release] 2021-02-01 10:01:10 +01:00
Balázs Orbán
7f670c5222 docs: clarify .env usage in CONTRIBUTING.md [skip release] (#1085) 2021-02-01 10:01:10 +01:00
Alex B
b1a99ec32f feat: replace blur/focus event to visibility API for getSession (#1081) 2021-02-01 10:01:10 +01:00
Balázs Orbán
ca065604a3 fix: pass csrfToken to signin renderer 2021-02-01 10:01:10 +01:00
Balázs Orbán
77de2abd14 feat: improve package development experience (#1064)
* chore(deps): add next and react to dev dependencies

* chore: move build configs to avoid crash with next dev

* chore: add next js dev app

* chore: remove .txt extension from LICENSE file

* chore: update CONTRIBUTING.md

* chore: watch css under development

* style(lint): run linter on index.css

* chore: fix some imports for dev server

* refactor: simplify client code

* chore: mention VSCode extension for linting

* docs: reword CONTRIBUTING.md

* chore: ignore linting pages and components
2021-02-01 10:01:10 +01:00
Balázs Orbán
f6d6c4344c refactor: code base improvements 3 (#1072)
* refactor: extend res.{end,send,json}, redirect

* refactor: chain res methods, remove unnecessary ones

* refactor: simplify oauth callback signature

* refactor: code simplifications

* refactor: re-export everything from routes in one

* refactor: split up main index.js to multiple files

* refactor: simplify passing of provider(s) around

* refactor: extend req with callbackUrl inline

* refactor: simplify page rendering

* refactor: move error page redirects to main file, simplify renderer

* refactor: inline req.options definition

* refactor: simplify error fallbacks

* refactor: remove else branches and unnecessary try..catch

* refactor: add docs, and simplify jwt functions

* refactor: prefer errors object over switch..case in signin page

* feat: log all params sent to logger instead of only first

* refactor: fewer lines input validation

* refactor: remove even more unnecessary else branches
2021-02-01 10:01:10 +01:00
Evgeniy Boreyko
e8b1513899 feat(provider): add vk.com provider (#1060)
* feat(provider): add vk.com provider

* refactor(provider): reduce vk.com provider api
2021-02-01 10:01:10 +01:00
Balázs Orbán
505efc8a5d fix: remove async from NextAuth default handler
This function should not return a Promise
2021-02-01 10:01:10 +01:00
Balázs Orbán
76b983229a feat(provider): reduce user facing API (#1023)
Co-authored-by: Balazs Orban <balazs@nhi.no>
2021-02-01 10:01:10 +01:00
Balázs Orbán
ecddaf696b fix: use authorizationUrl correctly 2021-02-01 10:01:10 +01:00
Balázs Orbán
b43e7dca43 fix: trigger release 2021-02-01 10:01:10 +01:00
Balázs Orbán
e7c34fd74b refactor: code base improvements 2 (#1045) 2021-02-01 10:01:10 +01:00
Balázs Orbán
e0dd8e400b docs: misc improvements [skip release] (#1043) 2021-02-01 10:01:10 +01:00
Balázs Orbán
21d22a7e08 chore: run tests on canary [skip release] 2021-02-01 10:01:10 +01:00
Balázs Orbán
cb2fe0cae6 docs: add powered by vercel logo [skip release] 2021-02-01 10:01:10 +01:00
Balázs Orbán
9ed75c7788 fix: don't chain on res.end on non-chainable res methods (#1031) 2021-02-01 10:01:10 +01:00
Balázs Orbán
8e8713755a fix: miscellaneous bugfixes (#1030)
* fix: use named params to fix order

* fix: avoid recursive redirects

* fix: revert to use parsed baseUrl

* fix: avoid recursive res.end calls

* fix: use named params in renderPage

* fix: promisify lib/oauth/callback result
2021-02-01 10:01:10 +01:00
Balázs Orbán
7fdde6268e chore: rename labeler.yaml to labeler.yml [skip release] 2021-02-01 10:01:10 +01:00
Balázs Orbán
5e949a3b97 chore: add auto labeling to PRs [skip release] (#1025)
* chore: add auto labeling to PRs [skip release]

* chore: allow any file type for test label to be added
2021-02-01 10:01:10 +01:00
Balázs Orbán
a979e040cd feat: forward id_token to jwt and signIn callbacks (#1024) 2021-02-01 10:01:10 +01:00
Didi Keke
2205cfa754 feat(provider): Add Mail.ru OAuth Service Provider and Callback snippet (#522)
* Update callback.js

- Fix Mail.ru bug (missing request parameter: access_token)

Note: setGetAccessTokenProfileUrl should be added to Mail.ru provider to enable support.

* Add Mail.ru OAuth Service Provider

* Update callbacks.md

- Fix broken callbacks snippet.

* Update callback.js

- Bug fix https://github.com/nextauthjs/next-auth/pull/522#issuecomment-669851914
- Minor refactoring.

* Fix: Code linting.

* Update callback.js

Improve approach for building of URL based review recommendation.

* Feat: Reduce API surface expansion

Make use of provider.id === "mailru" as suggested in review discussion in place of setGetAccessTokenProfileUrl.

* Fix: Code linting
2021-02-01 10:01:10 +01:00
Balázs Orbán
0989ef6171 refactor: code base improvements (#959)
* chore: fix casing of OAuth

* refacotr: simplify default callbacks lib file

* refactor: use native URL instead of string concats

* refactor: move redirect to res.redirect, done to res.end

* refactor: move options to req

* refactor: improve IntelliSense, name all functions

* fix(lint): fix lint errors

* refactor: remove jwt-decode dependency

* refactor: refactor some callbacks to Promises

* revert: "refactor: use native URL instead of string concats"

Refs: 690c55b04089e4f3157424c816d43ee4cecb77a0

* chore: misc changes

Co-authored-by: Balazs Orban <balazs@nhi.no>
2021-02-01 10:01:10 +01:00
Balázs Orbán
7979b1069e docs: fix typos in CONTRIBUTING.md [skip release] 2021-02-01 10:01:10 +01:00
Balázs Orbán
71b50082f8 docs: update contributing information [skip release] (#1011)
* docs: update CONTRIBUTING.md

* docs:  use db instead of database for more space

* docs: update CONTRIBUTING.md

* docs: update PR template

* docs: add note about skipping a release
2021-02-01 10:01:10 +01:00
Melanie Seltzer
f3cc4d1018 docs: small update to sign in/out examples (#1016)
* Update examples in client.md

* Update more examples

Co-authored-by: Balázs Orbán <info@balazsorban.com>
2021-02-01 10:01:10 +01:00
Balázs Orbán
15570b7479 feat: allow to return string in signIn callback (#1019) 2021-02-01 10:01:10 +01:00
Balázs Orbán
a5187b69e8 docs: Remove unnecessary promises (#915) 2021-02-01 10:01:10 +01:00
Florian Michaut
751fd7bb0e feat(db): make Fauna DB collections & indexes configurable (#968)
* Add collections & indexes overrides for Fauna DB

* Fix the name of the verification token index

Co-authored-by: Florian Michaut <florian@coding-days.com>
2021-02-01 10:01:10 +01:00
Ben West
94054db3f3 Change image to text from varchar (#777)
Co-authored-by: Nico Domino <yo@ndo.dev>
2021-02-01 10:01:10 +01:00
Yuma Matsune
f93dbbbfee fix(adapter): use findOne for typeorm (#1014) 2021-02-01 10:01:10 +01:00
Balázs Orbán
e3fd0ad450 fix: treat user.id as optional param (#1010) 2021-02-01 10:01:10 +01:00
Balázs Orbán
e06816a374 chore(release): trigger release on docs type 2021-02-01 10:01:10 +01:00
Balázs Orbán
284118e708 chore(release): delete old workflow 2021-02-01 10:01:10 +01:00
Junior Vidotti
84bcecbec1 docs(database): add mssql indexes in docs, fix typos (#925)
* added mssql indexes in docs, fixed typo

* docs: fix typo in www/docs/schemas/mssql.md

Co-authored-by: Balázs Orbán <info@balazsorban.com>
2021-02-01 10:01:10 +01:00
Balazs Orban
5060bd7f9b chore(release): change semantic-release/git to semantic-release/github 2021-02-01 10:01:10 +01:00
Balázs Orbán
16a8720b1d feat: add semantic-release (#920) 2021-02-01 10:01:10 +01:00
Nico Domino
3c056d7ff5 Update README.md 2021-02-01 10:01:10 +01:00
Nico Domino
49dd7a807d Update README.md 2021-02-01 10:01:10 +01:00
Paul Kenneth Kent
42596fbca5 feat: add strava provider (#986)
* Add Strava as a provider

* Add documentation for Strava provider

* Fix lint errors

Co-authored-by: Paul Kenneth Kent <paul@ventureharbour.com>
2021-02-01 10:01:10 +01:00
Nico Domino
68d0f9465a Update README.md
Updated the readme to include the projects logo, fixed some typos, and added license info and contributor image.
2021-02-01 10:01:10 +01:00
Balázs Orbán
71b4af0894 chore: hide comments from pull request template 2021-02-01 10:01:10 +01:00
pkabore
8bbb0ec344 docs: Correcting a typo. "available" Line 70 (#965)
* chore: use stale label, instead of wontfix

* chore: add link to issue explaining stalebot

* chore: fix typo in stalebot comment

* chore: run build GitHub Action on canary also

* chore: run build GitHub Actions on canary as well

* chore: add reproduction section to questions

* feat(provider): Add Azure Active Directory B2C (#809)

* add provider: Microsoft

* documentation

* support no tenant setup

* fix code style

* chore: rename Microsoft provider to AzureADB2C

* chore: alphabetical order in providers/index

* Revert "feat(provider): Add Azure Active Directory B2C (#809)" (#919)

This reverts commit 6e6a24a7af.

* chore: add myself to the contributors list 🙈

* Correcting a typo. "available" Line 70

Co-authored-by: Balázs Orbán <info@balazsorban.com>
Co-authored-by: Vladimir Evdokimov <evdokimov.vladimir@gmail.com>
2021-02-01 10:01:10 +01:00
pkabore
b2770d5a1f docs: We have twice the word "side" (#964)
* chore: use stale label, instead of wontfix

* chore: add link to issue explaining stalebot

* chore: fix typo in stalebot comment

* chore: run build GitHub Action on canary also

* chore: run build GitHub Actions on canary as well

* chore: add reproduction section to questions

* feat(provider): Add Azure Active Directory B2C (#809)

* add provider: Microsoft

* documentation

* support no tenant setup

* fix code style

* chore: rename Microsoft provider to AzureADB2C

* chore: alphabetical order in providers/index

* Revert "feat(provider): Add Azure Active Directory B2C (#809)" (#919)

This reverts commit 6e6a24a7af.

* chore: add myself to the contributors list 🙈

* We have twice the word "side"

Co-authored-by: Balázs Orbán <info@balazsorban.com>
Co-authored-by: Vladimir Evdokimov <evdokimov.vladimir@gmail.com>
2021-02-01 10:01:10 +01:00
imgregduh
192e5bf07e docs: fix typo Adapater -> Adapter (#960)
Co-authored-by: Balázs Orbán <info@balazsorban.com>
Co-authored-by: Vladimir Evdokimov <evdokimov.vladimir@gmail.com>
2021-02-01 10:01:10 +01:00
dependabot[bot]
9abdbb57eb chore(deps): Bump ini from 1.3.5 to 1.3.8 in /www (#953)
Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.8.
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.8)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-02-01 10:01:10 +01:00
Jakub Naskręski
989d23e827 feat: Display error if no [...nextauth].js found (#678)
* Display error if no [...nextauth].js found

fixes #647

* Log the error and describe it inside errors.md

Co-authored-by: Balázs Orbán <info@balazsorban.com>
2021-02-01 10:01:10 +01:00
Haldun Anil
025f33a91f docs: fix incorrect references in cypress docs (#932)
* chore: use stale label, instead of wontfix

* chore: add link to issue explaining stalebot

* chore: fix typo in stalebot comment

* chore: run build GitHub Action on canary also

* chore: run build GitHub Actions on canary as well

* chore: add reproduction section to questions

* feat(provider): Add Azure Active Directory B2C (#809)

* add provider: Microsoft

* documentation

* support no tenant setup

* fix code style

* chore: rename Microsoft provider to AzureADB2C

* chore: alphabetical order in providers/index

* Revert "feat(provider): Add Azure Active Directory B2C (#809)" (#919)

This reverts commit 6e6a24a7af.

* chore: add myself to the contributors list 🙈

* docs: fix incorrect references in cypress docs

* chore: add additional docs clarification

Co-authored-by: Balázs Orbán <info@balazsorban.com>
Co-authored-by: Vladimir Evdokimov <evdokimov.vladimir@gmail.com>
2021-02-01 10:01:10 +01:00
Luke Lau
6e2fc11d64 feat: Store user ID in sub claim of default JWT (#784)
This allows us to check if the user is signed in when using JWTs

Part of #625
2021-02-01 10:01:10 +01:00
Balázs Orbán
6b1b8613d0 chore: reword PR template 2021-02-01 10:01:10 +01:00
Balázs Orbán
6d023aa533 chore: create PULL_REQUEST_TEMPLATE.md 2021-02-01 10:01:10 +01:00
Balázs Orbán
080dd5f569 chore: add note about conveting questions to discussions 2021-02-01 10:01:10 +01:00
Balázs Orbán
a6867b3564 chore: disallow issues without template 2021-02-01 10:01:10 +01:00
dependabot[bot]
6750accc0a chore(dep): Bump highlight.js from 9.18.1 to 9.18.5 (#880)
Bumps [highlight.js](https://github.com/highlightjs/highlight.js) from 9.18.1 to 9.18.5.
- [Release notes](https://github.com/highlightjs/highlight.js/releases)
- [Changelog](https://github.com/highlightjs/highlight.js/blob/9.18.5/CHANGES.md)
- [Commits](https://github.com/highlightjs/highlight.js/compare/9.18.1...9.18.5)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Balázs Orbán <info@balazsorban.com>
Co-authored-by: Nico Domino <yo@ndo.dev>
2021-02-01 10:01:10 +01:00
Kristóf Poduszló
9aae7bbc54 refactor(db): update Prisma calls to support 2.12+ (#881)
Co-authored-by: Balázs Orbán <info@balazsorban.com>
Co-authored-by: Nico Domino <yo@ndo.dev>
2021-02-01 10:01:10 +01:00
Cathy Chen
a84fe596af update(provider): Update Slack provider to use V2 OAuth endpoints (#895)
* Update Slack to v2 authorize urls, option for additional authorize params
* acessTokenGetter + documentation
2021-02-01 10:01:10 +01:00
Vladimir Evdokimov
18840ead40 feat(provider): Add Azure Active Directory B2C (#921)
* add provider: Microsoft

* documentation

* support no tenant setup

* fix code style

* chore: rename Microsoft provider to AzureADB2C

* chore: alphabetical order in providers/index

* doc: add provider to FAQ
2021-02-01 10:01:10 +01:00
Joe Bell
f72ee5ec06 feat: add foursquare (#584) 2021-02-01 10:01:10 +01:00
RobertCraigie
958c31a4ee feat(provider): Add Bungie (#589)
* Add Bungie provider

* Use absolute URL for images

* Correct image URL and use consistent formatting

Co-authored-by: Nico Domino <yo@ndo.dev>
2021-02-01 10:01:10 +01:00
dependabot[bot]
f47f5c6c62 Bump next from 9.5.3 to 9.5.4 in /test/docker/app (#759)
Bumps [next](https://github.com/vercel/next.js) from 9.5.3 to 9.5.4.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v9.5.3...v9.5.4)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Nico Domino <yo@ndo.dev>
2021-02-01 10:01:10 +01:00
Joost Jansky
d5f5157366 feat(provider): add netlify (#555)
Co-authored-by: styxlab <cws@DE01WP777.scdom.net>
Co-authored-by: Balázs Orbán <info@balazsorban.com>
2021-02-01 10:01:10 +01:00
Joseph Vaughan
9fbfd90bb2 add(db): Add support for Fauna DB (#708)
* Add support for Fauna DB

* Add integration tests

Co-authored-by: Nico Domino <yo@ndo.dev>
2021-02-01 10:01:10 +01:00
Fabrizio Ruggeri
97d6f19fab Include callbackUrl in newUser page (#790)
* Include callbackUrl in newUser page

* Update src/server/routes/callback.js

Co-authored-by: Iain Collins <me@iaincollins.com>

* Update src/server/routes/callback.js

Co-authored-by: Iain Collins <me@iaincollins.com>

Co-authored-by: Iain Collins <me@iaincollins.com>
Co-authored-by: Nico Domino <yo@ndo.dev>
2021-02-01 10:01:10 +01:00
Nico Domino
fdcc62bd26 WIP: Update Docusaurus + Site dependencies (#802)
* update: deps

* fix: broken link

* fix: search upgrade change
2021-02-01 10:01:10 +01:00
Aymeric
4764d60268 Fix for Reddit Authentication (#866)
* Fixed Reddit Authentication

* updated fix for build test

* updated buffer to avoid deprecation message

* Updated for passing tests
2021-02-01 10:01:10 +01:00
Manish Chiniwalar
5f51c975c9 docs: Update default ports for support Databases (#839)
https://next-auth.js.org/configuration/databases
2021-02-01 10:01:10 +01:00
Balázs Orbán
0c9104c13a fix(provider): handle no profile image for Spotify (#914)
* chore(deps): upgrade "standard"

* style(lint): run lint fix

* fix(provider): optional chain Spotify provider profile img
2021-02-01 10:01:10 +01:00
Alan Ray
8a6f0944ef fix: update Okta routes (#763)
the current routing for the Okta provider does not follow the standard
set by Okta, and as such doesn't allow for custom subdomains. this
update amends the routes to allow for customer subdomains, and also
aligns next-auth with Okta's documentation.
2021-02-01 10:01:10 +01:00
Daggy1234
672cedc712 fix: ensure Images are produced for discord (#734) 2021-02-01 10:01:10 +01:00
Josh Padnick
05b275956b fix: update nodemailer version in response to CVE. (#860)
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-7769 reports a high-severity issue with the current version of nodemailer. This should be merged and released right away if possible.
2021-02-01 10:01:10 +01:00
Pauldic
219b01724b docs: fix typo in callbacks.md (#815)
This is a simple typographical error changed accesed to accessed
2021-02-01 10:01:10 +01:00
Joshua K. Martinez
94b0c68c8f docs: fix discord example code (#850) 2021-02-01 10:01:10 +01:00
James Perkins
81ebff897f docs: update for Now to Vercel (#847)
Vercel archived their now packages a while back, so you can use vercel env pull to pull in the .env
2021-02-01 10:01:10 +01:00
Luke Lau
8ac14ed298 feat: allow react 17 as a peer dependency (#819)
Co-authored-by: Balázs Orbán <info@balazsorban.com>
2021-02-01 10:01:10 +01:00
Balázs Orbán
51cfec92ba feat: simplify NextAuth instantiation (#911) 2021-02-01 10:01:10 +01:00
Balázs Orbán
78fd783bac docs: announce canary docs [skip release] (#1044) 2021-01-04 23:15:23 +01:00
Balázs Orbán
cec46b0d67 chore: add myself to the contributors list 🙈 2020-12-07 22:33:18 +01:00
Balázs Orbán
7bedd4afa9 Revert "feat(provider): Add Azure Active Directory B2C (#809)" (#919)
This reverts commit 6e6a24a7af.
2020-12-06 21:43:13 +01:00
Vladimir Evdokimov
6e6a24a7af feat(provider): Add Azure Active Directory B2C (#809)
* add provider: Microsoft

* documentation

* support no tenant setup

* fix code style

* chore: rename Microsoft provider to AzureADB2C

* chore: alphabetical order in providers/index
2020-12-06 21:34:40 +01:00
Balázs Orbán
6f067be7b0 chore: add reproduction section to questions 2020-12-05 21:40:12 +01:00
Balázs Orbán
e4cc3a92e5 chore: run build GitHub Actions on canary as well 2020-12-05 19:47:09 +01:00
Balázs Orbán
610ab39b40 chore: run build GitHub Action on canary also 2020-12-05 18:53:31 +01:00
Balázs Orbán
7d1168637d chore: fix typo in stalebot comment 2020-12-05 16:30:09 +01:00
Balázs Orbán
b2c1f32057 chore: add link to issue explaining stalebot 2020-12-05 14:19:45 +01:00
Balázs Orbán
9247495fee chore: use stale label, instead of wontfix 2020-12-05 14:15:55 +01:00
Balázs Orbán
341fae28d4 Revert "feat: simplify NextAuth instantiation" (#910)
This reverts commit b86ffa5dd5.
2020-12-05 10:39:03 +01:00
Balázs Orbán
b86ffa5dd5 feat: simplify NextAuth instantiation (#867) 2020-12-05 10:34:32 +01:00
Damien Guard
5415a9c3ab docs: remove redundant 3rd arg to sessions example (#874)
There is no third argument as per 8115a7c66c/src/server/routes/session.js (L82)
2020-12-05 10:15:28 +01:00
sankara
dc516e8be8 docs: fix typo in options.md (#873) 2020-12-05 10:13:43 +01:00
sAy
29a0d9d295 docs: update API docs about server-side getProviders (#879) 2020-12-05 10:12:17 +01:00
Balázs Orbán
5f5174f6e2 refactor: define _getSession in useEffect scope (#724) 2020-12-05 10:00:30 +01:00
Dennis Morello
424b4ee257 Update apple.md
Fixed a typo
2020-12-05 09:57:29 +01:00
Balázs Orbán
545a7e752e feat: forward auth params from signin to provider (#823) 2020-12-05 09:54:27 +01:00
Balázs Orbán
c564b84182 chore: add stalebot configuration 2020-12-05 09:37:27 +01:00
Siddharth Sharma
0db233d208 Add Sign in With Apple Tutorial (#896)
Co-authored-by: Iain Collins <me@iaincollins.com>
2020-12-02 11:13:53 +00:00
Yana Agun Siswanto
5126f4e342 CONTRIBUTING.md: Fix spelling and other issue (#810)
Co-authored-by: Iain Collins <me@iaincollins.com>
2020-10-30 17:09:42 +00:00
Michael McQuade
e09dfc6a7f Change definitively typed to definitely typed (#813) 2020-10-30 17:08:12 +00:00
Iain Collins
ccfa1d55bb Update errors.md 2020-10-30 14:18:25 +00:00
ndom91
d7dc7b0753 add: ndom91 dev.to tutorial 2020-10-27 15:39:30 +01:00
Alex Vilchis
0407e130c4 Fix grammar (#801) 2020-10-27 10:12:07 +00:00
Kristóf Poduszló
64084d634b fix: capitalization of errors in url params (#795) 2020-10-24 01:55:01 +01:00
Iain Collins
438a737837 Update providers.md 2020-10-20 11:54:19 +01:00
Iain Collins
a482a64f10 Update providers.md 2020-10-19 16:29:09 +01:00
Iain Collins
2227d34725 Update documentation (#786)
* Improved homepage and getting started guide
* Improved experience viewing docs on mobile devices
* Fixed typos
2020-10-19 14:19:09 +01:00
Jesus Castro (Tony)
9c6ef951a1 Typo: retuning should be returning
It ain't much but its honest work
2020-10-08 15:45:41 +01:00
Kristóf Poduszló
01c897f23e Correct typo of 'column' in the docs 2020-10-07 07:48:48 +01:00
Kristóf Poduszló
ea65d87d07 Correct tip about adapters in the docs 2020-10-07 07:48:48 +01:00
Kristóf Poduszló
8d1e479d12 Correct mistakenly duplicated word in the docs 2020-10-07 07:48:48 +01:00
Kristóf Poduszló
435b630849 Correct missing word in the docs 2020-10-07 07:48:48 +01:00
Kristóf Poduszló
773c74a756 Correct a typo in the docs 2020-10-07 07:48:48 +01:00
Kristóf Poduszló
6867bc92c8 Correct two typos of the same word in the docs 2020-10-07 07:48:48 +01:00
Kristóf Poduszló
eb6a4c46d9 Correct a typo in the docs 2020-10-06 20:50:28 +01:00
Iain Collins
cd3d2a138b Update tutorials and explainers 2020-10-06 13:50:42 +01:00
Iain Collins
0c356456bb Minor style changes 2020-10-06 13:50:42 +01:00
Iain Collins
6d44a34f7d Reduce image size 2020-10-06 13:50:42 +01:00
Iain Collins
7bda639361 Update branding (icons, homepage) 2020-10-06 13:50:42 +01:00
Francis Udeji
40e453076e Remove arrow syntax
Remove arrow syntax from regular function declaration
2020-10-02 17:34:47 +01:00
Daniel Jahodka
e065552784 Fix missing response_type=code in battlenet
Battle net's /oauth/authorize requires response_type query parameter. For authorization, this must be set to code.
2020-09-30 00:34:00 +01:00
Lluis Agusti
a3104a009c docs(readme): mention Typescript support 2020-09-25 17:38:23 +01:00
Iain Collins
e9eb6bc57e Add conditional to integration test workflow 2020-09-25 17:36:33 +01:00
Blocksmith
95e31b46af Update testing-with-cypress.md (#680)
misspelling depency cypress-social-logins
2020-09-24 13:52:50 +01:00
Jimmy Merritello
d5e70323f0 Update documentation for getProviders 2020-09-24 13:51:29 +01:00
S. Suzuki
4e4d1eac28 Update links to Slack documentation 2020-09-24 13:47:19 +01:00
Iain Collins
15316f069e Update FAQ to address automatic account linking (#702) 2020-09-24 13:45:45 +01:00
Wédney Yuri
e6995d21cd Update mongodb.md (#673)
Fix typo: MonogDB => MongoDB

Co-authored-by: Lori Karikari <lori.karikari@gmail.com>
2020-09-14 11:08:27 +02:00
Lance Hasson
433f096a63 jst -> jwt (#668)
Co-authored-by: Lori Karikari <lori.karikari@gmail.com>
2020-09-14 11:06:13 +02:00
Lance Hasson
9f487593fa add missing comma in type object (#669) 2020-09-14 11:04:16 +02:00
Matthieu
65caaa6c4c [providers]: Add Atlassian (#664)
* Atlassian provider

* rollback indentation

* fix alphabetical order

* add missing entry in menu sidebar
2020-09-12 19:49:21 +02:00
Iain Collins
0adfba8c5c Improve Puppeteer configuration (#658)
* Centralises configuration for Puppeteer used in tests to make it easier to maintain.
* Adds support for running tests on ARM, so we can use Raspberry Pi test runners off the cloud to get around block lists.
* Includes improved stealth mode to avoid detection which breaks integration tests.
2020-09-11 01:41:02 +01:00
Ugo Onali
2f0f738e2e Fix typo in Prisma adapter doc (#629)
Co-authored-by: Iain Collins <me@iaincollins.com>
2020-09-08 17:26:40 +01:00
Iain Collins
1777a87be3 Increase slow times for integration tests to 5 seconds
This seems like a reasonable theshold for these  integration tests.
2020-09-08 13:12:19 +01:00
Iain Collins
e94fd3b484 Fix typo in package.json 2020-09-08 12:55:26 +01:00
Iain Collins
3b40335202 Add full end-to-end integration tests
Full end-to-end integration tests for Twitter (OAuth 1) and GitHub (OAuth 2) using Puppeteer and Mocha.

This replaces Cypress tests due to issues with Cypress not being able to run tests against external URLs, which we need for our integration tests.

The integration test runner is hosted outside of GitHub Actions (it cannot be hosted by GitHub or on AWS due to IP access controls placed on sign in by providers like Twitter and GitHub) and so the integration tests may not pass if the test runner is offline. If this happens, tests can be re-run later when the test runner is available.

See Pull Request #641 for details.
2020-09-08 12:41:30 +01:00
John
6d63b74db9 Update faq.md 2020-09-07 11:20:55 +01:00
dependabot[bot]
eb26722833 Bump bl from 2.2.0 to 2.2.1
Bumps [bl](https://github.com/rvagg/bl) from 2.2.0 to 2.2.1.
- [Release notes](https://github.com/rvagg/bl/releases)
- [Commits](https://github.com/rvagg/bl/compare/v2.2.0...v2.2.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-09-04 11:29:48 +01:00
Iain Collins
4937047d19 Update GitHub workflows 2020-09-03 23:47:40 +01:00
Iain Collins
4305964864 Fix linting errors & ignore Cypress tests for now 2020-09-03 23:47:40 +01:00
Iain Collins
91d93fb8fd Integrate Cypress with Docker and GitHub Actions 2020-09-03 23:47:40 +01:00
Iain Collins
e2e28fcfd0 Update package-lock.json 2020-09-03 23:47:40 +01:00
Iain Collins
66afc69a57 Update workflow to run test app 2020-09-03 23:47:40 +01:00
Iain Collins
3046691119 Pass env vars through to test app
Using env vars for options we want to test means we can stop and restart the app to test different options without needing to rebuild the image.
2020-09-03 23:47:40 +01:00
Iain Collins
88b87a53ff Fix linter errors 2020-09-03 23:47:40 +01:00
Iain Collins
f1ae26efb6 Add Dockerfile to run build inside a container
Adds commands to start/rebuild/stop a Docker image of a sample Next.js app that loads the latest build of NextAuth.js from the current directory.

* `npm run test:app:start`
* `npm run test:app:rebuild`
* `npm run test:app:stop`

It is intended for further development for automated testing.

### About the build process

* The Dockerfile uses a multi-stage build process to optimise build performance, but the nature of the process is slow.
* Build times vary depending on computer speed and internet connection.
* Inital build times are slow (it may take 10 minutes or more).
* Subsequent builds on the same computer should be faster (1 minute or less).
* To ensure the package.json is valid, modules required in the next-auth package.json file are re-downloaded* on every build.
* A Docker compose file is used to allow us to extend the test app to run it again multiple databases.

Subsequent updates may look to improve performance, but it's important checks like checking package.json is valid and running the build in isolation are performed.
2020-09-03 23:47:40 +01:00
Matheus Calegaro
ba83685a86 docs(email): fix typo (#628) 2020-09-03 13:23:49 +01:00
Francis Udeji
d514733f13 Remove arrow syntax from examples on pages docs 2020-09-03 11:45:40 +01:00
Jefferson Bledsoe
15cd608b19 Add initial end-to-end tests (#298)
* Add cypress, testing-library/cypress and server dev helper to package dev dependencies

* Add initial signin test and placeholder cypress files

* Add initial signout tests

* Add initial verify-request test

* Move page-only tests into a 'pages' directory

* Add an invalid email signup workflow test

* Use home-page sign in button for email workflow

* Some tests to check that clicking the button takes the user to the correct OAuth page (warning: fragile!)

* Add a couple of npm scripts to make it easier to run/ developer e2e tests

Co-authored-by: Iain Collins <me@iaincollins.com>
2020-09-03 11:41:11 +01:00
Anish
08d7f5d778 Set Discord to Prompt = None (#605)
* Update discord.js

* Migrating from discordapp.com to discord.com
2020-09-01 10:42:10 +01:00
Francis Udeji
a2ba7e9229 Fix typo in options.md file 2020-09-01 10:40:42 +01:00
Iain Collins
7c71a15699 Fix getUserByProviderAccountId in Prisma adapter
Resolves #559
2020-09-01 10:39:38 +01:00
Mr D
351b804606 Adding 'nextauth' user as sysadmin. 2020-08-31 17:55:03 +01:00
Mr D
8f0501b7fe standardize the behaviour of the Docker images 2020-08-31 17:55:03 +01:00
Josh Pollock
73d21e66dd adjust URL for warnings in logger to match docs. (#593)
Fixes #592
2020-08-29 12:49:39 +02:00
youpy
6310311d52 Fix error when profile image is not set (#612) 2020-08-29 12:45:29 +02:00
Esteban Dalel R
d0caba1933 Update pages.md (#585) 2020-08-29 12:36:13 +02:00
Madusha Prasanjith
2f3291e48f [providers]: Add FusionAuth provider (#599)
* Add FusionAuth provider

* Fix issue with FusionAuth docs.
2020-08-29 12:35:13 +02:00
Ray Ma
43d8e3b894 [providers]: Updating Discord provider domain (#590)
Discord is migrating to discord.com, including their OAuth2 API routes. Support for the old domain, discordapp.com, will be dropped on 7 Nov 2020.

Note that the cdn.discordapp.com domain is unchanged. This is intentional, as the cdn domain will not be migrated due to technical restraints on Discord's side.
2020-08-29 12:32:59 +02:00
Nick Noble
5d4eb5d4e0 Fix some typos (#606) 2020-08-29 12:27:43 +02:00
S. Suzuki
7ccdec22cb Update slack.md (#617)
Fix code
2020-08-29 12:23:42 +02:00
Liam norris
2ea64045cb Documentation typos (#575)
Am I misreading this?  These changes should make the parameter to attribute mapping less ambiguous...
2020-08-13 14:34:13 +01:00
Iain Collins
daf97d298d Create SECURITY.md 2020-08-13 12:19:17 +01:00
Iain Collins
ababc7ecdb Merge branch 'main' of https://github.com/nextauthjs/next-auth into main 2020-08-13 12:00:05 +01:00
Iain Collins
33e72b2ae1 Update issue templates 2020-08-13 12:00:02 +01:00
Nico Domino
bf5716c674 Add: ldap auth tutorial example (#566)
* add: ldap auth tutorial example

* update: tutorials page list

* update: NEXTAUTH_SECRET

* Update tutorials.md

Co-authored-by: Iain Collins <me@iaincollins.com>
2020-08-11 13:33:44 +01:00
Iain Collins
c17a3b94f5 Update typeorm-custom-models.md 2020-08-11 12:38:36 +01:00
Iain Collins
19a9c313e0 Delete node.js.yml 2020-08-11 02:29:15 +01:00
Iain Collins
68043e65e4 Create test-build.yml 2020-08-11 02:28:50 +01:00
dependabot[bot]
a6ec60284d Bump prismjs from 1.20.0 to 1.21.0 in /www (#560)
Bumps [prismjs](https://github.com/PrismJS/prism) from 1.20.0 to 1.21.0.
- [Release notes](https://github.com/PrismJS/prism/releases)
- [Changelog](https://github.com/PrismJS/prism/blob/master/CHANGELOG.md)
- [Commits](https://github.com/PrismJS/prism/compare/v1.20.0...v1.21.0)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-08-11 01:11:54 +01:00
Nico Domino
ff79c4b95b add: warnings page (#567) 2020-08-11 01:11:18 +01:00
Iain Collins
9c4e41a4c6 Update callbacks.md 2020-08-10 16:16:56 +01:00
Bowen
07ef3d59c5 Update pages.md (#534)
Correct typos with getInitialProps

Co-authored-by: Iain Collins <me@iaincollins.com>
2020-08-06 01:07:10 +01:00
Eli José Carrasquero
4fe7162652 Update pages.md 2020-08-06 01:02:49 +01:00
Alex Cory
950a937633 Update callbacks.md
I was getting this error due to it being a string value.
```sh
[next-auth][error][jwt_session_error] JWTClaimInvalid: "auth_time" claim must be a JSON numeric value
    at isTimestamp (/Users/alex/code/trufans/node_modules/jose/lib/jwt/verify.js:24:11)
    at validateTypes (/Users/alex/code/trufans/node_modules/jose/lib/jwt/verify.js:159:3)
    at Object.module.exports [as verify] (/Users/alex/code/trufans/node_modules/jose/lib/jwt/verify.js:236:3)
    at Object.<anonymous> (/Users/alex/code/trufans/node_modules/next-auth/dist/lib/jwt.js:100:30)
    at Generator.next (<anonymous>)
    at asyncGeneratorStep (/Users/alex/code/trufans/node_modules/next-auth/dist/lib/jwt.js:22:103)
    at _next (/Users/alex/code/trufans/node_modules/next-auth/dist/lib/jwt.js:24:194)
    at /Users/alex/code/trufans/node_modules/next-auth/dist/lib/jwt.js:24:364
    at new Promise (<anonymous>)
    at Object.<anonymous> (/Users/alex/code/trufans/node_modules/next-auth/dist/lib/jwt.js:24:97) {
  code: 'ERR_JWT_CLAIM_INVALID',
  claim: 'auth_time',
  reason: 'invalid'
}
```
2020-08-06 01:01:57 +01:00
Nick Parks
1cc31def3e Update Google Provider example to be functional
Currently the Google Provider example will always fail due to checking for `email_verified` when the correct response from the server is `verified_email`

next-auth debug output for validation:

```
[next-auth][debug][profile_data] {
  id: 'XXXXXXX',
  email: 'nick@example',
  verified_email: true,
  name: 'Nick Parks',
  given_name: 'Nick',
  family_name: 'XXXX',
  picture: 'XXXX,
  locale: 'en',
  hd: 'example.com'
}
```
2020-08-06 01:00:45 +01:00
dependabot[bot]
92f53c532b Bump elliptic from 6.5.2 to 6.5.3 in /www
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.2 to 6.5.3.
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.2...v6.5.3)

Signed-off-by: dependabot[bot] <support@github.com>
2020-08-05 20:49:07 +01:00
Iain Collins
c6d6d9c002 Update documentation for Google 2020-07-31 13:06:22 +01:00
Iain Collins
85b859231c Update documentation 2020-07-31 10:17:07 +01:00
Iain Collins
ea093dc0fc Merge branch 'main' of github.com:iaincollins/next-auth into main 2020-07-31 10:01:04 +01:00
Iain Collins
cd61178f44 Bump version number to 3.1.0 2020-07-31 10:00:56 +01:00
Iain Collins
eb53219cbd Update website and documentation for mssql 2020-07-31 09:39:24 +01:00
Iain Collins
18d70ffbe9 Fix linting 2020-07-31 09:39:24 +01:00
Iain Collins
bdcf823d26 Prevent warning when using mssql conection string 2020-07-31 09:39:24 +01:00
Iain Collins
3aeba2aa09 Allow duplicate NULL email address in MSSQL 2020-07-31 09:39:24 +01:00
Mr D
0793e2c8d8 mssql: back to SnakeCaseNamingStrategy 2020-07-31 09:39:24 +01:00
Mr D
0f01279c91 mssqlTransform 2020-07-31 09:39:24 +01:00
Mr D
8fa9d00958 mssql support 2020-07-31 09:39:24 +01:00
Deeptesh Chagan
ab6ef8a19c re-sync entities on connection if changed in development 2020-07-31 00:26:21 +01:00
Iain Collins
8d68807bfe Remove node-jose dependency
This is an unused dependancy.

`jose` is used instead.
2020-07-30 23:33:29 +01:00
Ben Fox
35fc38c328 More explicit wording 2020-07-30 16:49:18 +01:00
Ben Fox
85eeda5755 Remove comments from config 2020-07-30 16:49:18 +01:00
Ben Fox
2e52c500a1 Add note to local environment setup on hot-reloading server files 2020-07-30 16:49:18 +01:00
Marc
5886f9bea8 Add Provider: Basecamp (#511)
Changes Include:
• Added Basecamp as a provider
• Added Basecamp provider to documentation
• Bumped Version to 3.0.1 in package-lock
2020-07-30 15:12:26 +02:00
Iain Collins
c497dcba26 Update options.md 2020-07-30 09:27:34 +01:00
Ben Silverman
493c45a864 Fix typo in typeorm-custom-models.md
Adapter expects `customModels` property, but doc uses `models`.
2020-07-29 23:10:16 +01:00
Iain Collins
b243b26a3d Update faq.md 2020-07-29 11:11:51 +01:00
Zeb Pykosz
1d0749970a fix(docs): remove arrow from function example 2020-07-29 09:23:02 +01:00
Iain Collins
3474d3e250 Add object type to updateSession save
This isn't technically required (it is working fine currently) but if we specify the type explicitly it should help avoid any problems in future that might be introduced with refactoring.
2020-07-28 09:41:06 +01:00
Iain Collins
a35c3a424c Fix problem updating user in TypeORM adapter
Resolves #493
2020-07-28 09:41:06 +01:00
Brady Caspar
6e65ba87a6 Removing localhost from link 2020-07-27 19:53:34 +01:00
Iain Collins
ae7247f14f Update options.md 2020-07-27 06:28:58 +01:00
Iain Collins
12a5d6b1f4 Fix linter errors 2020-07-27 05:32:01 +01:00
Iain Collins
19da066b04 Bump version number to 3.0.0 2020-07-27 05:22:15 +01:00
Iain Collins
8115a7c66c Add option to get raw JWT from getToken helper 2020-07-27 05:20:34 +01:00
Ben Fox
1ab029c60a Update local environment setup steps to fix npm link problems per issue #472 2020-07-27 05:20:34 +01:00
Iain Collins
d0dbacfc4b Display some error messages on the sign in page
Improves the UX by displaying some error messages on the sign in page
2020-07-27 05:20:34 +01:00
Diego Borges
f6b7e0aad9 Clarify script's intent 2020-07-27 05:20:34 +01:00
Iain Collins
4a23f88180 Add option to reject signIn/authorize callbacks 2020-07-27 05:20:34 +01:00
Iain Collins
f8dbd67a16 Improve homepage 2020-07-27 05:20:34 +01:00
Iain Collins
9406f8b332 Improve callbacks by adding User object to calls 2020-07-27 05:20:34 +01:00
Iain Collins
b0410ed9d4 Improvements to documentation 2020-07-27 05:20:34 +01:00
Iain Collins
8c0d0c4dea Update contributors page 2020-07-27 05:20:34 +01:00
Iain Collins
9446c26419 Improve documentation
* CSS Refactor to make it easier to maintain and UI more consistant.
* Misc small updates to docs
* Split off tutorials into a seperate menu item

I would like to add more tutorials and explainers, including those people have written on other sites.

This is a starting point for that.
2020-07-27 05:20:34 +01:00
Iain Collins
cdfa6008c7 Improve contast on links in docs 2020-07-27 05:20:34 +01:00
Iain Collins
b4bb8bda26 Hotfix for email_verified bug
Not being saved by default on sign in. Discovered in #477
2020-07-27 05:20:34 +01:00
Iain Collins
2c32504cc9 Bump version to 3.0.0-beta.21
Resolves #477 by fixing issue with last build being screwy
2020-07-27 05:20:34 +01:00
Iain Collins
bd188ff410 Improve examples and documentation 2020-07-27 05:20:34 +01:00
Iain Collins
89aedb1285 Fix contrast issues with sidebar menu 2020-07-27 05:20:34 +01:00
Iain Collins
db8c0820b6 Fix typo in docs 2020-07-27 05:20:34 +01:00
Iain Collins
d86464c822 Improve adapter documentation 2020-07-27 05:20:34 +01:00
Iain Collins
fcfeb0ce88 Update database documentation 2020-07-27 05:20:34 +01:00
Iain Collins
01e472912e Update MongoDB test (minor update) 2020-07-27 05:20:34 +01:00
Iain Collins
bbfeac408e Document SQL for MySQL and Postgres
These match up exactly with the models generated by TypeORM in v3 and are suitable for use with Prisma.
2020-07-27 05:20:34 +01:00
Iain Collins
364de1fc6c Update MySQL Model in TypeORM to use TIMESTAMP(6)
An issue with the defaults for MySQL used by TypeORM Adapter has been highlighted during testing parity with the Prisma Adapter.

This change ensures *all* TIMESTAMP columns use TIMESTAMP(6) to store six digits of precision after the number of seconds.

While this is level of precision is not required everywhere it ensures all timestamps in the default models use the same configuration (instead of a mix of values) and is consisitant with the level of precision on timestamps used Postgres.
2020-07-27 05:20:34 +01:00
Iain Collins
52af06cd33 Add Prisma client to optional peer dependencies
This doesn't technically do anything (except for the mongodb peer dependancy, which is invoked when a mongodb is being used) but it provides a way for us to indicate and track the last known good versions of database clients for NextAuth.js.
2020-07-27 05:20:34 +01:00
Iain Collins
8f472c5987 Prisma adapter refactor 2020-07-27 05:20:34 +01:00
Iain Collins
dcbd7a6703 Improve TypeORM adapter (#460)
* Uses `require_optional` and `peerOptionalDependencies` instead of dynamic import to resolve issue some users have experience with using using compliers/bundlers (especially on starter projects) that don't handle dynamic imports well.

This should (hopefully) also make it easier to support older versions of Internet Explorer by avoiding bundlers that choke on dynamic imports unless MongoDB is included as a dependancy (even though it's not code they need to compile).

We use `require_optional` to load `ObjectID` conditionally, if NextAuth.js is using MongoDB. This is also exactly how the MongoDB driver itself loads the ObjectID from the `bson/bson-ext` module.

Should resolve #251
    
* The default name for the TypeORM connection is now 'nextauth' instead of 'default'.

This should help people avoid problems with connection re-use when not using serverless (including in local development), especially if they are doing things with their default connection that differ from whats expected by NextAuth.js (like not using UTF-8 for encoding or UTC timezones).

* Now uses connection manager object from the connection, to allow a custom TypeORM connection name to be specified (resolves #459).
2020-07-27 05:20:34 +01:00
Iain Collins
e6fd4c2edc Improve sidebar apperance 2020-07-27 05:20:34 +01:00
Iain Collins
e19ca19a82 Add tips to provider documentation 2020-07-27 05:20:34 +01:00
Iain Collins
7b1b68e1c4 Fix typos in tutorial code 2020-07-27 05:20:34 +01:00
Iain Collins
56d848c868 Fix return type of sign in callback in docs 2020-07-27 05:20:34 +01:00
Timo Mämecke
100eece7a2 docs: Fix typos and wording in Client API (#455)
While reading through the new v3 docs, I spotted a few typos and some convoluted wording. Hence I directly fixed them.
2020-07-27 05:20:34 +01:00
Ty Lange-Smith
278ecc1e48 Explicitly set expires property for session on updateSession 2020-07-27 05:20:34 +01:00
Fredrik Pettersen
a3d379554b fix(prisma): Explicitly set fields to use when updating user (#449) 2020-07-27 05:20:34 +01:00
Iain Collins
983dd98a66 Fix typo in docs 2020-07-27 05:20:34 +01:00
Iain Collins
ca3f26b8d2 Update configuration docs 2020-07-27 05:20:34 +01:00
Iain Collins
d2a2352e9a Update callback docs
Addresses issue raised in comments on #429
2020-07-27 05:20:34 +01:00
Iain Collins
3043a9525a Update documentation for client methods 2020-07-27 05:20:34 +01:00
Iain Collins
e1c6632b6f Fix typo on homepage 2020-07-27 05:20:34 +01:00
Iain Collins
56e64e322e Move help menu on mobile (again)
So many UX issue with this. Hard to wrangle it given where it is semantically.

Hopefully Docusuarus beta will improve on it in a future release.
2020-07-27 05:20:34 +01:00
Iain Collins
cbd056f225 Fix typo in tutorial 2020-07-27 05:20:34 +01:00
Iain Collins
22ab66f9d8 Cosmetic improvements to docs 2020-07-27 05:20:34 +01:00
Iain Collins
3597733dae Improve FAQ documentation 2020-07-27 05:20:34 +01:00
Iain Collins
cb9ce69ba3 Update JWT questions in FAQ 2020-07-27 05:20:34 +01:00
Iain Collins
c19a79cbca Update database docs 2020-07-27 05:20:34 +01:00
Iain Collins
e97e090b65 Improve heading formatting on docs 2020-07-27 05:20:34 +01:00
Iain Collins
eda4a6d18b Add tutorial showing how to protect pages & routes 2020-07-27 05:20:34 +01:00
Iain Collins
94f66b60d8 Update documentation 2020-07-27 05:20:34 +01:00
Iain Collins
9a85e27c0c Update README 2020-07-27 05:20:34 +01:00
Iain Collins
7fb7e3d1bc Update documentation 2020-07-27 05:20:34 +01:00
Iain Collins
90066fdbec Update homepage copy and package description 2020-07-27 05:20:34 +01:00
Iain Collins
475f0e7b51 Update documentation 2020-07-27 05:20:34 +01:00
Iain Collins
a9131724d6 Update copy on homepage 2020-07-27 05:20:34 +01:00
Iain Collins
55bfb6d9dc Update docs 2020-07-27 05:20:34 +01:00
Iain Collins
af3da3abf8 Fix linting errors 2020-07-27 05:20:34 +01:00
Iain Collins
339d9f2d03 CSS tweaks 2020-07-27 05:20:34 +01:00
Iain Collins
a24fb8b380 Update JWT documentation and FAQs 2020-07-27 05:20:34 +01:00
Iain Collins
65319e3927 Update JWT defaults
* Set encryption: false  by default
 * Use 64 bit input for generated signing key
2020-07-27 05:20:34 +01:00
Iain Collins
19917972ef Review JWT comments; enable zip encoding 2020-07-27 05:20:34 +01:00
Iain Collins
c1b412814a WIP refactor JWT based on feedback 2020-07-27 05:20:34 +01:00
Iain Collins
53ea8407ea Remove default iss check (makes it optional) 2020-07-27 05:20:34 +01:00
Iain Collins
66f46e8cc7 Use URL to ensure secret is unique per instance 2020-07-27 05:20:34 +01:00
Iain Collins
fec69a21be Refactor JWT payload to use claims
Resovles #224
2020-07-27 05:20:34 +01:00
Iain Collins
505ebb8ae1 Clean up cruft in JWT class; add comments 2020-07-27 05:20:34 +01:00
Iain Collins
fb4381d8eb Implement JWE 2020-07-27 05:20:34 +01:00
Iain Collins
4772f5b571 WIP evaluating JWE solutions 2020-07-27 05:20:34 +01:00
Iain Collins
481db425d6 WIP Add JWE
Working implementation (with limited key length and no exp check) using node-jose from Cisco.

I want to compare it panva/jose which has more features before building it out.
2020-07-27 05:20:34 +01:00
Iain Collins
b886729bb8 Update version to 3.0.0-beta.18 2020-07-27 05:20:34 +01:00
Iain Collins
3a21a9c9f1 Enforce HMAC-256 on JWT
Now that we are going to expose the option to disable encryption on tokens we need to enforce the algorithm is valid (e.g. not 'None' or 'RSA') to prevent vultrabilties being exploited by tampering with the token.

Custom encode/decode routines can be specified if someone needs to use another algorithm.
2020-07-27 05:20:34 +01:00
Iain Collins
9e4a6fec59 Update JWT and session docs 2020-07-27 05:20:34 +01:00
Iain Collins
86921022dc Refactor JWT support
* Adds option to disable encryption
* Easy to add custom helper
* Removed getJWT helper
* Added getToken helper
* Helper does not fallback to trying non-prefixed cookie on HTTPS sites
* Supports bearer tokens in HTTP header on helper #397
2020-07-27 05:20:34 +01:00
Iain Collins
f57f11e6ff Bump version to 3.0.0-beta.17 2020-07-27 05:20:34 +01:00
Iain Collins
77ad6bd97e Update FAQ 2020-07-27 05:20:34 +01:00
Iain Collins
78c7041b3f Improve docs site on mobile 2020-07-27 05:20:34 +01:00
Iain Collins
99edead0f2 Add FAQ 2020-07-27 05:20:34 +01:00
Iain Collins
b0b3dbc0fc Add provider icons to homepage 2020-07-27 05:20:34 +01:00
Iain Collins
8b5af54e1c Update documentation 2020-07-27 05:20:34 +01:00
Iain Collins
0b5b04a22f Apply datetime transforms on properties in custom models
It makes sense to change this behaviour now we have a tutorial and have been testing this functionality.

Docs are being updated to reflect this change.
2020-07-27 05:20:34 +01:00
Iain Collins
890be1de0d Update email provider 2020-07-27 05:20:34 +01:00
Iain Collins
40ae747bc1 Add support for passing appContext to getCsrfToken
Requested in #345

getSession() already does this so seems reasonable to support it in getCsrfToken too.
2020-07-27 05:20:34 +01:00
Iain Collins
5a8022e9a2 Update homepage and refactor CSS
Making an attempt to clean up some of crusty CSS I've added.
2020-07-27 05:20:34 +01:00
Iain Collins
3e512b5cf5 Tweak CSS on homepage 2020-07-27 05:20:34 +01:00
Iain Collins
81071d7776 Update adapters documentation 2020-07-27 05:20:34 +01:00
Iain Collins
fc05140c1f Improve homepage 2020-07-27 05:20:34 +01:00
Iain Collins
faec6824ba Disable use of state on Apple provider
It is not supported by Apple ID.
2020-07-27 05:20:34 +01:00
Iain Collins
b91bfef16d Refactor and document state provider option 2020-07-27 05:20:34 +01:00
Iain Collins
ba9dc17e44 Update homepage 2020-07-27 05:20:34 +01:00
Iain Collins
c220bcc57e Update version to 3.0.0-beta.13 2020-07-27 05:20:34 +01:00
Iain Collins
f8a4808aa7 Fix bug with NEXTAUTH_URL parsing 2020-07-27 05:20:34 +01:00
ndo@$(hostname)
495d0a47db fix: marquee icons 2020-07-27 05:20:34 +01:00
Iain Collins
8cda627fe6 Update adapter documentation 2020-07-27 05:20:34 +01:00
Iain Collins
d0a0ccc6bc Update TypeORM tutorial 2020-07-27 05:20:34 +01:00
Iain Collins
999222cd97 Refactor to simplify site URL configuration
Includes some linter fixes
2020-07-27 05:20:34 +01:00
Iain Collins
72eb7fda3f Fix error merging branches for v3
Accidentally squashed a couple of lines in OAuth callback.
2020-07-27 05:20:34 +01:00
Iain Collins
3c94940ae6 Respect existing cookies on a request object
Unproven, but should fix #395 and improve middleware compatibility.
2020-07-27 05:20:34 +01:00
Iain Collins
1a8ed2aec1 Update version to 3.0.0-beta.9 2020-07-27 05:20:34 +01:00
Iain Collins
0e2321dc14 Update pages documentation 2020-07-27 05:20:34 +01:00
Iain Collins
78d1983f9a Update version to 3.0.0-beta.8 2020-07-27 05:20:34 +01:00
Iain Collins
5435df110c Fix linter errors 2020-07-27 05:20:34 +01:00
Iain Collins
32853b8d1e Update events, callbacks & pages to use camelCase
* This is a breaking change in v3
* Includes updated documentation
2020-07-27 05:20:34 +01:00
Iain Collins
9737b4c6ab Only invoke setTimeout client side
This should never be called server side, but just in case someone calls setOptions server side this prevents it from being invoked at all.
2020-07-27 05:20:34 +01:00
Iain Collins
e9bdd5c355 Improve client event handling
Improves how well syncing client state is handled and how well caching is leveraged.

Reduces network load, cpu load and memory footprint.
2020-07-27 05:20:34 +01:00
Iain Collins
9728567296 Improve client state syncing
* clientMaxAge now passive
* clientPollInterval added (works like old clientMaxAge)
* poll intervals uses timer (more efficent)
* updates state on window focus/blur
2020-07-27 05:20:34 +01:00
Iain Collins
ef6579a7ee Refactor redirect handling (WIP)
Passing a redirect function like this is a bit horrible, but is less horrible than before.
2020-07-27 05:20:34 +01:00
Iain Collins
8e810aa765 Fix linting errors and bug in getCsrfToken 2020-07-27 05:20:34 +01:00
Iain Collins
37596edf2b Improve CSRF security for all routes
Includes breaking changes for v3 and updates to documentation.

If using the client, the only required change should be setting the NEXTAUTH_URL environment variable.
2020-07-27 05:20:34 +01:00
tmayr@tmayr.com
229a3e430e Add tutorial on how to use custom typeorm models 2020-07-27 05:20:34 +01:00
Nico Domino
1d80f595c5 Add provider Vercel-style marquee to docs
* add: marquee provider section
* fix: lint
* update: adjust node sizes
* fix: window undefined SSR
* fix: path to imgs

Co-authored-by: Iain Collins <me@iaincollins.com>
2020-07-27 05:20:34 +01:00
Iain Collins
189a2c8e0e Fix for reading private key in Apple provider 2020-07-27 05:20:34 +01:00
Iain Collins
97096fb811 Fix linter errors and add comments 2020-07-27 05:20:34 +01:00
Gerald Nolan
e8b75e40b1 feat: Added UserData to ProfileData after return from Apple to get user name on first sign in 2020-07-27 05:20:34 +01:00
Iain Collins
d41c38e002 Add support for hitting cancel if using token id
When using a provider that uses Token ID option (like Apple) a user hitting cancel with no longer cause the app to crash.

Users who do this will now be taken back to the sign in page.

This was already working for other providers that didn't use this option but wasn't supported for providers that did use it.
2020-07-27 05:20:34 +01:00
Fredrik Pettersen
966bc7b433 docs(prisma): Add note about model names and set email to optional 2020-07-27 05:20:34 +01:00
Fredrik Pettersen
e7b06d3362 fix(prisma): Make sure provider id is a string 2020-07-27 05:20:34 +01:00
Fredrik Pettersen
d5d8eb8d7c feat(adapter): Add opinionated prisma adapter 2020-07-27 05:20:34 +01:00
nyedidikeke
8ec07f0224 Add LinkedIn provider 2020-07-27 03:22:43 +01:00
dan-kwiat
558536db1e docs(options): remove duplicate arrow 2020-07-23 10:30:50 +01:00
Lori Karikari
0c2fe054d1 [Docs] fix small typo 2020-07-20 17:46:48 +02:00
dependabot[bot]
b5a69fd787 Bump lodash from 4.17.15 to 4.17.19 in /www
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-17 12:31:11 +01:00
dependabot[bot]
9b29ed347d Bump lodash from 4.17.15 to 4.17.19
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-17 12:06:12 +01:00
Jibin George
c5c4ff4d51 Fix typo 2020-07-15 13:30:01 +01:00
styxlab
008b1a9f8d fallback if user.name is null (#424)
Co-authored-by: cws <cws@home.fritz.box>
2020-07-12 18:52:58 +02:00
Iain Collins
4a6f153aa6 Update question.md 2020-07-10 16:14:10 +01:00
Iain Collins
9eccc78e3a Update feature_request.md 2020-07-10 16:13:12 +01:00
Iain Collins
09938cc368 Update bug_report.md 2020-07-10 16:12:59 +01:00
Jake Harding
5db05e1031 Use id_str for reading in Twitter user ID 2020-07-09 11:07:18 +01:00
Ryuichi Okumura
f6ba72b4fa Fix wrong syntax in Apple provider example
It fixes a syntax error in Apple provider example code.
2020-07-07 19:38:44 +01:00
Jonathan Dean
bf7e555cfa Fix typo: curly brace should be square bracket 2020-07-07 19:35:36 +01:00
William Duplenne
26abc70a99 Add Spotify provider
Add Spotify to the sidebar
2020-07-07 08:35:55 +01:00
tmayr@tmayr.com
d38cd54dee Fix using merged models var instead of user provided models in props
Merged models were being overwritten by user provided models which
might come incomplete
2020-07-06 20:51:36 +01:00
felipe muner
200690ad6c Update pages.md (#389) 2020-07-05 10:58:09 +02:00
Nico Domino
52b69a6d68 Add: testing with cypress docs (#357)
* add: testing with cypress docs

* update: add tutorials group
2020-07-04 21:47:00 +02:00
Theo Gravity
f319b2af05 Fix reading of req in getSession() 2020-07-04 01:27:03 +01:00
Iain Collins
b80a005733 Update callbacks.md 2020-07-01 17:16:15 +01:00
Arunoda Susiripala
34936aecc0 Pass maxAge to the 'jwt.getJwt' function
With that it can pass that to the `jwt.decode` method. This will be useful, if we are using a custom `maxAge` value.
2020-07-01 10:46:35 +01:00
Tom Astley
b021f26f03 Update client.md (#370)
Fixed small syntax error on line 219 in the signout example. Added a '('
2020-06-30 11:56:47 +02:00
Iain Collins
fcf7197120 Fix indentation in example (trivial) 2020-06-30 10:47:29 +01:00
Iain Collins
bec8d8dff1 Update callbacks.md 2020-06-30 09:30:01 +01:00
Iain Collins
781c63e966 Update pages.md 2020-06-30 08:52:58 +01:00
Iain Collins
2da1883726 Fix typo in email.md 2020-06-30 08:52:18 +01:00
Thibaut Patel
83ffac7cd2 Fix missing closing tag in docs 2020-06-28 21:09:11 +01:00
Iain Collins
6198903cdf Update copy on homepage 2020-06-28 14:49:30 +01:00
Iain Collins
bd98f8188c Update introduction.md 2020-06-28 14:40:29 +01:00
Iain Collins
73ea402b1c Compress images
* Forgot to do this when I added them.
* May add a script to compress them on commit.
* Adding new binary blobs bloats repo over time, we can always purge old assets if it's a problem.
* The social card is slightly too bright to compensate for PNG gamma interpretation not being consistant between browsers (too bright in some looks better than too dark in others). Maybe it should be a JPEG.
2020-06-28 14:03:23 +01:00
Iain Collins
4284684a3b Improve apperance of documentation on mobile 2020-06-28 10:51:51 +01:00
Iain Collins
b5d522410a Update CSS
Additional changes following testing on mobile.
2020-06-28 10:51:51 +01:00
Iain Collins
284cb8e2a7 Improve website CSS on mobile and dark mode 2020-06-28 10:51:51 +01:00
Iain Collins
079aab2315 Improve mobile documentation secondary menu
Still not great, but somewhat better.
2020-06-28 10:51:51 +01:00
Iain Collins
645ee382cf Improve documentation structure / sidebar 2020-06-28 10:51:51 +01:00
Iain Collins
e947a772ce Website cosmetic refresh
Addressing quality issues with assets and layout
2020-06-28 10:51:51 +01:00
Iain Collins
5d63adf7df Update errors.md 2020-06-26 17:22:18 +01:00
Iain Collins
f1a872f861 Update errors.md 2020-06-26 16:39:48 +01:00
Iain Collins
02b1d02f09 Update cognito.md 2020-06-26 12:56:53 +01:00
Iain Collins
a3479b3503 Bump version to 2.2.0 2020-06-25 22:50:50 +01:00
Iain Collins
740535a8f2 Add support for mongodb+srv:// URLs 2020-06-25 22:49:56 +01:00
Iain Collins
19ed684a52 Add HTTP status codes to error pages 2020-06-25 22:47:08 +01:00
Iain Collins
bd72949fa7 Fix typos in docs 2020-06-25 22:44:31 +01:00
Iain Collins
a277cd5b0c Fix linter errors 2020-06-25 22:38:07 +01:00
Iain Collins
fd6e7e94df Update version to 2.2.0-beta.0
*  New email template
*  New callback error handling

I anticipate adding more changes and a new beta before we release 2.2.0 but wanted to test these changes.
2020-06-25 18:04:35 +01:00
Iain Collins
2f6403478d Improve apperance of sign in email
* Prevents links from being turned into hyperlinks by email clients
* Improve UI with a primary action button and better font sizing and spacing in the template
* Adds email address to body to clear indicate who they will be signing in as

While not exactly a bug in NextAuth.js it does resolve #331
2020-06-25 17:43:35 +01:00
Iain Collins
a4372ffc61 Handle OAuth sign in cancellations gracefully
Currently if a user hits a cancel button after selecting the option to sign in with an OAuth provider an error is displayed.

This error is only triggered in production.

This update refactors error handling so that in both dev and prod modes, the user is directed back to the sign in page.

Not all OAuth providers have a cancel button on their sign in page (e.g. Twitter does, Google doesn't).

The oAuthCallback has been slightly refactored to make debugging easier. It is still pretty horrible, but i don't want to do major refactoring of it until we have tests we trust in place.
2020-06-25 17:37:17 +01:00
Iain Collins
d6ce92811e Update documentation for credentials provider
There was a typo in the documentation and some of the documentation was outdated.
2020-06-25 02:35:43 +01:00
Sreetam Das
e5aecdf315 fix link in client API docs (#323) 2020-06-24 12:33:27 +02:00
Iain Collins
6d1c457a75 Add action to run npm test on new pull requests 2020-06-24 02:23:48 +01:00
Iain Collins
6e16aec6d3 Update test to run linter
The action to publish to NPM fails as it can't run the DB test yet so removing that.

Changing the test to run the linter instead so it does something (e.g. catch the worst syntax errors).
2020-06-24 02:13:02 +01:00
Iain Collins
f899d7bb04 Update version to 2.1.0 2020-06-24 01:57:28 +01:00
Iain Collins
e36646ce7f Update docs formatting 2020-06-24 01:53:49 +01:00
Iain Collins
f3d36a74c9 Update provider documentation 2020-06-24 01:53:49 +01:00
Iain Collins
4e11c9c36e Fix linter errors 2020-06-24 01:26:07 +01:00
Iain Collins
0a7ac36584 Update client documentation 2020-06-24 01:26:07 +01:00
Iain Collins
fc4850f354 Update documentation for client and options 2020-06-24 01:26:07 +01:00
Iain Collins
6e9a8d2074 Update beta version 2020-06-24 01:26:07 +01:00
Iain Collins
c712d7da07 Remove base URL cookie; no longer needed 2020-06-24 01:26:07 +01:00
Iain Collins
5183181d1c Refactor client to allow provider to be passed options 2020-06-24 01:26:07 +01:00
Iain Collins
b024f89ba8 Update client max age documentation 2020-06-24 01:26:07 +01:00
Iain Collins
fbbe516b9a Refactor client to take configuraiton from _app.js 2020-06-24 01:26:07 +01:00
Iain Collins
d48a3fd948 Rename internal debug env var for consistancy
This is a naughty global used server side to more easily facilitate debugging.
2020-06-24 01:26:07 +01:00
ndo@$(hostname)
86f0c53bd3 add: npm publish workflow 2020-06-24 01:19:51 +01:00
Onur
7f6cc2048b Minor typo fix 2020-06-24 00:00:09 +01:00
Lori Karikari
b2829f6384 added missing comma to homepage example 2020-06-23 18:33:59 +02:00
Iain Collins
67c5041860 Remove console.log from auth0 provider 2020-06-23 13:00:25 +01:00
Lori Karikari
33df9e3132 updated Cognito (#307) 2020-06-23 11:40:18 +02:00
Lori Karikari
602bc28a45 Add new OAuth providers (#221) 2020-06-23 11:31:18 +02:00
jose-donato
5a7a494701 typo 2020-06-23 00:34:44 +01:00
Iain Collins
9fa82cedbd Fix Auth0 provider
Resolves #301
2020-06-23 00:13:55 +01:00
Iain Collins
b0408284b8 Tweak homepage CSS 2020-06-21 17:30:43 +01:00
309 changed files with 37771 additions and 10277 deletions

4
.dockerignore Normal file
View File

@@ -0,0 +1,4 @@
# Exclude directories we don't need from Docker context to improve build time
node_modules
www
src

15
.env.example Normal file
View File

@@ -0,0 +1,15 @@
# Rename file to .env and populate values
# to be able to run tests
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_TWITTER_ID=
NEXTAUTH_TWITTER_SECRET=
NEXTAUTH_TWITTER_USERNAME=
NEXTAUTH_TWITTER_PASSWORD=
NEXTAUTH_GITHUB_ID=
NEXTAUTH_GITHUB_SECRET=
NEXTAUTH_GITHUB_USERNAME=
NEXTAUTH_GITHUB_PASSWORD=
NEXTAUTH_GOOGLE_ID=
NEXTAUTH_GOOGLE_SECRET=
NEXTAUTH_GOOGLE_USERNAME=
NEXTAUTH_GOOGLE_PASSWORD=

33
.env.local.example Normal file
View File

@@ -0,0 +1,33 @@
# Rename file to .env.local (or .env) and populate values
# to be able to run the dev app
NEXTAUTH_URL=http://localhost:3000
# You can use `openssl rand -hex 32` or
# https://generate-secret.now.sh/32 to generate a secret.
# Note: Changing a secret may invalidate existing sessions
# and/or verificaion tokens.
SECRET=
AUTH0_ID=
AUTH0_DOMAIN=
AUTH0_SECRET=
GITHUB_ID=
GITHUB_SECRET=
TWITTER_ID=
TWITTER_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
# You can use any of these as the "DATABASE_URL" for
# databases started with Docker using `npm run db:start`.
# Note: If using with Prisma adapter, you need to use a `.env`
# file rather than a `.env.local` file to configure env vars.
# Postgres: DATABASE_URL=postgres://nextauth:password@127.0.0.1:5432/nextauth?synchronize=true
# MySQL: DATABASE_URL=mysql://nextauth:password@127.0.0.1:3306/nextauth?synchronize=true
# MongoDB: DATABASE_URL=mongodb://nextauth:password@127.0.0.1:27017/nextauth?synchronize=true
DATABASE_URL=

View File

@@ -1,28 +1,30 @@
---
name: Bug report
about: Report a defect with the software
about: Report a defect with NextAuth.js
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the defect is.
A clear and concise description of the bug in NextAuth.js.
**To Reproduce**
Do not report bugs with your own project here, ask from help by raising a question instead - this helps us a lot with administration overhead.
**Steps to reproduce**
Steps to reproduce the behavior.
Include example code (or link to public repository) which can be used to reproduce the behaviour.
Include a link to public repository which can be used to reproduce the behaviour.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots or error logs**
If applicable, add screenshots or error logs to help explain the problem.
If applicable add screenshots or error logs to help explain the problem.
**Additional context**
Add any other context about the problem here.
**Documentation feedback**
**Feedback**
*Documentation refers to searching through [online documentation](https://next-auth.js.org), code comments and issue history. The example project refers to [next-auth-example](https://github.com/iaincollins/next-auth-example).*
* [ ] Found the documentation helpful

1
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1 @@
blank_issues_enabled: false

View File

@@ -1,17 +1,15 @@
---
name: Feature request
about: Suggest an idea for this project
about: Suggest an idea for NextAuth.js
labels: enhancement
assignees: ''
---
*Please stick to one distinct feature request per issue where possible and raise additional feature quests as separate issues. Try to avoid adding feature requests to existing issues in the comments of issues raised by other users.*
**Summary of proposed feature**
A clear and concise description of the feature being proposed.
**Purpose of proposed feature**
A clear and concise description description of why this feature is necessary and what problems it solves.
A clear and concise description of why this feature is necessary and what problems it solves.
**Detail about proposed feature**
A detailed description of how the proposal might work (if you have one).

View File

@@ -1,19 +1,21 @@
---
name: Question
about: Ask for information or support
about: Ask a question about NextAuth.js or for help using it
labels: question
assignees: ''
---
*Please refer to the [documentation](https://next-auth.js.org/getting-started/introduction), the [example project](https://github.com/iaincollins/next-auth-example) and existing issues before creating a new issue.*
<!-- NOTE: Questions will be converted to Discussions. You can find them at https://github.com/nextauthjs/next-auth/discussions! -->
**Your question**
A clear and concise question.
<!-- A clear and concise question. -->
**What are you trying to do**
A description of what you are trying to do.
<!-- A description of what you are trying to do, for context. -->
**Documentation feedback**
**Reproduction**
<!-- If your question is code related, adding a reproduction to your use case can greatly reduce the time it takes us to figure out how to better help you. -->
**Feedback**
*Documentation refers to searching through [online documentation](https://next-auth.js.org), code comments and issue history. The example project refers to [next-auth-example](https://github.com/iaincollins/next-auth-example).*
* [ ] Found the documentation helpful

41
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,41 @@
<!--
Thanks for your interest in the project. Bugs filed and PRs submitted are appreciated!
Please make sure that you are familiar with and follow the Code of Conduct for
this project (found in the CODE_OF_CONDUCT.md file).
Also, please make sure you're familiar with and follow the instructions in the
contributing guidelines (found in the CONTRIBUTING.md file).
If you're new to contributing to open source projects, you might find this free
video course helpful: https://kcd.im/pull-request
Please fill out the information below to expedite the review and (hopefully)
merge of your pull request!
-->
<!-- What changes are being made? (What feature/bug is being fixed here?) -->
**What**:
<!-- Why are these changes necessary? -->
**Why**:
<!-- How were these changes implemented? -->
**How**:
<!-- Have you done all of these things? -->
**Checklist**:
<!-- add "N/A" to the end of each line that's irrelevant to your changes -->
<!-- to check an item, place an "x" in the box like so: "- [x] Documentation" -->
- [ ] Documentation
- [ ] Tests
- [ ] Ready to be merged
<!-- In your opinion, is this ready to be merged as soon as it's reviewed? -->
<!-- feel free to add additional comments -->

35
.github/labeler.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
test:
- test/**/*
documentation:
- www/**/*
- ./**/*.md
providers:
- src/providers/**/*
- www/docs/configuration/providers.md
- test/integration/**/*
adapters:
- src/adapters/**/*
- www/docs/schemas/adapters.md
databases:
- www/docs/schemas/*.md
- test/docker/databases/**/*
- www/docs/configuration/databases.md
- test/fixtures/**/*
core:
- src/**/*
style:
- src/css/**/*
client:
- src/client/**/*
- www/docs/getting-started/client.md
pages:
- src/server/pages/**/*
- www/docs/configuration/pages.md

25
.github/stale.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- security
- priority
- bug
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
Hi there! It looks like this issue hasn't had any activity for a while.
It will be closed if no further activity occurs. If you think your issue
is still relevant, feel free to comment on it to keep it open. (Read more at #912)
Thanks!
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: >
Hi there! It looks like this issue hasn't had any activity for a while.
To keep things tidy, I am going to close this issue for now.
If you think your issue is still relevant, just leave a comment
and I will reopen it. (Read more at #912)
Thanks!

32
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
# Simple check that the build is valid and no linting errors.
# Currently is run as a seperate workflow as it's fast to fail.
name: Lint/Build
on:
push:
branches:
- main
- beta
- next
pull_request:
branches:
- main
- beta
- next
jobs:
lint-and-build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [10, 12, 14]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
uses: bahmutov/npm-install@v1
- run: npm run lint
- run: npm run build

67
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@@ -0,0 +1,67 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ main, beta, next ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ main ]
schedule:
- cron: '43 17 * * 2'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

57
.github/workflows/integration.yml vendored Normal file
View File

@@ -0,0 +1,57 @@
name: Integration Test
on:
push:
branches:
- main
- beta
- next
pull_request:
jobs:
test:
# Only run tests integration against Pull Requests from branches in
# this repository. We do this as integration tests require access to
# secrets in GitHub and they are not exposed to tests run against
# forks (for security reasons), so integration test against
# Pull Requests from external repos just fail and generate noise.
if: github.event.pull_request.head.repo.full_name == github.repository
# We use self-hosted runners as cloud based runnners (e.g. AWS, GPC)
# fail due to IP Address checks done by providers, which enforce
# CAPTCHA checks on login request from cloud compute IP addresses to
# prevent abuse.
runs-on: self-hosted
# Target time is under 5 minutes to run all tests. If it takes longer than
# 10 minutes should look at running tests in parallel. No individual flow
# should take longer than 5 minutes to build and run.
timeout-minutes: 10
strategy:
matrix:
node-version: [10, 12, 14]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
uses: bahmutov/npm-install@v1
# Run tests (build library, build + start test app in Docker, run tests)
- run: npm test
# TODO Tests should exit out if env vars not set (currently hangs)
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
NEXTAUTH_TWITTER_ID: ${{secrets.NEXTAUTH_TWITTER_ID}}
NEXTAUTH_TWITTER_SECRET: ${{secrets.NEXTAUTH_TWITTER_SECRET}}
NEXTAUTH_TWITTER_USERNAME: ${{secrets.NEXTAUTH_TWITTER_USERNAME}}
NEXTAUTH_TWITTER_PASSWORD: ${{secrets.NEXTAUTH_TWITTER_PASSWORD}}
NEXTAUTH_GITHUB_ID: ${{secrets.NEXTAUTH_GITHUB_ID}}
NEXTAUTH_GITHUB_SECRET: ${{secrets.NEXTAUTH_GITHUB_SECRET}}
NEXTAUTH_GITHUB_USERNAME: ${{secrets.NEXTAUTH_GITHUB_USERNAME}}
NEXTAUTH_GITHUB_PASSWORD: ${{secrets.NEXTAUTH_GITHUB_PASSWORD}}

11
.github/workflows/labeler.yml vendored Normal file
View File

@@ -0,0 +1,11 @@
name: "Pull Request Labeler"
on:
- pull_request_target
jobs:
triage:
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@main
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"

27
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
name: Release
on:
push:
branches:
- 'main'
- 'beta'
- 'next'
- '3.x'
pull_request:
jobs:
release:
name: 'Release'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v1
with:
node-version: 14
- name: Install dependencies
uses: bahmutov/npm-install@v1
- run: npm run build
- run: npx semantic-release@17
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
NPM_TOKEN: ${{secrets.NPM_TOKEN}}

25
.github/workflows/types.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: Types
on:
push:
branches:
- main
- beta
- next
pull_request:
branches:
- main
- beta
- next
jobs:
lint-and-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v1
- name: Install dependencies
uses: bahmutov/npm-install@v1
- name: Check types
run: npm run test:types

48
.gitignore vendored
View File

@@ -1,20 +1,7 @@
.next
.env
.vscode
node_modules
dist
.DS_Store# Dependencies
/node_modules
# Production
/build
# Generated files
.docusaurus
.cache-loader
# Misc
.DS_Store
.env
.env.local
.env.development.local
.env.test.local
@@ -24,5 +11,32 @@ npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Docusaurus
www/build
yarn.lock
# Dependencies
node_modules
# Build dirs
.next
/build
/dist
/www/build
# Generated files
.docusaurus
.cache-loader
.next
www/providers.json
# VS
/.vs/slnx.sqlite-journal
/.vs/slnx.sqlite
/.vs
.vscode
# GitHub Actions runner
/actions-runner
/_work
# Prisma migrations
/prisma/migrations

5
CHANGELOG.md Normal file
View File

@@ -0,0 +1,5 @@
# CHANGELOG
The changelog is automatically updated using
[semantic-release](https://github.com/semantic-release/semantic-release). You
can see it on the [releases page](../../releases).

View File

@@ -2,107 +2,117 @@
Contributions and feedback on your experience of using this software are welcome.
This includes bug reports, feature requests, ideas, pull requests and examples of how you have used this software.
This includes bug reports, feature requests, ideas, pull requests, and examples of how you have used this software.
Please see the [Code of Conduct](CODE_OF_CONDUCT.md) and follow any templates configured in GitHub when reporting bugs, requesting enhancements or contributing code.
Please see the [Code of Conduct](CODE_OF_CONDUCT.md) and follow any templates configured in GitHub when reporting bugs, requesting enhancements, or contributing code.
Please raise any significant new functionality or breaking change an issue for discussion before raising a Pull Request for it.
## Pull Requests
## For contributors
* The latest changes are always in `main`
* Pull Requests should be raised for larger changes
* Pull Requests do not need approval before merging for those with contributor access (it's just helpful to have them to track changes)
* Rebasing in Pull Requests is prefered to keep a clean commit history (see below)
* Running `npm run lint:fix` before committing can make resolving conflicts easier, but is not required
* Merge commits (and pushing merge commits to `main`) are disabled in this repo; but commits in PR can be squashed so this is not a blocker
* Pushing directly to main should ideally be reserved for minor updates (e.g. correcting typos) or small single-commit fixes
Anyone can be a contributor. Either you found a typo, or you have an awesome feature request you could implement, we encourage you to create a Pull Request.
### Pull Requests
## Rebasing
* The latest changes are always in `main`, so please make your Pull Request against that branch.
* Pull Requests should be raised for any change
* Pull Requests need approval of a [core contributor](https://next-auth.js.org/contributors#core-team) before merging
* We use ESLint/Prettier for linting/formatting, so please run `npm run lint:fix` before committing to make resolving conflicts easier (VSCode users, check out [this ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) and [this Prettier extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) to fix lint and formatting issues in development)
* We encourage you to test your changes, and if you have the opportunity, please make those tests part of the Pull Request
* If you add new functionality, please provide the corresponding documentation as well and make it part of the Pull Request
*If you don't rebase and end up with merge commits in a PR then it's not a blocker, we can alway squash the commits when merging!*
### Setting up local environment
If you create a branch and there are conflicting updates in the `main` branch, you can resolve them by rebasing from a check out of your branch:
git fetch
git rebase origin/main
If there are any conflicts, you can resolve them and stage the files, then run:
git rebase --continue
*If there are a lot of changes you may be prompted to step more than once.*
When the rebase is complete (i.e. there are no more conflicts) you should push your changes to your branch before doing anyhing else:
git push --force-with-lease
You should see that any conflicts in your PR are now resolved. You can review changes to make sure it contains changes you intended to make.
*If you accidentally sync before pushing, it will trigger a merge. Uou can use `git merge --abort` to undo the merge.*
You can use `npm run lint:fix` to automatically apply Standard JS rules to resolve formatting differences (tabs vs spaces, line endings, etc).
## Setting up local environment
A quick and dirty guide on how to setup *next-auth* locally to work on it and test out any changes:
A quick guide on how to setup *next-auth* locally to work on it and test out any changes:
1. Clone the repo:
```sh
git clone git@github.com:nextauthjs/next-auth.git
cd next-auth
```
git clone git@github.com:iaincollins/next-auth.git
cd next-auth/
2. Install packages:
```sh
npm i
```
2. Install packages and run the build command:
3. Populate `.env.local`:
Copy `.env.local.example` to `.env.local`, and add your env variables for each provider you want to test.
npm i
npm run build
> NOTE: You can add any environment variables to .env.local that you would like to use in your dev app.
> You can find the next-auth config under`pages/api/auth/[...nextauth].js`.
3. Link React between the repo and the version installed in your project:
1. Start the dev application/server and CSS watching:
```sh
npm run dev
```
npm link ../your-application/node_modules/react
Your dev application will be available on ```http://localhost:3000```
*This is an annoying step and not obvious, but is needed because of how React has been written (otherwise React crashes when you try to use the `useSession()` hook in your project).*
4. Finally link your project back to your local copy of next auth:
cd ../your-application
npm link ../next-auth
That's it!
Notes: You may need to repeat both `npm link` steps if you install / update additional dependancies with `npm i`.
That's it! 🎉
If you need an example project to link to, you can use [next-auth-example](https://github.com/iaincollins/next-auth-example).
### Hot reloading
#### Hot reloading
You might find it helpful to use the `npm run watch` command in the next-auth project, which will automatically (and silently) rebuild JS and CSS files as you edit them.
When running `npm run dev`, you start a Next.js dev server on `http://localhost:3000`, which includes hot reloading out of the box. Make changes on any of the files in `src` and see the changes immediately.
cd next-auth/
npm run watch
>NOTE: When working on CSS, you will need to manually refresh the page after changes. (Improving this through a PR is very welcome!)
If you are working on `next-auth/src/client/index.js` hot reloading will work as normal in your Next.js app.
#### Databases
However if you are working on anything else (e.g. `next-auth/src/server/*` etc) then you will need to *stop and start* your app for changes to apply as **Next.js will not hot reload those changes**.
Included is a Docker Compose file that starts up MySQL, Postgres, and MongoDB databases on localhost.
### Databases
Included is a Docker Compose file that starts up MySQL, Postgres and MongoDB databases on localhost.
It will use port 3306, 5432 and 27017 on localhost respectively; it will not work if are running existing databases on localhost.
It will use port `3306`, `5432`, and `27017` on localhost respectively; please make sure those ports are not used by other services on localhost.
You can start them with `npm run db:start` and stop them with `npm run db:stop`.
You will need Docker installed to be able to start / stop the databases.
You will need Docker and Docker Compose installed to be able to start / stop the databases.
When stop the databases, it will reset their contents.
When stopping the databases, it will reset their contents.
### Testing
#### Testing
Tests can be run with `npm run test`.
Automated tests are currently crude and limited in functionality, but improvements are in development.
Currently to run tests you need to first have started local test databases (e.g. using `npm run db:start`).
Currently, to run tests you need to first have started local test databases (e.g. using `npm run db:start`).
The databases can take a few seconds to start up, so you might need to give it a minute before running the tests.
## For maintainers
We use [semantic-release](https://github.com/semantic-release/semantic-release) together with [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0) to automate releases. This makes the maintainenance process easier and less error-prone to human error. Please study the "Conventional Commits" site to understand how to write a good commit message.
When accepting Pull Requests, make sure the following:
* Use "Squash and merge"
* Make sure you merge contributor PRs into `main`
* Rewrite the commit message to conform to the `Conventional Commits` style. Check the "Recommended Scopes" section for further advice.
* Optionally link issues the PR will resolve (You can add "close" in front of the issue numbers to close the issues automatically, when the PR is merged. `semantic-release` will also comment back to connected issues and PRs, notifying the users that a feature is added/bug fixed, etc.)
### Recommended Scopes
A typical conventional commit looks like this:
```
type(scope): title
body
```
Scope is the part that will help groupping the different commit types in the release notes.
Some recommened scopes are:
- **provider** - Provider related changes. (eg.: "feat(provider): add X provider", "docs(provider): fix typo in X documentation"
- **adapter** - Adapter related changes. (eg.: "feat(adapter): add X provider", "docs(provider): fix typo in X documentation"
- **db** - Database related changes. (eg.: "feat(db): add X database", "docs(db): fix typo in X documentation"
- **deps** - Adding/removing/updating a dependency (eg.: "chore(deps): add X")
> NOTE: If you are not sure which scope to use, you can simply ignore it. (eg.: "feat: add something"). Adding the correct type already helps a lot when analyzing the commit messages.
### Skipping a release
Every commit that contains [skip release] or [release skip] in their message will be excluded from the commit analysis and won't participate in the release type determination. This is useful, if the PR being merged should not trigger a new `npm` release.

30
Dockerfile Normal file
View File

@@ -0,0 +1,30 @@
# Multi stage build to allow us to improve performance
FROM node:10-alpine as base
WORKDIR /usr/src/app
# Install basic dependancies (Next.js, React)
COPY test/docker/app/package*.json ./
RUN npm ci --only=production
FROM node:10-alpine as app
COPY --from=base /usr/src/app ./
# Copy last build of library into the image and install dependences for it.
# This ensures the build is valid and package.json contains everything needed
# to actually run the library.
# Note: You must run `npm run build` first to build a release of the library
RUN mkdir -p node_modules/next-auth
# Copy all entrypoints for the library (if creating a new one, add it here)
COPY index.js providers.js adapters.js client.js jwt.js node_modules/next-auth/
# Copy the dist dir
COPY dist node_modules/next-auth/dist
# Copy the package.json for the library and install it's dependences
COPY package*.json node_modules/next-auth/
RUN cd node_modules/next-auth/ && npm ci --only=production
# Copy test pages across
COPY test/docker/app/pages ./pages
RUN npm run build
CMD [ "npm", "start" ]

3
FUNDING.yml Normal file
View File

@@ -0,0 +1,3 @@
# https://docs.github.com/en/github/administering-a-repository/displaying-a-sponsor-button-in-your-repository
github: [balazsorban44]

View File

@@ -1,6 +1,6 @@
ISC License
Copyright (c) 2018-2020, Iain Collins
Copyright (c) 2018-2021, Iain Collins
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above

129
README.md
View File

@@ -1,4 +1,33 @@
# NextAuth.js
<p align="center">
<br/>
<a href="https://next-auth.js.org" target="_blank"><img width="150px" src="https://next-auth.js.org/img/logo/logo-sm.png" /></a>
<h3 align="center">NextAuth.js</h3>
<p align="center">Authentication for Next.js</p>
<p align="center">
Open Source. Full Stack. Own Your Data.
</p>
<p align="center" style="align: center;">
<a href="https://github.com/nextauthjs/next-auth/actions?query=workflow%3ARelease">
<img src="https://github.com/nextauthjs/next-auth/workflows/Release/badge.svg" alt="Release" />
</a>
<a href="https://github.com/nextauthjs/next-auth/actions?query=workflow%3A%22Integration+Test%22">
<img src="https://github.com/nextauthjs/next-auth/workflows/Integration%20Test/badge.svg" alt="Integration Test" />
</a>
<a href="https://bundlephobia.com/result?p=next-auth">
<img src="https://img.shields.io/bundlephobia/minzip/next-auth" alt="Bundle Size"/>
</a>
<a href="https://www.npmtrends.com/next-auth">
<img src="https://img.shields.io/npm/dm/next-auth" alt="Downloads" />
</a>
<a href="https://github.com/nextauthjs/next-auth/stargazers">
<img src="https://img.shields.io/github/stars/nextauthjs/next-auth" alt="Github Stars" />
</a>
<a href="https://www.npmjs.com/package/next-auth">
<img src="https://img.shields.io/github/v/release/nextauthjs/next-auth?label=latest" alt="Github Stable Release" />
</a>
<img src="https://img.shields.io/github/v/release/nextauthjs/next-auth?include_prereleases&label=prerelease&sort=semver" alt="Github Prelease" />
</p>
</p>
## Overview
@@ -6,35 +35,58 @@ NextAuth.js is a complete open source authentication solution for [Next.js](http
It is designed from the ground up to support Next.js and Serverless.
[Follow the examples](https://next-auth.js.org/getting-started/example) to see how easy it is to use NextAuth.js for authentication.
## Getting Started
Install: `npm i next-auth`
```
npm install --save next-auth
```
The easiest way to continue getting started, is to follow the [getting started](https://next-auth.js.org/getting-started/example) section in our docs.
We also have a section of [tutorials](https://next-auth.js.org/tutorials) for those looking for more specific examples.
See [next-auth.js.org](https://next-auth.js.org) for more information and documentation.
## Features
### Authentication
### Flexible and easy to use
* Designed to work with any OAuth service, it supports OAuth 1.0, 1.0A and 2.0
* Built-in support for [many popular OAuth sign-in services](https://next-auth.js.org/configuration/providers)
* Built-in support for [many popular sign-in services](https://next-auth.js.org/configuration/providers)
* Supports email / passwordless authentication
* Supports stateless authentication with any backend (Active Directory, LDAP, etc)
* Supports both JSON Web Tokens and database sessions
* Designed for Serverless but runs anywhere (AWS Lambda, Docker, Heroku, etc…)
### Own your own data
NextAuth.js can be used with or without a database.
* An open source solution that allows you to keep control of your data
* Supports Bring Your Own Database (BYOD) and can be used with any database
* Built-in support for for [MySQL, MariaDB, Postgres, MongoDB and SQLite](https://next-auth.js.org/configuration/database)
* Built-in support for [MySQL, MariaDB, Postgres, Microsoft SQL Server, MongoDB and SQLite](https://next-auth.js.org/configuration/databases)
* Works great with databases from popular hosting providers
* Can also be used without a database (e.g. OAuth + JWT)
* Can also be used *without a database* (e.g. OAuth + JWT)
### Secure by default
* Designed to be secure by default and promote best practice for safeguarding user data
* Promotes the use of passwordless sign in mechanisms
* Designed to be secure by default and encourage best practice for safeguarding user data
* Uses Cross Site Request Forgery Tokens on POST routes (sign in, sign out)
* Default cookie policy aims for the most restrictive policy appropriate for each cookie
* When JSON Web Tokens are enabled, they are signed by default (JWS) with HS512
* Use JWT encryption (JWE) by setting the option `encryption: true` (defaults to A256GCM)
* Auto-generates symmetric signing and encryption keys for developer convenience
* Features tab/window syncing and keepalive messages to support short lived sessions
* Attempts to implement the latest guidance published by [Open Web Application Security Project](https://owasp.org/)
Security focused features include CSRF protection, use of signed cookies, cookie prefixes, secure cookies, HTTP only, host only and secure only cookies and promoting passwordless sign-in.
Advanced options allow you to define your own routines to handle controlling what accounts are allowed to sign in, for encoding and decoding JSON Web Tokens and to set custom cookie security policies and session properties, so you can control who is able to sign in and how often sessions have to be re-validated.
### TypeScript
NextAuth.js comes with built-in types. For more information and usage, check out the [TypeScript section](https://next-auth.js.org/getting-started/typescript) in the documentaion.
The package at `@types/next-auth` is now deprecated.
## Example
@@ -44,8 +96,7 @@ Security focused features include CSRF protection, use of signed cookies, cookie
import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'
const options = {
site: 'https://example.com'
export default NextAuth({
providers: [
// OAuth authentication providers
Providers.Apple({
@@ -64,45 +115,51 @@ const options = {
],
// SQL or MongoDB database (or leave empty)
database: process.env.DATABASE_URL
}
export default (req, res) => NextAuth(req, res, options)
})
```
### Add React Component
```javascript
import React from 'react'
import {
useSession,
signin,
signout
import {
useSession, signIn, signOut
} from 'next-auth/client'
export default () => {
export default function Component() {
const [ session, loading ] = useSession()
return <p>
{!session && <>
Not signed in <br/>
<button onClick={signin}>Sign in</button>
</>}
{session && <>
if(session) {
return <>
Signed in as {session.user.email} <br/>
<button onClick={signout}>Sign out</button>
</>}
</p>
<button onClick={() => signOut()}>Sign out</button>
</>
}
return <>
Not signed in <br/>
<button onClick={() => signIn()}>Sign in</button>
</>
}
```
## Acknowledgement
## Acknowledgements
[NextAuth.js 2.0 is possible thanks to its contributors.](https://next-auth.js.org/contributors)
[NextAuth.js is made possible thanks to all of its contributors.](https://next-auth.js.org/contributors)
## Getting started
[Follow the examples to get started.](https://next-auth.js.org/getting-started/example)
<a href="https://github.com/nextauthjs/next-auth/graphs/contributors">
<img width="500px" src="https://contrib.rocks/image?repo=nextauthjs/next-auth" />
</a>
<div>
<a href="https://vercel.com?utm_source=nextauthjs&utm_campaign=oss">
<img width="170px" src="https://raw.githubusercontent.com/nextauthjs/next-auth/canary/www/static/img/powered-by-vercel.svg" alt="Powered By Vercel" />
</a>
</div>
<div>
<p align="left">Thanks to Vercel sponsoring this project by allowing it to be deployed for free for the entire NextAuth.js Team</p>
</div>
## Contributing
If you'd like to contribute to you can find useful information in our [Contributing Guide](https://github.com/iaincollins/next-auth/blob/main/CONTRIBUTING.md).
We're open to all community contributions! If you'd like to contribute in any way, please first read our [Contributing Guide](https://github.com/nextauthjs/next-auth/blob/canary/CONTRIBUTING.md).
## License
ISC

24
SECURITY.md Normal file
View File

@@ -0,0 +1,24 @@
# Security Policy
NextAuth.js practices responsible disclosure.
## Supported Versions
Security updates are only released for the current version.
Old releases are not maintained and do not receive updates.
## Reporting a Vulnerability
We request that you contact us directly to report serious issues that might impact the security of sites using NextAuth.js.
If you contact us regarding a serious issue:
* We will endeavor to get back to you within 72 hours.
* We will aim to publish a fix within 30 days.
* We will disclose the issue (and credit you, with your consent) once a fix to resolve the issue has been released.
* If 90 days has elapsed and we still don't have a fix, we will disclose the issue publically.
Currently, the best way to report an issue is by emailing me@iaincollins.com
For less serious issues (e.g. RFC compliance for unsupported flows or potential issues that may cause a problem future or default behaviour / options) it is appropriate to submit these these publically as bug reports or feature requests or to raise a question to open a discussion around them.

View File

@@ -1 +0,0 @@
module.exports = require('./dist/adapters').default

View File

@@ -1,12 +0,0 @@
{
"presets": [
["@babel/preset-env", { "targets": { "esmodules": true } } ]
],
"comments": false,
"overrides": [
{
"test": [ "./src/server/pages/**" ],
"presets": [ "preact" ]
}
]
}

View File

@@ -1 +0,0 @@
module.exports = require('./dist/client').default

View File

@@ -0,0 +1,19 @@
import { signIn } from 'next-auth/client'
export default function AccessDenied () {
return (
<>
<h1>Access Denied</h1>
<p>
<a
href='/api/auth/signin'
onClick={(e) => {
e.preventDefault()
signIn()
}}
>You must be signed in to view this page
</a>
</p>
</>
)
}

18
components/footer.js Normal file
View File

@@ -0,0 +1,18 @@
import Link from 'next/link'
import styles from './footer.module.css'
import { version } from 'package.json'
export default function Footer () {
return (
<footer className={styles.footer}>
<hr />
<ul className={styles.navItems}>
<li className={styles.navItem}><a href='https://next-auth.js.org'>Documentation</a></li>
<li className={styles.navItem}><a href='https://www.npmjs.com/package/next-auth'>NPM</a></li>
<li className={styles.navItem}><a href='https://github.com/nextauthjs/next-auth-example'>GitHub</a></li>
<li className={styles.navItem}><Link href='/policy'><a>Policy</a></Link></li>
<li className={styles.navItem}><em>{version}</em></li>
</ul>
</footer>
)
}

View File

@@ -0,0 +1,14 @@
.footer {
margin-top: 2rem;
}
.navItems {
margin-bottom: 1rem;
padding: 0;
list-style: none;
}
.navItem {
display: inline-block;
margin-right: 1rem;
}

112
components/header.js Normal file
View File

@@ -0,0 +1,112 @@
import Link from 'next/link'
import { signIn, signOut, useSession } from 'next-auth/client'
import styles from './header.module.css'
// The approach used in this component shows how to built a sign in and sign out
// component that works on pages which support both client and server side
// rendering, and avoids any flash incorrect content on initial page load.
export default function Header () {
const [session, loading] = useSession()
return (
<header>
<noscript>
<style>{'.nojs-show { opacity: 1; top: 0; }'}</style>
</noscript>
<div className={styles.signedInStatus}>
<p
className={`nojs-show ${
!session && loading ? styles.loading : styles.loaded
}`}
>
{!session && (
<>
<span className={styles.notSignedInText}>
You are not signed in
</span>
<a
href='/api/auth/signin'
className={styles.buttonPrimary}
onClick={(e) => {
e.preventDefault()
signIn()
}}
>
Sign in
</a>
</>
)}
{session && (
<>
{session.user.image && (
<span
style={{ backgroundImage: `url(${session.user.image})` }}
className={styles.avatar}
/>
)}
<span className={styles.signedInText}>
<small>Signed in as</small>
<br />
<strong>{session.user.email || session.user.name}</strong>
</span>
<a
href='/api/auth/signout'
className={styles.button}
onClick={(e) => {
e.preventDefault()
signOut()
}}
>
Sign out
</a>
</>
)}
</p>
</div>
<nav>
<ul className={styles.navItems}>
<li className={styles.navItem}>
<Link href='/'>
<a>Home</a>
</Link>
</li>
<li className={styles.navItem}>
<Link href='/client'>
<a>Client</a>
</Link>
</li>
<li className={styles.navItem}>
<Link href='/server'>
<a>Server</a>
</Link>
</li>
<li className={styles.navItem}>
<Link href='/protected'>
<a>Protected</a>
</Link>
</li>
<li className={styles.navItem}>
<Link href='/protected-ssr'>
<a>Protected(SSR)</a>
</Link>
</li>
<li className={styles.navItem}>
<Link href='/api-example'>
<a>API</a>
</Link>
</li>
<li className={styles.navItem}>
<Link href='/credentials'>
<a>Credentials</a>
</Link>
</li>
<li className={styles.navItem}>
<Link href='/email'>
<a>Email</a>
</Link>
</li>
</ul>
</nav>
</header>
)
}

View File

@@ -0,0 +1,92 @@
/* Set min-height to avoid page reflow while session loading */
.signedInStatus {
display: block;
min-height: 4rem;
width: 100%;
}
.loading,
.loaded {
position: relative;
top: 0;
opacity: 1;
overflow: hidden;
border-radius: 0 0 .6rem .6rem;
padding: .6rem 1rem;
margin: 0;
background-color: rgba(0,0,0,.05);
transition: all 0.2s ease-in;
}
.loading {
top: -2rem;
opacity: 0;
}
.signedInText,
.notSignedInText {
position: absolute;
padding-top: .8rem;
left: 1rem;
right: 6.5rem;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
display: inherit;
z-index: 1;
line-height: 1.3rem;
}
.signedInText {
padding-top: 0rem;
left: 4.6rem;
}
.avatar {
border-radius: 2rem;
float: left;
height: 2.8rem;
width: 2.8rem;
background-color: white;
background-size: cover;
background-repeat: no-repeat;
}
.button,
.buttonPrimary {
float: right;
margin-right: -.4rem;
font-weight: 500;
border-radius: .3rem;
cursor: pointer;
font-size: 1rem;
line-height: 1.4rem;
padding: .7rem .8rem;
position: relative;
z-index: 10;
background-color: transparent;
color: #555;
}
.buttonPrimary {
background-color: #346df1;
border-color: #346df1;
color: #fff;
text-decoration: none;
padding: .7rem 1.4rem;
}
.buttonPrimary:hover {
box-shadow: inset 0 0 5rem rgba(0,0,0,0.2)
}
.navItems {
margin-bottom: 2rem;
padding: 0;
list-style: none;
}
.navItem {
display: inline-block;
margin-right: 1rem;
}

14
components/layout.js Normal file
View File

@@ -0,0 +1,14 @@
import Header from 'components/header'
import Footer from 'components/footer'
export default function Layout ({ children }) {
return (
<>
<Header />
<main>
{children}
</main>
<Footer />
</>
)
}

12
config/babel.config.json Normal file
View File

@@ -0,0 +1,12 @@
{
"presets": [
["@babel/preset-env", { "targets": { "esmodules": true } }]
],
"comments": false,
"overrides": [
{
"test": ["../src/server/pages/**"],
"presets": ["preact"]
}
]
}

45
config/build.js Normal file
View File

@@ -0,0 +1,45 @@
const fs = require("fs-extra")
const path = require("path")
const MODULE_ENTRIES = {
SERVER: "index",
CLIENT: "client",
PROVIDERS: "providers",
ADAPTERS: "adapters",
JWT: "jwt",
}
const BUILD_TARGETS = {
[`${MODULE_ENTRIES.SERVER}.js`]: "module.exports = require('./dist/server').default\n",
[`${MODULE_ENTRIES.CLIENT}.js`]: "module.exports = require('./dist/client').default\n",
[`${MODULE_ENTRIES.ADAPTERS}.js`]: "module.exports = require('./dist/adapters').default\n",
[`${MODULE_ENTRIES.PROVIDERS}.js`]: "module.exports = require('./dist/providers').default\n",
[`${MODULE_ENTRIES.JWT}.js`]: "module.exports = require('./dist/lib/jwt').default\n",
}
Object.entries(BUILD_TARGETS).forEach(([target, content]) => {
fs.writeFile(path.join(process.cwd(), target), content, (err) => {
if (err) throw err
console.log(`[build] created "${target}" in root folder`)
})
})
const TYPES_TARGETS = [
`${MODULE_ENTRIES.SERVER}.d.ts`,
`${MODULE_ENTRIES.CLIENT}.d.ts`,
`${MODULE_ENTRIES.ADAPTERS}.d.ts`,
`${MODULE_ENTRIES.PROVIDERS}.d.ts`,
`${MODULE_ENTRIES.JWT}.d.ts`,
"internals",
]
TYPES_TARGETS.forEach((target) => {
fs.copy(
path.resolve("types", target),
path.join(process.cwd(), target),
(err) => {
if (err) throw err
console.log(`[build-types] copying "${target}" to root folder`)
}
)
})

View File

@@ -1,6 +1,6 @@
// Serverless target in Next.js does work if you try to read in files at runtime
// Serverless target in Next.js does not work if you try to read in files at runtime
// that are not JavaScript or JSON (e.g. CSS files).
// https://github.com/iaincollins/next-auth/issues/281
// https://github.com/nextauthjs/next-auth/issues/281
//
// To work around this issue, this script is a manual step that wraps CSS in a
// JavaScript file that has the compiled CSS embedded in it, and exports only
@@ -15,4 +15,4 @@ const css = fs.readFileSync(pathToCss, 'utf8')
const cssWithEscapedQuotes = css.replace(/"/gm, '\\"')
const js = `module.exports = function() { return "${cssWithEscapedQuotes}" }`
fs.writeFileSync(pathToCssJs, js)
fs.writeFileSync(pathToCssJs, js)

View File

@@ -1 +0,0 @@
module.exports = require('./dist/server')

1
jwt.js
View File

@@ -1 +0,0 @@
module.exports = require('./dist/lib/jwt').default

2
next-env.d.ts vendored Normal file
View File

@@ -0,0 +1,2 @@
/// <reference types="next" />
/// <reference types="next/types/global" />

15151
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,66 +1,151 @@
{
"name": "next-auth",
"version": "2.0.0",
"description": "An authentication library for Next.js",
"repository": "https://github.com/iaincollins/next-auth.git",
"version": "0.0.0-semantically-released",
"description": "Authentication for Next.js",
"homepage": "https://next-auth.js.org",
"repository": "https://github.com/nextauthjs/next-auth.git",
"author": "Iain Collins <me@iaincollins.com>",
"main": "index.js",
"scripts": {
"build": "npm run build:js && npm run build:css",
"build:js": "babel src --out-dir dist",
"build:css": "postcss src/**/*.css --base src --dir dist && node scripts/wrap-css.js",
"build:js": "babel --config-file ./config/babel.config.json src --out-dir dist && node ./config/build.js",
"build:css": "postcss --config config/postcss.config.js src/**/*.css --base src --dir dist && node config/wrap-css.js",
"dev": "next | npm run watch:css",
"watch": "npm run watch:js | npm run watch:css",
"watch:js": "babel --watch src --out-dir dist",
"watch:css": "postcss --watch src/**/*.css --base src --dir dist",
"test": "npm run test:db",
"test:db": "npm run test:db:mysql && npm run test:db:postgres && npm run test:db:mongodb",
"watch:js": "babel --config-file ./config/babel.config.json --watch src --out-dir dist",
"watch:css": "postcss --config config/postcss.config.js --watch src/**/*.css --base src --dir dist",
"test:app:start": "docker-compose -f test/docker/app.yml up -d",
"test:app:rebuild": "npm run build && docker-compose -f test/docker/app.yml up -d --build",
"test:app:stop": "docker-compose -f test/docker/app.yml down",
"test": "npm run test:app:rebuild && npm run test:integration && npm run test:app:stop && npm run test:types",
"test:db": "npm run test:db:mysql && npm run test:db:postgres && npm run test:db:mongodb && npm run test:db:mssql",
"test:db:mysql": "node test/mysql.js",
"test:db:postgres": "node test/postgres.js",
"test:db:mongodb": "node test/mongodb.js",
"db:start": "docker-compose -f test/docker/docker-compose.yml up -d",
"db:stop": "docker-compose -f test/docker/docker-compose.yml down",
"test:db:mssql": "node test/mssql.js",
"test:integration": "mocha test/integration",
"test:types": "dtslint types",
"db:start": "docker-compose -f test/docker/databases.yml up -d",
"db:stop": "docker-compose -f test/docker/databases.yml down",
"prepublishOnly": "npm run build",
"publish:beta": "npm publish --tag beta",
"publish:canary": "npm publish --tag canary",
"lint": "standard",
"lint:fix": "standard --fix"
"lint": "eslint .",
"lint:fix": "eslint . --fix"
},
"files": [
"dist",
"index.js",
"index.d.ts",
"providers.js",
"providers.d.ts",
"adapters.js",
"adapters.d.ts",
"client.js",
"jwt.js"
"client.d.ts",
"jwt.js",
"jwt.d.ts",
"internals"
],
"license": "ISC",
"dependencies": {
"crypto-js": "^4.0.0",
"futoin-hkdf": "^1.3.2",
"jose": "^1.27.2",
"jsonwebtoken": "^8.5.1",
"jwt-decode": "^2.2.0",
"nodemailer": "^6.4.6",
"nodemailer": "^6.4.16",
"oauth": "^0.9.15",
"pkce-challenge": "^2.1.0",
"preact": "^10.4.1",
"preact-render-to-string": "^5.1.7",
"preact-render-to-string": "^5.1.14",
"querystring": "^0.2.0",
"typeorm": "^0.2.24"
"require_optional": "^1.0.1",
"typeorm": "^0.2.30"
},
"peerDependencies": {
"react": "^16.13.1",
"react-dom": "^16.13.1"
"react": "^16.13.1 || ^17",
"react-dom": "16.13.1 || ^17"
},
"peerOptionalDependencies": {
"mongodb": "^3.5.9",
"mysql": "^2.18.1",
"mssql": "^6.2.1",
"pg": "^8.2.1",
"@prisma/client": "^2.16.1"
},
"devDependencies": {
"@babel/cli": "^7.8.4",
"@babel/core": "^7.9.6",
"@babel/preset-env": "^7.9.6",
"@prisma/client": "^2.16.1",
"@semantic-release/commit-analyzer": "^8.0.1",
"@semantic-release/github": "^7.2.0",
"@semantic-release/npm": "7.0.8",
"@semantic-release/release-notes-generator": "^9.0.1",
"@types/react": "^17.0.0",
"@typescript-eslint/eslint-plugin": "^4.22.0",
"@typescript-eslint/parser": "^4.22.0",
"autoprefixer": "^9.7.6",
"babel-preset-preact": "^2.0.0",
"conventional-changelog-conventionalcommits": "4.4.0",
"cssnano": "^4.1.10",
"dotenv": "^8.2.0",
"dtslint": "^4.0.8",
"eslint": "^7.19.0",
"eslint-config-prettier": "^8.2.0",
"eslint-config-standard-with-typescript": "^19.0.1",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.3.1",
"eslint-plugin-standard": "^5.0.0",
"mocha": "^8.1.3",
"mongodb": "^3.5.9",
"mssql": "^6.2.1",
"mysql": "^2.18.1",
"next": "^10.0.5",
"pg": "^8.2.1",
"postcss-cli": "^7.1.1",
"postcss-nested": "^4.2.1",
"standard": "^14.3.3"
}
"prettier": "^2.2.1",
"prisma": "^2.16.1",
"puppeteer": "^5.2.1",
"puppeteer-extra": "^3.1.15",
"puppeteer-extra-plugin-stealth": "^2.6.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"typescript": "^4.1.3"
},
"prettier": {
"semi": false
},
"eslintConfig": {
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json"
},
"extends": [
"standard-with-typescript",
"prettier"
],
"ignorePatterns": [
"node_modules",
"test",
"next-env.d.ts",
"types",
"www",
".next",
"dist"
],
"globals": {
"localStorage": "readonly",
"location": "readonly",
"fetch": "readonly"
}
},
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/balazsorban44"
}
]
}

31
pages/_app.js Normal file
View File

@@ -0,0 +1,31 @@
import { Provider } from 'next-auth/client'
import './styles.css'
// Use the <Provider> to improve performance and allow components that call
// `useSession()` anywhere in your application to access the `session` object.
export default function App ({ Component, pageProps }) {
return (
<Provider
// Provider options are not required but can be useful in situations where
// you have a short session maxAge time. Shown here with default values.
options={{
// Client Max Age controls how often the useSession in the client should
// contact the server to sync the session state. Value in seconds.
// e.g.
// * 0 - Disabled (always use cache value)
// * 60 - Sync session state with server if it's older than 60 seconds
clientMaxAge: 0,
// Keep Alive tells windows / tabs that are signed in to keep sending
// a keep alive request (which extends the current session expiry) to
// prevent sessions in open windows from expiring. Value in seconds.
//
// Note: If a session has expired when keep alive is triggered, all open
// windows / tabs will be updated to reflect the user is signed out.
keepAlive: 0
}}
session={pageProps.session}
>
<Component {...pageProps} />
</Provider>
)
}

17
pages/api-example.js Normal file
View File

@@ -0,0 +1,17 @@
import Layout from '../components/layout'
export default function Page () {
return (
<Layout>
<h1>API Example</h1>
<p>The examples below show responses from the example API endpoints.</p>
<p><em>You must be signed in to see responses.</em></p>
<h2>Session</h2>
<p>/api/examples/session</p>
<iframe src='/api/examples/session' />
<h2>JSON Web Token</h2>
<p>/api/examples/jwt</p>
<iframe src='/api/examples/jwt' />
</Layout>
)
}

View File

@@ -0,0 +1,61 @@
import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'
// import Adapters from 'next-auth/adapters'
// import { PrismaClient } from '@prisma/client'
// const prisma = new PrismaClient()
export default NextAuth({
providers: [
Providers.Email({
server: process.env.EMAIL_SERVER,
from: process.env.EMAIL_FROM
}),
Providers.GitHub({
clientId: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_SECRET
}),
Providers.Auth0({
clientId: process.env.AUTH0_ID,
clientSecret: process.env.AUTH0_SECRET,
domain: process.env.AUTH0_DOMAIN,
protection: 'pkce'
}),
Providers.Twitter({
clientId: process.env.TWITTER_ID,
clientSecret: process.env.TWITTER_SECRET
}),
Providers.Credentials({
name: 'Credentials',
credentials: {
password: { label: 'Password', type: 'password' }
},
async authorize (credentials) {
if (credentials.password === 'password') {
return {
id: 1,
name: 'Fill Murray',
email: 'bill@fillmurray.com',
image: 'https://www.fillmurray.com/64/64'
}
}
return null
}
})
],
jwt: {
encryption: true,
secret: process.env.SECRET
},
debug: false,
theme: 'auto'
// Default Database Adapter (TypeORM)
// database: process.env.DATABASE_URL
// Prisma Database Adapter
// To configure this app to use the schema in `prisma/schema.prisma` run:
// npx prisma generate
// npx prisma migrate dev --preview-feature
// adapter: Adapters.Prisma.Adapter({ prisma })
})

View File

@@ -0,0 +1,9 @@
// This is an example of how to read a JSON Web Token from an API route
import jwt from 'next-auth/jwt'
const secret = process.env.SECRET
export default async (req, res) => {
const token = await jwt.getToken({ req, secret })
res.send(JSON.stringify(token, null, 2))
}

View File

@@ -0,0 +1,12 @@
// This is an example of to protect an API route
import { getSession } from 'next-auth/client'
export default async (req, res) => {
const session = await getSession({ req })
if (session) {
res.send({ content: 'This is protected content. You can access this content because you are signed in.' })
} else {
res.send({ error: 'You must be sign in to view the protected content on this page.' })
}
}

View File

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

22
pages/client.js Normal file
View File

@@ -0,0 +1,22 @@
import Layout from '../components/layout'
export default function Page () {
return (
<Layout>
<h1>Client Side Rendering</h1>
<p>
This page uses the <strong>useSession()</strong> React Hook in the <strong>&lt;/Header&gt;</strong> component.
</p>
<p>
The <strong>useSession()</strong> React Hook easy to use and allows pages to render very quickly.
</p>
<p>
The advantage of this approach is that session state is shared between pages by using the <strong>Provider</strong> in <strong>_app.js</strong> so
that navigation between pages using <strong>useSession()</strong> is very fast.
</p>
<p>
The disadvantage of <strong>useSession()</strong> is that it requires client side JavaScript.
</p>
</Layout>
)
}

53
pages/credentials.js Normal file
View File

@@ -0,0 +1,53 @@
// eslint-disable-next-line no-use-before-define
import * as React from 'react'
import { signIn, signOut, useSession } from 'next-auth/client'
import Layout from 'components/layout'
export default function Page () {
const [response, setResponse] = React.useState(null)
const handleLogin = (options) => async () => {
if (options.redirect) {
return signIn('credentials', options)
}
const response = await signIn('credentials', options)
setResponse(response)
}
const handleLogout = (options) => async () => {
if (options.redirect) {
return signOut(options)
}
const response = await signOut(options)
setResponse(response)
}
const [session] = useSession()
if (session) {
return (
<Layout>
<h1>Test different flows for Credentials logout</h1>
<span className='spacing'>Default:</span>
<button onClick={handleLogout({ redirect: true })}>Logout</button><br />
<span className='spacing'>No redirect:</span>
<button onClick={handleLogout({ redirect: false })}>Logout</button><br />
<p>Response:</p>
<pre style={{ background: '#eee', padding: 16 }}>{JSON.stringify(response, null, 2)}</pre>
</Layout>
)
}
return (
<Layout>
<h1>Test different flows for Credentials login</h1>
<span className='spacing'>Default:</span>
<button onClick={handleLogin({ redirect: true, password: 'password' })}>Login</button><br />
<span className='spacing'>No redirect:</span>
<button onClick={handleLogin({ redirect: false, password: 'password' })}>Login</button><br />
<span className='spacing'>No redirect, wrong password:</span>
<button onClick={handleLogin({ redirect: false, password: '' })}>Login</button>
<p>Response:</p>
<pre style={{ background: '#eee', padding: 16 }}>{JSON.stringify(response, null, 2)}</pre>
</Layout>
)
}

67
pages/email.js Normal file
View File

@@ -0,0 +1,67 @@
// eslint-disable-next-line no-use-before-define
import * as React from 'react'
import { signIn, signOut, useSession } from 'next-auth/client'
import Layout from 'components/layout'
export default function Page () {
const [response, setResponse] = React.useState(null)
const [email, setEmail] = React.useState('')
const handleChange = (event) => {
setEmail(event.target.value)
}
const handleLogin = (options) => async (event) => {
event.preventDefault()
if (options.redirect) {
return signIn('email', options)
}
const response = await signIn('email', options)
setResponse(response)
}
const handleLogout = (options) => async (event) => {
if (options.redirect) {
return signOut(options)
}
const response = await signOut(options)
setResponse(response)
}
const [session] = useSession()
if (session) {
return (
<Layout>
<h1>Test different flows for Email logout</h1>
<span className='spacing'>Default:</span>
<button onClick={handleLogout({ redirect: true })}>Logout</button><br />
<span className='spacing'>No redirect:</span>
<button onClick={handleLogout({ redirect: false })}>Logout</button><br />
<p>Response:</p>
<pre style={{ background: '#eee', padding: 16 }}>{JSON.stringify(response, null, 2)}</pre>
</Layout>
)
}
return (
<Layout>
<h1>Test different flows for Email login</h1>
<label className='spacing'>
Email address:{' '}
<input type='text' id='email' name='email' value={email} onChange={handleChange} />
</label><br />
<form onSubmit={handleLogin({ redirect: true, email })}>
<span className='spacing'>Default:</span>
<button type='submit'>Sign in with Email</button>
</form>
<form onSubmit={handleLogin({ redirect: false, email })}>
<span className='spacing'>No redirect:</span>
<button type='submit'>Sign in with Email</button>
</form>
<p>Response:</p>
<pre style={{ background: '#eee', padding: 16 }}>{JSON.stringify(response, null, 2)}</pre>
</Layout>
)
}

12
pages/index.js Normal file
View File

@@ -0,0 +1,12 @@
import Layout from 'components/layout'
export default function Page () {
return (
<Layout>
<h1>NextAuth.js Example</h1>
<p>
This is an example site to demonstrate how to use <a href='https://next-auth.js.org'>NextAuth.js</a> for authentication.
</p>
</Layout>
)
}

30
pages/policy.js Normal file
View File

@@ -0,0 +1,30 @@
import Layout from '../components/layout'
export default function Page () {
return (
<Layout>
<p>
This is an example site to demonstrate how to use <a href='https://next-auth.js.org'>NextAuth.js</a> for authentication.
</p>
<h2>Terms of Service</h2>
<p>
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.
</p>
<h2>Privacy Policy</h2>
<p>
This site uses JSON Web Tokens and an in-memory database which resets every ~2 hours.
</p>
<p>
Data provided to this site is exclusively used to support signing in
and is not passed to any third party services, other than via SMTP or OAuth for the
purposes of authentication.
</p>
</Layout>
)
}

37
pages/protected-ssr.js Normal file
View File

@@ -0,0 +1,37 @@
// This is an example of how to protect content using server rendering
import { getSession } from 'next-auth/client'
import Layout from '../components/layout'
import AccessDenied from '../components/access-denied'
export default function Page ({ content, session }) {
// If no session exists, display access denied message
if (!session) { return <Layout><AccessDenied /></Layout> }
// If session exists, display content
return (
<Layout>
<h1>Protected Page</h1>
<p><strong>{content}</strong></p>
</Layout>
)
}
export async function getServerSideProps (context) {
const session = await getSession(context)
let content = null
if (session) {
const hostname = process.env.NEXTAUTH_URL || 'http://localhost:3000'
const options = { headers: { cookie: context.req.headers.cookie } }
const res = await fetch(`${hostname}/api/examples/protected`, options)
const json = await res.json()
if (json.content) { content = json.content }
}
return {
props: {
session,
content
}
}
}

33
pages/protected.js Normal file
View File

@@ -0,0 +1,33 @@
import { useState, useEffect } from 'react'
import { useSession } from 'next-auth/client'
import Layout from '../components/layout'
import AccessDenied from '../components/access-denied'
export default function Page () {
const [session, loading] = useSession()
const [content, setContent] = useState()
// Fetch content from protected route
useEffect(() => {
const fetchData = async () => {
const res = await fetch('/api/examples/protected')
const json = await res.json()
if (json.content) { setContent(json.content) }
}
fetchData()
}, [session])
// When rendering client side don't display anything until loading is complete
if (typeof window !== 'undefined' && loading) return null
// If no session exists, display access denied message
if (!session) { return <Layout><AccessDenied /></Layout> }
// If session exists, display content
return (
<Layout>
<h1>Protected Page</h1>
<p><strong>{content}</strong></p>
</Layout>
)
}

37
pages/server.js Normal file
View File

@@ -0,0 +1,37 @@
import { getSession } from 'next-auth/client'
import Layout from '../components/layout'
export default function Page () {
// As this page uses Server Side Rendering, the `session` will be already
// populated on render without needing to go through a loading stage.
// This is possible because of the shared context configured in `_app.js` that
// is used by `useSession()`.
return (
<Layout>
<h1>Server Side Rendering</h1>
<p>
This page uses the universal <strong>getSession()</strong> method in <strong>getServerSideProps()</strong>.
</p>
<p>
Using <strong>getSession()</strong> in <strong>getServerSideProps()</strong> is the recommended approach if you need to
support Server Side Rendering with authentication.
</p>
<p>
The advantage of Server Side Rendering is this page does not require client side JavaScript.
</p>
<p>
The disadvantage of Server Side Rendering is that this page is slower to render.
</p>
</Layout>
)
}
// Export the `session` prop to use sessions with Server Side Rendering
export async function getServerSideProps (context) {
return {
props: {
session: await getSession(context)
}
}
}

30
pages/styles.css Normal file
View File

@@ -0,0 +1,30 @@
body {
font-family: -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
padding: 0 1rem 1rem 1rem;
max-width: 680px;
margin: 0 auto;
background: #fff;
color: #333;
}
li,
p {
line-height: 1.5rem;
}
a {
font-weight: 500;
}
hr {
border: 1px solid #ddd;
}
iframe {
background: #ccc;
border: 1px solid #ccc;
height: 10rem;
width: 100%;
border-radius: .5rem;
filter: invert(1);
}

63
prisma/schema.prisma Normal file
View File

@@ -0,0 +1,63 @@
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Account {
id Int @default(autoincrement()) @id
compoundId String @unique @map(name: "compound_id")
userId Int @map(name: "user_id")
providerType String @map(name: "provider_type")
providerId String @map(name: "provider_id")
providerAccountId String @map(name: "provider_account_id")
refreshToken String? @map(name: "refresh_token")
accessToken String? @map(name: "access_token")
accessTokenExpires DateTime? @map(name: "access_token_expires")
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @default(now()) @map(name: "updated_at")
@@index([providerAccountId], name: "providerAccountId")
@@index([providerId], name: "providerId")
@@index([userId], name: "userId")
@@map(name: "accounts")
}
model Session {
id Int @default(autoincrement()) @id
userId Int @map(name: "user_id")
expires DateTime
sessionToken String @unique @map(name: "session_token")
accessToken String @unique @map(name: "access_token")
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @default(now()) @map(name: "updated_at")
@@map(name: "sessions")
}
model User {
id Int @default(autoincrement()) @id
name String?
email String? @unique
emailVerified DateTime? @map(name: "email_verified")
image String?
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @default(now()) @map(name: "updated_at")
@@map(name: "users")
}
model VerificationRequest {
id Int @default(autoincrement()) @id
identifier String
token String @unique
expires DateTime
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @default(now()) @map(name: "updated_at")
@@map(name: "verification_requests")
}

View File

@@ -1 +0,0 @@
module.exports = require('./dist/providers').default

8
release.config.js Normal file
View File

@@ -0,0 +1,8 @@
module.exports = {
branches: [
'+([0-9])?(.{+([0-9]),x}).x',
'main',
{ name: 'beta', prerelease: true },
{ name: 'next', prerelease: true }
]
}

View File

@@ -1,84 +1,83 @@
const Adapter = (config, options = {}) => {
async function getAdapter (appOptions) {
const { logger } = appOptions
// Display debug output if debug option enabled
function _debug (...args) {
if (appOptions.debug) {
console.log('[next-auth][debug]', ...args)
}
function debug (debugCode, ...args) {
logger.debug(`ADAPTER_${debugCode}`, ...args)
}
async function createUser (profile) {
_debug('createUser', profile)
debug('createUser', profile)
return null
}
async function getUser (id) {
_debug('getUser', id)
debug('getUser', id)
return null
}
async function getUserByEmail (email) {
_debug('getUserByEmail', email)
debug('getUserByEmail', email)
return null
}
async function getUserByProviderAccountId (providerId, providerAccountId) {
_debug('getUserByProviderAccountId', providerId, providerAccountId)
debug('getUserByProviderAccountId', providerId, providerAccountId)
return null
}
async function updateUser (user) {
_debug('updateUser', user)
debug('updateUser', user)
return null
}
async function deleteUser (userId) {
_debug('deleteUser', userId)
debug('deleteUser', userId)
return null
}
async function linkAccount (userId, providerId, providerType, providerAccountId, refreshToken, accessToken, accessTokenExpires) {
_debug('linkAccount', userId, providerId, providerType, providerAccountId, refreshToken, accessToken, accessTokenExpires)
debug('linkAccount', userId, providerId, providerType, providerAccountId, refreshToken, accessToken, accessTokenExpires)
return null
}
async function unlinkAccount (userId, providerId, providerAccountId) {
_debug('unlinkAccount', userId, providerId, providerAccountId)
debug('unlinkAccount', userId, providerId, providerAccountId)
return null
}
async function createSession (user) {
_debug('createSession', user)
debug('createSession', user)
return null
}
async function getSession (sessionToken) {
_debug('getSession', sessionToken)
debug('getSession', sessionToken)
return null
}
async function updateSession (session, force) {
_debug('updateSession', session)
debug('updateSession', session)
return null
}
async function deleteSession (sessionToken) {
_debug('deleteSession', sessionToken)
debug('deleteSession', sessionToken)
return null
}
async function createVerificationRequest (identifier, url, token, secret, provider) {
_debug('createVerificationRequest', identifier)
debug('createVerificationRequest', identifier)
return null
}
async function getVerificationRequest (identifier, token, secret, provider) {
_debug('getVerificationRequest', identifier, token)
debug('getVerificationRequest', identifier, token)
return null
}
async function deleteVerificationRequest (identifier, token, secret, provider) {
_debug('deleteVerification', identifier, token)
debug('deleteVerification', identifier, token)
return null
}

View File

@@ -1,6 +1,8 @@
import TypeORM from './typeorm'
import Prisma from './prisma'
export default {
Default: TypeORM.Adapter,
TypeORM
TypeORM,
Prisma
}

View File

@@ -0,0 +1,340 @@
import { createHash, randomBytes } from 'crypto'
import { CreateUserError } from '../../lib/errors'
const Adapter = (config) => {
const {
prisma,
modelMapping = {
User: 'user',
Account: 'account',
Session: 'session',
VerificationRequest: 'verificationRequest'
}
} = config
const { User, Account, Session, VerificationRequest } = modelMapping
function getCompoundId (providerId, providerAccountId) {
return createHash('sha256').update(`${providerId}:${providerAccountId}`).digest('hex')
}
async function getAdapter (appOptions) {
const { logger } = appOptions
function debug (debugCode, ...args) {
logger.debug(`PRISMA_${debugCode}`, ...args)
}
if (appOptions && (!appOptions.session || !appOptions.session.maxAge)) {
debug('GET_ADAPTER', 'Session expiry not configured (defaulting to 30 days')
}
const defaultSessionMaxAge = 30 * 24 * 60 * 60 * 1000
const sessionMaxAge = (appOptions && appOptions.session && appOptions.session.maxAge)
? appOptions.session.maxAge * 1000
: defaultSessionMaxAge
const sessionUpdateAge = (appOptions && appOptions.session && appOptions.session.updateAge)
? appOptions.session.updateAge * 1000
: 0
async function createUser (profile) {
debug('CREATE_USER', profile)
try {
return prisma[User].create({
data: {
name: profile.name,
email: profile.email,
image: profile.image,
emailVerified: profile.emailVerified ? profile.emailVerified.toISOString() : null
}
})
} catch (error) {
logger.error('CREATE_USER_ERROR', error)
return Promise.reject(new CreateUserError(error))
}
}
async function getUser (id) {
debug('GET_USER', id)
try {
return prisma[User].findUnique({ where: { id } })
} catch (error) {
logger.error('GET_USER_BY_ID_ERROR', error)
return Promise.reject(new Error('GET_USER_BY_ID_ERROR', error))
}
}
async function getUserByEmail (email) {
debug('GET_USER_BY_EMAIL', email)
try {
if (!email) { return Promise.resolve(null) }
return prisma[User].findUnique({ where: { email } })
} catch (error) {
logger.error('GET_USER_BY_EMAIL_ERROR', error)
return Promise.reject(new Error('GET_USER_BY_EMAIL_ERROR', error))
}
}
async function getUserByProviderAccountId (providerId, providerAccountId) {
debug('GET_USER_BY_PROVIDER_ACCOUNT_ID', providerId, providerAccountId)
try {
const account = await prisma[Account].findUnique({ where: { compoundId: getCompoundId(providerId, providerAccountId) } })
if (!account) { return null }
return prisma[User].findUnique({ where: { id: account.userId } })
} catch (error) {
logger.error('GET_USER_BY_PROVIDER_ACCOUNT_ID_ERROR', error)
return Promise.reject(new Error('GET_USER_BY_PROVIDER_ACCOUNT_ID_ERROR', error))
}
}
async function updateUser (user) {
debug('UPDATE_USER', user)
try {
const { id, name, email, image, emailVerified } = user
return prisma[User].update({
where: { id },
data: {
name,
email,
image,
emailVerified: emailVerified ? emailVerified.toISOString() : null
}
})
} catch (error) {
logger.error('UPDATE_USER_ERROR', error)
return Promise.reject(new Error('UPDATE_USER_ERROR', error))
}
}
async function deleteUser (userId) {
debug('DELETE_USER', userId)
try {
return prisma[User].delete({ where: { id: userId } })
} catch (error) {
logger.error('DELETE_USER_ERROR', error)
return Promise.reject(new Error('DELETE_USER_ERROR', error))
}
}
async function linkAccount (userId, providerId, providerType, providerAccountId, refreshToken, accessToken, accessTokenExpires) {
debug('LINK_ACCOUNT', userId, providerId, providerType, providerAccountId, refreshToken, accessToken, accessTokenExpires)
try {
return prisma[Account].create({
data: {
accessToken,
refreshToken,
compoundId: getCompoundId(providerId, providerAccountId),
providerAccountId: `${providerAccountId}`,
providerId,
providerType,
accessTokenExpires,
userId
}
})
} catch (error) {
logger.error('LINK_ACCOUNT_ERROR', error)
return Promise.reject(new Error('LINK_ACCOUNT_ERROR', error))
}
}
async function unlinkAccount (userId, providerId, providerAccountId) {
debug('UNLINK_ACCOUNT', userId, providerId, providerAccountId)
try {
return prisma[Account].delete({ where: { compoundId: getCompoundId(providerId, providerAccountId) } })
} catch (error) {
logger.error('UNLINK_ACCOUNT_ERROR', error)
return Promise.reject(new Error('UNLINK_ACCOUNT_ERROR', error))
}
}
async function createSession (user) {
debug('CREATE_SESSION', user)
try {
let expires = null
if (sessionMaxAge) {
const dateExpires = new Date()
dateExpires.setTime(dateExpires.getTime() + sessionMaxAge)
expires = dateExpires.toISOString()
}
return prisma[Session].create({
data: {
expires,
userId: user.id,
sessionToken: randomBytes(32).toString('hex'),
accessToken: randomBytes(32).toString('hex')
}
})
} catch (error) {
logger.error('CREATE_SESSION_ERROR', error)
return Promise.reject(new Error('CREATE_SESSION_ERROR', error))
}
}
async function getSession (sessionToken) {
debug('GET_SESSION', sessionToken)
try {
const session = await prisma[Session].findUnique({ where: { sessionToken } })
// Check session has not expired (do not return it if it has)
if (session && session.expires && new Date() > session.expires) {
await prisma[Session].delete({ where: { sessionToken } })
return null
}
return session
} catch (error) {
logger.error('GET_SESSION_ERROR', error)
return Promise.reject(new Error('GET_SESSION_ERROR', error))
}
}
async function updateSession (session, force) {
debug('UPDATE_SESSION', session)
try {
if (sessionMaxAge && (sessionUpdateAge || sessionUpdateAge === 0) && session.expires) {
// Calculate last updated date, to throttle write updates to database
// Formula: ({expiry date} - sessionMaxAge) + sessionUpdateAge
// e.g. ({expiry date} - 30 days) + 1 hour
//
// Default for sessionMaxAge is 30 days.
// Default for sessionUpdateAge is 1 hour.
const dateSessionIsDueToBeUpdated = new Date(session.expires)
dateSessionIsDueToBeUpdated.setTime(dateSessionIsDueToBeUpdated.getTime() - sessionMaxAge)
dateSessionIsDueToBeUpdated.setTime(dateSessionIsDueToBeUpdated.getTime() + sessionUpdateAge)
// Trigger update of session expiry date and write to database, only
// if the session was last updated more than {sessionUpdateAge} ago
if (new Date() > dateSessionIsDueToBeUpdated) {
const newExpiryDate = new Date()
newExpiryDate.setTime(newExpiryDate.getTime() + sessionMaxAge)
session.expires = newExpiryDate
} else if (!force) {
return null
}
} else {
// If session MaxAge, session UpdateAge or session.expires are
// missing then don't even try to save changes, unless force is set.
if (!force) { return null }
}
const { id, expires } = session
return prisma[Session].update({ where: { id }, data: { expires: expires.toISOString() } })
} catch (error) {
logger.error('UPDATE_SESSION_ERROR', error)
return Promise.reject(new Error('UPDATE_SESSION_ERROR', error))
}
}
async function deleteSession (sessionToken) {
debug('DELETE_SESSION', sessionToken)
try {
return prisma[Session].delete({ where: { sessionToken } })
} catch (error) {
logger.error('DELETE_SESSION_ERROR', error)
return Promise.reject(new Error('DELETE_SESSION_ERROR', error))
}
}
async function createVerificationRequest (identifier, url, token, secret, provider) {
debug('CREATE_VERIFICATION_REQUEST', identifier)
try {
const { baseUrl } = appOptions
const { sendVerificationRequest, maxAge } = provider
// Store hashed token (using secret as salt) so that tokens cannot be exploited
// even if the contents of the database is compromised.
// @TODO Use bcrypt function here instead of simple salted hash
const hashedToken = createHash('sha256').update(`${token}${secret}`).digest('hex')
let expires = null
if (maxAge) {
const dateExpires = new Date()
dateExpires.setTime(dateExpires.getTime() + (maxAge * 1000))
expires = dateExpires.toISOString()
}
// Save to database
const verificationRequest = await prisma[VerificationRequest].create({
data: {
identifier,
token: hashedToken,
expires
}
})
// With the verificationCallback on a provider, you can send an email, or queue
// an email to be sent, or perform some other action (e.g. send a text message)
await sendVerificationRequest({ identifier, url, token, baseUrl, provider })
return verificationRequest
} catch (error) {
logger.error('CREATE_VERIFICATION_REQUEST_ERROR', error)
return Promise.reject(new Error('CREATE_VERIFICATION_REQUEST_ERROR', error))
}
}
async function getVerificationRequest (identifier, token, secret, provider) {
debug('GET_VERIFICATION_REQUEST', identifier, token)
try {
// Hash token provided with secret before trying to match it with database
// @TODO Use bcrypt instead of salted SHA-256 hash for token
const hashedToken = createHash('sha256').update(`${token}${secret}`).digest('hex')
const verificationRequest = await prisma[VerificationRequest].findFirst({
where: {
identifier,
token: hashedToken
}
})
if (verificationRequest && verificationRequest.expires && new Date() > verificationRequest.expires) {
// Delete verification entry so it cannot be used again
await prisma[VerificationRequest].deleteMany({ where: { identifier, token: hashedToken } })
return null
}
return verificationRequest
} catch (error) {
logger.error('GET_VERIFICATION_REQUEST_ERROR', error)
return Promise.reject(new Error('GET_VERIFICATION_REQUEST_ERROR', error))
}
}
async function deleteVerificationRequest (identifier, token, secret, provider) {
debug('DELETE_VERIFICATION', identifier, token)
try {
// Delete verification entry so it cannot be used again
const hashedToken = createHash('sha256').update(`${token}${secret}`).digest('hex')
await prisma[VerificationRequest].deleteMany({ where: { identifier, token: hashedToken } })
} catch (error) {
logger.error('DELETE_VERIFICATION_REQUEST_ERROR', error)
return Promise.reject(new Error('DELETE_VERIFICATION_REQUEST_ERROR', error))
}
}
return Promise.resolve({
createUser,
getUser,
getUserByEmail,
getUserByProviderAccountId,
updateUser,
deleteUser,
linkAccount,
unlinkAccount,
createSession,
getSession,
updateSession,
deleteSession,
createVerificationRequest,
getVerificationRequest,
deleteVerificationRequest
})
}
return {
getAdapter
}
}
export default {
Adapter
}

View File

@@ -1,11 +1,13 @@
import { createConnection, getConnection, getManager } from 'typeorm'
import { createConnection, getConnection } from 'typeorm'
import { createHash } from 'crypto'
import require_optional from 'require_optional' // eslint-disable-line camelcase
import { CreateUserError } from '../../lib/errors'
import adapterConfig from './lib/config'
import adapterTransform from './lib/transform'
import Models from './models'
import logger from '../../lib/logger'
import { updateConnectionEntities } from './lib/utils'
const Adapter = (typeOrmConfig, options = {}) => {
// Ensure typeOrmConfigObject is normalized to an object
@@ -28,7 +30,7 @@ const Adapter = (typeOrmConfig, options = {}) => {
// anything to do them). This function updates arguments by reference.
adapterTransform(typeOrmConfigObject, models, options)
const config = adapterConfig.loadConfig(typeOrmConfigObject, { models, ...options })
const config = adapterConfig.loadConfig(typeOrmConfigObject, { ...options, models })
// Create objects from models that can be consumed by functions in the adapter
const User = models.User.model
@@ -39,6 +41,12 @@ const Adapter = (typeOrmConfig, options = {}) => {
let connection = null
async function getAdapter (appOptions) {
const { logger } = appOptions
// Display debug output if debug option enabled
function debug (debugCode, ...args) {
logger.debug(`TYPEORM_${debugCode}`, ...args)
}
// Helper function to reuse / restablish connections
// (useful if they drop when after being idle)
async function _connect () {
@@ -67,14 +75,14 @@ const Adapter = (typeOrmConfig, options = {}) => {
await _connect()
}
// Display debug output if debug option enabled
// @TODO Refactor logger so is passed in appOptions
function debugMessage (debugCode, ...args) {
if (appOptions && appOptions.debug) {
logger.debug(`TYPEORM_${debugCode}`, ...args)
}
if (process.env.NODE_ENV !== 'production') {
await updateConnectionEntities(connection, config.entities)
}
// Get manager from connection object
// https://github.com/typeorm/typeorm/blob/master/docs/entity-manager-api.md
const { manager } = connection
// The models are primarily designed for ANSI SQL database, but some
// flexiblity is required in the adapter to support non-SQL databases such
// as MongoDB which have different pragmas.
@@ -86,7 +94,11 @@ const Adapter = (typeOrmConfig, options = {}) => {
let ObjectId
if (config.type === 'mongodb') {
idKey = '_id'
const mongodb = await import('mongodb')
// Using a dynamic import causes problems for some compilers/bundlers
// that don't handle dynamic imports. To try and work around this we are
// using the same method mongodb uses to load Object ID type, which is to
// use the require_optional loader.
const mongodb = require_optional('mongodb')
ObjectId = mongodb.ObjectId
}
@@ -96,7 +108,7 @@ const Adapter = (typeOrmConfig, options = {}) => {
// Use a conditional to default to 30 day session age if not set - it should
// always be set but a meaningful fallback is helpful to facilitate testing.
if (appOptions && (!appOptions.session || !appOptions.session.maxAge)) {
debugMessage('GET_ADAPTER', 'Session expiry not configured (defaulting to 30 days')
debug('GET_ADAPTER', 'Session expiry not configured (defaulting to 30 days')
}
const defaultSessionMaxAge = 30 * 24 * 60 * 60 * 1000
const sessionMaxAge = (appOptions && appOptions.session && appOptions.session.maxAge)
@@ -107,11 +119,11 @@ const Adapter = (typeOrmConfig, options = {}) => {
: 0
async function createUser (profile) {
debugMessage('CREATE_USER', profile)
debug('CREATE_USER', profile)
try {
// Create user account
const user = new User(profile.name, profile.email, profile.image, profile.emailVerified)
return await getManager().save(user)
return await manager.save(user)
} catch (error) {
logger.error('CREATE_USER_ERROR', error)
return Promise.reject(new CreateUserError(error))
@@ -119,7 +131,7 @@ const Adapter = (typeOrmConfig, options = {}) => {
}
async function getUser (id) {
debugMessage('GET_USER', id)
debug('GET_USER', id)
// In the very specific case of both using JWT for storing session data
// and using MongoDB to store user data, the ID is a string rather than
@@ -132,7 +144,7 @@ const Adapter = (typeOrmConfig, options = {}) => {
}
try {
return connection.getRepository(User).findOne({ [idKey]: id })
return manager.findOne(User, { [idKey]: id })
} catch (error) {
logger.error('GET_USER_BY_ID_ERROR', error)
return Promise.reject(new Error('GET_USER_BY_ID_ERROR', error))
@@ -140,10 +152,10 @@ const Adapter = (typeOrmConfig, options = {}) => {
}
async function getUserByEmail (email) {
debugMessage('GET_USER_BY_EMAIL', email)
debug('GET_USER_BY_EMAIL', email)
try {
if (!email) { return Promise.resolve(null) }
return connection.getRepository(User).findOne({ email })
return manager.findOne(User, { email })
} catch (error) {
logger.error('GET_USER_BY_EMAIL_ERROR', error)
return Promise.reject(new Error('GET_USER_BY_EMAIL_ERROR', error))
@@ -151,11 +163,11 @@ const Adapter = (typeOrmConfig, options = {}) => {
}
async function getUserByProviderAccountId (providerId, providerAccountId) {
debugMessage('GET_USER_BY_PROVIDER_ACCOUNT_ID', providerId, providerAccountId)
debug('GET_USER_BY_PROVIDER_ACCOUNT_ID', providerId, providerAccountId)
try {
const account = await connection.getRepository(Account).findOne({ providerId, providerAccountId })
const account = await manager.findOne(Account, { providerId, providerAccountId })
if (!account) { return null }
return connection.getRepository(User).findOne({ [idKey]: account.userId })
return manager.findOne(User, { [idKey]: account.userId })
} catch (error) {
logger.error('GET_USER_BY_PROVIDER_ACCOUNT_ID_ERROR', error)
return Promise.reject(new Error('GET_USER_BY_PROVIDER_ACCOUNT_ID_ERROR', error))
@@ -163,22 +175,22 @@ const Adapter = (typeOrmConfig, options = {}) => {
}
async function updateUser (user) {
debugMessage('UPDATE_USER', user)
return getManager().save(user)
debug('UPDATE_USER', user)
return manager.save(User, user)
}
async function deleteUser (userId) {
debugMessage('DELETE_USER', userId)
debug('DELETE_USER', userId)
// @TODO Delete user from DB
return false
}
async function linkAccount (userId, providerId, providerType, providerAccountId, refreshToken, accessToken, accessTokenExpires) {
debugMessage('LINK_ACCOUNT', userId, providerId, providerType, providerAccountId, refreshToken, accessToken, accessTokenExpires)
debug('LINK_ACCOUNT', userId, providerId, providerType, providerAccountId, refreshToken, accessToken, accessTokenExpires)
try {
// Create provider account linked to user
const account = new Account(userId, providerId, providerType, providerAccountId, refreshToken, accessToken, accessTokenExpires)
return getManager().save(account)
return manager.save(account)
} catch (error) {
logger.error('LINK_ACCOUNT_ERROR', error)
return Promise.reject(new Error('LINK_ACCOUNT_ERROR', error))
@@ -186,7 +198,7 @@ const Adapter = (typeOrmConfig, options = {}) => {
}
async function unlinkAccount (userId, providerId, providerAccountId) {
debugMessage('UNLINK_ACCOUNT', userId, providerId, providerAccountId)
debug('UNLINK_ACCOUNT', userId, providerId, providerAccountId)
// @TODO Get current user from DB
// @TODO Delete [provider] object from user object
// @TODO Save changes to user object in DB
@@ -194,7 +206,7 @@ const Adapter = (typeOrmConfig, options = {}) => {
}
async function createSession (user) {
debugMessage('CREATE_SESSION', user)
debug('CREATE_SESSION', user)
try {
let expires = null
if (sessionMaxAge) {
@@ -205,7 +217,7 @@ const Adapter = (typeOrmConfig, options = {}) => {
const session = new Session(user.id, expires)
return getManager().save(session)
return manager.save(session)
} catch (error) {
logger.error('CREATE_SESSION_ERROR', error)
return Promise.reject(new Error('CREATE_SESSION_ERROR', error))
@@ -213,9 +225,9 @@ const Adapter = (typeOrmConfig, options = {}) => {
}
async function getSession (sessionToken) {
debugMessage('GET_SESSION', sessionToken)
debug('GET_SESSION', sessionToken)
try {
const session = await connection.getRepository(Session).findOne({ sessionToken })
const session = await manager.findOne(Session, { sessionToken })
// Check session has not expired (do not return it if it has)
if (session && session.expires && new Date() > new Date(session.expires)) {
@@ -231,7 +243,7 @@ const Adapter = (typeOrmConfig, options = {}) => {
}
async function updateSession (session, force) {
debugMessage('UPDATE_SESSION', session)
debug('UPDATE_SESSION', session)
try {
if (sessionMaxAge && (sessionUpdateAge || sessionUpdateAge === 0) && session.expires) {
// Calculate last updated date, to throttle write updates to database
@@ -259,7 +271,7 @@ const Adapter = (typeOrmConfig, options = {}) => {
if (!force) { return null }
}
return getManager().save(session)
return manager.save(Session, session)
} catch (error) {
logger.error('UPDATE_SESSION_ERROR', error)
return Promise.reject(new Error('UPDATE_SESSION_ERROR', error))
@@ -267,9 +279,9 @@ const Adapter = (typeOrmConfig, options = {}) => {
}
async function deleteSession (sessionToken) {
debugMessage('DELETE_SESSION', sessionToken)
debug('DELETE_SESSION', sessionToken)
try {
return await connection.getRepository(Session).delete({ sessionToken })
return await manager.delete(Session, { sessionToken })
} catch (error) {
logger.error('DELETE_SESSION_ERROR', error)
return Promise.reject(new Error('DELETE_SESSION_ERROR', error))
@@ -277,9 +289,9 @@ const Adapter = (typeOrmConfig, options = {}) => {
}
async function createVerificationRequest (identifier, url, token, secret, provider) {
debugMessage('CREATE_VERIFICATION_REQUEST', identifier)
debug('CREATE_VERIFICATION_REQUEST', identifier)
try {
const { site } = appOptions
const { baseUrl } = appOptions
const { sendVerificationRequest, maxAge } = provider
// Store hashed token (using secret as salt) so that tokens cannot be exploited
@@ -296,11 +308,11 @@ const Adapter = (typeOrmConfig, options = {}) => {
// Save to database
const newVerificationRequest = new VerificationRequest(identifier, hashedToken, expires)
const verificationRequest = await getManager().save(newVerificationRequest)
const verificationRequest = await manager.save(newVerificationRequest)
// With the verificationCallback on a provider, you can send an email, or queue
// an email to be sent, or perform some other action (e.g. send a text message)
await sendVerificationRequest({ identifier, url, token, site, provider })
await sendVerificationRequest({ identifier, url, token, baseUrl, provider })
return verificationRequest
} catch (error) {
@@ -310,16 +322,16 @@ const Adapter = (typeOrmConfig, options = {}) => {
}
async function getVerificationRequest (identifier, token, secret, provider) {
debugMessage('GET_VERIFICATION_REQUEST', identifier, token)
debug('GET_VERIFICATION_REQUEST', identifier, token)
try {
// Hash token provided with secret before trying to match it with database
// @TODO Use bcrypt instead of salted SHA-256 hash for token
const hashedToken = createHash('sha256').update(`${token}${secret}`).digest('hex')
const verificationRequest = await connection.getRepository(VerificationRequest).findOne({ identifier, token: hashedToken })
const verificationRequest = await manager.findOne(VerificationRequest, { identifier, token: hashedToken })
if (verificationRequest && verificationRequest.expires && new Date() > new Date(verificationRequest.expires)) {
// Delete verification entry so it cannot be used again
await connection.getRepository(VerificationRequest).delete({ token: hashedToken })
await manager.delete(VerificationRequest, { identifier, token: hashedToken })
return null
}
@@ -331,11 +343,11 @@ const Adapter = (typeOrmConfig, options = {}) => {
}
async function deleteVerificationRequest (identifier, token, secret, provider) {
debugMessage('DELETE_VERIFICATION', identifier, token)
debug('DELETE_VERIFICATION', identifier, token)
try {
// Delete verification entry so it cannot be used again
const hashedToken = createHash('sha256').update(`${token}${secret}`).digest('hex')
await connection.getRepository(VerificationRequest).delete({ token: hashedToken })
await manager.delete(VerificationRequest, { identifier, token: hashedToken })
} catch (error) {
logger.error('DELETE_VERIFICATION_REQUEST_ERROR', error)
return Promise.reject(new Error('DELETE_VERIFICATION_REQUEST_ERROR', error))

View File

@@ -7,19 +7,36 @@ const parseConnectionString = (configString) => {
// to make configuration easier (in most use cases).
//
// TypeORM accepts connection string as a 'url' option, but unfortunately
// not for all databases (e.g. SQLite) or options, so we handle parsing it
// in this function..
// not for all databases (e.g. SQLite) or for all options, so we handle
// parsing it in this function.
try {
const parsedUrl = new URL(configString)
const config = {}
// Remove : and convert strings like 'mongodb+srv' into 'mongodb'
config.type = parsedUrl.protocol.replace(/:$/, '').replace(/\+(.*)?$/, '')
config.host = parsedUrl.hostname
config.port = Number(parsedUrl.port)
config.username = parsedUrl.username
config.password = parsedUrl.password
config.database = parsedUrl.pathname.replace(/^\//, '')
if (parsedUrl.protocol.startsWith('mongodb+srv')) {
// Special case handling is required for mongodb+srv with TypeORM
config.type = 'mongodb'
config.url = configString.replace(/\?(.*)$/, '')
config.useNewUrlParser = true
} else {
config.type = parsedUrl.protocol.replace(/:$/, '')
config.host = parsedUrl.hostname
config.port = Number(parsedUrl.port)
config.username = parsedUrl.username
config.password = parsedUrl.password
config.database = parsedUrl.pathname.replace(/^\//, '').replace(/\?(.*)$/, '')
config.options = {}
}
// This option is recommended by mongodb
if (config.type === 'mongodb') {
config.useUnifiedTopology = true
}
// Prevents warning about deprecated option (sets default value)
if (config.type === 'mssql') {
config.options.enableArithAbort = true
}
if (parsedUrl.search) {
parsedUrl.search.replace(/^\?/, '').split('&').forEach(keyValuePair => {
@@ -42,7 +59,7 @@ const parseConnectionString = (configString) => {
const loadConfig = (config, { models, namingStrategy }) => {
const defaultConfig = {
name: 'default',
name: 'nextauth',
autoLoadEntities: true,
entities: [
new EntitySchema(models.User.schema),

View File

@@ -1,51 +1,45 @@
// Perform transforms on SQL models so they can be used with other databases
import { SnakeCaseNamingStrategy, CamelCaseNamingStrategy } from './naming-strategies'
const postgres = (models, options) => {
const postgresTransform = (models, options) => {
// Apply snake case naming strategy for Postgres databases
if (!options.namingStrategy) {
options.namingStrategy = new SnakeCaseNamingStrategy()
}
// Only transforms models that are not custom models
const { models: customModels = {} } = options
// For Postgres we need to use the `timestamp with time zone` type
// aka `timestamptz` to store timestamps correctly in UTC.
if (!customModels.User) {
for (const column in models.User.schema.columns) {
if (models.User.schema.columns[column].type === 'timestamp') {
models.User.schema.columns[column].type = 'timestamptz'
}
}
}
if (!customModels.Account) {
for (const column in models.Account.schema.columns) {
if (models.Account.schema.columns[column].type === 'timestamp') {
models.Account.schema.columns[column].type = 'timestamptz'
}
}
}
if (!customModels.Session) {
for (const column in models.Session.schema.columns) {
if (models.Session.schema.columns[column].type === 'timestamp') {
models.Session.schema.columns[column].type = 'timestamptz'
}
}
}
if (!customModels.VerificationRequest) {
for (const column in models.VerificationRequest.schema.columns) {
if (models.VerificationRequest.schema.columns[column].type === 'timestamp') {
models.VerificationRequest.schema.columns[column].type = 'timestamptz'
for (const model in models) {
for (const column in models[model].schema.columns) {
if (models[model].schema.columns[column].type === 'timestamp') {
models[model].schema.columns[column].type = 'timestamptz'
}
}
}
}
const mongodb = (models, options) => {
const mysqlTransform = (models, options) => {
// Apply snake case naming strategy for MySQL databases
if (!options.namingStrategy) {
options.namingStrategy = new SnakeCaseNamingStrategy()
}
// For MySQL we default milisecond precision of all timestamps to 6 digits.
// This ensures all timestamp fields use the same precision (unless explictly
// configured otherwise) and that values in MySQL match those Postgress.
for (const model in models) {
for (const column in models[model].schema.columns) {
if (models[model].schema.columns[column].type === 'timestamp') {
// If precision explictly set (including to null) don't change it
if (typeof models[model].schema.columns[column].precision === 'undefined') {
models[model].schema.columns[column].precision = 6
}
}
}
}
}
const mongodbTransform = (models, options) => {
// A CamelCase naming strategy is used for all document databases
if (!options.namingStrategy) {
options.namingStrategy = new CamelCaseNamingStrategy()
@@ -65,56 +59,38 @@ const mongodb = (models, options) => {
// see the result of queries like find() is wrong. You will see the same
// Object ID in every property of type Object ID in the result (but the
// database will look fine); so use `type: 'objectId'` for them instead.
// Only transforms models that are not custom models
const { models: customModels = {} } = options
if (!customModels.User) {
delete models.User.schema.columns.id.type
models.User.schema.columns.id.objectId = true
// The options `unique: true` and `nullable: true` don't work the same
// with MongoDB as they do with SQL databases like MySQL and Postgres,
// we also to add sparce to the index. This still doesn't allow multiple
// *null* values, but does allow some records to omit the property.
delete models.User.schema.columns.email.unique
models.User.schema.indices = [
{
name: 'email',
unique: true,
sparse: true,
columns: ['email']
}
]
for (const model in models) {
delete models[model].schema.columns.id.type
models[model].schema.columns.id.objectId = true
}
if (!customModels.Account) {
delete models.Account.schema.columns.id.type
models.Account.schema.columns.id.objectId = true
models.Account.schema.columns.userId.type = 'objectId'
}
// Ensure reference to User ID in other models are Object IDs
// This needs to done for any properties that reference another entity by ID
models.Account.schema.columns.userId.type = 'objectId'
models.Session.schema.columns.userId.type = 'objectId'
if (!customModels.Session) {
delete models.Session.schema.columns.id.type
models.Session.schema.columns.id.objectId = true
models.Session.schema.columns.userId.type = 'objectId'
}
// The options `unique: true` and `nullable: true` don't work the same
// with MongoDB as they do with SQL databases like MySQL and Postgres,
// we need to create a sparse index to only allow unique values, while
// still allowing multiple entires to omit the email address.
delete models.User.schema.columns.email.unique
if (!customModels.VerificationRequest) {
delete models.VerificationRequest.schema.columns.id.type
models.VerificationRequest.schema.columns.id.objectId = true
}
if (!models.User.schema.indices) { models.User.schema.indices = [] }
models.User.schema.indices.push({
name: 'email',
unique: true,
sparse: true,
columns: ['email']
})
}
const sqlite = (models, options) => {
const sqliteTransform = (models, options) => {
// Apply snake case naming strategy for SQLite databases
if (!options.namingStrategy) {
options.namingStrategy = new SnakeCaseNamingStrategy()
}
// Only transforms models that are not custom models
const { models: customModels = {} } = options
// SQLite does not support `timestamp` fields so we remap them to `datetime`
// in all models.
//
@@ -123,51 +99,66 @@ const sqlite = (models, options) => {
//
// NB: SQLite adds 'create' and 'update' fields to allow rows, but that is
// specific to SQLite and so we ignore that behaviour.
if (!customModels.User) {
for (const column in models.User.schema.columns) {
if (models.User.schema.columns[column].type === 'timestamp') {
models.User.schema.columns[column].type = 'datetime'
}
}
}
if (!customModels.Account) {
for (const column in models.Account.schema.columns) {
if (models.Account.schema.columns[column].type === 'timestamp') {
models.Account.schema.columns[column].type = 'datetime'
}
}
}
if (!customModels.Session) {
for (const column in models.Session.schema.columns) {
if (models.Session.schema.columns[column].type === 'timestamp') {
models.Session.schema.columns[column].type = 'datetime'
}
}
}
if (!customModels.VerificationRequest) {
for (const column in models.VerificationRequest.schema.columns) {
if (models.VerificationRequest.schema.columns[column].type === 'timestamp') {
models.VerificationRequest.schema.columns[column].type = 'datetime'
for (const model in models) {
for (const column in models[model].schema.columns) {
if (models[model].schema.columns[column].type === 'timestamp') {
models[model].schema.columns[column].type = 'datetime'
}
}
}
}
const mssqlTransform = (models, options) => {
// Apply snake case naming strategy for SQL Server databases
if (!options.namingStrategy) {
// @TODO Add TitleCase instead as more common MSSQL convention?
options.namingStrategy = new SnakeCaseNamingStrategy()
}
// SQL Server deprecated TIMESTAMP in favor of ROWVERSION.
// But ROWVERSION is not what it was intended in the other adapters.
for (const model in models) {
for (const column in models[model].schema.columns) {
if (models[model].schema.columns[column].type === 'timestamp') {
models[model].schema.columns[column].type = 'datetime'
}
}
}
// Support UNIQUE on on User.email that allows duplicate NULL values
// Note: This is ANSI SQL behaviour for UNIQUE not default in SQL Server
delete models.User.schema.columns.email.unique
if (!models.User.schema.indices) { models.User.schema.indices = [] }
models.User.schema.indices.push({
name: 'email',
columns: ['email'],
unique: true,
where: 'email IS NOT NULL'
})
}
export default (config, models, options) => {
// @TODO Refactor into switch statement
if ((config.type && config.type.startsWith('mongodb')) ||
(config.url && config.url.startsWith('mongodb'))) {
mongodb(models, options)
mongodbTransform(models, options)
} else if ((config.type && config.type.startsWith('postgres')) ||
(config.url && config.url.startsWith('postgres'))) {
postgres(models, options)
postgresTransform(models, options)
} else if ((config.type && config.type.startsWith('mysql')) ||
(config.url && config.url.startsWith('mysql'))) {
mysqlTransform(models, options)
} else if ((config.type && config.type.startsWith('sqlite')) ||
(config.url && config.url.startsWith('sqlite'))) {
sqlite(models, options)
sqliteTransform(models, options)
} else if ((config.type && config.type.startsWith('mssql')) ||
(config.url && config.url.startsWith('mssql'))) {
mssqlTransform(models, options)
} else {
// Apply snake case naming strategy by default for SQL databases
// For all other SQL databases (e.g. MySQL) apply snake case naming
// strategy, but otherwise use the models and schemas as they are.
if (!options.namingStrategy) {
options.namingStrategy = new SnakeCaseNamingStrategy()
}

View File

@@ -0,0 +1,18 @@
const entitiesChanged = (prevEntities, newEntities) => {
if (prevEntities.length !== newEntities.length) return true
for (let i = 0; i < prevEntities.length; i++) {
if (prevEntities[i] !== newEntities[i]) return true
}
return false
}
export const updateConnectionEntities = async (connection, entities) => {
// Check if the entities passed have changed and if so replace them
// and re-sync the typeorm connection.
if (!connection || !entitiesChanged(connection.options.entities, entities)) return
connection.options.entities = entities
connection.buildMetadatas()
if (connection.options.synchronize) {
await connection.synchronize()
}
}

View File

@@ -1,250 +1,378 @@
// fetch() is built in to Next.js 9.4 (you can use a polyfill if using an older version)
/* global fetch:false */
// Note about signIn() and signOut() methods:
//
// On signIn() and signOut() we pass 'json: true' to request a response in JSON
// instead of HTTP as redirect URLs on other domains are not returned to
// requests made using the fetch API in the browser, and we need to ask the API
// to return the response as a JSON object (the end point still defaults to
// returning an HTTP response with a redirect for non-JavaScript clients).
//
// We use HTTP POST requests with CSRF Tokens to protect against CSRF attacks.
import { useState, useEffect, useContext, createContext, createElement } from 'react'
import logger from '../lib/logger'
import _logger, { proxyLogger } from '../lib/logger'
import parseUrl from '../lib/parse-url'
// Note: In calls to fetch() from universal methods, all cookies are passed
// through from the browser, when the server makes the HTTP request, so that
// it can authenticate as the browser.
// These can be overridden with NEXTAUTH_ env vars in next.config.js
// e.g. process.env.NEXTAUTH_SITE
const NEXTAUTH_DEFAULT_BASE_URL_COOKIE_NAME = 'next-auth.base-url'
const NEXTAUTH_DEFAULT_SITE = ''
const NEXTAUTH_DEFAULT_BASE_PATH = '/api/auth'
const NEXTAUTH_DEFAULT_CLIENT_MAXAGE = 0 // e.g. 0 == disabled, 60 == 60 seconds
let NEXTAUTH_EVENT_LISTENER_ADDED = false
// Universal method (client + server)
const getSession = async ({ req } = {}) => {
const baseUrl = _baseUrl({ req })
const options = req ? { headers: { cookie: req.headers.cookie } } : {}
const session = await _fetchData(`${baseUrl}/session`, options)
_sendMessage({ event: 'session', data: { triggeredBy: 'getSession' } })
return session
// This behaviour mirrors the default behaviour for getting the site name that
// happens server side in server/index.js
// 1. An empty value is legitimate when the code is being invoked client side as
// relative URLs are valid in that context and so defaults to empty.
// 2. When invoked server side the value is picked up from an environment
// variable and defaults to 'http://localhost:3000'.
/** @type {import("types/internals/client").NextAuthConfig} */
const __NEXTAUTH = {
baseUrl: parseUrl(process.env.NEXTAUTH_URL || process.env.VERCEL_URL).baseUrl,
basePath: parseUrl(process.env.NEXTAUTH_URL).basePath,
baseUrlServer: parseUrl(process.env.NEXTAUTH_URL_INTERNAL || process.env.NEXTAUTH_URL || process.env.VERCEL_URL).baseUrl,
basePathServer: parseUrl(process.env.NEXTAUTH_URL_INTERNAL || process.env.NEXTAUTH_URL).basePath,
keepAlive: 0,
clientMaxAge: 0,
// Properties starting with _ are used for tracking internal app state
_clientLastSync: 0,
_clientSyncTimer: null,
_eventListenersAdded: false,
_clientSession: undefined,
_getSession: () => {}
}
// Universal method (client + server)
const getProviders = async ({ req } = {}) => {
const baseUrl = _baseUrl({ req })
const options = req ? { headers: { cookie: req.headers.cookie } } : {}
return _fetchData(`${baseUrl}/providers`, options)
}
const logger = proxyLogger(_logger, __NEXTAUTH.basePath)
// Universal method (client + server)
const getCsrfToken = async ({ req } = {}) => {
const baseUrl = _baseUrl({ req })
const options = req ? { headers: { cookie: req.headers.cookie } } : {}
const data = await _fetchData(`${baseUrl}/csrf`, options)
return data.csrfToken
const broadcast = BroadcastChannel()
// Add event listners on load
if (typeof window !== 'undefined' && !__NEXTAUTH._eventListenersAdded) {
__NEXTAUTH._eventListenersAdded = true
// Listen for storage events and update session if event fired from
// another window (but suppress firing another event to avoid a loop)
// Fetch new session data but tell it to not to fire another event to
// avoid an infinite loop.
// Note: We could pass session data through and do something like
// `setData(message.data)` but that can cause problems depending
// on how the session object is being used in the client; it is
// more robust to have each window/tab fetch it's own copy of the
// session object rather than share it across instances.
broadcast.receive(() => __NEXTAUTH._getSession({ event: 'storage' }))
// Listen for document visibility change events and
// if visibility of the document changes, re-fetch the session.
document.addEventListener('visibilitychange', () => {
!document.hidden && __NEXTAUTH._getSession({ event: 'visibilitychange' })
}, false)
}
// Context to store session data globally
/** @type {import("types/internals/client").SessionContext} */
const SessionContext = createContext()
// Client side method
// Hook to access the session data stored in the context
const useSession = (session) => {
const value = useContext(SessionContext)
// If we have no Provider in the tree we call the actual hook for fetching the session
if (value === undefined) {
return useSessionData(session)
}
return value
export function useSession (session) {
const context = useContext(SessionContext)
if (context) return context
return _useSessionHook(session)
}
// Internal hook for getting session from the api.
const useSessionData = (session) => {
const clientMaxAge = (process.env.NEXTAUTH_CLIENT_MAXAGE || NEXTAUTH_DEFAULT_CLIENT_MAXAGE) * 1000
function _useSessionHook (session) {
const [data, setData] = useState(session)
const [loading, setLoading] = useState(true)
const _getSession = async (sendEvent = true) => {
try {
setData(await getSession())
setLoading(false)
const [loading, setLoading] = useState(!data)
// Send event to trigger other tabs to update (unless sendEvent is false)
if (sendEvent) {
_sendMessage({ event: 'session', data: { triggeredBy: 'useSessionData' } })
}
useEffect(() => {
__NEXTAUTH._getSession = async ({ event = null } = {}) => {
try {
const triggredByEvent = event !== null
const triggeredByStorageEvent = event === 'storage'
if (typeof window !== 'undefined' && NEXTAUTH_EVENT_LISTENER_ADDED === false) {
NEXTAUTH_EVENT_LISTENER_ADDED = true
window.addEventListener('storage', async (event) => {
if (event.key === 'nextauth.message') {
const message = JSON.parse(event.newValue)
if (message.event && message.event === 'session' && message.data) {
// Fetch new session data but tell it not to fire an event to
// avoid an infinate loop.
//
// Note: We could pass session data through and do something like
// `setData(message.data)` but that causes problems depending on
// how the session object is being used and may expose session
// data to 3rd party scripts, it's safer to update the session
// this way.
await _getSession(false)
}
const clientMaxAge = __NEXTAUTH.clientMaxAge
const clientLastSync = parseInt(__NEXTAUTH._clientLastSync)
const currentTime = _now()
const clientSession = __NEXTAUTH._clientSession
// Updates triggered by a storage event *always* trigger an update and we
// always update if we don't have any value for the current session state.
if (!triggeredByStorageEvent && clientSession !== undefined) {
if (clientMaxAge === 0 && triggredByEvent !== true) {
// If there is no time defined for when a session should be considered
// stale, then it's okay to use the value we have until an event is
// triggered which updates it.
return
} else if (clientMaxAge > 0 && clientSession === null) {
// If the client doesn't have a session then we don't need to call
// the server to check if it does (if they have signed in via another
// tab or window that will come through as a triggeredByStorageEvent
// event and will skip this logic)
return
} else if (clientMaxAge > 0 && currentTime < (clientLastSync + clientMaxAge)) {
// If the session freshness is within clientMaxAge then don't request
// it again on this call (avoids too many invokations).
return
}
})
}
}
// If CLIENT_MAXAGE is greater than zero, trigger auto re-fetching session
if (clientMaxAge > 0) {
setTimeout(async (session) => {
await _getSession()
}, clientMaxAge)
if (clientSession === undefined) { __NEXTAUTH._clientSession = null }
// Update clientLastSync before making response to avoid repeated
// invokations that would otherwise be triggered while we are still
// waiting for a response.
__NEXTAUTH._clientLastSync = _now()
// If this call was invoked via a storage event (i.e. another window) then
// tell getSession not to trigger an event when it calls to avoid an
// infinate loop.
const newClientSessionData = await getSession({
triggerEvent: !triggeredByStorageEvent
})
// Save session state internally, just so we can track that we've checked
// if a session exists at least once.
__NEXTAUTH._clientSession = newClientSessionData
setData(newClientSessionData)
setLoading(false)
} catch (error) {
logger.error('CLIENT_USE_SESSION_ERROR', error)
setLoading(false)
}
} catch (error) {
logger.error('CLIENT_USE_SESSION_ERROR', error)
}
}
useEffect(() => { _getSession() }, [])
__NEXTAUTH._getSession()
})
return [data, loading]
}
// Client side method
const signin = async (provider, args) => {
const callbackUrl = (args && args.callbackUrl) ? args.callbackUrl : window.location
export async function getSession (ctx) {
const session = await _fetchData('session', ctx)
if (ctx?.triggerEvent ?? true) {
broadcast.post({ event: 'session', data: { trigger: 'getSession' } })
}
return session
}
if (!provider) {
// Redirect to sign in page if no provider specified
const baseUrl = _baseUrl()
async function getCsrfToken (ctx) {
return (await _fetchData('csrf', ctx))?.csrfToken
}
export async function getProviders () {
return _fetchData('providers')
}
export async function signIn (provider, options = {}, authorizationParams = {}) {
const {
callbackUrl = window.location,
redirect = true
} = options
const baseUrl = _apiBaseUrl()
const providers = await getProviders()
// Redirect to sign in page if no valid provider specified
if (!(provider in providers)) {
// If Provider not recognized, redirect to sign in page
window.location = `${baseUrl}/signin?callbackUrl=${encodeURIComponent(callbackUrl)}`
return
}
const isCredentials = providers[provider].type === 'credentials'
const isEmail = providers[provider].type === 'email'
const canRedirectBeDisabled = isCredentials || isEmail
const providers = await getProviders()
if (!providers[provider]) {
// If Provider not recognized, redirect to sign in page
const baseUrl = _baseUrl()
window.location = `${baseUrl}/signin?callbackUrl=${encodeURIComponent(callbackUrl)}`
} else if (providers[provider].type === 'oauth') {
// If is an OAuth provider, redirect to providers[provider].signinUrl
window.location = `${providers[provider].signinUrl}?callbackUrl=${encodeURIComponent(callbackUrl)}`
} else {
// If is any other provider type, POST to providers[provider].signinUrl (with CSRF Token)
const options = {
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: _encodedForm({
csrfToken: await getCsrfToken(),
callbackUrl: callbackUrl,
...args
})
}
const res = await fetch(providers[provider].signinUrl, options)
window.location = res.url ? res.url : callbackUrl
}
}
const signInUrl = isCredentials
? `${baseUrl}/callback/${provider}`
: `${baseUrl}/signin/${provider}`
// Client side method
const signout = async (args) => {
const callbackUrl = (args && args.callbackUrl) ? args.callbackUrl : window.location
const baseUrl = _baseUrl()
const options = {
// If is any other provider type, POST to provider URL with CSRF Token,
// callback URL and any other parameters supplied.
const fetchOptions = {
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: _encodedForm({
body: new URLSearchParams({
...options,
csrfToken: await getCsrfToken(),
callbackUrl: callbackUrl
callbackUrl,
json: true
})
}
const res = await fetch(`${baseUrl}/signout`, options)
const _signInUrl = `${signInUrl}?${new URLSearchParams(authorizationParams)}`
const res = await fetch(_signInUrl, fetchOptions)
const data = await res.json()
if (redirect || !canRedirectBeDisabled) {
const url = data.url ?? callbackUrl
window.location = url
// If url contains a hash, the browser does not reload the page. We reload manually
if (url.includes('#')) window.location.reload()
_sendMessage({ event: 'session', data: { triggeredBy: 'signout' } })
return
}
window.location = res.url ? res.url : callbackUrl
}
const error = new URL(data.url).searchParams.get('error')
// Provider to wrap the app in to make session data available globally
const Provider = ({ children, session }) => {
const value = useSession(session)
return createElement(SessionContext.Provider, { value }, children)
}
if (res.ok) {
await __NEXTAUTH._getSession({ event: 'storage' })
}
const _fetchData = async (url, options) => {
try {
const res = await fetch(url, options)
const data = await res.json()
return Promise.resolve(Object.keys(data).length > 0 ? data : null) // Return null if data empty
} catch (error) {
logger.error('CLIENT_FETCH_ERROR', url, error)
return Promise.resolve(null)
return {
error,
status: res.status,
ok: res.ok,
url: error ? null : data.url
}
}
const _baseUrl = ({ req } = {}) => {
if (req) {
// Server Side
// If we have a 'req' object are running sever side, so we should grab the
// base URL from cookie that is set by the API route - which is how config
// is shared automatically between the API route and the client.
const cookies = req ? _parseCookies(req.headers.cookie) : null
const baseUrlCookieName = process.env.NEXTAUTH_BASE_URL_COOKIE_NAME || NEXTAUTH_DEFAULT_BASE_URL_COOKIE_NAME
const cookieValue = cookies[`__Secure-${baseUrlCookieName}`] || cookies[baseUrlCookieName]
const [baseUrl] = cookieValue ? cookieValue.split('|') : [null]
return baseUrl
} else {
// Client Side
// Note: 'site' is empty by default; URL is normally relative.
const site = process.env.NEXTAUTH_SITE || NEXTAUTH_DEFAULT_SITE
const basePath = process.env.NEXTAUTH_BASE_PATH || NEXTAUTH_DEFAULT_BASE_PATH
return `${site}${basePath}`
export async function signOut (options = {}) {
const {
callbackUrl = window.location,
redirect = true
} = options
const baseUrl = _apiBaseUrl()
const fetchOptions = {
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
csrfToken: await getCsrfToken(),
callbackUrl,
json: true
})
}
const res = await fetch(`${baseUrl}/signout`, fetchOptions)
const data = await res.json()
broadcast.post({ event: 'session', data: { trigger: 'signout' } })
if (redirect) {
const url = data.url ?? callbackUrl
window.location = url
// If url contains a hash, the browser does not reload the page. We reload manually
if (url.includes('#')) window.location.reload()
return
}
await __NEXTAUTH._getSession({ event: 'storage' })
return data
}
// Adapted from https://github.com/felixfong227/simple-cookie-parser/blob/master/index.js
const _parseCookies = (string) => {
if (!string) { return {} }
try {
const object = {}
const a = string.split(';')
for (let i = 0; i < a.length; i++) {
const b = a[i].split('=')
if (b[0].length > 1 && b[1]) {
object[b[0].trim()] = decodeURIComponent(b[1])
}
// Method to set options. The documented way is to use the provider, but this
// method is being left in as an alternative, that will be helpful if/when we
// expose a vanilla JavaScript version that doesn't depend on React.
export function setOptions ({ baseUrl, basePath, clientMaxAge, keepAlive } = {}) {
if (baseUrl) __NEXTAUTH.baseUrl = baseUrl
if (basePath) __NEXTAUTH.basePath = basePath
if (clientMaxAge) __NEXTAUTH.clientMaxAge = clientMaxAge
if (keepAlive) {
__NEXTAUTH.keepAlive = keepAlive
if (typeof window === 'undefined') return
// Clear existing timer (if there is one)
if (__NEXTAUTH._clientSyncTimer !== null) {
clearTimeout(__NEXTAUTH._clientSyncTimer)
}
return object
} catch (error) {
logger.error('CLIENT_COOKIE_PARSE_ERROR', error)
return {}
// Set next timer to trigger in number of seconds
__NEXTAUTH._clientSyncTimer = setTimeout(async () => {
// Only invoke keepalive when a session exists
if (!__NEXTAUTH._clientSession) return
await __NEXTAUTH._getSession({ event: 'timer' })
}, keepAlive * 1000)
}
}
const _encodedForm = (formData) => {
return Object.keys(formData).map((key) => {
return encodeURIComponent(key) + '=' + encodeURIComponent(formData[key])
}).join('&')
export function Provider ({ children, session, options }) {
setOptions(options)
return createElement(
SessionContext.Provider,
{ value: useSession(session) },
children
)
}
const _sendMessage = (message) => {
if (typeof localStorage !== 'undefined') {
localStorage.setItem('nextauth.message', JSON.stringify(message)) // eslint-disable-line
/**
* If passed 'appContext' via getInitialProps() in _app.js
* then get the req object from ctx and use that for the
* req value to allow _fetchData to
* work seemlessly in getInitialProps() on server side
* pages *and* in _app.js.
*/
async function _fetchData (path, { ctx, req = ctx?.req } = {}) {
try {
const baseUrl = await _apiBaseUrl()
const options = req ? { headers: { cookie: req.headers.cookie } } : {}
const res = await fetch(`${baseUrl}/${path}`, options)
const data = await res.json()
return Object.keys(data).length > 0 ? data : null // Return null if data empty
} catch (error) {
logger.error('CLIENT_FETCH_ERROR', path, error)
return null
}
}
function _apiBaseUrl () {
if (typeof window === 'undefined') {
// NEXTAUTH_URL should always be set explicitly to support server side calls - log warning if not set
if (!process.env.NEXTAUTH_URL) {
logger.warn('NEXTAUTH_URL', 'NEXTAUTH_URL environment variable not set')
}
// Return absolute path when called server side
return `${__NEXTAUTH.baseUrlServer}${__NEXTAUTH.basePathServer}`
}
// Return relative path when called client side
return __NEXTAUTH.basePath
}
/** Returns the number of seconds elapsed since January 1, 1970 00:00:00 UTC. */
function _now () {
return Math.floor(Date.now() / 1000)
}
/**
* Inspired by [Broadcast Channel API](https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API)
* Only not using it directly, because Safari does not support it.
*
* https://caniuse.com/?search=broadcastchannel
*/
function BroadcastChannel (name = 'nextauth.message') {
return {
/**
* Get notified by other tabs/windows.
* @param {(message: import("types/internals/client").BroadcastMessage) => void} onReceive
*/
receive (onReceive) {
if (typeof window === 'undefined') return
window.addEventListener('storage', async (event) => {
if (event.key !== name) return
/** @type {import("types/internals/client").BroadcastMessage} */
const message = JSON.parse(event.newValue)
if (message?.event !== 'session' || !message?.data) return
onReceive(message)
})
},
/** Notify other tabs/windows. */
post (message) {
if (typeof localStorage === 'undefined') return
localStorage.setItem(name,
JSON.stringify({ ...message, timestamp: _now() })
)
}
}
}
export default {
// Some methods are exported with more than one name. This provides
// flexibility over how they can be invoked and compatibility with earlier
// releases (going back to v1 and earlier v2 beta releases).
// e.g. NextAuth.session() or const { getSession } from 'next-auth/client'
getSession,
getCsrfToken,
getProviders,
useSession,
signIn,
signOut,
Provider,
/* Deprecated / unsupported features below this line */
// Use setOptions() set options globally in the app.
setOptions,
// Some methods are exported with more than one name. This provides some
// flexibility over how they can be invoked and backwards compatibility
// with earlier releases.
options: setOptions,
session: getSession,
providers: getProviders,
csrfToken: getCsrfToken,
getSession,
getProviders,
getCsrfToken,
useSession,
Provider,
signin,
signout
signin: signIn,
signout: signOut
}

View File

@@ -1,14 +1,46 @@
:root {
--color-primary: #444;
--color-control-border: #bbb;
--color-button-hover-background: #f9f9f9;
--color-button-active-background: #f5f5f5;
--color-button-active-border: #aaa;
--border-width: 1px;
--border-radius: .3rem;
--color-error: #c94b4b;
--color-info: #157efb;
--color-info-text: #fff;
}
.__next-auth-theme-auto,
.__next-auth-theme-light {
--color-background: #fff;
--color-text: #000;
--color-primary: #444;
--color-control-border: #bbb;
--color-button-active-background: #f9f9f9;
--color-button-active-border: #aaa;
--color-seperator: #ccc;
}
.__next-auth-theme-dark {
--color-background: #000;
--color-text: #fff;
--color-primary: #ccc;
--color-control-border: #555;
--color-button-active-background: #060606;
--color-button-active-border: #666;
--color-seperator: #444;
}
@media (prefers-color-scheme: dark) {
.__next-auth-theme-auto {
--color-background: #000;
--color-text: #fff;
--color-primary: #ccc;
--color-control-border: #555;
--color-button-active-background: #060606;
--color-button-active-border: #666;
--color-seperator: #444;
}
}
body {
background-color: var(--color-background);
margin: 0;
padding: 0;
font-family: -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
@@ -18,6 +50,11 @@ h1 {
font-weight: 400;
margin-bottom: 1.5rem;
padding: 0 1rem;
color: var(--color-text);
}
p {
color: var(--color-text)
}
form {
@@ -39,10 +76,11 @@ input[type] {
width: 100%;
padding: .5rem 1rem;
border: var(--border-width) solid var(--color-control-border);
background: #fff;
background: var(--color-background);
font-size: 1rem;
border-radius: var(--border-radius);
box-shadow: inset 0 .1rem .2rem rgba(0,0,0,.2);
box-shadow: inset 0 .1rem .2rem rgba(0, 0, 0, .2);
color: var(--color-text);
&:focus {
box-shadow: none;
@@ -59,9 +97,10 @@ p {
a.button {
text-decoration: none;
line-height: 1rem;
&:link,
&:visited {
background-color: #fff;
background-color: var(--color-background);
color: var(--color-primary);
}
}
@@ -72,21 +111,20 @@ a.button {
padding: .75rem 1rem;
border: var(--border-width) solid var(--color-control-border);
color: var(--color-primary);
background-color: #fff;
background-color: var(--color-background);
font-size: 1rem;
border-radius: var(--border-radius);
transition: all .1s ease-in-out;
box-shadow: 0 0.15rem 0.3rem rgba(0,0,0,.15), inset 0 .1rem .2rem #fff, inset 0 -.1rem .1rem rgba(0,0,0,.05);
transition: all .1s ease-in-out;
box-shadow: 0 0.15rem 0.3rem rgba(0, 0, 0, .15), inset 0 .1rem .2rem var(--color-background), inset 0 -.1rem .1rem rgba(0, 0, 0, .05);
font-weight: 500;
position: relative;
&:hover {
background-color: var(--color-button-hover-background);
cursor: pointer;
}
&:active {
box-shadow: 0 0.15rem 0.3rem rgba(0,0,0,.15), inset 0 .1rem .2rem #fff, inset 0 -.1rem .1rem rgba(0,0,0,.1);
box-shadow: 0 0.15rem 0.3rem rgba(0, 0, 0, .15), inset 0 .1rem .2rem var(--color-background), inset 0 -.1rem .1rem rgba(0, 0, 0, .1);
background-color: var(--color-button-active-background);
border-color: var(--color-button-active-border);
cursor: pointer;
@@ -98,20 +136,21 @@ a.site {
text-decoration: none;
font-size: 1rem;
line-height: 2rem;
&:hover {
text-decoration: underline;
}
}
.page {
position: absolute;
width: 100%;
position: absolute;
width: 100%;
height: 100%;
display: table;
margin: 0;
padding: 0;
> div {
>div {
display: table-cell;
vertical-align: middle;
text-align: center;
@@ -126,12 +165,14 @@ a.site {
padding-right: 2rem;
margin-top: .5rem;
}
.message {
margin-bottom: 1.5rem;
}
}
.signin {
button,
a.button,
input[type="text"] {
@@ -143,29 +184,48 @@ a.site {
hr {
display: block;
border: 0;
border-top: 1px solid #ccc;
border-top: 1px solid var(--color-seperator);
margin: 1.5em auto 0 auto;
overflow: visible;
&::before {
content: "or";
background: #fff;
background: var(--color-background);
color: #888;
padding: 0 .4rem;
position: relative;
top: -.6rem;
}
}
> div,
.error {
background: #f5f5f5;
font-weight: 500;
border-radius: 0.3rem;
background: var(--color-info);
p {
text-align: left;
padding: 0.5rem 1rem;
font-size: 0.9rem;
line-height: 1.2rem;
color: var(--color-info-text);
}
}
>div,
form {
display: block;
margin: 0 auto 0.5rem auto;
input[type] {
margin-bottom: 0.5rem;
}
button {
width: 100%;
}
max-width: 300px;
}
}

View File

@@ -4,7 +4,8 @@
import fs from 'fs'
import path from 'path'
const pathToCss = path.join(__dirname, '/index.css')
const css = fs.readFileSync(pathToCss, 'utf8')
const pathToCss = path.join(process.cwd(), '/dist/css/index.css')
export default () => css
export default function css () {
return fs.readFileSync(pathToCss, 'utf8')
}

View File

@@ -1,8 +1,7 @@
class UnknownError extends Error {
export class UnknownError extends Error {
constructor (message) {
super(message)
this.name = 'UnknownError'
this.message = message
}
toJSON () {
@@ -16,26 +15,25 @@ class UnknownError extends Error {
}
}
class CreateUserError extends UnknownError {
export class CreateUserError extends UnknownError {
constructor (message) {
super(message)
this.name = 'CreateUserError'
this.message = message
}
}
// Thrown when an Email address is already associated with an account
// but the user is trying an oAuth account that is not linked to it.
class AccountNotLinkedError extends UnknownError {
// but the user is trying an OAuth account that is not linked to it.
export class AccountNotLinkedError extends UnknownError {
constructor (message) {
super(message)
this.name = 'AccountNotLinkedError'
this.message = message
}
}
module.exports = {
UnknownError,
CreateUserError,
AccountNotLinkedError
export class OAuthCallbackError extends UnknownError {
constructor (message) {
super(message)
this.name = 'OAuthCallbackError'
}
}

View File

@@ -1,44 +1,187 @@
import jwt from 'jsonwebtoken'
import CryptoJS from 'crypto-js'
import crypto from 'crypto'
import jose from 'jose'
import logger from './logger'
const encode = async ({ secret, key = secret, token = {}, maxAge }) => {
// If maxAge is set remove any existing created/expiry dates and replace them
if (maxAge) {
if (token.iat) { delete token.iat }
if (token.exp) { delete token.exp }
// Set default algorithm to use for auto-generated signing key
const DEFAULT_SIGNATURE_ALGORITHM = 'HS512'
// Set default algorithm for auto-generated symmetric encryption key
const DEFAULT_ENCRYPTION_ALGORITHM = 'A256GCM'
// Use encryption or not by default
const DEFAULT_ENCRYPTION_ENABLED = false
const DEFAULT_MAX_AGE = 30 * 24 * 60 * 60 // 30 days
async function encode ({
token = {},
maxAge = DEFAULT_MAX_AGE,
secret,
signingKey,
signingOptions = {
expiresIn: `${maxAge}s`
},
encryptionKey,
encryptionOptions = {
alg: 'dir',
enc: DEFAULT_ENCRYPTION_ALGORITHM,
zip: 'DEF'
},
encryption = DEFAULT_ENCRYPTION_ENABLED
} = {}) {
// Signing Key
const _signingKey = signingKey
? jose.JWK.asKey(JSON.parse(signingKey))
: getDerivedSigningKey(secret)
// Sign token
const signedToken = jose.JWT.sign(token, _signingKey, signingOptions)
if (encryption) {
// Encryption Key
const _encryptionKey = encryptionKey
? jose.JWK.asKey(JSON.parse(encryptionKey))
: getDerivedEncryptionKey(secret)
// Encrypt token
return jose.JWE.encrypt(signedToken, _encryptionKey, encryptionOptions)
}
const signedToken = jwt.sign(token, secret, { expiresIn: maxAge })
const encryptedToken = CryptoJS.AES.encrypt(signedToken, key).toString()
return encryptedToken
return signedToken
}
const decode = async ({ secret, key = secret, token, maxAge }) => {
async function decode ({
secret,
token,
maxAge = DEFAULT_MAX_AGE,
signingKey,
verificationKey = signingKey, // Optional (defaults to encryptionKey)
verificationOptions = {
maxTokenAge: `${maxAge}s`,
algorithms: [DEFAULT_SIGNATURE_ALGORITHM]
},
encryptionKey,
decryptionKey = encryptionKey, // Optional (defaults to encryptionKey)
decryptionOptions = {
algorithms: [DEFAULT_ENCRYPTION_ALGORITHM]
},
encryption = DEFAULT_ENCRYPTION_ENABLED
} = {}) {
if (!token) return null
const decryptedBytes = CryptoJS.AES.decrypt(token, key)
const decryptedToken = decryptedBytes.toString(CryptoJS.enc.Utf8)
const verifiedToken = jwt.verify(decryptedToken, secret, { maxAge })
return verifiedToken
let tokenToVerify = token
if (encryption) {
// Encryption Key
const _encryptionKey = decryptionKey
? jose.JWK.asKey(JSON.parse(decryptionKey))
: getDerivedEncryptionKey(secret)
// Decrypt token
const decryptedToken = jose.JWE.decrypt(token, _encryptionKey, decryptionOptions)
tokenToVerify = decryptedToken.toString('utf8')
}
// Signing Key
const _signingKey = verificationKey
? jose.JWK.asKey(JSON.parse(verificationKey))
: getDerivedSigningKey(secret)
// Verify token
return jose.JWT.verify(tokenToVerify, _signingKey, verificationOptions)
}
// This is a simple helper method to make it easier to use JWT from an API route
const getJwt = async ({ req, secret, cookieName }) => {
if (!req || !secret) throw new Error('Must pass { req, secret } to getJWT()')
/**
* Server-side method to retrieve the JWT from `req`.
* @param {{
* req: NextApiRequest
* secureCookie?: boolean
* cookieName?: string
* raw?: boolean
* }} params
*/
async function getToken (params) {
const {
req,
// Use secure prefix for cookie name, unless URL is NEXTAUTH_URL is http://
// or not set (e.g. development or test instance) case use unprefixed name
secureCookie = !(!process.env.NEXTAUTH_URL || process.env.NEXTAUTH_URL.startsWith('http://')),
cookieName = (secureCookie) ? '__Secure-next-auth.session-token' : 'next-auth.session-token',
raw = false
} = params
if (!req) throw new Error('Must pass `req` to JWT getToken()')
const secureCookieName = '__Secure-next-auth.session-token'
const insecureCookieName = 'next-auth.session-token'
const cookieValue = cookieName ? req.cookies[cookieName] : req.cookies[secureCookieName] || req.cookies[insecureCookieName]
// Try to get token from cookie
let token = req.cookies[cookieName]
if (!cookieValue) { return null }
// If cookie not found in cookie look for bearer token in authorization header.
// This allows clients that pass through tokens in headers rather than as
// cookies to use this helper function.
if (!token && req.headers.authorization?.split(' ')[0] === 'Bearer') {
const urlEncodedToken = req.headers.authorization.split(' ')[1]
token = decodeURIComponent(urlEncodedToken)
}
if (raw) {
return token
}
try {
return await decode({ secret, token: cookieValue })
} catch (error) {
return decode({ token, ...params })
} catch {
return null
}
}
// Generate warning (but only once at startup) when auto-generated keys are used
let DERIVED_SIGNING_KEY_WARNING = false
let DERIVED_ENCRYPTION_KEY_WARNING = false
// Do the better hkdf of Node.js one added in `v15.0.0` and Third Party one
function hkdf (secret, { byteLength, encryptionInfo, digest = 'sha256' }) {
if (crypto.hkdfSync) {
return Buffer.from(
crypto.hkdfSync(
digest,
secret,
Buffer.alloc(0),
encryptionInfo,
byteLength
)
)
}
return require('futoin-hkdf')(secret, byteLength, { info: encryptionInfo, hash: digest })
}
function getDerivedSigningKey (secret) {
if (!DERIVED_SIGNING_KEY_WARNING) {
logger.warn('JWT_AUTO_GENERATED_SIGNING_KEY')
DERIVED_SIGNING_KEY_WARNING = true
}
const buffer = hkdf(secret, {
byteLength: 64,
encryptionInfo: 'NextAuth.js Generated Signing Key'
})
const key = jose.JWK.asKey(buffer, { alg: DEFAULT_SIGNATURE_ALGORITHM, use: 'sig', kid: 'nextauth-auto-generated-signing-key' })
return key
}
function getDerivedEncryptionKey (secret) {
if (!DERIVED_ENCRYPTION_KEY_WARNING) {
logger.warn('JWT_AUTO_GENERATED_ENCRYPTION_KEY')
DERIVED_ENCRYPTION_KEY_WARNING = true
}
const buffer = hkdf(secret, {
byteLength: 32,
encryptionInfo: 'NextAuth.js Generated Encryption Key'
})
const key = jose.JWK.asKey(buffer, { alg: DEFAULT_ENCRYPTION_ALGORITHM, use: 'enc', kid: 'nextauth-auto-generated-encryption-key' })
return key
}
export default {
encode,
decode,
getJwt
getToken
}

View File

@@ -1,23 +1,82 @@
const logger = {
error: (errorCode, ...text) => {
if (console) {
!text
? console.error(errorCode)
: console.error(
`[next-auth][error][${errorCode}]`,
text,
`\nhttps://next-auth.js.org/errors#${errorCode.toLowerCase()}`
)
}
/** @type {import("types").LoggerInstance} */
const _logger = {
error (code, ...message) {
console.error(
`[next-auth][error][${code.toLowerCase()}]`,
`\nhttps://next-auth.js.org/errors#${code.toLowerCase()}`,
...message
)
},
debug: (debugCode, ...text) => {
if (process && process.env && process.env._NEXT_AUTH_DEBUG) {
console.log(
`[next-auth][debug][${debugCode}]`,
text
)
}
warn (code, ...message) {
console.warn(
`[next-auth][warn][${code.toLowerCase()}]`,
`\nhttps://next-auth.js.org/warnings#${code.toLowerCase()}`,
...message
)
},
debug (code, ...message) {
if (!process?.env?._NEXTAUTH_DEBUG) return
console.log(
`[next-auth][debug][${code.toLowerCase()}]`,
...message
)
}
}
export default logger
/**
* Override the built-in logger.
* Any `undefined` level will use the default logger.
* @param {Partial<import("types").LoggerInstance>} newLogger
*/
export function setLogger (newLogger = {}) {
if (newLogger.error) _logger.error = newLogger.error
if (newLogger.warn) _logger.warn = newLogger.warn
if (newLogger.debug) _logger.debug = newLogger.debug
}
export default _logger
/**
* Serializes client-side log messages and sends them to the server
* @param {import("types").LoggerInstance} logger
* @param {string} basePath
* @return {import("types").LoggerInstance}
*/
export function proxyLogger (logger = _logger, basePath) {
try {
if (typeof window === 'undefined') {
return logger
}
const clientLogger = console
for (const level in logger) {
clientLogger[level] = (code, ...message) => {
_logger[level](code, ...message) // Log on client as usual
const url = `${basePath}/_log`
const body = new URLSearchParams({
level,
code,
message: JSON.stringify(message.map(m => {
if (m instanceof Error) {
// Serializing errors: https://iaincollins.medium.com/error-handling-in-javascript-a6172ccdf9af
return { name: m.name, message: m.message, stack: m.stack }
}
return m
}))
})
if (navigator.sendBeacon) {
return navigator.sendBeacon(url, body)
}
return fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body
})
}
}
return clientLogger
} catch {
return _logger
}
}

27
src/lib/parse-url.js Normal file
View File

@@ -0,0 +1,27 @@
/**
* Simple universal (client/server) function to split host and path
* We use this rather than a library because we need to use the same logic both
* client and server side and we only need to parse out the host and path, while
* supporting a default value, so a simple split is sufficent.
* @param {string} url
*/
export default function parseUrl (url) {
// Default values
const defaultHost = 'http://localhost:3000'
const defaultPath = '/api/auth'
if (!url) { url = `${defaultHost}${defaultPath}` }
// Default to HTTPS if no protocol explictly specified
const protocol = url.startsWith('http:') ? 'http' : 'https'
// Normalize URLs by stripping protocol and no trailing slash
url = url.replace(/^https?:\/\//, '').replace(/\/$/, '')
// Simple split based on first /
const [_host, ..._path] = url.split('/')
const baseUrl = _host ? `${protocol}://${_host}` : defaultHost
const basePath = _path.length > 0 ? `/${_path.join('/')}` : defaultPath
return { baseUrl, basePath }
}

View File

@@ -1,5 +1,3 @@
import jwt from 'jsonwebtoken'
export default (options) => {
return {
id: 'apple',
@@ -13,36 +11,20 @@ export default (options) => {
profileUrl: null,
idToken: true,
profile: (profile) => {
// The name of the user will only return on first login
return {
id: profile.sub,
name: profile.name == null ? profile.sub : profile.name,
name: profile.user != null ? profile.user.name.firstName + ' ' + profile.user.name.lastName : null,
email: profile.email
}
},
clientId: null,
clientSecret: {
appleId: null,
teamId: null,
privateKey: null,
keyId: null
},
clientSecretCallback: async ({ appleId, keyId, teamId, privateKey }) => {
const response = jwt.sign(
{
iss: teamId,
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + (86400 * 180), // 6 months
aud: 'https://appleid.apple.com',
sub: appleId
},
privateKey,
{
algorithm: 'ES256',
keyid: keyId
}
)
return Promise.resolve(response)
},
protection: 'none', // REVIEW: Apple does not support state, as far as I know. Can we use "pkce" then?
...options
}
}

View File

@@ -0,0 +1,24 @@
export default (options) => {
return {
id: 'atlassian',
name: 'Atlassian',
type: 'oauth',
version: '2.0',
params: {
grant_type: 'authorization_code'
},
accessTokenUrl: 'https://auth.atlassian.com/oauth/token',
authorizationUrl:
'https://auth.atlassian.com/authorize?audience=api.atlassian.com&response_type=code&prompt=consent',
profileUrl: 'https://api.atlassian.com/me',
profile: (profile) => {
return {
id: profile.account_id,
name: profile.name,
email: profile.email,
image: profile.picture
}
},
...options
}
}

View File

@@ -4,11 +4,11 @@ export default (options) => {
name: 'Auth0',
type: 'oauth',
version: '2.0',
params: { grant_type: 'authorization_code', response_type: 'code' },
params: { grant_type: 'authorization_code' },
scope: 'openid email profile',
accessTokenUrl: `https://${options.subdomain}.auth0/oauth/token`,
authorizationUrl: `https://${options.subdomain}.auth0.com/authorize?`,
profileUrl: `http://${options.subdomain}.auth0.com/userinfo`,
accessTokenUrl: `https://${options.domain}/oauth/token`,
authorizationUrl: `https://${options.domain}/authorize?response_type=code`,
profileUrl: `https://${options.domain}/userinfo`,
profile: (profile) => {
return {
id: profile.sub,

View File

@@ -0,0 +1,24 @@
export default (options) => {
const tenant = options.tenantId ? options.tenantId : 'common'
return {
id: 'azure-ad-b2c',
name: 'Azure Active Directory B2C',
type: 'oauth',
version: '2.0',
params: {
grant_type: 'authorization_code'
},
accessTokenUrl: `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/token`,
authorizationUrl: `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/authorize?response_type=code&response_mode=query`,
profileUrl: 'https://graph.microsoft.com/v1.0/me/',
profile: (profile) => {
return {
id: profile.id,
name: profile.displayName,
email: profile.userPrincipalName
}
},
...options
}
}

20
src/providers/basecamp.js Normal file
View File

@@ -0,0 +1,20 @@
export default (options) => {
return {
id: 'basecamp',
name: 'Basecamp',
type: 'oauth',
version: '2.0',
accessTokenUrl: 'https://launchpad.37signals.com/authorization/token?type=web_server',
authorizationUrl: 'https://launchpad.37signals.com/authorization/new?type=web_server',
profileUrl: 'https://launchpad.37signals.com/authorization.json',
profile: (profile) => {
return {
id: profile.identity.id,
name: `${profile.identity.first_name} ${profile.identity.last_name}`,
email: profile.identity.email_address,
image: null
}
},
...options
}
}

View File

@@ -0,0 +1,29 @@
export default (options) => {
const { region } = options
return {
id: 'battlenet',
name: 'Battle.net',
type: 'oauth',
version: '2.0',
scope: 'openid',
params: { grant_type: 'authorization_code' },
accessTokenUrl:
region === 'CN'
? 'https://www.battlenet.com.cn/oauth/token'
: `https://${region}.battle.net/oauth/token`,
authorizationUrl:
region === 'CN'
? 'https://www.battlenet.com.cn/oauth/authorize?response_type=code'
: `https://${region}.battle.net/oauth/authorize?response_type=code`,
profileUrl: 'https://us.battle.net/oauth/userinfo',
profile: (profile) => {
return {
id: profile.id,
name: profile.battletag,
email: null,
image: null
}
},
...options
}
}

30
src/providers/bungie.js Normal file
View File

@@ -0,0 +1,30 @@
export default (options) => {
return {
id: 'bungie',
name: 'Bungie',
type: 'oauth',
version: '2.0',
scope: '',
params: { reauth: 'true', grant_type: 'authorization_code' },
accessTokenUrl: 'https://www.bungie.net/platform/app/oauth/token/',
requestTokenUrl: 'https://www.bungie.net/platform/app/oauth/token/',
authorizationUrl: 'https://www.bungie.net/en/OAuth/Authorize?response_type=code',
profileUrl: 'https://www.bungie.net/platform/User/GetBungieAccount/{membershipId}/254/',
profile: (profile) => {
const { bungieNetUser: user } = profile.Response
return {
id: user.membershipId,
name: user.displayName,
image: `https://www.bungie.net${user.profilePicturePath.startsWith('/') ? '' : '/'}${user.profilePicturePath}`,
email: null
}
},
headers: {
'X-API-Key': null
},
clientId: null,
clientSecret: null,
...options
}
}

23
src/providers/cognito.js Normal file
View File

@@ -0,0 +1,23 @@
export default (options) => {
const { domain } = options
return {
id: 'cognito',
name: 'Cognito',
type: 'oauth',
version: '2.0',
scope: 'openid profile email',
params: { grant_type: 'authorization_code' },
accessTokenUrl: `https://${domain}/oauth2/token`,
authorizationUrl: `https://${domain}/oauth2/authorize?response_type=code`,
profileUrl: `https://${domain}/oauth2/userInfo`,
profile: (profile) => {
return {
id: profile.sub,
name: profile.username,
email: profile.email,
image: null
}
},
...options
}
}

View File

@@ -6,15 +6,21 @@ export default (options) => {
version: '2.0',
scope: 'identify email',
params: { grant_type: 'authorization_code' },
accessTokenUrl: 'https://discordapp.com/api/oauth2/token',
authorizationUrl:
'https://discordapp.com/api/oauth2/authorize?response_type=code&prompt=consent',
profileUrl: 'https://discordapp.com/api/users/@me',
accessTokenUrl: 'https://discord.com/api/oauth2/token',
authorizationUrl: 'https://discord.com/api/oauth2/authorize?response_type=code&prompt=none',
profileUrl: 'https://discord.com/api/users/@me',
profile: (profile) => {
if (profile.avatar === null) {
const defaultAvatarNumber = parseInt(profile.discriminator) % 5
profile.image_url = `https://cdn.discordapp.com/embed/avatars/${defaultAvatarNumber}.png`
} else {
const format = profile.avatar.startsWith('a_') ? 'gif' : 'png'
profile.image_url = `https://cdn.discordapp.com/avatars/${profile.id}/${profile.avatar}.${format}`
}
return {
id: profile.id,
name: profile.username,
image: `https://cdn.discordapp.com/avatars/${profile.id}/${profile.avatar}.png`,
image: profile.image_url,
email: profile.email
}
},

View File

@@ -22,22 +22,23 @@ export default (options) => {
}
}
const sendVerificationRequest = ({ identifier: emailAddress, url, token, site, provider }) => {
const sendVerificationRequest = ({ identifier: email, url, baseUrl, provider }) => {
return new Promise((resolve, reject) => {
const { server, from } = provider
const siteName = site.replace(/^https?:\/\//, '')
// Strip protocol from URL and use domain as site name
const site = baseUrl.replace(/^https?:\/\//, '')
nodemailer
.createTransport(server)
.sendMail({
to: emailAddress,
to: email,
from,
subject: `Sign in to ${siteName}`,
text: text({ url, siteName }),
html: html({ url, siteName })
subject: `Sign in to ${site}`,
text: text({ url, site, email }),
html: html({ url, site, email })
}, (error) => {
if (error) {
logger.error('SEND_VERIFICATION_EMAIL_ERROR', emailAddress, error)
logger.error('SEND_VERIFICATION_EMAIL_ERROR', email, error)
return reject(new Error('SEND_VERIFICATION_EMAIL_ERROR', error))
}
return resolve()
@@ -46,28 +47,55 @@ const sendVerificationRequest = ({ identifier: emailAddress, url, token, site, p
}
// Email HTML body
const html = ({ url, siteName }) => {
const buttonBackgroundColor = '#444444'
const html = ({ url, site, email }) => {
// Insert invisible space into domains and email address to prevent both the
// email address and the domain from being turned into a hyperlink by email
// clients like Outlook and Apple mail, as this is confusing because it seems
// like they are supposed to click on their email address to sign in.
const escapedEmail = `${email.replace(/\./g, '&#8203;.')}`
const escapedSite = `${site.replace(/\./g, '&#8203;.')}`
// Some simple styling options
const backgroundColor = '#f9f9f9'
const textColor = '#444444'
const mainBackgroundColor = '#ffffff'
const buttonBackgroundColor = '#346df1'
const buttonBorderColor = '#346df1'
const buttonTextColor = '#ffffff'
return `
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="center" style="padding: 8px 0; font-size: 22px; font-family: Helvetica, Arial, sans-serif; color: #888888;">
${siteName}
</td>
</tr>
<tr>
<td align="center" style="padding: 16px 0;">
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="center" style="border-radius: 3px;" bgcolor="${buttonBackgroundColor}"><a href="${url}" target="_blank" style="font-size: 18px; font-family: Helvetica, Arial, sans-serif; color: ${buttonTextColor}; text-decoration: none; text-decoration: none;border-radius: 3px; padding: 12px 18px; border: 1px solid ${buttonBackgroundColor}; display: inline-block; font-weight: bold;">Sign in</a></td>
</tr>
</table>
</td>
</tr>
</table>
<body style="background: ${backgroundColor};">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="center" style="padding: 10px 0px 20px 0px; font-size: 22px; font-family: Helvetica, Arial, sans-serif; color: ${textColor};">
<strong>${escapedSite}</strong>
</td>
</tr>
</table>
<table width="100%" border="0" cellspacing="20" cellpadding="0" style="background: ${mainBackgroundColor}; max-width: 600px; margin: auto; border-radius: 10px;">
<tr>
<td align="center" style="padding: 10px 0px 0px 0px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; color: ${textColor};">
Sign in as <strong>${escapedEmail}</strong>
</td>
</tr>
<tr>
<td align="center" style="padding: 20px 0;">
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="center" style="border-radius: 5px;" bgcolor="${buttonBackgroundColor}"><a href="${url}" target="_blank" style="font-size: 18px; font-family: Helvetica, Arial, sans-serif; color: ${buttonTextColor}; text-decoration: none; text-decoration: none;border-radius: 5px; padding: 10px 20px; border: 1px solid ${buttonBorderColor}; display: inline-block; font-weight: bold;">Sign in</a></td>
</tr>
</table>
</td>
</tr>
<tr>
<td align="center" style="padding: 0px 0px 10px 0px; font-size: 16px; line-height: 22px; font-family: Helvetica, Arial, sans-serif; color: ${textColor};">
If you did not request this email you can safely ignore it.
</td>
</tr>
</table>
</body>
`
}
// Email Text body (fallback for email clients that don't render HTML, e.g. feature phones)
const text = ({ url, siteName }) => `Sign in to ${siteName}\n${url}\n\n`
const text = ({ url, site }) => `Sign in to ${site}\n${url}\n\n`

View File

@@ -0,0 +1,21 @@
export default (options) => {
return {
id: 'eveonline',
name: 'EVE Online',
type: 'oauth',
version: '2.0',
params: { grant_type: 'authorization_code' },
accessTokenUrl: 'https://login.eveonline.com/oauth/token',
authorizationUrl: 'https://login.eveonline.com/oauth/authorize?response_type=code',
profileUrl: 'https://login.eveonline.com/oauth/verify',
profile: (profile) => {
return {
id: profile.CharacterID,
name: profile.CharacterName,
image: `https://image.eveonline.com/Character/${profile.CharacterID}_128.jpg`,
email: null
}
},
...options
}
}

25
src/providers/faceit.js Normal file
View File

@@ -0,0 +1,25 @@
export default (options) => {
return {
id: 'faceit',
name: 'FACEIT',
type: 'oauth',
version: '2.0',
params: { grant_type: 'authorization_code' },
headers: {
Authorization: `Basic ${Buffer.from(`${options.clientId}:${options.clientSecret}`).toString('base64')}`
},
accessTokenUrl: 'https://api.faceit.com/auth/v1/oauth/token',
authorizationUrl: 'https://accounts.faceit.com/accounts?redirect_popup=true&response_type=code',
profileUrl: 'https://api.faceit.com/auth/v1/resources/userinfo',
profile (profile) {
const { guid: id, nickname: name, email, picture: image } = profile
return {
id,
name,
email,
image
}
},
...options
}
}

View File

@@ -0,0 +1,22 @@
export default ({ apiVersion, ...options }) => {
return {
id: 'foursquare',
name: 'Foursquare',
type: 'oauth',
version: '2.0',
params: { grant_type: 'authorization_code' },
accessTokenUrl: 'https://foursquare.com/oauth2/access_token',
authorizationUrl:
'https://foursquare.com/oauth2/authenticate?response_type=code',
profileUrl: `https://api.foursquare.com/v2/users/self?v=${apiVersion}`,
profile: (profile) => {
return {
id: profile.id,
name: `${profile.firstName} ${profile.lastName}`,
image: `${profile.prefix}original${profile.suffix}`,
email: profile.contact.email
}
},
...options
}
}

View File

@@ -0,0 +1,27 @@
export default (options) => {
let authorizationUrl = `https://${options.domain}/oauth2/authorize?response_type=code`
if (options.tenantId) {
authorizationUrl += `&tenantId=${options.tenantId}`
}
return {
id: 'fusionauth',
name: 'FusionAuth',
type: 'oauth',
version: '2.0',
scope: 'openid',
params: { grant_type: 'authorization_code' },
accessTokenUrl: `https://${options.domain}/oauth2/token`,
authorizationUrl,
profileUrl: `https://${options.domain}/oauth2/userinfo`,
profile: (profile) => {
return {
id: profile.sub,
name: profile.name,
email: profile.email,
image: profile.picture
}
},
...options
}
}

View File

@@ -11,7 +11,7 @@ export default (options) => {
profile: (profile) => {
return {
id: profile.id,
name: profile.name,
name: profile.name || profile.login,
email: profile.email,
image: profile.avatar_url
}

View File

@@ -12,7 +12,6 @@ export default (options) => {
profile: (profile) => {
return { ...profile, id: profile.sub }
},
setGetAccessTokenAuthHeader: false,
...options
}
}

View File

@@ -1,37 +1,83 @@
import Auth0 from './auth0'
import Apple from './apple'
import Atlassian from './atlassian'
import Auth0 from './auth0'
import AzureADB2C from './azure-ad-b2c'
import Basecamp from './basecamp'
import BattleNet from './battlenet'
import Box from './box'
import Bungie from './bungie'
import Cognito from './cognito'
import Credentials from './credentials'
import Discord from './discord'
import Email from './email'
import Facebook from './facebook' // @TODO
import EVEOnline from './eveonline'
import Facebook from './facebook'
import FACEIT from './faceit'
import Foursquare from './foursquare'
import FusionAuth from './fusionauth'
import GitHub from './github'
import GitLab from './gitlab'
import Google from './google'
import IdentityServer4 from './identity-server4'
import Mixer from './mixer'
import Instagram from './instagram'
import Kakao from './kakao'
import LINE from './line'
import LinkedIn from './linkedin'
import MailRu from './mailru'
import Medium from './medium'
import Netlify from './netlify'
import Okta from './okta'
import Osso from './osso'
import Reddit from './reddit'
import Salesforce from './salesforce'
import Slack from './slack'
import Spotify from './spotify'
import Strava from './strava'
import Twitch from './twitch'
import Twitter from './twitter'
import VK from './vk'
import Yandex from './yandex'
import Zoho from './zoho'
export default {
Auth0,
Apple,
Atlassian,
Auth0,
AzureADB2C,
Basecamp,
BattleNet,
Box,
Bungie,
Cognito,
Credentials,
Discord,
Email,
EVEOnline,
Facebook,
FACEIT,
Foursquare,
FusionAuth,
GitHub,
GitLab,
Google,
IdentityServer4,
Mixer,
Instagram,
Kakao,
LINE,
LinkedIn,
MailRu,
Medium,
Netlify,
Okta,
Osso,
Reddit,
Salesforce,
Slack,
Twitter,
Spotify,
Strava,
Twitch,
Yandex
Twitter,
VK,
Yandex,
Zoho
}

View File

@@ -0,0 +1,50 @@
/**
* @type {import("types/providers").OAuthProvider} options
* @example
*
* ```js
* // pages/api/auth/[...nextauth].js
* import Providers from `next-auth/providers`
* ...
* providers: [
* Providers.Instagram({
* clientId: process.env.INSTAGRAM_CLIENT_ID,
* clientSecret: process.env.INSTAGRAM_CLIENT_SECRET
* })
* ]
* ...
*
* // pages/index
* import { signIn } from "next-auth/client"
* ...
* <button onClick={() => signIn("instagram")}>
* Sign in
* </button>
* ...
* ```
* [NextAuth.js Documentation](https://next-auth.js.org/providers/instagram) | [Instagram Documentation](https://developers.facebook.com/docs/instagram-basic-display-api/getting-started) | [Configuration](https://developers.facebook.com/apps)
*/
export default function Instagram(options) {
return {
id: "instagram",
name: "Instagram",
type: "oauth",
version: "2.0",
scope: "user_profile",
params: { grant_type: "authorization_code" },
accessTokenUrl: "https://api.instagram.com/oauth/access_token",
authorizationUrl:
"https://api.instagram.com/oauth/authorize?response_type=code",
profileUrl:
"https://graph.instagram.com/me?fields=id,username,account_type,name",
async profile(profile) {
return {
id: profile.id,
name: profile.username,
email: null,
image: null,
}
},
...options,
}
}

21
src/providers/kakao.js Normal file
View File

@@ -0,0 +1,21 @@
export default (options) => {
return {
id: 'kakao',
name: 'Kakao',
type: 'oauth',
version: '2.0',
params: { grant_type: 'authorization_code' },
accessTokenUrl: 'https://kauth.kakao.com/oauth/token',
authorizationUrl: 'https://kauth.kakao.com/oauth/authorize?response_type=code',
profileUrl: 'https://kapi.kakao.com/v2/user/me',
profile: (profile) => {
return {
id: profile.id,
name: profile.kakao_account?.profile.nickname,
email: profile.kakao_account?.email,
image: profile.kakao_account?.profile.profile_image_url
}
},
...options
}
}

22
src/providers/line.js Normal file
View File

@@ -0,0 +1,22 @@
export default (options) => {
return {
id: 'line',
name: 'LINE',
type: 'oauth',
version: '2.0',
scope: 'profile openid',
params: { grant_type: 'authorization_code' },
accessTokenUrl: 'https://api.line.me/oauth2/v2.1/token',
authorizationUrl: 'https://access.line.me/oauth2/v2.1/authorize?response_type=code',
profileUrl: 'https://api.line.me/v2/profile',
profile: (profile) => {
return {
id: profile.userId,
name: profile.displayName,
email: null,
image: profile.pictureUrl
}
},
...options
}
}

26
src/providers/linkedin.js Normal file
View File

@@ -0,0 +1,26 @@
export default (options) => {
return {
id: 'linkedin',
name: 'LinkedIn',
type: 'oauth',
version: '2.0',
scope: 'r_liteprofile',
params: {
grant_type: 'authorization_code',
client_id: options.clientId,
client_secret: options.clientSecret
},
accessTokenUrl: 'https://www.linkedin.com/oauth/v2/accessToken',
authorizationUrl: 'https://www.linkedin.com/oauth/v2/authorization?response_type=code',
profileUrl: 'https://api.linkedin.com/v2/me?projection=(id,localizedFirstName,localizedLastName)',
profile: (profile) => {
return {
id: profile.id,
name: profile.localizedFirstName + ' ' + profile.localizedLastName,
email: null,
image: null
}
},
...options
}
}

25
src/providers/mailru.js Normal file
View File

@@ -0,0 +1,25 @@
export default (options) => {
return {
id: 'mailru',
name: 'Mail.ru',
type: 'oauth',
version: '2.0',
scope: 'userinfo',
params: {
grant_type: 'authorization_code'
},
accessTokenUrl: 'https://oauth.mail.ru/token',
requestTokenUrl: 'https://oauth.mail.ru/token',
authorizationUrl: 'https://oauth.mail.ru/login?response_type=code',
profileUrl: 'https://oauth.mail.ru/userinfo',
profile: (profile) => {
return {
id: profile.id,
name: profile.name,
email: profile.email,
image: profile.image
}
},
...options
}
}

22
src/providers/medium.js Normal file
View File

@@ -0,0 +1,22 @@
export default (options) => {
return {
id: 'medium',
name: 'Medium',
type: 'oauth',
version: '2.0',
scope: 'basicProfile',
params: { grant_type: 'authorization_code' },
accessTokenUrl: 'https://api.medium.com/v1/tokens',
authorizationUrl: 'https://medium.com/m/oauth/authorize?response_type=code',
profileUrl: 'https://api.medium.com/v1/me',
profile: (profile) => {
return {
id: profile.data.id,
name: profile.data.name,
email: null,
image: profile.data.imageUrl
}
},
...options
}
}

View File

@@ -1,22 +0,0 @@
export default (options) => {
return {
id: 'mixer',
name: 'Mixer',
type: 'oauth',
version: '2.0',
scope: 'user:details:self',
params: { grant_type: 'authorization_code' },
accessTokenUrl: 'https://mixer.com/api/v1/oauth/token',
authorizationUrl: 'https://mixer.com/oauth/authorize?response_type=code',
profileUrl: 'https://mixer.com/api/v1/users/current',
profile: (profile) => {
return {
id: profile.id,
name: profile.username,
image: profile.avatarUrl,
email: profile.email
}
},
...options
}
}

21
src/providers/netlify.js Normal file
View File

@@ -0,0 +1,21 @@
export default (options) => {
return {
id: 'netlify',
name: 'Netlify',
type: 'oauth',
version: '2.0',
params: { grant_type: 'authorization_code' },
accessTokenUrl: 'https://api.netlify.com/oauth/token',
authorizationUrl: 'https://app.netlify.com/authorize?response_type=code',
profileUrl: 'https://api.netlify.com/api/v1/user',
profile: (profile) => {
return {
id: profile.id,
name: profile.full_name,
email: profile.email,
image: profile.avatar_url
}
},
...options
}
}

View File

@@ -11,13 +11,12 @@ export default (options) => {
client_secret: options.clientSecret
},
// These will be different depending on the Org.
accessTokenUrl: `https://${options.domain}/oauth2/v1/token`,
authorizationUrl: `https://${options.domain}/oauth2/v1/authorize/?response_type=code`,
profileUrl: `https://${options.domain}/oauth2/v1/userinfo/`,
accessTokenUrl: `https://${options.domain}/v1/token`,
authorizationUrl: `https://${options.domain}/v1/authorize/?response_type=code`,
profileUrl: `https://${options.domain}/v1/userinfo/`,
profile: (profile) => {
return { ...profile, id: profile.sub }
},
setGetAccessTokenAuthHeader: false,
...options
}
}

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