├── .babelrc.js ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── dynamodb.yaml │ ├── faunadb.yaml │ ├── firebase.yaml │ ├── mongodb.yaml │ ├── pouchdb.yaml │ ├── prisma.yaml │ ├── typeorm-legacy.yaml │ └── upstash-redis.yaml ├── PULL_REQUEST_TEMPLATE.md ├── labeler.yml ├── stale.yml └── workflows │ ├── codeql-analysis.yml │ ├── labeler.yml │ └── release.yml ├── .gitignore ├── .husky ├── .gitignore └── pre-commit ├── .nvmrc ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── basic-tests.ts ├── jest.config.js ├── lerna.json ├── package.json ├── packages ├── dgraph │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── client.ts │ │ ├── graphql │ │ │ ├── fragments.ts │ │ │ └── schema.gql │ │ ├── index.ts │ │ └── utils.ts │ ├── tests │ │ ├── index.test.ts │ │ ├── private.key │ │ ├── public.key │ │ └── test.sh │ └── tsconfig.json ├── dynamodb │ ├── CHANGELOG.md │ ├── README.md │ ├── jest-dynamodb-config.js │ ├── jest.config.js │ ├── logo.png │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── utils.ts │ ├── tests │ │ ├── custom.test.ts │ │ ├── format.test.ts │ │ └── index.test.ts │ └── tsconfig.json ├── fauna │ ├── .babelrc.js │ ├── .fauna-migrate.js │ ├── CHANGELOG.md │ ├── README.md │ ├── fauna │ │ ├── resources │ │ │ ├── collections │ │ │ │ ├── accounts.fql │ │ │ │ ├── sessions.fql │ │ │ │ ├── users.fql │ │ │ │ └── verification_tokens.fql │ │ │ └── indexes │ │ │ │ ├── account_by_provider_and_provider_account_id.fql │ │ │ │ ├── accounts_by_user_id.fql │ │ │ │ ├── session_by_session_token.fql │ │ │ │ ├── sessions_by_user_id.fql │ │ │ │ ├── user_by_email.fql │ │ │ │ └── verification_token_by_identifier_and_token.fql │ │ └── schema.fql │ ├── jest.config.js │ ├── logo.svg │ ├── package.json │ ├── src │ │ └── index.ts │ ├── tests │ │ ├── index.test.ts │ │ └── test.sh │ └── tsconfig.json ├── firebase │ ├── .firebaserc │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── firebase.json │ ├── jest.config.js │ ├── logo.svg │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── utils.ts │ ├── tests │ │ └── index.test.ts │ └── tsconfig.json ├── mikro-orm │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── jest.config.js │ ├── logo.svg │ ├── package.json │ ├── src │ │ ├── entities.ts │ │ └── index.ts │ ├── tests │ │ ├── index.test.ts │ │ └── testEntities.ts │ └── tsconfig.json ├── mongodb │ ├── CHANGELOG.md │ ├── README.md │ ├── jest.config.js │ ├── logo.svg │ ├── package.json │ ├── src │ │ └── index.ts │ ├── tests │ │ ├── custom.test.ts │ │ ├── index.test.ts │ │ └── test.sh │ └── tsconfig.json ├── neo4j │ ├── CHANGELOG.md │ ├── README.md │ ├── jest.config.js │ ├── logo.svg │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── utils.ts │ ├── tests │ │ ├── index.test.ts │ │ ├── resources │ │ │ └── statements.ts │ │ └── test.sh │ └── tsconfig.json ├── pouchdb │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── jest.config.js │ ├── logo.svg │ ├── package.json │ ├── src │ │ └── index.ts │ ├── tests │ │ └── index.test.ts │ └── tsconfig.json ├── prisma │ ├── CHANGELOG.md │ ├── README.md │ ├── jest.config.js │ ├── logo.svg │ ├── package.json │ ├── prisma │ │ ├── custom.prisma │ │ ├── migrations │ │ │ └── migration_lock.toml │ │ └── schema.prisma │ ├── src │ │ └── index.ts │ ├── tests │ │ └── index.test.ts │ └── tsconfig.json ├── sequelize │ ├── CHANGELOG.md │ ├── README.md │ ├── jest.config.js │ ├── logo.svg │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── models.ts │ ├── tests │ │ └── index.test.ts │ └── tsconfig.json ├── typeorm-legacy │ ├── .dockerignore │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── docs │ │ └── tutorials │ │ │ └── typeorm-custom-models.md │ ├── jest.config.js │ ├── logo.png │ ├── package.json │ ├── src │ │ ├── entities.ts │ │ ├── index.ts │ │ └── utils.ts │ ├── tests │ │ ├── custom-entities.ts │ │ ├── helpers.ts │ │ ├── index.test.ts │ │ ├── init.sh │ │ ├── mysql │ │ │ ├── index.custom.test.ts │ │ │ ├── index.test.ts │ │ │ └── test.sh │ │ ├── postgresql │ │ │ ├── index.custom.test.ts │ │ │ ├── index.test.ts │ │ │ └── test.sh │ │ ├── sqlite │ │ │ ├── index.custom.test.ts │ │ │ ├── index.test.ts │ │ │ └── test.sh │ │ └── test.sh │ └── tsconfig.json └── upstash-redis │ ├── .env.example │ ├── CHANGELOG.md │ ├── README.md │ ├── jest.config.js │ ├── logo.svg │ ├── package.json │ ├── src │ └── index.ts │ ├── tests │ └── index.test.ts │ └── tsconfig.json ├── tsconfig.json └── yarn.lock /.babelrc.js: -------------------------------------------------------------------------------- 1 | // We aim to have the same support as Next.js 2 | // https://nextjs.org/docs/getting-started#system-requirements 3 | // https://nextjs.org/docs/basic-features/supported-browsers-features 4 | 5 | module.exports = { 6 | presets: [["@babel/preset-env", { targets: { node: "12" } }]], 7 | plugins: ["@babel/plugin-transform-runtime"], 8 | comments: false, 9 | } 10 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/github/administering-a-repository/displaying-a-sponsor-button-in-your-repository 2 | 3 | open_collective: nextauth 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/dynamodb.yaml: -------------------------------------------------------------------------------- 1 | name: DynamoDB 2 | description: File a support request for the DynamoDB adapter 3 | labels: dynamodb 4 | 5 | # note: markdown sections will NOT appear as part of the issue as per documentation, rather they provide context to the user 6 | # https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#markdown 7 | 8 | 9 | body: 10 | - type: markdown 11 | attributes: 12 | value: | 13 | Thanks for taking the time to fill out this issue! 14 | ### Important :exclamation: 15 | Please help us maintain this project more efficiently! Before creating the issue make sure you shouldn't be creating it in one the below repos instead: 16 | - Core related: https://github.com/nextauthjs/next-auth 17 | - Docs related: https://github.com/nextauthjs/docs 18 | 19 | If you are in the correct repo, then proceed by providing the following information: 20 | - type: input 21 | id: title 22 | attributes: 23 | label: Title 24 | validations: 25 | required: true 26 | - type: textarea 27 | id: reproduction 28 | attributes: 29 | label: How to reproduce ☕️ 30 | description: Please provide a link or code snippets to a minimal reproduction of the bug 31 | validations: 32 | required: true 33 | - type: markdown 34 | attributes: 35 | value: | 36 | We encourage you to use one of the templates set up on **CodeSandbox** to reproduce your issue: 37 | - [`next-auth-example`](https://codesandbox.io/s/next-auth-example-1kktb) 38 | - [`next-auth-typescript-example`](https://codesandbox.io/s/next-auth-typescript-example-se32w) 39 | 40 | 🚧 – _If you don't provide any way to reproduce the bug, the issue is at risk of being closed._ 41 | 42 | - type: textarea 43 | id: description 44 | attributes: 45 | label: Your question/bug report 📓 46 | description: Please provide a more in-depth description of your request. 47 | validations: 48 | required: true 49 | - type: dropdown 50 | id: contributing 51 | attributes: 52 | label: Contributing 🙌🏽 53 | description: Are you willing to open a PR for this? 54 | multiple: false 55 | options: 56 | - "Yes, I am" 57 | - "No, I'm afraid I cannot help regarding this" 58 | validations: 59 | required: true 60 | - type: markdown 61 | attributes: 62 | value: | 63 | It takes a lot of work 🏋🏻‍♀️ maintaining a library like `next-auth`; any contribution is more than welcome 💚 64 | 65 | 66 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/faunadb.yaml: -------------------------------------------------------------------------------- 1 | name: FaunaDB 2 | description: File a support request for the FaunaDB adapter 3 | labels: fauna 4 | 5 | # note: markdown sections will NOT appear as part of the issue as per documentation, rather they provide context to the user 6 | # https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#markdown 7 | 8 | 9 | body: 10 | - type: markdown 11 | attributes: 12 | value: | 13 | Thanks for taking the time to fill out this issue! 14 | ### Important :exclamation: 15 | Please help us maintain this project more efficiently! Before creating the issue make sure you shouldn't be creating it in one the below repos instead: 16 | - Core related: https://github.com/nextauthjs/next-auth 17 | - Docs related: https://github.com/nextauthjs/docs 18 | 19 | If you are in the correct repo, then proceed by providing the following information: 20 | - type: input 21 | id: title 22 | attributes: 23 | label: Title 24 | validations: 25 | required: true 26 | - type: textarea 27 | id: reproduction 28 | attributes: 29 | label: How to reproduce ☕️ 30 | description: Please provide a link or code snippets to a minimal reproduction of the bug 31 | validations: 32 | required: true 33 | - type: markdown 34 | attributes: 35 | value: | 36 | We encourage you to use one of the templates set up on **CodeSandbox** to reproduce your issue: 37 | - [`next-auth-example`](https://codesandbox.io/s/next-auth-example-1kktb) 38 | - [`next-auth-typescript-example`](https://codesandbox.io/s/next-auth-typescript-example-se32w) 39 | 40 | 🚧 – _If you don't provide any way to reproduce the bug, the issue is at risk of being closed._ 41 | 42 | - type: textarea 43 | id: description 44 | attributes: 45 | label: Your question/bug report 📓 46 | description: Please provide a more in-depth description of your request. 47 | validations: 48 | required: true 49 | - type: dropdown 50 | id: contributing 51 | attributes: 52 | label: Contributing 🙌🏽 53 | description: Are you willing to open a PR for this? 54 | multiple: false 55 | options: 56 | - "Yes, I am" 57 | - "No, I'm afraid I cannot help regarding this" 58 | validations: 59 | required: true 60 | - type: markdown 61 | attributes: 62 | value: | 63 | It takes a lot of work 🏋🏻‍♀️ maintaining a library like `next-auth`; any contribution is more than welcome 💚 64 | 65 | 66 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/firebase.yaml: -------------------------------------------------------------------------------- 1 | name: Firebase 2 | description: File a support request for the Firebase adapter 3 | labels: firebase 4 | 5 | # note: markdown sections will NOT appear as part of the issue as per documentation, rather they provide context to the user 6 | # https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#markdown 7 | 8 | 9 | body: 10 | - type: markdown 11 | attributes: 12 | value: | 13 | Thanks for taking the time to fill out this issue! 14 | ### Important :exclamation: 15 | Please help us maintain this project more efficiently! Before creating the issue make sure you shouldn't be creating it in one the below repos instead: 16 | - Core related: https://github.com/nextauthjs/next-auth 17 | - Docs related: https://github.com/nextauthjs/docs 18 | 19 | If you are in the correct repo, then proceed by providing the following information: 20 | - type: input 21 | id: title 22 | attributes: 23 | label: Title 24 | validations: 25 | required: true 26 | - type: textarea 27 | id: reproduction 28 | attributes: 29 | label: How to reproduce ☕️ 30 | description: Please provide a link or code snippets to a minimal reproduction of the bug 31 | validations: 32 | required: true 33 | - type: markdown 34 | attributes: 35 | value: | 36 | We encourage you to use one of the templates set up on **CodeSandbox** to reproduce your issue: 37 | - [`next-auth-example`](https://codesandbox.io/s/next-auth-example-1kktb) 38 | - [`next-auth-typescript-example`](https://codesandbox.io/s/next-auth-typescript-example-se32w) 39 | 40 | 🚧 – _If you don't provide any way to reproduce the bug, the issue is at risk of being closed._ 41 | 42 | - type: textarea 43 | id: description 44 | attributes: 45 | label: Your question/bug report 📓 46 | description: Please provide a more in-depth description of your request. 47 | validations: 48 | required: true 49 | - type: dropdown 50 | id: contributing 51 | attributes: 52 | label: Contributing 🙌🏽 53 | description: Are you willing to open a PR for this? 54 | multiple: false 55 | options: 56 | - "Yes, I am" 57 | - "No, I'm afraid I cannot help regarding this" 58 | validations: 59 | required: true 60 | - type: markdown 61 | attributes: 62 | value: | 63 | It takes a lot of work 🏋🏻‍♀️ maintaining a library like `next-auth`; any contribution is more than welcome 💚 64 | 65 | 66 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/mongodb.yaml: -------------------------------------------------------------------------------- 1 | name: MongoDB 2 | description: File a support request for the MongoDB adapter 3 | labels: mongodb 4 | 5 | # note: markdown sections will NOT appear as part of the issue as per documentation, rather they provide context to the user 6 | # https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#markdown 7 | 8 | 9 | body: 10 | - type: markdown 11 | attributes: 12 | value: | 13 | Thanks for taking the time to fill out this issue! 14 | ### Important :exclamation: 15 | Please help us maintain this project more efficiently! Before creating the issue make sure you shouldn't be creating it in one the below repos instead: 16 | - Core related: https://github.com/nextauthjs/next-auth 17 | - Docs related: https://github.com/nextauthjs/docs 18 | 19 | If you are in the correct repo, then proceed by providing the following information: 20 | - type: input 21 | id: title 22 | attributes: 23 | label: Title 24 | validations: 25 | required: true 26 | - type: textarea 27 | id: reproduction 28 | attributes: 29 | label: How to reproduce ☕️ 30 | description: Please provide a link or code snippets to a minimal reproduction of the bug 31 | validations: 32 | required: true 33 | - type: markdown 34 | attributes: 35 | value: | 36 | We encourage you to use one of the templates set up on **CodeSandbox** to reproduce your issue: 37 | - [`next-auth-example`](https://codesandbox.io/s/next-auth-example-1kktb) 38 | - [`next-auth-typescript-example`](https://codesandbox.io/s/next-auth-typescript-example-se32w) 39 | 40 | 🚧 – _If you don't provide any way to reproduce the bug, the issue is at risk of being closed._ 41 | 42 | - type: textarea 43 | id: description 44 | attributes: 45 | label: Your question/bug report 📓 46 | description: Please provide a more in-depth description of your request. 47 | validations: 48 | required: true 49 | - type: dropdown 50 | id: contributing 51 | attributes: 52 | label: Contributing 🙌🏽 53 | description: Are you willing to open a PR for this? 54 | multiple: false 55 | options: 56 | - "Yes, I am" 57 | - "No, I'm afraid I cannot help regarding this" 58 | validations: 59 | required: true 60 | - type: markdown 61 | attributes: 62 | value: | 63 | It takes a lot of work 🏋🏻‍♀️ maintaining a library like `next-auth`; any contribution is more than welcome 💚 64 | 65 | 66 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/pouchdb.yaml: -------------------------------------------------------------------------------- 1 | name: PouchDP 2 | description: File a support request for the PouchDB adapter 3 | labels: pouchdb 4 | 5 | # note: markdown sections will NOT appear as part of the issue as per documentation, rather they provide context to the user 6 | # https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#markdown 7 | 8 | 9 | body: 10 | - type: markdown 11 | attributes: 12 | value: | 13 | Thanks for taking the time to fill out this issue! 14 | ### Important :exclamation: 15 | Please help us maintain this project more efficiently! Before creating the issue make sure you shouldn't be creating it in one the below repos instead: 16 | - Core related: https://github.com/nextauthjs/next-auth 17 | - Docs related: https://github.com/nextauthjs/docs 18 | 19 | If you are in the correct repo, then proceed by providing the following information: 20 | - type: input 21 | id: title 22 | attributes: 23 | label: Title 24 | validations: 25 | required: true 26 | - type: textarea 27 | id: reproduction 28 | attributes: 29 | label: How to reproduce ☕️ 30 | description: Please provide a link or code snippets to a minimal reproduction of the bug 31 | validations: 32 | required: true 33 | - type: markdown 34 | attributes: 35 | value: | 36 | We encourage you to use one of the templates set up on **CodeSandbox** to reproduce your issue: 37 | - [`next-auth-example`](https://codesandbox.io/s/next-auth-example-1kktb) 38 | - [`next-auth-typescript-example`](https://codesandbox.io/s/next-auth-typescript-example-se32w) 39 | 40 | 🚧 – _If you don't provide any way to reproduce the bug, the issue is at risk of being closed._ 41 | 42 | - type: textarea 43 | id: description 44 | attributes: 45 | label: Your question/bug report 📓 46 | description: Please provide a more in-depth description of your request. 47 | validations: 48 | required: true 49 | - type: dropdown 50 | id: contributing 51 | attributes: 52 | label: Contributing 🙌🏽 53 | description: Are you willing to open a PR for this? 54 | multiple: false 55 | options: 56 | - "Yes, I am" 57 | - "No, I'm afraid I cannot help regarding this" 58 | validations: 59 | required: true 60 | - type: markdown 61 | attributes: 62 | value: | 63 | It takes a lot of work 🏋🏻‍♀️ maintaining a library like `next-auth`; any contribution is more than welcome 💚 64 | 65 | 66 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/prisma.yaml: -------------------------------------------------------------------------------- 1 | name: Prisma 2 | description: File a support request for the Prisma adapter 3 | labels: prisma 4 | 5 | # note: markdown sections will NOT appear as part of the issue as per documentation, rather they provide context to the user 6 | # https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#markdown 7 | 8 | 9 | body: 10 | - type: markdown 11 | attributes: 12 | value: | 13 | Thanks for taking the time to fill out this issue! 14 | ### Important :exclamation: 15 | Please help us maintain this project more efficiently! Before creating the issue make sure you shouldn't be creating it in one the below repos instead: 16 | - Core related: https://github.com/nextauthjs/next-auth 17 | - Docs related: https://github.com/nextauthjs/docs 18 | 19 | If you are in the correct repo, then proceed by providing the following information: 20 | - type: input 21 | id: title 22 | attributes: 23 | label: Title 24 | validations: 25 | required: true 26 | - type: textarea 27 | id: reproduction 28 | attributes: 29 | label: How to reproduce ☕️ 30 | description: Please provide a link or code snippets to a minimal reproduction of the bug 31 | validations: 32 | required: true 33 | - type: markdown 34 | attributes: 35 | value: | 36 | We encourage you to use one of the templates set up on **CodeSandbox** to reproduce your issue: 37 | - [`next-auth-example`](https://codesandbox.io/s/next-auth-example-1kktb) 38 | - [`next-auth-typescript-example`](https://codesandbox.io/s/next-auth-typescript-example-se32w) 39 | 40 | 🚧 – _If you don't provide any way to reproduce the bug, the issue is at risk of being closed._ 41 | 42 | - type: textarea 43 | id: description 44 | attributes: 45 | label: Your question/bug report 📓 46 | description: Please provide a more in-depth description of your request. 47 | validations: 48 | required: true 49 | - type: dropdown 50 | id: contributing 51 | attributes: 52 | label: Contributing 🙌🏽 53 | description: Are you willing to open a PR for this? 54 | multiple: false 55 | options: 56 | - "Yes, I am" 57 | - "No, I'm afraid I cannot help regarding this" 58 | validations: 59 | required: true 60 | - type: markdown 61 | attributes: 62 | value: | 63 | It takes a lot of work 🏋🏻‍♀️ maintaining a library like `next-auth`; any contribution is more than welcome 💚 64 | 65 | 66 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/typeorm-legacy.yaml: -------------------------------------------------------------------------------- 1 | name: TypeORM 2 | description: File a support request for the TypeORM adapter 3 | labels: typeorm 4 | 5 | # note: markdown sections will NOT appear as part of the issue as per documentation, rather they provide context to the user 6 | # https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#markdown 7 | 8 | 9 | body: 10 | - type: markdown 11 | attributes: 12 | value: | 13 | Thanks for taking the time to fill out this issue! 14 | ### Important :exclamation: 15 | Please help us maintain this project more efficiently! Before creating the issue make sure you shouldn't be creating it in one the below repos instead: 16 | - Core related: https://github.com/nextauthjs/next-auth 17 | - Docs related: https://github.com/nextauthjs/docs 18 | 19 | If you are in the correct repo, then proceed by providing the following information: 20 | - type: input 21 | id: title 22 | attributes: 23 | label: Title 24 | validations: 25 | required: true 26 | - type: textarea 27 | id: reproduction 28 | attributes: 29 | label: How to reproduce ☕️ 30 | description: Please provide a link or code snippets to a minimal reproduction of the bug 31 | validations: 32 | required: true 33 | - type: markdown 34 | attributes: 35 | value: | 36 | We encourage you to use one of the templates set up on **CodeSandbox** to reproduce your issue: 37 | - [`next-auth-example`](https://codesandbox.io/s/next-auth-example-1kktb) 38 | - [`next-auth-typescript-example`](https://codesandbox.io/s/next-auth-typescript-example-se32w) 39 | 40 | 🚧 – _If you don't provide any way to reproduce the bug, the issue is at risk of being closed._ 41 | 42 | - type: textarea 43 | id: description 44 | attributes: 45 | label: Your question/bug report 📓 46 | description: Please provide a more in-depth description of your request. 47 | validations: 48 | required: true 49 | - type: dropdown 50 | id: contributing 51 | attributes: 52 | label: Contributing 🙌🏽 53 | description: Are you willing to open a PR for this? 54 | multiple: false 55 | options: 56 | - "Yes, I am" 57 | - "No, I'm afraid I cannot help regarding this" 58 | validations: 59 | required: true 60 | - type: markdown 61 | attributes: 62 | value: | 63 | It takes a lot of work 🏋🏻‍♀️ maintaining a library like `next-auth`; any contribution is more than welcome 💚 64 | 65 | 66 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/upstash-redis.yaml: -------------------------------------------------------------------------------- 1 | name: Upstash Redis 2 | description: File a support request for the Upstash Redis adapter 3 | labels: upstash-redis 4 | 5 | # note: markdown sections will NOT appear as part of the issue as per documentation, rather they provide context to the user 6 | # https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#markdown 7 | 8 | body: 9 | - type: markdown 10 | attributes: 11 | value: | 12 | Thanks for taking the time to fill out this issue! 13 | ### Important :exclamation: 14 | Please help us maintain this project more efficiently! Before creating the issue make sure you shouldn't be creating it in one the below repos instead: 15 | - Core related: https://github.com/nextauthjs/next-auth 16 | - Docs related: https://github.com/nextauthjs/docs 17 | 18 | If you are in the correct repo, then proceed by providing the following information: 19 | - type: input 20 | id: title 21 | attributes: 22 | label: Title 23 | validations: 24 | required: true 25 | - type: textarea 26 | id: reproduction 27 | attributes: 28 | label: How to reproduce ☕️ 29 | description: Please provide a link or code snippets to a minimal reproduction of the bug 30 | validations: 31 | required: true 32 | - type: markdown 33 | attributes: 34 | value: | 35 | We encourage you to use one of the templates set up on **CodeSandbox** to reproduce your issue: 36 | - [`next-auth-example`](https://codesandbox.io/s/next-auth-example-1kktb) 37 | - [`next-auth-typescript-example`](https://codesandbox.io/s/next-auth-typescript-example-se32w) 38 | 39 | 🚧 – _If you don't provide any way to reproduce the bug, the issue is at risk of being closed._ 40 | 41 | - type: textarea 42 | id: description 43 | attributes: 44 | label: Your question/bug report 📓 45 | description: Please provide a more in-depth description of your request. 46 | validations: 47 | required: true 48 | - type: dropdown 49 | id: contributing 50 | attributes: 51 | label: Contributing 🙌🏽 52 | description: Are you willing to open a PR for this? 53 | multiple: false 54 | options: 55 | - "Yes, I am" 56 | - "No, I'm afraid I cannot help regarding this" 57 | validations: 58 | required: true 59 | - type: markdown 60 | attributes: 61 | value: | 62 | It takes a lot of work 🏋🏻‍♀️ maintaining a library like `next-auth`; any contribution is more than welcome 💚 63 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 16 | 17 | ## Reasoning 💡 18 | 19 | 25 | 26 | ## Checklist 🧢 27 | 28 | 31 | 32 | - [ ] Documentation 33 | - [ ] Tests 34 | - [ ] Ready to be merged 35 | 36 | 37 | 38 | ## Affected issues 🎟 39 | 40 | 52 | -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | fauna: 2 | - packages/fauna/**/* 3 | 4 | dynamodb: 5 | - packages/dynamodb/**/* 6 | 7 | prisma: 8 | - packages/prisma/**/* 9 | 10 | mongodb: 11 | - packages/mongodb/**/* 12 | 13 | neo4j: 14 | - packages/neo4j/**/* 15 | 16 | typeorm: 17 | - packages/typeorm-legacy/**/* 18 | 19 | firebase: 20 | - packages/firebase/**/* 21 | 22 | pouchdb: 23 | - packages/pouchdb/**/* 24 | 25 | sequelize: 26 | - packages/sequelize/**/* 27 | 28 | dgraph: 29 | - packages/dgraph/**/* 30 | 31 | upstash-redis: 32 | - packages/upstash-redis/**/* 33 | 34 | documentation: 35 | - ./**/*.md 36 | 37 | tests: 38 | - jest.config.js 39 | - packages/**/tests/* 40 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 90 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 14 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | - priority 10 | - bug 11 | # Label to use when marking an issue as stale 12 | staleLabel: stale 13 | # Comment to post when marking an issue as stale. Set to `false` to disable 14 | markComment: > 15 | Hi there! It looks like this issue hasn't had any activity for a while. 16 | It will be closed if no further activity occurs. If you think your issue 17 | is still relevant, feel free to comment on it to keep it open. 18 | Thanks! 19 | # Comment to post when closing a stale issue. Set to `false` to disable 20 | closeComment: > 21 | Hi there! It looks like this issue hasn't had any activity for a while. 22 | To keep things tidy, I am going to close this issue for now. 23 | If you think your issue is still relevant, just leave a comment 24 | and I will reopen it. 25 | Thanks! 26 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ canary, main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ canary ] 20 | schedule: 21 | - cron: '19 2 * * 4' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | language: [ 'javascript' ] 32 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 33 | # Learn more: 34 | # 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 35 | 36 | steps: 37 | - name: Checkout repository 38 | uses: actions/checkout@v2 39 | 40 | # Initializes the CodeQL tools for scanning. 41 | - name: Initialize CodeQL 42 | uses: github/codeql-action/init@v1 43 | with: 44 | languages: ${{ matrix.language }} 45 | # If you wish to specify custom queries, you can do so here or in a config file. 46 | # By default, queries listed here will override any specified in a config file. 47 | # Prefix the list here with "+" to use these queries and those in the config file. 48 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 49 | 50 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 51 | # If this step fails, then you should remove it and run the build manually (see below) 52 | - name: Autobuild 53 | uses: github/codeql-action/autobuild@v1 54 | 55 | # ℹ️ Command-line programs to run using the OS shell. 56 | # 📚 https://git.io/JvXDl 57 | 58 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 59 | # and modify them (or add more) to build your code if your project 60 | # uses a compiled language 61 | 62 | #- run: | 63 | # make bootstrap 64 | # make release 65 | 66 | - name: Perform CodeQL Analysis 67 | uses: github/codeql-action/analyze@v1 68 | -------------------------------------------------------------------------------- /.github/workflows/labeler.yml: -------------------------------------------------------------------------------- 1 | name: "Pull Request Labeler" 2 | on: 3 | - pull_request_target 4 | 5 | jobs: 6 | triage: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/labeler@main 10 | with: 11 | repo-token: "${{ secrets.GITHUB_TOKEN }}" 12 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - next 8 | pull_request: 9 | branches: 10 | - main 11 | - next 12 | 13 | jobs: 14 | publish: 15 | name: "Test, Build, Publish" 16 | runs-on: ubuntu-latest 17 | env: 18 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 20 | UPSTASH_REDIS_KEY: ${{ secrets.UPSTASH_REDIS_KEY }} 21 | UPSTASH_REDIS_URL: ${{ secrets.UPSTASH_REDIS_URL }} 22 | steps: 23 | - uses: actions/checkout@v2 24 | with: 25 | fetch-depth: 0 26 | persist-credentials: false 27 | token: ${{ secrets.GH_PAT }} 28 | - uses: actions/setup-node@v2 29 | with: 30 | node-version: "16" 31 | - uses: bahmutov/npm-install@v1 32 | - name: Test (changed packages) 33 | run: lerna run test --concurrency 3 --since HEAD~ 34 | - name: Build (changed packages) 35 | run: lerna run build --concurrency 3 --since 36 | - name: Authenticate with NPM 37 | run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> .npmrc 38 | - name: Configure CI Git User 39 | run: | 40 | git config --global user.email nextauth@gmail.com 41 | git config --global user.name "NextAuth.js" 42 | git config remote.origin.url https://x-access-token:${{ secrets.GH_PAT }}@github.com/$GITHUB_REPOSITORY 43 | - name: Publish to @next 44 | if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/next' }} 45 | run: | 46 | lerna publish --canary --preid next \ 47 | --dist-tag next --conventional-prerelease \ 48 | --create-release github --yes \ 49 | -m "chore(release): create release [skip ci]" 50 | - name: Publish to @experimental 51 | if: ${{ github.base_ref == 'main' || github.base_ref == 'next' }} 52 | run: | 53 | lerna publish --preid pr.${{ github.event.number }}-${GITHUB_SHA::8} \ 54 | --no-changelog --no-push --canary --dist-tag experimental --yes 55 | - name: Publish to @latest 56 | if: ${{github.event_name == 'push' && github.ref == 'refs/heads/main'}} 57 | run: | 58 | lerna publish --create-release github --yes \ 59 | -m "chore(release): create release [skip ci]" 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lerna-debug.log 3 | dist 4 | dev.db 5 | .eslintcache 6 | .devcontainer 7 | coverage 8 | dev.db-journal 9 | packages/prisma/prisma/migrations 10 | packages/fauna/fauna/migrations 11 | dynamodblocal-bin -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 16 -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | - Using welcoming and inclusive language 18 | - Being respectful of differing viewpoints and experiences 19 | - Gracefully accepting constructive criticism 20 | - Focusing on what is best for the community 21 | - Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | - The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | - Trolling, insulting/derogatory comments, and personal or political attacks 28 | - Public or private harassment 29 | - Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | - Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting me@iaincollins.com. All complaints will be reviewed and 59 | investigated and will result in a response that is deemed necessary and 60 | appropriate to the circumstances. The project team is obligated to maintain 61 | confidentiality with regard to the reporter of an incident. Further details of 62 | specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing guide 2 | 3 | Contributions and feedback on your experience of using this software are welcome. 4 | 5 | This includes bug reports, feature requests, ideas, pull requests, and examples of how you have used this software. 6 | 7 | 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. 8 | 9 | Please raise any significant new functionality or breaking change an issue for discussion before raising a Pull Request for it. 10 | 11 | ## For contributors 12 | 13 | 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. 14 | 15 | ### Pull Requests 16 | 17 | - The latest changes are always in `main`, so please make your Pull Request against that branch. 18 | - Pull Requests should be raised for any change 19 | 20 | ### Setting up local environment 21 | 22 | The local environment consists of two pieces: 23 | 24 | 1. The top-level package.json and its dependencies, like `lerna`. 25 | 2. The package-level dependencies which can be installed via npm. Any other package specific instructions can be found in their README's or the [docs](https://next-auth.js.org/adapters/overview). 26 | 27 | #### Testing 28 | 29 | Testing takes place on a per-adapter basis. Each adapter in the `packages` subdirectory should have an npm test script which will setup a test environment, often consiting of a docker container of the appropriate databse, and a test-runner like jest. 30 | 31 | ## For maintainers 32 | 33 | ### Recommended Scopes 34 | 35 | A typical conventional commit looks like this: 36 | 37 | ``` 38 | type(scope): title 39 | 40 | body 41 | ``` 42 | 43 | Scope is the part that will help groupping the different commit types in the release notes. 44 | 45 | Some recommened scopes are: 46 | 47 | - **adapter** - Adapter related changes. (eg.: "feat(prisma): add prisma adapter", "docs(prisma): fix typo in X documentation" 48 | 49 | > 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. 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2018-2021, Iain Collins 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: "ts-jest", 3 | testEnvironment: "node", 4 | modulePathIgnorePatterns: ["dist/", "node_modules/"], 5 | // TODO: Set-up with Codecov https://about.codecov.io/ 6 | // collectCoverage: true, 7 | // coverageReporters: ["json", "html"], 8 | } 9 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "lerna": "2.11.0", 3 | "packages": ["packages/*"], 4 | "version": "independent", 5 | "npmClient": "yarn", 6 | "useWorkspaces": true, 7 | "command": { 8 | "publish": { 9 | "conventionalCommits": true, 10 | "verifyAccess": false 11 | } 12 | }, 13 | "ignoreChanges": ["**/tests/**", "**/CHANGELOG.md"], 14 | "publishConfig": { 15 | "access": "public" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "adapters", 3 | "version": "0.0.0", 4 | "repository": "https://github.com/nextauthjs/adapters.git", 5 | "author": "William Luke ", 6 | "contributors": [ 7 | "Balázs Orbán ", 8 | "Nico Domino " 9 | ], 10 | "license": "ISC", 11 | "private": true, 12 | "scripts": { 13 | "build": "lerna run build", 14 | "canary": "lerna publish --canary", 15 | "prepare": "husky install", 16 | "lint": "eslint .", 17 | "lint:fix": "eslint . --fix && prettier --write" 18 | }, 19 | "workspaces": [ 20 | "packages/*" 21 | ], 22 | "devDependencies": { 23 | "@babel/cli": "^7.14.3", 24 | "@babel/plugin-transform-runtime": "^7.14.3", 25 | "@babel/preset-env": "^7.14.2", 26 | "@types/jest": "^26.0.23", 27 | "@types/nodemailer": "^6.4.4", 28 | "@typescript-eslint/eslint-plugin": "^4.24.0", 29 | "@typescript-eslint/parser": "^4.24.0", 30 | "eslint": "^7.27.0", 31 | "eslint-config-prettier": "^8.3.0", 32 | "eslint-config-standard-with-typescript": "^20.0.0", 33 | "eslint-plugin-import": "^2.23.3", 34 | "eslint-plugin-node": "^11.1.0", 35 | "eslint-plugin-promise": "^5.1.0", 36 | "husky": ">=6", 37 | "jest": "^27.0.3", 38 | "lerna": "^4.0.0", 39 | "lint-staged": ">=10", 40 | "next-auth": "^4.0.1", 41 | "prettier": "^2.3.0", 42 | "ts-jest": "^27.0.3", 43 | "typescript": "^4.2.4" 44 | }, 45 | "prettier": { 46 | "semi": false 47 | }, 48 | "eslintConfig": { 49 | "parser": "@typescript-eslint/parser", 50 | "parserOptions": { 51 | "project": "./tsconfig.json" 52 | }, 53 | "rules": { 54 | "@typescript-eslint/explicit-function-return-type": "off", 55 | "@typescript-eslint/strict-boolean-expressions": "off", 56 | "@typescript-eslint/naming-convention": "off", 57 | "@typescript-eslint/consistent-type-imports": "warn" 58 | }, 59 | "extends": [ 60 | "standard-with-typescript", 61 | "prettier" 62 | ], 63 | "ignorePatterns": [ 64 | "node_modules", 65 | "test", 66 | "next-env.d.ts", 67 | "types", 68 | "www", 69 | ".next", 70 | "dist" 71 | ], 72 | "globals": { 73 | "localStorage": "readonly", 74 | "location": "readonly", 75 | "fetch": "readonly" 76 | } 77 | }, 78 | "lint-staged": { 79 | "*.{js,ts}": [ 80 | "eslint --cache --fix", 81 | "prettier --write" 82 | ], 83 | "*.{css,md,json}": "prettier --write" 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /packages/dgraph/.gitignore: -------------------------------------------------------------------------------- 1 | test.schema.gql -------------------------------------------------------------------------------- /packages/dgraph/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [1.0.2](https://github.com/nextauthjs/adapters/compare/@next-auth/dgraph-adapter@1.0.1...@next-auth/dgraph-adapter@1.0.2) (2021-12-06) 7 | 8 | **Note:** Version bump only for package @next-auth/dgraph-adapter 9 | -------------------------------------------------------------------------------- /packages/dgraph/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require("../../jest.config") 2 | -------------------------------------------------------------------------------- /packages/dgraph/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@next-auth/dgraph-adapter", 3 | "version": "1.0.2", 4 | "description": "Dgraph adapter for next-auth.", 5 | "homepage": "https://next-auth.js.org", 6 | "repository": "https://github.com/nextauthjs/adapters", 7 | "bugs": { 8 | "url": "https://github.com/nextauthjs/adapters/issues" 9 | }, 10 | "author": "Arnaud Derbey ", 11 | "contributors": [], 12 | "main": "dist/index.js", 13 | "files": [ 14 | "dist", 15 | "index.d.ts" 16 | ], 17 | "license": "ISC", 18 | "keywords": [ 19 | "next-auth", 20 | "next.js", 21 | "dgraph", 22 | "graphql" 23 | ], 24 | "private": false, 25 | "publishConfig": { 26 | "access": "public" 27 | }, 28 | "scripts": { 29 | "build": "tsc", 30 | "test": "./tests/test.sh" 31 | }, 32 | "peerDependencies": { 33 | "jsonwebtoken": "^8.5.1", 34 | "next-auth": "^4.0.1" 35 | }, 36 | "devDependencies": { 37 | "@types/jest": "^26.0.24", 38 | "@types/jsonwebtoken": "^8.5.5", 39 | "@types/node-fetch": "^2.5.11", 40 | "jest": "^27.0.6", 41 | "ts-jest": "^27.0.3" 42 | }, 43 | "dependencies": { 44 | "jsonwebtoken": "^8.5.1", 45 | "node-fetch": "^2.6.1" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/dgraph/src/client.ts: -------------------------------------------------------------------------------- 1 | import * as jwt from "jsonwebtoken" 2 | import fetch from "node-fetch" 3 | 4 | import type { HeadersInit } from "node-fetch" 5 | 6 | export interface DgraphClientParams { 7 | endpoint: string 8 | /** 9 | * `X-Auth-Token` header value 10 | * 11 | * [Dgraph Cloud Authentication](https://dgraph.io/docs/cloud/cloud-api/overview/#dgraph-cloud-authentication) 12 | */ 13 | authToken: string 14 | /** [Using JWT and authorization claims](https://dgraph.io/docs/graphql/authorization/authorization-overview#using-jwts-and-authorization-claims) */ 15 | jwtSecret?: string 16 | /** 17 | * @default "RS256" 18 | * 19 | * [Using JWT and authorization claims](https://dgraph.io/docs/graphql/authorization/authorization-overview#using-jwts-and-authorization-claims) 20 | */ 21 | jwtAlgorithm?: "HS256" | "RS256" 22 | /** 23 | * @default "Authorization" 24 | * 25 | * [Using JWT and authorization claims](https://dgraph.io/docs/graphql/authorization/authorization-overview#using-jwts-and-authorization-claims) 26 | */ 27 | authHeader?: string 28 | } 29 | 30 | export class DgraphClientError extends Error { 31 | name = "DgraphClientError" 32 | constructor(errors: any[], query: string, variables: any) { 33 | super(errors.map((error) => error.message).join("\n")) 34 | console.error({ query, variables }) 35 | } 36 | } 37 | 38 | export function client(params: DgraphClientParams) { 39 | if (!params.authToken) { 40 | throw new Error("Dgraph client error: Please provide an api key") 41 | } 42 | if (!params.endpoint) { 43 | throw new Error("Dgraph client error: Please provide a graphql endpoint") 44 | } 45 | 46 | const { 47 | endpoint, 48 | authToken, 49 | jwtSecret, 50 | jwtAlgorithm = "HS256", 51 | authHeader = "Authorization", 52 | } = params 53 | const headers: HeadersInit = { 54 | "Content-Type": "application/json", 55 | "X-Auth-Token": authToken, 56 | } 57 | 58 | if (authHeader && jwtSecret) { 59 | headers[authHeader] = jwt.sign({ nextAuth: true }, jwtSecret, { 60 | algorithm: jwtAlgorithm, 61 | }) 62 | } 63 | 64 | return { 65 | async run( 66 | query: string, 67 | variables?: Record 68 | ): Promise { 69 | const response = await fetch(endpoint, { 70 | method: "POST", 71 | headers, 72 | body: JSON.stringify({ query, variables }), 73 | }) 74 | 75 | const { data = {}, errors } = await response.json() 76 | if (errors?.length) { 77 | throw new DgraphClientError(errors, query, variables) 78 | } 79 | return Object.values(data)[0] as any 80 | }, 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /packages/dgraph/src/graphql/fragments.ts: -------------------------------------------------------------------------------- 1 | export const User = /* GraphQL */ ` 2 | fragment UserFragment on User { 3 | email 4 | id 5 | image 6 | name 7 | emailVerified 8 | } 9 | ` 10 | 11 | export const Account = /* GraphQL */ ` 12 | fragment AccountFragment on Account { 13 | id 14 | type 15 | provider 16 | providerAccountId 17 | expires_at 18 | token_type 19 | scope 20 | access_token 21 | refresh_token 22 | id_token 23 | session_state 24 | } 25 | ` 26 | export const Session = /* GraphQL */ ` 27 | fragment SessionFragment on Session { 28 | expires 29 | id 30 | sessionToken 31 | } 32 | ` 33 | 34 | export const VerificationToken = /* GraphQL */ ` 35 | fragment VerificationTokenFragment on VerificationToken { 36 | identifier 37 | token 38 | expires 39 | } 40 | ` 41 | -------------------------------------------------------------------------------- /packages/dgraph/src/graphql/schema.gql: -------------------------------------------------------------------------------- 1 | type Account 2 | @auth( 3 | delete: { rule: "{$nextAuth: { eq: true } }" } 4 | add: { rule: "{$nextAuth: { eq: true } }" } 5 | query: { rule: "{$nextAuth: { eq: true } }" } 6 | update: { rule: "{$nextAuth: { eq: true } }" } 7 | ) { 8 | id: ID 9 | type: String 10 | provider: String @search(by: [hash]) 11 | providerAccountId: String @search(by: [hash]) 12 | refreshToken: String 13 | expires_at: Int64 14 | accessToken: String 15 | token_type: String 16 | refresh_token: String 17 | access_token: String 18 | scope: String 19 | id_token: String 20 | session_state: String 21 | user: User @hasInverse(field: "accounts") 22 | } 23 | type Session 24 | @auth( 25 | delete: { rule: "{$nextAuth: { eq: true } }" } 26 | add: { rule: "{$nextAuth: { eq: true } }" } 27 | query: { rule: "{$nextAuth: { eq: true } }" } 28 | update: { rule: "{$nextAuth: { eq: true } }" } 29 | ) { 30 | id: ID 31 | expires: DateTime 32 | sessionToken: String @search(by: [hash]) 33 | user: User @hasInverse(field: "sessions") 34 | } 35 | type User 36 | @auth( 37 | query: { 38 | or: [ 39 | { 40 | rule: """ 41 | query ($userId: String!) {queryUser(filter: { id: { eq: $userId } } ) {id}} 42 | """ 43 | } 44 | { rule: "{$nextAuth: { eq: true } }" } 45 | ] 46 | } 47 | delete: { rule: "{$nextAuth: { eq: true } }" } 48 | add: { rule: "{$nextAuth: { eq: true } }" } 49 | update: { 50 | or: [ 51 | { 52 | rule: """ 53 | query ($userId: String!) {queryUser(filter: { id: { eq: $userId } } ) {id}} 54 | """ 55 | } 56 | { rule: "{$nextAuth: { eq: true } }" } 57 | ] 58 | } 59 | ) { 60 | id: ID 61 | name: String 62 | email: String @search(by: [hash]) 63 | emailVerified: DateTime 64 | image: String 65 | accounts: [Account] @hasInverse(field: "user") 66 | sessions: [Session] @hasInverse(field: "user") 67 | } 68 | 69 | type VerificationToken 70 | @auth( 71 | delete: { rule: "{$nextAuth: { eq: true } }" } 72 | add: { rule: "{$nextAuth: { eq: true } }" } 73 | query: { rule: "{$nextAuth: { eq: true } }" } 74 | update: { rule: "{$nextAuth: { eq: true } }" } 75 | ) { 76 | id: ID 77 | identifier: String @search(by: [hash]) 78 | token: String @search(by: [hash]) 79 | expires: DateTime 80 | } 81 | 82 | # Dgraph.Authorization {"VerificationKey":"","Header":"","Namespace":"","Algo":"RS256"} 83 | -------------------------------------------------------------------------------- /packages/dgraph/src/utils.ts: -------------------------------------------------------------------------------- 1 | // https://github.com/honeinc/is-iso-date/blob/master/index.js 2 | const isoDateRE = 3 | /(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))/ 4 | 5 | function isDate(value: any) { 6 | return value && isoDateRE.test(value) && !isNaN(Date.parse(value)) 7 | } 8 | 9 | export const format = { 10 | from(object?: Record): T | null { 11 | const newObject: Record = {} 12 | if (!object) return null 13 | for (const key in object) { 14 | const value = object[key] 15 | if (isDate(value)) { 16 | newObject[key] = new Date(value) 17 | } else { 18 | newObject[key] = value 19 | } 20 | } 21 | 22 | return newObject as T 23 | }, 24 | } 25 | -------------------------------------------------------------------------------- /packages/dgraph/tests/index.test.ts: -------------------------------------------------------------------------------- 1 | import { DgraphAdapter, format } from "../src" 2 | import { client as dgraphClient } from "../src/client" 3 | import * as fragments from "../src/graphql/fragments" 4 | import { runBasicTests } from "../../../basic-tests" 5 | import fs from "fs" 6 | import path from "path" 7 | 8 | import type { DgraphClientParams } from "../src" 9 | 10 | const params: DgraphClientParams = { 11 | endpoint: "http://localhost:8080/graphql", 12 | authToken: "test", 13 | jwtAlgorithm: "RS256", 14 | jwtSecret: fs.readFileSync(path.join(process.cwd(), "/tests/private.key"), { 15 | encoding: "utf8", 16 | }), 17 | } 18 | 19 | /** TODO: Add test to `dgraphClient` */ 20 | const c = dgraphClient(params) 21 | 22 | runBasicTests({ 23 | adapter: DgraphAdapter(params), 24 | db: { 25 | id: () => "0x0a0a00a00", 26 | async disconnect() { 27 | await c.run(/* GraphQL */ ` 28 | mutation { 29 | deleteUser(filter: {}) { 30 | numUids 31 | } 32 | deleteVerificationToken(filter: {}) { 33 | numUids 34 | } 35 | deleteSession(filter: {}) { 36 | numUids 37 | } 38 | deleteAccount(filter: {}) { 39 | numUids 40 | } 41 | } 42 | `) 43 | }, 44 | async user(id) { 45 | const result = await c.run( 46 | /* GraphQL */ ` 47 | query ($id: ID!) { 48 | getUser(id: $id) { 49 | ...UserFragment 50 | } 51 | } 52 | ${fragments.User} 53 | `, 54 | { id } 55 | ) 56 | 57 | return format.from(result) 58 | }, 59 | async session(sessionToken) { 60 | const result = await c.run( 61 | /* GraphQL */ ` 62 | query ($sessionToken: String!) { 63 | querySession(filter: { sessionToken: { eq: $sessionToken } }) { 64 | ...SessionFragment 65 | user { 66 | id 67 | } 68 | } 69 | } 70 | ${fragments.Session} 71 | `, 72 | { sessionToken } 73 | ) 74 | 75 | const { user, ...session } = result?.[0] ?? {} 76 | if (!user?.id) return null 77 | return format.from({ ...session, userId: user.id }) 78 | }, 79 | async account(provider_providerAccountId) { 80 | const result = await c.run( 81 | /* GraphQL */ ` 82 | query ($providerAccountId: String = "", $provider: String = "") { 83 | queryAccount( 84 | filter: { 85 | providerAccountId: { eq: $providerAccountId } 86 | provider: { eq: $provider } 87 | } 88 | ) { 89 | ...AccountFragment 90 | user { 91 | id 92 | } 93 | } 94 | } 95 | ${fragments.Account} 96 | `, 97 | provider_providerAccountId 98 | ) 99 | 100 | const account = format.from(result?.[0]) 101 | if (!account?.user) return null 102 | 103 | account.userId = account.user.id 104 | delete account.user 105 | return account 106 | }, 107 | async verificationToken(identifier_token) { 108 | const result = await c.run( 109 | /* GraphQL */ ` 110 | query ($identifier: String = "", $token: String = "") { 111 | queryVerificationToken( 112 | filter: { identifier: { eq: $identifier }, token: { eq: $token } } 113 | ) { 114 | ...VerificationTokenFragment 115 | } 116 | } 117 | ${fragments.VerificationToken} 118 | `, 119 | identifier_token 120 | ) 121 | 122 | return format.from(result?.[0]) 123 | }, 124 | }, 125 | }) 126 | -------------------------------------------------------------------------------- /packages/dgraph/tests/private.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIJKQIBAAKCAgEAxqyvd82VacXMBLUADZt+euSNUNJ276XgvH4HW4ms5iQZDgYI 3 | PKxyaZ+wk8EMYSB1dymJ3WQpm0JKHqgTW+z/edfYFQXkduHN/zoIpxMAMyZGsTBi 4 | dGo0xJSHTCDCdYCCBlG9R1ljjhf0l9ChBP7W7lSXaRU/XS/tMH1qYMpsUwDav4G/ 5 | RDI3A4t29JRGqU4mnFa5o3XBCxU4ANCp1JaQevzAYox8EGPZ1YZGmhRgca51dBee 6 | d9QKqWjfXP4wboC1ppglm+kPgFUaCiXB8KyfIixhlvzZiO4RLvZw+cILt586vXGz 7 | Ny49eVUTiIOoTZuG/79pCeBS8BCbB4l6y274y42hUN83gHxQ32Y++DI40jz5iGN8 8 | 5Dj6yDDjKwvwqVhCx/kVJFrmyrTJz/E0cp38FeIi7D6e0eXj7G97K+wkNdc4oTs1 9 | DsDPzhO/7wxQOZIjvNp+DJAfxin5MbM+UKoopvJj3sUMHVrTteWxZg94mmLjg2Kn 10 | JYBuSn8kiFPYQ0F5MjE7df4tDDTGJ/VEFIG5EkQffaNYhW0Z5ORLvW1R1Yd1/ew3 11 | UWo+mZ7XAUGLF6clsWSQvzSrrNMYzCk5Fa0LwvMtQdEVLL3q7/KsEHD7N78EVlmE 12 | DlOtC21UidUqXnawCE1QIjAHqFsNNPR2j0lgOoEjrGdzrvUg6hNV9m6CbSECAwEA 13 | AQKCAgAjr8kk/+yiv0DSZ6DG0PN7J6qqpeNvUKB5uzmfG6/O9xT5C+RW4bL7fg+9 14 | uqN6ntX6vZ9iASfoF5QwxYgUrxGE1VyfChvrrsvN2KLNQAB9L5brJQHKX3lzBir3 15 | ZbsIWDkC4ZPaSRg04eCxlGwX9Z6t2MwJuCNVndJBL4X4NOQYVML2O1wb59kx7c9E 16 | R44Zw0v0MS/PSMuQLhONMe4Pnav+K4BzM0DlwMnULPZpntdkFC5M2CFC7PetToUw 17 | swgIEV6PuiynQMnkB2VSBU486QT8onQ1Jt38VqcHhITumAh6x0NJ3C6Q7uFj9gA4 18 | OU32AsXREpTPjVfYf2MZi3xfJmPR+1JTqmnhWY7g/v3K5MpFO9HGmcETNpV4YXRv 19 | U18Bx+m5FsKp0tFASyS/6PJoDAJ/a6yQxVNc1nYL8AKTFqod/0pQz2w2yFGR2t1g 20 | Ui+7HQrWRpdvp2vDJK2GJLs+thybtd73QwsKJ2LFHS91eQ1y1BsSI4z1Ph8/66xK 21 | uQVWfeQqQIhbM8m/pzOYNw90jRx9raKZ6QpdmLqoKj4WF3a/KvLc0TO678wzVoSM 22 | qBDH9FwmkebNHWEMR8rR5Fb1ZVHclSde6DqdPBTvcQzMk66ZGMHB746G68620iKs 23 | YJ6dFDBt3XBnhhOjPhCCH4XR8ZIGTgwxC9hry17/sUMEU5iS8QKCAQEA7WnbfI+h 24 | oLnfw0M6uxIrvl1MMip1Zq/f2/q3HIpE6qLuPoy4fzjONNYm8QBwdJSVPviMCsFx 25 | rU2IIHLeQGUSvMIIcWzn+EWKl3XTzirdn9uYZPPqGr/YuoLW/uN2TCppBbzT1jtA 26 | bbQYUfvyF+ysU+F9amLSdDsqM3MwaFMNChcf3XLMz7QFgoWIDSejq4Uhy6y22KEi 27 | qg+VprX9OejzUQLb0I8ko9S3dKAHkhUZJ8kxowu5oqaaGpowBhko84zKpNrGbinG 28 | bA0+LTxAUKaHhioWWgXya976DQRBdTkp7wOWuD/jnL3wnIHDYF0TKYuidu98d+zH 29 | b/+EH/wPEK4DrwKCAQEA1jpwJm2CDkw41TNexLectOlAjVw9xMt+10hLZ2PYOuwd 30 | kThLYU9zqYIp9thj9/Ddqkx286qHaX92W2q0SZmhuXeNLmcG71QGJp/6uC+up0Hk 31 | 7aFPoQ3uS7JQN5YwinUy/0vbTsxmko0Ie9y2gA0bWDV4Yu5zr/vYd/bLD55GPRD/ 32 | WWGWkDlzlQqedQkjaCSRskm6nyFdTSsruw6RMdNiZK6jBR2aY0SsFmJmOwrTrPCS 33 | llg+zaUtqwgC4tLROx8R5rkJh8S+/KjRN46oXPphQLTJlNZu1uTjV5Ue/BqpHgor 34 | hJLgZwfA7YXJFfiSfjYFYTj9vm9Wx50zJSKiEZxALwKCAQEA6Czcy8y/GKqN7Kwj 35 | lGypwMoGyQyCsYCPoNZoGo4R5ZCfAyalCy2nYz6G6KswTqI77lAszBvvqramyGzt 36 | cvYlQ9lRXnNNy5tedM5y6y06fanIN/ndWHmDXqqzzKLvvn6/JDBMzjY1xNMZ8Zs9 37 | Xy5CPOnIt7Ca9bYiiBw/G9cUamjA7dTl/L2locYqjgrU4dkZetCWI/Y5KyyAgn95 38 | fBeXVANCqoxCHcHaA0C5BqCBcEous6+0xB6/mAJvspcKWFu4lU2qPnO2K1csFhrV 39 | HsoswQUJxNIKCHoP+YjO5u+XVbohvGAmnNOXqcaxJdz/72Ix6LQ9+h3h0GKGeK0M 40 | opg62wKCAQEAnyRoXdOp8s8ixRbVRtOTwT0prBmi9UeqoWjeQx8D6bmvuUqVjOOF 41 | 6517aRmVIgI32SPWlerPj0qV9RFOfwJ3Bp1OLvNwTmgf7Z+YlC0v1KZ51yGnUuBT 42 | br43IyQaSTEJQmfqsh3b8PB+Je1vUa7q6ltGZE/5dvli9LNMY/zS9thiqNZ7EAbt 43 | 2wE5d33jZKEN7uEglsglVIdGhD4tFFOQ23R0O/+iyi2gnTxZ73B6kRVh//fsJ76W 44 | L2DTLAcqUX4iQUCiWM6Kho0uZtQ+NFv31Sa4PS4SxubgEBcCHov7qAosC99EfqVe 45 | 59Qj7oNq6AFfe7rnnQl+8OjRrruMpAJsFwKCAQBxq1apDQTav7QW9Sfe19POZas0 46 | b0XIETL3mEh25uCqPTmoaKo45opgw0Cn7zpuy/NntKlG/cnPYneQh91bViqid/Iv 47 | 3M88vQJmS2e4abozqa7iNjd/XwmBcCgdR2yx51oJ9q9dfd2ejKfMDzm0uHs5U7ay 48 | pOlUch5OT0s5utZC4FbeziZ8Th61DtmIHOxQpNYpPXogdkbGSaOhL6dezPOAwJnJ 49 | B2zjH7N1c+dz+5HheVbN3M08aN9DdyD1xsmd8eZVTAi1wcp51GY6cb7G0gE2SzOp 50 | UNtVbc17n82jJ5Qr4ggSRU1QWNBZT9KX4U2/nTe3U5C3+ni4p+opI9Q3vSYw 51 | -----END RSA PRIVATE KEY----- -------------------------------------------------------------------------------- /packages/dgraph/tests/public.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxqyvd82VacXMBLUADZt+ 3 | euSNUNJ276XgvH4HW4ms5iQZDgYIPKxyaZ+wk8EMYSB1dymJ3WQpm0JKHqgTW+z/ 4 | edfYFQXkduHN/zoIpxMAMyZGsTBidGo0xJSHTCDCdYCCBlG9R1ljjhf0l9ChBP7W 5 | 7lSXaRU/XS/tMH1qYMpsUwDav4G/RDI3A4t29JRGqU4mnFa5o3XBCxU4ANCp1JaQ 6 | evzAYox8EGPZ1YZGmhRgca51dBeed9QKqWjfXP4wboC1ppglm+kPgFUaCiXB8Kyf 7 | IixhlvzZiO4RLvZw+cILt586vXGzNy49eVUTiIOoTZuG/79pCeBS8BCbB4l6y274 8 | y42hUN83gHxQ32Y++DI40jz5iGN85Dj6yDDjKwvwqVhCx/kVJFrmyrTJz/E0cp38 9 | FeIi7D6e0eXj7G97K+wkNdc4oTs1DsDPzhO/7wxQOZIjvNp+DJAfxin5MbM+UKoo 10 | pvJj3sUMHVrTteWxZg94mmLjg2KnJYBuSn8kiFPYQ0F5MjE7df4tDDTGJ/VEFIG5 11 | EkQffaNYhW0Z5ORLvW1R1Yd1/ew3UWo+mZ7XAUGLF6clsWSQvzSrrNMYzCk5Fa0L 12 | wvMtQdEVLL3q7/KsEHD7N78EVlmEDlOtC21UidUqXnawCE1QIjAHqFsNNPR2j0lg 13 | OoEjrGdzrvUg6hNV9m6CbSECAwEAAQ== 14 | -----END PUBLIC KEY----- -------------------------------------------------------------------------------- /packages/dgraph/tests/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CONTAINER_NAME=next-auth-dgraph 4 | JEST_WATCH=false 5 | 6 | # Is the watch flag passed to the script? 7 | while getopts w flag 8 | do 9 | case "${flag}" in 10 | w) JEST_WATCH=true;; 11 | *) continue;; 12 | esac 13 | done 14 | 15 | # Start db 16 | 17 | docker run -d --rm \ 18 | -p 8000:8000 -p 8080:8080 \ 19 | --name "${CONTAINER_NAME}" \ 20 | dgraph/standalone 21 | 22 | echo "Waiting 15 sec for db to start..." && sleep 15 23 | 24 | head -n -1 src/graphql/schema.gql > tests/test.schema.gql 25 | PUBLIC_KEY=$(sed 's/$/\\n/' tests/public.key | tr -d '\n') 26 | echo "# Dgraph.Authorization {\"VerificationKey\":\"$PUBLIC_KEY\",\"Namespace\":\"https://dgraph.io/jwt/claims\",\"Header\":\"Authorization\",\"Algo\":\"RS256\"}" >> tests/test.schema.gql 27 | 28 | curl -X POST localhost:8080/admin/schema --data-binary '@tests/test.schema.gql' 29 | 30 | printf "\nWaiting 5 sec for schema to be uploaded..." && sleep 5 31 | 32 | if $JEST_WATCH; then 33 | # Run jest in watch mode 34 | npx jest tests --watch 35 | # Only stop the container after jest has been quit 36 | docker stop "${CONTAINER_NAME}" 37 | else 38 | # Always stop container, but exit with 1 when tests are failing 39 | if npx jest tests; then 40 | docker stop "${CONTAINER_NAME}" 41 | else 42 | docker stop "${CONTAINER_NAME}" 43 | fi 44 | fi 45 | 46 | rm tests/test.schema.gql -------------------------------------------------------------------------------- /packages/dgraph/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "outDir": "dist" 6 | }, 7 | "exclude": ["tests", "dist"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/dynamodb/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [1.1.0](https://github.com/nextauthjs/adapters/compare/@next-auth/dynamodb-adapter@1.0.2...@next-auth/dynamodb-adapter@1.1.0) (2022-02-02) 7 | 8 | ### Features 9 | 10 | - **dynamodb:** configurable dynamodb keys ([#380](https://github.com/nextauthjs/adapters/issues/380)) ([c9555aa](https://github.com/nextauthjs/adapters/commit/c9555aa33447a365640d6253a0e73d3cb1f64c14)) 11 | 12 | ## [1.0.2](https://github.com/nextauthjs/adapters/compare/@next-auth/dynamodb-adapter@1.0.1...@next-auth/dynamodb-adapter@1.0.2) (2022-01-10) 13 | 14 | ### Bug Fixes 15 | 16 | - **dynamo:** Ensure expires attribute is stored as a UNIX timestamp ([#367](https://github.com/nextauthjs/adapters/issues/367)) ([c262110](https://github.com/nextauthjs/adapters/commit/c2621103d01c10929fb3e2b0aef4443d1e2e0be1)) 17 | 18 | ## [1.0.1](https://github.com/nextauthjs/adapters/compare/@next-auth/dynamodb-adapter@1.0.0...@next-auth/dynamodb-adapter@1.0.1) (2021-12-06) 19 | 20 | **Note:** Version bump only for package @next-auth/dynamodb-adapter 21 | 22 | ## [0.4.2](https://github.com/nextauthjs/adapters/compare/@next-auth/dynamodb-adapter@0.4.1...@next-auth/dynamodb-adapter@0.4.2) (2021-07-02) 23 | 24 | **Note:** Version bump only for package @next-auth/dynamodb-adapter 25 | 26 | ## [0.4.1](https://github.com/nextauthjs/adapters/compare/@next-auth/dynamodb-adapter@0.4.0...@next-auth/dynamodb-adapter@0.4.1) (2021-06-30) 27 | 28 | **Note:** Version bump only for package @next-auth/dynamodb-adapter 29 | -------------------------------------------------------------------------------- /packages/dynamodb/jest-dynamodb-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | tables: [ 3 | { 4 | TableName: `next-auth`, 5 | KeySchema: [ 6 | { AttributeName: "pk", KeyType: "HASH" }, 7 | { AttributeName: "sk", KeyType: "RANGE" }, 8 | ], 9 | AttributeDefinitions: [ 10 | { AttributeName: "sk", AttributeType: "S" }, 11 | { AttributeName: "pk", AttributeType: "S" }, 12 | { AttributeName: "GSI1PK", AttributeType: "S" }, 13 | { AttributeName: "GSI1SK", AttributeType: "S" }, 14 | ], 15 | GlobalSecondaryIndexes: [ 16 | { 17 | IndexName: "GSI1", 18 | KeySchema: [ 19 | { AttributeName: "GSI1PK", KeyType: "HASH" }, 20 | { AttributeName: "GSI1SK", KeyType: "RANGE" }, 21 | ], 22 | Projection: { 23 | ProjectionType: "ALL", 24 | }, 25 | ProvisionedThroughput: { 26 | ReadCapacityUnits: 1, 27 | WriteCapacityUnits: 1, 28 | }, 29 | }, 30 | ], 31 | ProvisionedThroughput: { ReadCapacityUnits: 1, WriteCapacityUnits: 1 }, 32 | }, 33 | { 34 | TableName: `next-auth-custom`, 35 | KeySchema: [ 36 | { AttributeName: "PK", KeyType: "HASH" }, 37 | { AttributeName: "SK", KeyType: "RANGE" }, 38 | ], 39 | AttributeDefinitions: [ 40 | { AttributeName: "PK", AttributeType: "S" }, 41 | { AttributeName: "SK", AttributeType: "S" }, 42 | { AttributeName: "gsi1pk", AttributeType: "S" }, 43 | { AttributeName: "gsi1sk", AttributeType: "S" }, 44 | ], 45 | GlobalSecondaryIndexes: [ 46 | { 47 | IndexName: "gsi1", 48 | KeySchema: [ 49 | { AttributeName: "gsi1pk", KeyType: "HASH" }, 50 | { AttributeName: "gsi1sk", KeyType: "RANGE" }, 51 | ], 52 | Projection: { 53 | ProjectionType: "ALL", 54 | }, 55 | ProvisionedThroughput: { 56 | ReadCapacityUnits: 1, 57 | WriteCapacityUnits: 1, 58 | }, 59 | }, 60 | ], 61 | ProvisionedThroughput: { ReadCapacityUnits: 1, WriteCapacityUnits: 1 }, 62 | }, 63 | // etc 64 | ], 65 | port: 8000, 66 | installerConfig: { 67 | installPath: "./dynamodblocal-bin", 68 | downloadUrl: 69 | "https://s3.eu-central-1.amazonaws.com/dynamodb-local-frankfurt/dynamodb_local_latest.tar.gz", 70 | }, 71 | } 72 | -------------------------------------------------------------------------------- /packages/dynamodb/jest.config.js: -------------------------------------------------------------------------------- 1 | /* 2 | * For a detailed explanation regarding each configuration property and type check, visit: 3 | * https://jestjs.io/docs/en/configuration.html 4 | */ 5 | const { defaults: tsjPreset } = require("ts-jest/presets") 6 | 7 | module.exports = { 8 | // Indicates whether the coverage information should be collected while executing the test 9 | collectCoverage: true, 10 | // Indicates which provider should be used to instrument code for coverage 11 | coverageProvider: "v8", 12 | // A preset that is used as a base for Jest's configuration 13 | preset: "@shelf/jest-dynamodb", 14 | transform: { 15 | ...tsjPreset.transform, 16 | }, 17 | // The test environment that will be used for testing 18 | testEnvironment: "node", 19 | } 20 | -------------------------------------------------------------------------------- /packages/dynamodb/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nextauthjs/adapters/1237a5372004951eccb37c5f7481e5fcb62415b6/packages/dynamodb/logo.png -------------------------------------------------------------------------------- /packages/dynamodb/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@next-auth/dynamodb-adapter", 3 | "repository": "https://github.com/nextauthjs/adapters", 4 | "version": "1.1.0", 5 | "description": "AWS DynamoDB adapter for next-auth.", 6 | "keywords": [ 7 | "next-auth", 8 | "next.js", 9 | "oauth", 10 | "dynamodb" 11 | ], 12 | "homepage": "https://next-auth.js.org", 13 | "bugs": { 14 | "url": "https://github.com/nextauthjs/next-auth/issues" 15 | }, 16 | "main": "dist/index.js", 17 | "private": false, 18 | "publishConfig": { 19 | "access": "public" 20 | }, 21 | "scripts": { 22 | "test:default": "jest", 23 | "test:custom": "CUSTOM_MODEL=1 jest", 24 | "test": "yarn test:default && yarn test:custom", 25 | "build": "tsc" 26 | }, 27 | "files": [ 28 | "README.md", 29 | "dist" 30 | ], 31 | "author": "Pol Marnette", 32 | "license": "ISC", 33 | "peerDependencies": { 34 | "@aws-sdk/lib-dynamodb": "^3.36.1", 35 | "next-auth": "^4.0.1" 36 | }, 37 | "devDependencies": { 38 | "@aws-sdk/client-dynamodb": "^3.36.1", 39 | "@aws-sdk/lib-dynamodb": "^3.36.1", 40 | "@shelf/jest-dynamodb": "^2.1.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/dynamodb/src/utils.ts: -------------------------------------------------------------------------------- 1 | // https://github.com/honeinc/is-iso-date/blob/master/index.js 2 | const isoDateRE = 3 | /(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))/ 4 | function isDate(value: any) { 5 | return value && isoDateRE.test(value) && !isNaN(Date.parse(value)) 6 | } 7 | 8 | export const format = { 9 | /** Takes a plain old JavaScript object and turns it into a Dynamodb object */ 10 | to(object: Record) { 11 | const newObject: Record = {} 12 | for (const key in object) { 13 | const value = object[key] 14 | if (value instanceof Date) { 15 | // DynamoDB requires the TTL attribute be a UNIX timestamp (in secs). 16 | if (key === "expires") newObject[key] = value.getTime() / 1000 17 | else newObject[key] = value.toISOString() 18 | } else newObject[key] = value 19 | } 20 | return newObject 21 | }, 22 | /** Takes a Dynamo object and returns a plain old JavaScript object */ 23 | from>( 24 | object: Record | undefined, 25 | keys: string[] = ["pk", "sk", "GSI1PK", "GSI1SK"] 26 | ): T | null { 27 | if (!object) return null 28 | const newObject: Record = {} 29 | for (const key in object) { 30 | // Filter DynamoDB table and GSI keys so it doesn't get passed to core, 31 | // to avoid revealing the type of database 32 | if (keys.includes(key)) continue 33 | 34 | const value = object[key] 35 | 36 | if (isDate(value)) newObject[key] = new Date(value) 37 | // hack to keep type property in account 38 | else if (key === "type" && ["SESSION", "VT", "USER"].includes(value)) 39 | continue 40 | // The expires property is stored as a UNIX timestamp in seconds, but 41 | // JavaScript needs it in milliseconds, so multiply by 1000. 42 | else if (key === "expires" && typeof value === "number") 43 | newObject[key] = new Date(value * 1000) 44 | else newObject[key] = value 45 | } 46 | return newObject as T 47 | }, 48 | } 49 | 50 | export function generateUpdateExpression(object: Record): { 51 | UpdateExpression: string 52 | ExpressionAttributeNames: Record 53 | ExpressionAttributeValues: Record 54 | } { 55 | const formatedSession = format.to(object) 56 | let UpdateExpression = "set" 57 | const ExpressionAttributeNames: Record = {} 58 | const ExpressionAttributeValues: Record = {} 59 | for (const property in formatedSession) { 60 | UpdateExpression += ` #${property} = :${property},` 61 | ExpressionAttributeNames["#" + property] = property 62 | ExpressionAttributeValues[":" + property] = formatedSession[property] 63 | } 64 | UpdateExpression = UpdateExpression.slice(0, -1) 65 | return { 66 | UpdateExpression, 67 | ExpressionAttributeNames, 68 | ExpressionAttributeValues, 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /packages/dynamodb/tests/custom.test.ts: -------------------------------------------------------------------------------- 1 | import type { DynamoDBAdapterOptions } from "../src" 2 | import { DynamoDBAdapter, format } from "../src" 3 | import { runBasicTests } from "../../../basic-tests" 4 | import { DynamoDBDocument } from "@aws-sdk/lib-dynamodb" 5 | import { DynamoDB } from "@aws-sdk/client-dynamodb" 6 | 7 | const config = { 8 | endpoint: "http://127.0.0.1:8000", 9 | region: "eu-central-1", 10 | tls: false, 11 | credentials: { 12 | accessKeyId: "foo", 13 | secretAccessKey: "bar", 14 | }, 15 | } 16 | 17 | export const client = DynamoDBDocument.from(new DynamoDB(config), { 18 | marshallOptions: { 19 | convertEmptyValues: true, 20 | removeUndefinedValues: true, 21 | convertClassInstanceToMap: true, 22 | }, 23 | }) 24 | 25 | const tableName = "next-auth-custom" 26 | const indexName = "gsi1" 27 | const partitionKey = "PK" 28 | const sortKey = "SK" 29 | const indexPartitionKey = "gsi1pk" 30 | const indexSortKey = "gsi1sk" 31 | const keys = [partitionKey, sortKey, indexPartitionKey, indexSortKey] 32 | 33 | const options: DynamoDBAdapterOptions = { 34 | tableName, 35 | partitionKey, 36 | sortKey, 37 | indexName, 38 | indexPartitionKey, 39 | indexSortKey, 40 | } 41 | 42 | const adapter = DynamoDBAdapter(client, options) 43 | const TableName = tableName 44 | const IndexName = indexName 45 | 46 | runBasicTests({ 47 | adapter, 48 | db: { 49 | async user(id) { 50 | const user = await client.get({ 51 | TableName, 52 | Key: { 53 | [partitionKey]: `USER#${id}`, 54 | [sortKey]: `USER#${id}`, 55 | }, 56 | }) 57 | 58 | return format.from(user.Item, keys) 59 | }, 60 | async session(token) { 61 | const session = await client.query({ 62 | TableName, 63 | IndexName, 64 | KeyConditionExpression: "#gsi1pk = :gsi1pk AND #gsi1sk = :gsi1sk", 65 | ExpressionAttributeNames: { 66 | "#gsi1pk": indexPartitionKey, 67 | "#gsi1sk": indexSortKey, 68 | }, 69 | ExpressionAttributeValues: { 70 | ":gsi1pk": `SESSION#${token}`, 71 | ":gsi1sk": `SESSION#${token}`, 72 | }, 73 | }) 74 | 75 | return format.from(session.Items?.[0], keys) 76 | }, 77 | async account({ provider, providerAccountId }) { 78 | const account = await client.query({ 79 | TableName, 80 | IndexName, 81 | KeyConditionExpression: "#gsi1pk = :gsi1pk AND #gsi1sk = :gsi1sk", 82 | ExpressionAttributeNames: { 83 | "#gsi1pk": indexPartitionKey, 84 | "#gsi1sk": indexSortKey, 85 | }, 86 | ExpressionAttributeValues: { 87 | ":gsi1pk": `ACCOUNT#${provider}`, 88 | ":gsi1sk": `ACCOUNT#${providerAccountId}`, 89 | }, 90 | }) 91 | 92 | return format.from(account.Items?.[0], keys) 93 | }, 94 | async verificationToken({ token, identifier }) { 95 | const vt = await client.get({ 96 | TableName, 97 | Key: { 98 | [partitionKey]: `VT#${identifier}`, 99 | [sortKey]: `VT#${token}`, 100 | }, 101 | }) 102 | return format.from(vt.Item, keys) 103 | }, 104 | }, 105 | }) 106 | -------------------------------------------------------------------------------- /packages/dynamodb/tests/format.test.ts: -------------------------------------------------------------------------------- 1 | import { format } from "../src/utils" 2 | 3 | describe("dynamodb utils.format", () => { 4 | it("format.to() preserves non-Date non-expires properties", () => { 5 | expect( 6 | format.to({ 7 | pk: "test-pk", 8 | email: "test@example.com", 9 | }) 10 | ).toEqual({ 11 | pk: "test-pk", 12 | email: "test@example.com", 13 | }) 14 | }) 15 | 16 | it("format.to() converts non-expires Date properties to ISO strings", () => { 17 | const date = new Date() 18 | expect( 19 | format.to({ 20 | dateProp: date, 21 | }) 22 | ).toEqual({ 23 | dateProp: date.toISOString(), 24 | }) 25 | }) 26 | 27 | it("format.to() converts expires property to a UNIX timestamp", () => { 28 | // DynamoDB requires that the property used for TTL is a UNIX timestamp. 29 | const date = new Date() 30 | const timestamp = date.getTime() / 1000 31 | expect( 32 | format.to({ 33 | expires: date, 34 | }) 35 | ).toEqual({ 36 | expires: timestamp, 37 | }) 38 | }) 39 | 40 | it("format.from() preserves non-special attributes", () => { 41 | expect( 42 | format.from({ 43 | testAttr1: "test-value", 44 | testAttr2: 5, 45 | }) 46 | ).toEqual({ 47 | testAttr1: "test-value", 48 | testAttr2: 5, 49 | }) 50 | }) 51 | 52 | it("format.from() removes dynamodb key attributes", () => { 53 | expect( 54 | format.from({ 55 | pk: "test-pk", 56 | sk: "test-sk", 57 | GSI1PK: "test-GSI1PK", 58 | GSI1SK: "test-GSI1SK", 59 | }) 60 | ).toEqual({}) 61 | }) 62 | 63 | it("format.from() only removes type attribute from Session, VT, and User", () => { 64 | expect(format.from({ type: "SESSION" })).toEqual({}) 65 | expect(format.from({ type: "VT" })).toEqual({}) 66 | expect(format.from({ type: "USER" })).toEqual({}) 67 | expect(format.from({ type: "ANYTHING" })).toEqual({ type: "ANYTHING" }) 68 | expect(format.from({ type: "ELSE" })).toEqual({ type: "ELSE" }) 69 | }) 70 | 71 | it("format.from() converts ISO strings to Date instances", () => { 72 | const date = new Date() 73 | expect( 74 | format.from({ 75 | someDate: date.toISOString(), 76 | }) 77 | ).toEqual({ 78 | someDate: date, 79 | }) 80 | }) 81 | 82 | it("format.from() converts expires attribute from timestamp to Date instance", () => { 83 | // AdapterSession["expires"] and VerificationToken["expires"] are both meant 84 | // to be Date instances. 85 | const date = new Date() 86 | const timestamp = date.getTime() / 1000 87 | expect( 88 | format.from({ 89 | expires: timestamp, 90 | }) 91 | ).toEqual({ 92 | expires: date, 93 | }) 94 | }) 95 | 96 | it("format.from() converts expires attribute from ISO string to Date instance", () => { 97 | // Due to a bug in an old version, some expires attributes were stored as 98 | // ISO strings, so we need to handle those properly too. 99 | const date = new Date() 100 | expect( 101 | format.from({ 102 | expires: date.toISOString(), 103 | }) 104 | ).toEqual({ 105 | expires: date, 106 | }) 107 | }) 108 | 109 | it("format.from(format.to()) preserves expires attribute", () => { 110 | const date = new Date() 111 | expect( 112 | format.from( 113 | format.to({ 114 | expires: date, 115 | }) 116 | ) 117 | ).toEqual({ 118 | expires: date, 119 | }) 120 | }) 121 | }) 122 | -------------------------------------------------------------------------------- /packages/dynamodb/tests/index.test.ts: -------------------------------------------------------------------------------- 1 | import { DynamoDB } from "@aws-sdk/client-dynamodb" 2 | import { DynamoDBDocument } from "@aws-sdk/lib-dynamodb" 3 | import { DynamoDBAdapter } from "../src" 4 | import { runBasicTests } from "../../../basic-tests" 5 | import { format } from "../src/" 6 | const config = { 7 | endpoint: "http://127.0.0.1:8000", 8 | region: "eu-central-1", 9 | tls: false, 10 | credentials: { 11 | accessKeyId: "foo", 12 | secretAccessKey: "bar", 13 | }, 14 | } 15 | 16 | const client = DynamoDBDocument.from(new DynamoDB(config), { 17 | marshallOptions: { 18 | convertEmptyValues: true, 19 | removeUndefinedValues: true, 20 | convertClassInstanceToMap: true, 21 | }, 22 | }) 23 | 24 | const adapter = DynamoDBAdapter(client) 25 | 26 | const TableName = "next-auth" 27 | 28 | runBasicTests({ 29 | adapter, 30 | db: { 31 | async user(id) { 32 | const user = await client.get({ 33 | TableName, 34 | Key: { 35 | pk: `USER#${id}`, 36 | sk: `USER#${id}`, 37 | }, 38 | }) 39 | 40 | return format.from(user.Item) 41 | }, 42 | async session(token) { 43 | const session = await client.query({ 44 | TableName, 45 | IndexName: "GSI1", 46 | KeyConditionExpression: "#gsi1pk = :gsi1pk AND #gsi1sk = :gsi1sk", 47 | ExpressionAttributeNames: { 48 | "#gsi1pk": "GSI1PK", 49 | "#gsi1sk": "GSI1SK", 50 | }, 51 | ExpressionAttributeValues: { 52 | ":gsi1pk": `SESSION#${token}`, 53 | ":gsi1sk": `SESSION#${token}`, 54 | }, 55 | }) 56 | 57 | return format.from(session.Items?.[0]) 58 | }, 59 | async account({ provider, providerAccountId }) { 60 | const account = await client.query({ 61 | TableName, 62 | IndexName: "GSI1", 63 | KeyConditionExpression: "#gsi1pk = :gsi1pk AND #gsi1sk = :gsi1sk", 64 | ExpressionAttributeNames: { 65 | "#gsi1pk": "GSI1PK", 66 | "#gsi1sk": "GSI1SK", 67 | }, 68 | ExpressionAttributeValues: { 69 | ":gsi1pk": `ACCOUNT#${provider}`, 70 | ":gsi1sk": `ACCOUNT#${providerAccountId}`, 71 | }, 72 | }) 73 | 74 | return format.from(account.Items?.[0]) 75 | }, 76 | async verificationToken({ token, identifier }) { 77 | const vt = await client.get({ 78 | TableName, 79 | Key: { 80 | pk: `VT#${identifier}`, 81 | sk: `VT#${token}`, 82 | }, 83 | }) 84 | return format.from(vt.Item) 85 | }, 86 | }, 87 | }) 88 | -------------------------------------------------------------------------------- /packages/dynamodb/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "outDir": "dist" 6 | }, 7 | "exclude": ["tests", "dist"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/fauna/.babelrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require("../../.babelrc.js") 2 | -------------------------------------------------------------------------------- /packages/fauna/.fauna-migrate.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | directories: { 3 | root: "fauna", 4 | resources: "resources", 5 | migrations: "migrations", 6 | children: "dbs", 7 | temp: "temp", 8 | }, 9 | collection: "migrations", 10 | } 11 | -------------------------------------------------------------------------------- /packages/fauna/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [1.0.2](https://github.com/nextauthjs/adapters/compare/@next-auth/fauna-adapter@1.0.1...@next-auth/fauna-adapter@1.0.2) (2022-01-10) 7 | 8 | ### Bug Fixes 9 | 10 | - **fauna:** Convert `value` prop to `Date` only if of type `string` ([#365](https://github.com/nextauthjs/adapters/issues/365)) ([c8dad2b](https://github.com/nextauthjs/adapters/commit/c8dad2b1bf74ab1574c92ee5fda879d798a43977)), closes [#364](https://github.com/nextauthjs/adapters/issues/364) 11 | 12 | ## [1.0.1](https://github.com/nextauthjs/adapters/compare/@next-auth/fauna-adapter@1.0.0...@next-auth/fauna-adapter@1.0.1) (2021-12-06) 13 | 14 | **Note:** Version bump only for package @next-auth/fauna-adapter 15 | 16 | ## [0.2.4](https://github.com/nextauthjs/adapters/compare/@next-auth/fauna-adapter@0.2.3...@next-auth/fauna-adapter@0.2.4) (2021-10-27) 17 | 18 | ### Reverts 19 | 20 | - Revert "docs(fauna): update `README.md` (#244)" (#246) ([26cd24a](https://github.com/nextauthjs/adapters/commit/26cd24a6eba3d42ed7febd5eb45b13c236c57819)), closes [#244](https://github.com/nextauthjs/adapters/issues/244) [#246](https://github.com/nextauthjs/adapters/issues/246) 21 | 22 | ## [0.2.3](https://github.com/nextauthjs/adapters/compare/@next-auth/fauna-adapter@0.2.2...@next-auth/fauna-adapter@0.2.3) (2021-09-19) 23 | 24 | **Note:** Version bump only for package @next-auth/fauna-adapter 25 | 26 | ## [0.2.2](https://github.com/nextauthjs/adapters/compare/@next-auth/fauna-adapter@0.2.1...@next-auth/fauna-adapter@0.2.2) (2021-07-02) 27 | 28 | **Note:** Version bump only for package @next-auth/fauna-adapter 29 | 30 | ## [0.2.1](https://github.com/nextauthjs/adapters/compare/@next-auth/fauna-adapter@0.2.0...@next-auth/fauna-adapter@0.2.1) (2021-06-30) 31 | 32 | ### Bug Fixes 33 | 34 | - **fauna:** change the name of the index to `verification_request_by_token_and_identifier` ([#157](https://github.com/nextauthjs/adapters/issues/157)) ([01a3c52](https://github.com/nextauthjs/adapters/commit/01a3c5205f30eec57c7b9298b762cccf1f2400fd)) 35 | 36 | # [0.2.0](https://github.com/nextauthjs/adapters/compare/@next-auth/fauna-adapter@0.1.0...@next-auth/fauna-adapter@0.2.0) (2021-06-30) 37 | 38 | ### Bug Fixes 39 | 40 | - adapter export function name ([eb6a21a](https://github.com/nextauthjs/adapters/commit/eb6a21a0302ef42a32314e48a75542bade26605e)) 41 | - include /dist files in published build ([751ea95](https://github.com/nextauthjs/adapters/commit/751ea95a3b40dc3a94bf4de6253974e1664a2661)) 42 | - merge conflicts ([aa48f2f](https://github.com/nextauthjs/adapters/commit/aa48f2f7586345764d0a586df23534f9abc2b53d)) 43 | - rm type=module from package.json ([c207348](https://github.com/nextauthjs/adapters/commit/c207348d126a766abe341e6afe36b04d47c6bac6)) 44 | - specify module in package.json ([d6e85ce](https://github.com/nextauthjs/adapters/commit/d6e85ce68b0a7d70f6b6078ac8d66e36c4724131)) 45 | - test match new export ([ee96664](https://github.com/nextauthjs/adapters/commit/ee966647dadbc649d6a93f5ae4d5fb5deb6f6772)) 46 | 47 | ### Features 48 | 49 | - add build step to package.json ([28a4f40](https://github.com/nextauthjs/adapters/commit/28a4f403b07fc115c171623d6801c9392f50bd28)) 50 | -------------------------------------------------------------------------------- /packages/fauna/README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 | 4 |      5 |

Fauna Adapter - NextAuth.js

6 |

7 | Open Source. Full Stack. Own Your Data. 8 |

9 |

10 | Build Test 11 | Bundle Size 12 | @next-auth/fauna-adapter Version 13 |

14 |

15 | 16 | ## Overview 17 | 18 | This is the Fauna Adapter for [`next-auth`](https://next-auth.js.org). This package can only be used in conjunction with the primary `next-auth` package. It is not a standalone package. 19 | 20 | You can find the Fauna schema and seed information in the docs at [next-auth.js.org/adapters/fauna](https://next-auth.js.org/adapters/fauna). 21 | 22 | ## Getting Started 23 | 24 | 1. Install `faunadb`, `next-auth` and `@next-auth/fauna-adapter` 25 | 26 | ```js 27 | npm install faunadb next-auth @next-auth/fauna-adapter@next 28 | ``` 29 | 30 | 2. Add this adapter to your `pages/api/[...nextauth].js` next-auth configuration object. 31 | 32 | ```js 33 | import NextAuth from "next-auth" 34 | import { Client as FaunaClient } from "faunadb" 35 | import { FaunaAdapter } from "@next-auth/fauna-adapter" 36 | 37 | const client = new FaunaClient({ 38 | secret: "secret", 39 | scheme: "http", 40 | domain: "localhost", 41 | port: 8443, 42 | }) 43 | 44 | // For more information on each option (and a full list of options) go to 45 | // https://next-auth.js.org/configuration/options 46 | export default NextAuth({ 47 | // https://next-auth.js.org/configuration/providers 48 | providers: [], 49 | adapter: FaunaAdapter(client) 50 | ... 51 | }) 52 | ``` 53 | 54 | ## Contributing 55 | 56 | We're open to all community contributions! If you'd like to contribute in any way, please read our [Contributing Guide](https://github.com/nextauthjs/adapters/blob/main/CONTRIBUTING.md). 57 | 58 | ## License 59 | 60 | ISC 61 | -------------------------------------------------------------------------------- /packages/fauna/fauna/resources/collections/accounts.fql: -------------------------------------------------------------------------------- 1 | CreateCollection({ name: "accounts" }) -------------------------------------------------------------------------------- /packages/fauna/fauna/resources/collections/sessions.fql: -------------------------------------------------------------------------------- 1 | CreateCollection({ name: "sessions" }) -------------------------------------------------------------------------------- /packages/fauna/fauna/resources/collections/users.fql: -------------------------------------------------------------------------------- 1 | CreateCollection({ name: "users" }) -------------------------------------------------------------------------------- /packages/fauna/fauna/resources/collections/verification_tokens.fql: -------------------------------------------------------------------------------- 1 | CreateCollection({ name: "verification_tokens" }) -------------------------------------------------------------------------------- /packages/fauna/fauna/resources/indexes/account_by_provider_and_provider_account_id.fql: -------------------------------------------------------------------------------- 1 | CreateIndex({ 2 | name: "account_by_provider_and_provider_account_id", 3 | source: Collection("accounts"), 4 | unique: true, 5 | terms: [ 6 | { field: ["data", "provider"] }, 7 | { field: ["data", "providerAccountId"] }, 8 | ], 9 | }) -------------------------------------------------------------------------------- /packages/fauna/fauna/resources/indexes/accounts_by_user_id.fql: -------------------------------------------------------------------------------- 1 | CreateIndex({ 2 | name: "accounts_by_user_id", 3 | source: Collection("accounts"), 4 | terms: [{ field: ["data", "userId"] }] 5 | }) -------------------------------------------------------------------------------- /packages/fauna/fauna/resources/indexes/session_by_session_token.fql: -------------------------------------------------------------------------------- 1 | CreateIndex({ 2 | name: "session_by_session_token", 3 | source: Collection("sessions"), 4 | unique: true, 5 | terms: [{ field: ["data", "sessionToken"] }], 6 | }) -------------------------------------------------------------------------------- /packages/fauna/fauna/resources/indexes/sessions_by_user_id.fql: -------------------------------------------------------------------------------- 1 | CreateIndex({ 2 | name: "sessions_by_user_id", 3 | source: Collection("sessions"), 4 | terms: [{ field: ["data", "userId"] }] 5 | }) -------------------------------------------------------------------------------- /packages/fauna/fauna/resources/indexes/user_by_email.fql: -------------------------------------------------------------------------------- 1 | CreateIndex({ 2 | name: "user_by_email", 3 | source: Collection("users"), 4 | unique: true, 5 | terms: [{ field: ["data", "email"] }], 6 | }) -------------------------------------------------------------------------------- /packages/fauna/fauna/resources/indexes/verification_token_by_identifier_and_token.fql: -------------------------------------------------------------------------------- 1 | CreateIndex({ 2 | name: "verification_token_by_identifier_and_token", 3 | source: Collection("verification_tokens"), 4 | unique: true, 5 | terms: [ 6 | { field: ["data", "identifier"] }, 7 | { field: ["data", "token"] }, 8 | ], 9 | }) -------------------------------------------------------------------------------- /packages/fauna/fauna/schema.fql: -------------------------------------------------------------------------------- 1 | // First run this in the Fauna shell to create the collections 2 | Do( 3 | CreateCollection({ name: "users" }), 4 | CreateCollection({ name: "accounts" }), 5 | CreateCollection({ name: "sessions" }), 6 | CreateCollection({ name: "verification_tokens" }) 7 | ) 8 | 9 | // Then run this in the Fauna shell to create indexes 10 | Do( 11 | CreateIndex({ 12 | name: "accounts_by_user_id", 13 | source: Collection("accounts"), 14 | terms: [{ field: ["data", "userId"] }] 15 | }), 16 | CreateIndex({ 17 | name: "session_by_session_token", 18 | source: Collection("sessions"), 19 | unique: true, 20 | terms: [{ field: ["data", "sessionToken"] }], 21 | }), 22 | CreateIndex({ 23 | name: "sessions_by_user_id", 24 | source: Collection("sessions"), 25 | terms: [{ field: ["data", "userId"] }] 26 | }), 27 | CreateIndex({ 28 | name: "user_by_email", 29 | source: Collection("users"), 30 | unique: true, 31 | terms: [{ field: ["data", "email"] }], 32 | }), 33 | CreateIndex({ 34 | name: "account_by_provider_and_provider_account_id", 35 | source: Collection("accounts"), 36 | unique: true, 37 | terms: [ 38 | { field: ["data", "provider"] }, 39 | { field: ["data", "providerAccountId"] }, 40 | ], 41 | }), 42 | CreateIndex({ 43 | name: "verification_token_by_identifier_and_token", 44 | source: Collection("verification_tokens"), 45 | unique: true, 46 | terms: [ 47 | { field: ["data", "identifier"] }, 48 | { field: ["data", "token"] }, 49 | ], 50 | }) 51 | ) -------------------------------------------------------------------------------- /packages/fauna/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | ...require("../../jest.config"), 3 | transform: { 4 | "^.+\\.jsx?$": "babel-jest", 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /packages/fauna/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /packages/fauna/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@next-auth/fauna-adapter", 3 | "version": "1.0.2", 4 | "description": "Fauna Adapter for NextAuth", 5 | "homepage": "https://next-auth.js.org", 6 | "repository": "https://github.com/nextauthjs/adapters", 7 | "bugs": { 8 | "url": "https://github.com/nextauthjs/next-auth/issues" 9 | }, 10 | "files": [ 11 | "dist", 12 | "README.md" 13 | ], 14 | "author": "Bhanu Teja P", 15 | "contributors": [ 16 | { 17 | "name": "Nico Domino", 18 | "email": "yo@ndo.dev" 19 | }, 20 | { 21 | "name": "Balázs Orbán", 22 | "email": "info@balazsorban.com" 23 | } 24 | ], 25 | "main": "dist/index.js", 26 | "license": "ISC", 27 | "keywords": [ 28 | "next-auth", 29 | "next.js", 30 | "fauna", 31 | "faunadb" 32 | ], 33 | "private": false, 34 | "publishConfig": { 35 | "access": "public" 36 | }, 37 | "scripts": { 38 | "migrate": "fauna-schema-migrate generate", 39 | "build": "tsc", 40 | "test": "./tests/test.sh" 41 | }, 42 | "peerDependencies": { 43 | "faunadb": "^4.3.0", 44 | "next-auth": "^4.0.1" 45 | }, 46 | "devDependencies": { 47 | "@fauna-labs/fauna-schema-migrate": "^2.1.3", 48 | "faunadb": "^4.3.0" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/fauna/tests/index.test.ts: -------------------------------------------------------------------------------- 1 | import { collections, FaunaAdapter, format, indexes, query } from "../src" 2 | import { runBasicTests } from "../../../basic-tests" 3 | import { Client as FaunaClient, Get, Match, Ref } from "faunadb" 4 | 5 | const client = new FaunaClient({ 6 | secret: "secret", 7 | scheme: "http", 8 | domain: "localhost", 9 | port: 8443, 10 | }) 11 | 12 | const q = query(client, format.from) 13 | 14 | runBasicTests({ 15 | adapter: FaunaAdapter(client), 16 | db: { 17 | disconnect: async () => await client.close({ force: true }), 18 | user: async (id) => await q(Get(Ref(collections.Users, id))), 19 | session: async (sessionToken) => 20 | await q(Get(Match(indexes.SessionByToken, sessionToken))), 21 | async account({ provider, providerAccountId }) { 22 | const key = [provider, providerAccountId] 23 | const ref = Match(indexes.AccountByProviderAndProviderAccountId, key) 24 | return await q(Get(ref)) 25 | }, 26 | async verificationToken({ identifier, token }) { 27 | const key = [identifier, token] 28 | const ref = Match(indexes.VerificationTokenByIdentifierAndToken, key) 29 | const verificationToken = await q(Get(ref)) 30 | // @ts-expect-error 31 | if (verificationToken) delete verificationToken.id 32 | return verificationToken 33 | }, 34 | }, 35 | }) 36 | -------------------------------------------------------------------------------- /packages/fauna/tests/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CONTAINER_NAME=next-auth-fauna-test 4 | export FAUNADB_PORT=8443 5 | export FAUNA_ADMIN_KEY=secret 6 | export FAUNADB_DOMAIN=localhost 7 | export FAUNADB_SCHEME=http 8 | 9 | 10 | # Start db 11 | docker run -d --rm \ 12 | --name ${CONTAINER_NAME} \ 13 | -p ${FAUNADB_PORT}:${FAUNADB_PORT} \ 14 | fauna/faunadb 15 | 16 | echo "Waiting 20 sec for db to start..." 17 | sleep 20 18 | 19 | # Create tables and indeces 20 | npx fauna-schema-migrate generate 21 | npx fauna-schema-migrate apply all 22 | 23 | # Always stop container, but exit with 1 when tests are failing 24 | if npx jest;then 25 | docker stop ${CONTAINER_NAME} 26 | else 27 | docker stop ${CONTAINER_NAME} && exit 1 28 | fi 29 | -------------------------------------------------------------------------------- /packages/fauna/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "outDir": "dist" 6 | }, 7 | "exclude": ["tests", "dist"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/firebase/.firebaserc: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /packages/firebase/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | firebase-debug.log* 8 | firebase-debug.*.log* 9 | 10 | # Firebase cache 11 | .firebase/ 12 | 13 | # Firebase config 14 | 15 | # Uncomment this if you'd like others to create their own Firebase project. 16 | # For a team working on the same Firebase project(s), it is recommended to leave 17 | # it commented so all members can deploy to the same project(s) in .firebaserc. 18 | # .firebaserc 19 | 20 | # Runtime data 21 | pids 22 | *.pid 23 | *.seed 24 | *.pid.lock 25 | 26 | # Directory for instrumented libs generated by jscoverage/JSCover 27 | lib-cov 28 | 29 | # Coverage directory used by tools like istanbul 30 | coverage 31 | 32 | # nyc test coverage 33 | .nyc_output 34 | 35 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 36 | .grunt 37 | 38 | # Bower dependency directory (https://bower.io/) 39 | bower_components 40 | 41 | # node-waf configuration 42 | .lock-wscript 43 | 44 | # Compiled binary addons (http://nodejs.org/api/addons.html) 45 | build/Release 46 | 47 | # Dependency directories 48 | node_modules/ 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Optional REPL history 57 | .node_repl_history 58 | 59 | # Output of 'npm pack' 60 | *.tgz 61 | 62 | # Yarn Integrity file 63 | .yarn-integrity 64 | 65 | # dotenv environment variables file 66 | .env 67 | -------------------------------------------------------------------------------- /packages/firebase/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [0.1.3](https://github.com/nextauthjs/adapters/compare/@next-auth/firebase-adapter@0.1.2...@next-auth/firebase-adapter@0.1.3) (2021-08-17) 7 | 8 | **Note:** Version bump only for package @next-auth/firebase-adapter 9 | 10 | ## [0.1.2](https://github.com/nextauthjs/adapters/compare/@next-auth/firebase-adapter@0.1.1...@next-auth/firebase-adapter@0.1.2) (2021-07-02) 11 | 12 | **Note:** Version bump only for package @next-auth/firebase-adapter 13 | 14 | ## [0.1.1](https://github.com/nextauthjs/adapters/compare/@next-auth/firebase-adapter@0.1.0...@next-auth/firebase-adapter@0.1.1) (2021-06-30) 15 | 16 | **Note:** Version bump only for package @next-auth/firebase-adapter 17 | -------------------------------------------------------------------------------- /packages/firebase/README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 | 4 | 5 |

Firebase Adapter - NextAuth.js

6 |

7 | Open Source. Full Stack. Own Your Data. 8 |

9 |

10 | Build Test 11 | Bundle Size 12 | @next-auth/firebase-adapter Version 13 |

14 |

15 | 16 | ## Overview 17 | 18 | This is the Firebase Adapter for [`next-auth`](https://next-auth.js.org). This package can only be used in conjunction with the primary `next-auth` package. It is not a standalone package. 19 | 20 | You can find more Firebase information in the docs at [next-auth.js.org/adapters/firebase](https://next-auth.js.org/adapters/firebase). 21 | 22 | ## Getting Started 23 | 24 | 1. Install `next-auth` and `@next-auth/firebase-adapter`. 25 | 26 | ```js 27 | npm install next-auth @next-auth/firebase-adapter 28 | ``` 29 | 30 | 2. Add this adapter to your `pages/api/[...nextauth].js` next-auth configuration object. 31 | 32 | ```js 33 | import NextAuth from "next-auth" 34 | import Providers from "next-auth/providers" 35 | import { FirebaseAdapter } from "@next-auth/firebase-adapter" 36 | 37 | import firebase from "firebase/app" 38 | import "firebase/firestore" 39 | 40 | const firestore = ( 41 | firebase.apps[0] ?? firebase.initializeApp(/* your config */) 42 | ).firestore() 43 | 44 | // For more information on each option (and a full list of options) go to 45 | // https://next-auth.js.org/configuration/options 46 | export default NextAuth({ 47 | // https://next-auth.js.org/configuration/providers 48 | providers: [ 49 | Providers.Google({ 50 | clientId: process.env.GOOGLE_ID, 51 | clientSecret: process.env.GOOGLE_SECRET, 52 | }), 53 | ], 54 | adapter: FirebaseAdapter(firestore), 55 | ... 56 | }) 57 | ``` 58 | 59 | ## Options 60 | 61 | When initializing the firestore adapter, you must pass in the firebase config object with the details from your project. More details on how to obtain that config object can be found [here](https://support.google.com/firebase/answer/7015592). 62 | 63 | An example firebase config looks like this: 64 | 65 | ```js 66 | const firebaseConfig = { 67 | apiKey: "AIzaSyDOCAbC123dEf456GhI789jKl01-MnO", 68 | authDomain: "myapp-project-123.firebaseapp.com", 69 | databaseURL: "https://myapp-project-123.firebaseio.com", 70 | projectId: "myapp-project-123", 71 | storageBucket: "myapp-project-123.appspot.com", 72 | messagingSenderId: "65211879809", 73 | appId: "1:65211879909:web:3ae38ef1cdcb2e01fe5f0c", 74 | measurementId: "G-8GSGZQ44ST", 75 | } 76 | ``` 77 | 78 | See [firebase.google.com/docs/web/setup](https://firebase.google.com/docs/web/setup) for more details. 79 | 80 | > **From Firebase - Caution**: We do not recommend manually modifying an app's Firebase config file or object. If you initialize an app with invalid or missing values for any of these required "Firebase options", then your end users may experience serious issues. 81 | > 82 | > For open source projects, we generally do not recommend including the app's Firebase config file or object in source control because, in most cases, your users should create their own Firebase projects and point their apps to their own Firebase resources (via their own Firebase config file or object). 83 | 84 | ## Contributing 85 | 86 | We're open to all community contributions! If you'd like to contribute in any way, please read our [Contributing Guide](https://github.com/nextauthjs/adapters/blob/main/CONTRIBUTING.md). 87 | 88 | ## License 89 | 90 | ISC 91 | -------------------------------------------------------------------------------- /packages/firebase/firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "emulators": { 3 | "firestore": { 4 | "port": 8080 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/firebase/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require("../../jest.config") 2 | -------------------------------------------------------------------------------- /packages/firebase/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /packages/firebase/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@next-auth/firebase-adapter", 3 | "version": "0.1.3", 4 | "description": "Firebase adapter for next-auth.", 5 | "homepage": "https://next-auth.js.org", 6 | "repository": "https://github.com/nextauthjs/adapters", 7 | "bugs": { 8 | "url": "https://github.com/nextauthjs/adapters/issues" 9 | }, 10 | "author": "Ron Houben ", 11 | "contributors": [ 12 | "Nico Domino ", 13 | "Alex Meuer " 14 | ], 15 | "main": "dist/index.js", 16 | "files": [ 17 | "dist", 18 | "index.d.ts" 19 | ], 20 | "license": "ISC", 21 | "keywords": [ 22 | "next-auth", 23 | "next.js", 24 | "firebase" 25 | ], 26 | "private": false, 27 | "publishConfig": { 28 | "access": "public" 29 | }, 30 | "scripts": { 31 | "build": "tsc", 32 | "test": "FIRESTORE_EMULATOR_HOST=localhost:8080 firebase emulators:exec --only firestore --project next-auth-test jest" 33 | }, 34 | "peerDependencies": { 35 | "firebase": "^8.6.2", 36 | "next-auth": "latest" 37 | }, 38 | "devDependencies": { 39 | "firebase": "^8.6.2", 40 | "firebase-tools": "^9.11.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/firebase/src/utils.ts: -------------------------------------------------------------------------------- 1 | import type firebase from "firebase" 2 | 3 | /** 4 | * Takes in a snapshot and returns all of its `data()`, 5 | * as well as `id` and `createdAt` and `updatedAt` `Date` 6 | */ 7 | export function docSnapshotToObject( 8 | snapshot: firebase.firestore.DocumentSnapshot 9 | ): T | null { 10 | if (!snapshot.exists) { 11 | return null 12 | } 13 | const data: any = snapshot.data() 14 | if (data.expires) { 15 | data.expires = data.expires.toDate() 16 | } 17 | return { id: snapshot.id, ...data } 18 | } 19 | 20 | export function querySnapshotToObject( 21 | snapshot: firebase.firestore.QuerySnapshot 22 | ): T | null { 23 | if (snapshot.empty) { 24 | return null 25 | } 26 | const doc = snapshot.docs[0] 27 | 28 | const data: any = doc.data() 29 | if (data.expires) { 30 | data.expires = data.expires.toDate() 31 | } 32 | return { id: doc.id, ...data } 33 | } 34 | 35 | /** Firebase does not like `undefined` values */ 36 | export function stripUndefined(obj: any) { 37 | return Object.fromEntries( 38 | Object.entries(obj).filter(([, value]) => typeof value !== "undefined") 39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /packages/firebase/tests/index.test.ts: -------------------------------------------------------------------------------- 1 | import { runBasicTests } from "../../../basic-tests" 2 | import { FirebaseAdapter } from "../src" 3 | import { docSnapshotToObject, querySnapshotToObject } from "../src/utils" 4 | 5 | import firebase from "firebase/app" 6 | import "firebase/firestore" 7 | 8 | const firestore = ( 9 | firebase.apps[0] ?? firebase.initializeApp({ projectId: "next-auth-test" }) 10 | ).firestore() 11 | firestore.useEmulator("localhost", 8080) 12 | 13 | runBasicTests({ 14 | adapter: FirebaseAdapter(firestore), 15 | db: { 16 | async disconnect() { 17 | await firestore.terminate() 18 | }, 19 | async session(sessionToken) { 20 | const snapshot = await firestore 21 | .collection("sessions") 22 | .where("sessionToken", "==", sessionToken) 23 | .limit(1) 24 | .get() 25 | return querySnapshotToObject(snapshot) 26 | }, 27 | async expireSession(sessionToken, expires) { 28 | const snapshot = await firestore 29 | .collection("sessions") 30 | .where("sessionToken", "==", sessionToken) 31 | .limit(1) 32 | .get() 33 | 34 | if (snapshot.empty) { 35 | console.error(sessionToken, expires) 36 | throw new Error("Could not expire session") 37 | } 38 | 39 | return await firestore 40 | .collection("sessions") 41 | .doc(snapshot.docs[0].id) 42 | .update({ expires }) 43 | }, 44 | async user(id) { 45 | const snapshot = await firestore.collection("users").doc(id).get() 46 | return docSnapshotToObject(snapshot) 47 | }, 48 | async account(providerId, providerAccountId) { 49 | const snapshot = await firestore 50 | .collection("accounts") 51 | .where("providerId", "==", providerId) 52 | .where("providerAccountId", "==", providerAccountId) 53 | .limit(1) 54 | .get() 55 | return querySnapshotToObject(snapshot) 56 | }, 57 | async verificationRequest(identifier, token) { 58 | const snapshot = await firestore 59 | .collection("verificationRequests") 60 | .where("identifier", "==", identifier) 61 | .where("token", "==", token) 62 | .limit(1) 63 | .get() 64 | return querySnapshotToObject(snapshot) 65 | }, 66 | }, 67 | }) 68 | -------------------------------------------------------------------------------- /packages/firebase/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "outDir": "dist" 6 | }, 7 | "exclude": ["tests", "dist"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/mikro-orm/.gitignore: -------------------------------------------------------------------------------- 1 | db.sqlite* 2 | -------------------------------------------------------------------------------- /packages/mikro-orm/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [1.0.1](https://github.com/nextauthjs/adapters/compare/@next-auth/mikro-orm-adapter@1.0.0...@next-auth/mikro-orm-adapter@1.0.1) (2021-12-06) 7 | 8 | **Note:** Version bump only for package @next-auth/mikro-orm-adapter 9 | -------------------------------------------------------------------------------- /packages/mikro-orm/README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 |      4 |

Mikro ORM Adapter - NextAuth.js

5 |

6 | Open Source. Full Stack. Own Your Data. 7 |

8 |

9 | CI Test 10 | Bundle Size 11 | @next-auth/mikro-orm-adapter Version 12 |

13 |

14 | 15 | ## Overview 16 | 17 | This is the MikroORM Adapter for [`next-auth`](https://next-auth.js.org). This package can only be used in conjunction with the primary `next-auth` package. It is not a standalone package. 18 | 19 | ## Getting Started 20 | 21 | 1. Install `next-auth` and `@next-auth/mikro-orm-adapter` 22 | 23 | ```js 24 | npm install next-auth @next-auth/mikro-orm-adapter@next 25 | ``` 26 | 27 | 2. Add this adapter to your `pages/api/[...nextauth].ts` next-auth configuration object. 28 | 29 | ```typescript title="pages/api/auth/[...nextauth].ts" 30 | import NextAuth from "next-auth" 31 | import { MikroOrmAdapter } from "@next-auth/mikro-orm-adapter" 32 | 33 | // For more information on each option (and a full list of options) go to 34 | // https://next-auth.js.org/configuration/options 35 | export default NextAuth({ 36 | // https://next-auth.js.org/configuration/providers 37 | providers: [], 38 | // optionally pass extended models as { entities: { } } 39 | adapter: MikroOrmAdapter({ 40 | dbName: "./db.sqlite", 41 | type: "sqlite", 42 | debug: process.env.DEBUG === "true" || process.env.DEBUG?.includes("db"), 43 | ... 44 | }), 45 | ... 46 | }); 47 | ``` 48 | 49 | ## Contributing 50 | 51 | We're open to all community contributions! If you'd like to contribute in any way, please read our [Contributing Guide](https://github.com/nextauthjs/adapters/blob/main/CONTRIBUTING.md). 52 | 53 | ## License 54 | 55 | ISC 56 | -------------------------------------------------------------------------------- /packages/mikro-orm/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require("../../jest.config") 2 | -------------------------------------------------------------------------------- /packages/mikro-orm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@next-auth/mikro-orm-adapter", 3 | "version": "1.0.1", 4 | "description": "MikroORM adapter for next-auth.", 5 | "homepage": "https://next-auth.js.org", 6 | "repository": "https://github.com/nextauthjs/adapters", 7 | "bugs": { 8 | "url": "https://github.com/nextauthjs/next-auth/issues" 9 | }, 10 | "author": "Jonas Strassel ", 11 | "contributors": [ 12 | "Balázs Orbán " 13 | ], 14 | "main": "dist/index.js", 15 | "license": "ISC", 16 | "keywords": [ 17 | "next-auth", 18 | "next.js", 19 | "oauth", 20 | "mikro-orm" 21 | ], 22 | "private": false, 23 | "publishConfig": { 24 | "access": "public" 25 | }, 26 | "scripts": { 27 | "test": "jest --runInBand", 28 | "build": "tsc" 29 | }, 30 | "files": [ 31 | "README.md", 32 | "dist" 33 | ], 34 | "peerDependencies": { 35 | "@mikro-orm/core": ">5 || 5.0.0-dev.527 - 5.0.0-dev.x", 36 | "next-auth": "^4.0.1" 37 | }, 38 | "devDependencies": { 39 | "@mikro-orm/core": "^5.0.0-dev.527", 40 | "@mikro-orm/sqlite": "^5.0.0-dev.527" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/mikro-orm/src/entities.ts: -------------------------------------------------------------------------------- 1 | import { v4 as randomUUID } from "uuid" 2 | 3 | import { 4 | Property, 5 | Unique, 6 | PrimaryKey, 7 | Entity, 8 | Enum, 9 | OneToMany, 10 | Collection, 11 | ManyToOne, 12 | } from "@mikro-orm/core" 13 | import type { DefaultAccount } from "next-auth" 14 | import type { 15 | AdapterSession, 16 | AdapterUser, 17 | VerificationToken as AdapterVerificationToken, 18 | } from "next-auth/adapters" 19 | import type { ProviderType } from "next-auth/providers" 20 | 21 | type RemoveIndex = { 22 | // eslint-disable-next-line @typescript-eslint/ban-types 23 | [K in keyof T as {} extends Record ? never : K]: T[K] 24 | } 25 | 26 | @Entity() 27 | export class User implements RemoveIndex { 28 | @PrimaryKey() 29 | id: string = randomUUID() 30 | 31 | @Property({ nullable: true }) 32 | name?: string 33 | 34 | @Property({ nullable: true }) 35 | @Unique() 36 | email?: string 37 | 38 | @Property({ type: "Date", nullable: true }) 39 | emailVerified: Date | null = null 40 | 41 | @Property({ nullable: true }) 42 | image?: string 43 | 44 | @OneToMany({ 45 | entity: () => Session, 46 | mappedBy: (session) => session.user, 47 | hidden: true, 48 | orphanRemoval: true, 49 | }) 50 | sessions = new Collection(this) 51 | 52 | @OneToMany({ 53 | entity: () => Account, 54 | mappedBy: (account) => account.user, 55 | hidden: true, 56 | orphanRemoval: true, 57 | }) 58 | accounts = new Collection(this) 59 | } 60 | 61 | @Entity() 62 | export class Session implements AdapterSession { 63 | @PrimaryKey() 64 | id: string = randomUUID() 65 | 66 | @ManyToOne({ 67 | entity: () => User, 68 | hidden: true, 69 | onDelete: "cascade", 70 | }) 71 | user!: User 72 | 73 | @Property({ persist: false }) 74 | userId!: string 75 | 76 | @Property() 77 | expires!: Date 78 | 79 | @Property() 80 | @Unique() 81 | sessionToken!: string 82 | } 83 | 84 | @Entity() 85 | @Unique({ properties: ["provider", "providerAccountId"] }) 86 | export class Account implements RemoveIndex { 87 | @PrimaryKey() 88 | id: string = randomUUID() 89 | 90 | @ManyToOne({ 91 | entity: () => User, 92 | hidden: true, 93 | onDelete: "cascade", 94 | }) 95 | user!: User 96 | 97 | @Property({ persist: false }) 98 | userId!: string 99 | 100 | @Enum() 101 | type!: ProviderType 102 | 103 | @Property() 104 | provider!: string 105 | 106 | @Property() 107 | providerAccountId!: string 108 | 109 | @Property({ nullable: true }) 110 | refresh_token?: string 111 | 112 | @Property({ nullable: true }) 113 | access_token?: string 114 | 115 | @Property({ nullable: true }) 116 | expires_at?: number 117 | 118 | @Property({ nullable: true }) 119 | token_type?: string 120 | 121 | @Property({ nullable: true }) 122 | scope?: string 123 | 124 | @Property({ nullable: true }) 125 | id_token?: string 126 | 127 | @Property({ nullable: true }) 128 | session_state?: string 129 | } 130 | 131 | @Entity() 132 | @Unique({ properties: ["token", "identifier"] }) 133 | export class VerificationToken implements AdapterVerificationToken { 134 | @PrimaryKey() 135 | @Property() 136 | token!: string 137 | 138 | @Property() 139 | expires!: Date 140 | 141 | @Property() 142 | identifier!: string 143 | } 144 | -------------------------------------------------------------------------------- /packages/mikro-orm/tests/index.test.ts: -------------------------------------------------------------------------------- 1 | import { MikroORM, wrap } from "@mikro-orm/core" 2 | import { runBasicTests } from "../../../basic-tests" 3 | import { MikroOrmAdapter, defaultEntities } from "../src" 4 | import { User, VeryImportantEntity } from "./testEntities" 5 | 6 | let _init: MikroORM 7 | 8 | const entities = [ 9 | User, 10 | defaultEntities.Account, 11 | defaultEntities.Session, 12 | defaultEntities.VerificationToken, 13 | VeryImportantEntity, 14 | ] 15 | 16 | async function getORM() { 17 | if (_init) return _init 18 | 19 | _init = await MikroORM.init({ 20 | dbName: "./db.sqlite", 21 | type: "sqlite", 22 | entities, 23 | debug: process.env.DEBUG === "true" || process.env.DEBUG?.includes("db"), 24 | }) 25 | return _init 26 | } 27 | 28 | runBasicTests({ 29 | adapter: MikroOrmAdapter( 30 | { 31 | dbName: "./db.sqlite", 32 | type: "sqlite", 33 | entities: [ 34 | defaultEntities.User, 35 | defaultEntities.Account, 36 | defaultEntities.Session, 37 | defaultEntities.VerificationToken, 38 | VeryImportantEntity, 39 | ], 40 | debug: process.env.DEBUG === "true" || process.env.DEBUG?.includes("db"), 41 | }, 42 | { entities: { User } } 43 | ), 44 | db: { 45 | async connect() { 46 | const orm = await getORM() 47 | await orm.getSchemaGenerator().dropSchema() 48 | await orm.getSchemaGenerator().createSchema() 49 | }, 50 | async disconnect() { 51 | const orm = await getORM() 52 | // its fine to tear down the connection if it has been already closed 53 | await orm 54 | .getSchemaGenerator() 55 | .dropSchema() 56 | .catch(() => null) 57 | await orm.close().catch(() => null) 58 | }, 59 | async verificationToken(identifier_token) { 60 | const orm = await getORM() 61 | const token = await orm.em 62 | .fork() 63 | .findOne(defaultEntities.VerificationToken, identifier_token) 64 | if (!token) return null 65 | return wrap(token).toObject() 66 | }, 67 | async user(id) { 68 | const orm = await getORM() 69 | const user = await orm.em.fork().findOne(defaultEntities.User, { id }) 70 | if (!user) return null 71 | return wrap(user).toObject() 72 | }, 73 | async account(provider_providerAccountId) { 74 | const orm = await getORM() 75 | const account = await orm.em 76 | .fork() 77 | .findOne(defaultEntities.Account, { ...provider_providerAccountId }) 78 | if (!account) return null 79 | return wrap(account).toObject() 80 | }, 81 | async session(sessionToken) { 82 | const orm = await getORM() 83 | const session = await orm.em 84 | .fork() 85 | .findOne(defaultEntities.Session, { sessionToken }) 86 | if (!session) return null 87 | return wrap(session).toObject() 88 | }, 89 | }, 90 | }) 91 | -------------------------------------------------------------------------------- /packages/mikro-orm/tests/testEntities.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Cascade, 3 | Collection, 4 | Entity, 5 | OneToMany, 6 | PrimaryKey, 7 | Property, 8 | Unique, 9 | } from "@mikro-orm/core" 10 | import { randomUUID } from "../../../basic-tests" 11 | import type { defaultEntities } from "../src" 12 | import { Account, Session } from "../src/entities" 13 | 14 | @Entity() 15 | export class User implements defaultEntities.User { 16 | @PrimaryKey() 17 | id: string = randomUUID() 18 | 19 | @Property({ nullable: true }) 20 | name?: string 21 | 22 | @Property({ nullable: true }) 23 | @Unique() 24 | email?: string 25 | 26 | @Property({ type: "Date", nullable: true }) 27 | emailVerified: Date | null = null 28 | 29 | @Property({ nullable: true }) 30 | image?: string 31 | 32 | @OneToMany({ 33 | entity: () => Session, 34 | mappedBy: (session) => session.user, 35 | hidden: true, 36 | orphanRemoval: true, 37 | cascade: [Cascade.ALL], 38 | }) 39 | sessions = new Collection(this) 40 | 41 | @OneToMany({ 42 | entity: () => Account, 43 | mappedBy: (account) => account.user, 44 | hidden: true, 45 | orphanRemoval: true, 46 | cascade: [Cascade.ALL], 47 | }) 48 | accounts = new Collection(this) 49 | 50 | @Property({ hidden: true }) 51 | role = "ADMIN" 52 | } 53 | 54 | @Entity() 55 | export class VeryImportantEntity { 56 | @PrimaryKey() 57 | id: string = randomUUID() 58 | 59 | @Property() 60 | important = true 61 | } 62 | -------------------------------------------------------------------------------- /packages/mikro-orm/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "experimentalDecorators": true, 5 | "emitDecoratorMetadata": true, 6 | "esModuleInterop": true, 7 | "rootDir": "src", 8 | "outDir": "dist", 9 | "stripInternal": true 10 | }, 11 | "exclude": ["tests", "dist"] 12 | } 13 | -------------------------------------------------------------------------------- /packages/mongodb/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [1.0.1](https://github.com/nextauthjs/adapters/compare/@next-auth/mongodb-adapter@1.0.0...@next-auth/mongodb-adapter@1.0.1) (2021-12-06) 7 | 8 | **Note:** Version bump only for package @next-auth/mongodb-adapter 9 | -------------------------------------------------------------------------------- /packages/mongodb/README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 |      4 |

MongoDB Adapter - NextAuth.js

5 |

6 | Open Source. Full Stack. Own Your Data. 7 |

8 |

9 | CI Test 10 | Bundle Size 11 | @next-auth/mongodb-adapter Version 12 |

13 |

14 | 15 | ## Overview 16 | 17 | This is the MongoDB Adapter for [`next-auth`](https://next-auth.js.org). This package can only be used in conjunction with the primary `next-auth` package. It is not a standalone package. 18 | 19 | ## Getting Started 20 | 21 | 1. Install `mongodb`, `next-auth` and `@next-auth/mongodb-adapter` 22 | 23 | ```js 24 | npm install mongodb next-auth @next-auth/mongodb-adapter@next 25 | ``` 26 | 27 | 2. Add `lib/mongodb.js` 28 | 29 | ```js 30 | // This approach is taken from https://github.com/vercel/next.js/tree/canary/examples/with-mongodb 31 | import { MongoClient } from "mongodb" 32 | 33 | const uri = process.env.MONGODB_URI 34 | const options = { 35 | useUnifiedTopology: true, 36 | useNewUrlParser: true, 37 | } 38 | 39 | let client 40 | let clientPromise 41 | 42 | if (!process.env.MONGODB_URI) { 43 | throw new Error("Please add your Mongo URI to .env.local") 44 | } 45 | 46 | if (process.env.NODE_ENV === "development") { 47 | // In development mode, use a global variable so that the value 48 | // is preserved across module reloads caused by HMR (Hot Module Replacement). 49 | if (!global._mongoClientPromise) { 50 | client = new MongoClient(uri, options) 51 | global._mongoClientPromise = client.connect() 52 | } 53 | clientPromise = global._mongoClientPromise 54 | } else { 55 | // In production mode, it's best to not use a global variable. 56 | client = new MongoClient(uri, options) 57 | clientPromise = client.connect() 58 | } 59 | 60 | // Export a module-scoped MongoClient promise. By doing this in a 61 | // separate module, the client can be shared across functions. 62 | export default clientPromise 63 | ``` 64 | 65 | 3. Add this adapter to your `pages/api/[...nextauth].js` next-auth configuration object. 66 | 67 | ```js 68 | import NextAuth from "next-auth" 69 | import { MongoDBAdapter } from "@next-auth/mongodb-adapter" 70 | import clientPromise from "lib/mongodb" 71 | 72 | // For more information on each option (and a full list of options) go to 73 | // https://next-auth.js.org/configuration/options 74 | export default NextAuth({ 75 | adapter: MongoDBAdapter(clientPromise), 76 | ... 77 | }) 78 | ``` 79 | 80 | ## Contributing 81 | 82 | We're open to all community contributions! If you'd like to contribute in any way, please read our [Contributing Guide](https://github.com/nextauthjs/adapters/blob/main/CONTRIBUTING.md). 83 | 84 | ## License 85 | 86 | ISC 87 | -------------------------------------------------------------------------------- /packages/mongodb/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require("../../jest.config") 2 | -------------------------------------------------------------------------------- /packages/mongodb/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@next-auth/mongodb-adapter", 3 | "version": "1.0.1", 4 | "description": "mongoDB adapter for next-auth.", 5 | "homepage": "https://next-auth.js.org", 6 | "repository": "https://github.com/nextauthjs/adapters", 7 | "bugs": { 8 | "url": "https://github.com/nextauthjs/next-auth/issues" 9 | }, 10 | "author": "Balázs Orbán ", 11 | "main": "dist/index.js", 12 | "license": "ISC", 13 | "keywords": [ 14 | "next-auth", 15 | "next.js", 16 | "oauth", 17 | "mongodb", 18 | "adapter" 19 | ], 20 | "private": false, 21 | "publishConfig": { 22 | "access": "public" 23 | }, 24 | "scripts": { 25 | "test": "./tests/test.sh", 26 | "build": "tsc" 27 | }, 28 | "files": [ 29 | "README.md", 30 | "dist" 31 | ], 32 | "peerDependencies": { 33 | "mongodb": "^4.1.1", 34 | "next-auth": "^4.0.1" 35 | }, 36 | "devDependencies": { 37 | "mongodb": "^4.1.1" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/mongodb/tests/custom.test.ts: -------------------------------------------------------------------------------- 1 | import { runBasicTests } from "../../../basic-tests" 2 | import { defaultCollections, format, MongoDBAdapter, _id } from "../src" 3 | import { MongoClient } from "mongodb" 4 | const name = "custom-test" 5 | const client = new MongoClient(`mongodb://localhost:27017/${name}`) 6 | const clientPromise = client.connect() 7 | 8 | const collections = { ...defaultCollections, Users: "some_userz" } 9 | 10 | runBasicTests({ 11 | adapter: MongoDBAdapter(clientPromise, { 12 | collections, 13 | }), 14 | db: { 15 | async disconnect() { 16 | await client.db().dropDatabase() 17 | await client.close() 18 | }, 19 | async user(id) { 20 | const user = await client 21 | .db() 22 | .collection(collections.Users) 23 | .findOne({ _id: _id(id) }) 24 | 25 | if (!user) return null 26 | return format.from(user) 27 | }, 28 | async account(provider_providerAccountId) { 29 | const account = await client 30 | .db() 31 | .collection(collections.Accounts) 32 | .findOne(provider_providerAccountId) 33 | if (!account) return null 34 | return format.from(account) 35 | }, 36 | async session(sessionToken) { 37 | const session = await client 38 | .db() 39 | .collection(collections.Sessions) 40 | .findOne({ sessionToken }) 41 | if (!session) return null 42 | return format.from(session) 43 | }, 44 | async verificationToken(identifier_token) { 45 | const token = await client 46 | .db() 47 | .collection(collections.VerificationTokens) 48 | .findOne(identifier_token) 49 | if (!token) return null 50 | const { _id, ...rest } = token 51 | return rest 52 | }, 53 | }, 54 | }) 55 | -------------------------------------------------------------------------------- /packages/mongodb/tests/index.test.ts: -------------------------------------------------------------------------------- 1 | import { runBasicTests } from "../../../basic-tests" 2 | import { defaultCollections, format, MongoDBAdapter, _id } from "../src" 3 | import { MongoClient } from "mongodb" 4 | 5 | const name = "test" 6 | const client = new MongoClient(`mongodb://localhost:27017/${name}`) 7 | const clientPromise = client.connect() 8 | 9 | runBasicTests({ 10 | adapter: MongoDBAdapter(clientPromise), 11 | db: { 12 | async disconnect() { 13 | await client.db().dropDatabase() 14 | await client.close() 15 | }, 16 | async user(id) { 17 | const user = await client 18 | .db() 19 | .collection(defaultCollections.Users) 20 | .findOne({ _id: _id(id) }) 21 | 22 | if (!user) return null 23 | return format.from(user) 24 | }, 25 | async account(provider_providerAccountId) { 26 | const account = await client 27 | .db() 28 | .collection(defaultCollections.Accounts) 29 | .findOne(provider_providerAccountId) 30 | if (!account) return null 31 | return format.from(account) 32 | }, 33 | async session(sessionToken) { 34 | const session = await client 35 | .db() 36 | .collection(defaultCollections.Sessions) 37 | .findOne({ sessionToken }) 38 | if (!session) return null 39 | return format.from(session) 40 | }, 41 | async verificationToken(identifier_token) { 42 | const token = await client 43 | .db() 44 | .collection(defaultCollections.VerificationTokens) 45 | .findOne(identifier_token) 46 | if (!token) return null 47 | const { _id, ...rest } = token 48 | return rest 49 | }, 50 | }, 51 | }) 52 | -------------------------------------------------------------------------------- /packages/mongodb/tests/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CONTAINER_NAME=next-auth-mongodb-test 4 | 5 | JEST_WATCH=false 6 | 7 | # Is the watch flag passed to the script? 8 | while getopts w flag 9 | do 10 | case "${flag}" in 11 | w) JEST_WATCH=true;; 12 | *) continue;; 13 | esac 14 | done 15 | 16 | # Start db 17 | docker run -d --rm -p 27017:27017 --name ${CONTAINER_NAME} mongo 18 | 19 | echo "Waiting 3 sec for db to start..." 20 | sleep 3 21 | 22 | if $JEST_WATCH; then 23 | # Run jest in watch mode 24 | npx jest tests --watch 25 | # Only stop the container after jest has been quit 26 | docker stop "${CONTAINER_NAME}" 27 | else 28 | # Always stop container, but exit with 1 when tests are failing 29 | if npx jest;then 30 | docker stop ${CONTAINER_NAME} 31 | else 32 | docker stop ${CONTAINER_NAME} && exit 1 33 | fi 34 | fi -------------------------------------------------------------------------------- /packages/mongodb/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "outDir": "dist" 6 | }, 7 | "exclude": ["tests", "dist"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/neo4j/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [1.0.2](https://github.com/nextauthjs/adapters/compare/@next-auth/neo4j-adapter@1.0.1...@next-auth/neo4j-adapter@1.0.2) (2021-12-23) 7 | 8 | ### Bug Fixes 9 | 10 | - **neo4j:** update user bugfix ([#352](https://github.com/nextauthjs/adapters/issues/352)) ([8c4ddbd](https://github.com/nextauthjs/adapters/commit/8c4ddbd70d04520a073354870e59040103e3d58d)) 11 | 12 | ## [1.0.1](https://github.com/nextauthjs/adapters/compare/@next-auth/neo4j-adapter@1.0.0...@next-auth/neo4j-adapter@1.0.1) (2021-12-06) 13 | 14 | **Note:** Version bump only for package @next-auth/neo4j-adapter 15 | -------------------------------------------------------------------------------- /packages/neo4j/README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 |      4 |

Neo4j Adapter - NextAuth.js

5 |

6 | Open Source. Full Stack. Own Your Data. 7 |

8 |

9 | Canary CI Test 10 | Bundle Size 11 | @next-auth/neo4j-adapter Version 12 |

13 |

14 | 15 | ## Overview 16 | 17 | This is the Neo4j Adapter for [`next-auth`](https://next-auth.js.org). This package can only be used in conjunction with the primary `next-auth` package. It is not a standalone package. 18 | 19 | You can find the Neo4j schema in the docs at [next-auth.js.org/adapters/neo4j](next-auth.js.org/adapters/neo4j). 20 | 21 | ## Getting Started 22 | 23 | 1. Install `neo4j-driver`, `next-auth` and `@next-auth/neo4j-adapter` 24 | 25 | ```js 26 | npm install neo4j-driver next-auth @next-auth/neo4j-adapter@next 27 | ``` 28 | 29 | 2. Add this adapter to your `pages/api/[...nextauth].js` next-auth configuration object. 30 | 31 | ```js 32 | import NextAuth from "next-auth" 33 | import neo4j from "neo4j-driver" 34 | import { Neo4jAdapter } from "@next-auth/neo4j-adapter" 35 | 36 | // Setup your neo4j driver instance 37 | const driver = neo4j.driver( 38 | "bolt://localhost", 39 | neo4j.auth.basic("neo4j", "password") 40 | ) 41 | const neo4jSession = driver.session() 42 | 43 | export default NextAuth({ 44 | // https://next-auth.js.org/configuration/providers 45 | providers: [], 46 | adapter: Neo4jAdapter(neo4jSession), 47 | ... 48 | }) 49 | ``` 50 | 51 | ## Contributing 52 | 53 | We're open to all community contributions! If you'd like to contribute in any way, please first read our [Contributing Guide](https://github.com/nextauthjs/adapters/blob/canary/CONTRIBUTING.md). 54 | 55 | ## License 56 | 57 | ISC 58 | -------------------------------------------------------------------------------- /packages/neo4j/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require("../../jest.config") 2 | -------------------------------------------------------------------------------- /packages/neo4j/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/neo4j/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@next-auth/neo4j-adapter", 3 | "version": "1.0.2", 4 | "description": "neo4j adapter for next-auth.", 5 | "homepage": "https://next-auth.js.org", 6 | "repository": "https://github.com/nextauthjs/adapters", 7 | "bugs": { 8 | "url": "https://github.com/nextauthjs/next-auth/issues" 9 | }, 10 | "author": "Davey Brown", 11 | "contributors": [ 12 | "Balázs Orbán " 13 | ], 14 | "main": "dist/index.js", 15 | "license": "ISC", 16 | "keywords": [ 17 | "next-auth", 18 | "next.js", 19 | "oauth", 20 | "neo4j" 21 | ], 22 | "private": false, 23 | "publishConfig": { 24 | "access": "public" 25 | }, 26 | "scripts": { 27 | "test:watch": "./tests/test.sh -w", 28 | "test": "./tests/test.sh", 29 | "build": "tsc" 30 | }, 31 | "files": [ 32 | "README.md", 33 | "dist" 34 | ], 35 | "peerDependencies": { 36 | "neo4j-driver": "^4.0.0", 37 | "next-auth": "^4.0.1" 38 | }, 39 | "devDependencies": { 40 | "@types/uuid": "^8.3.3", 41 | "neo4j-driver": "^4.4.0" 42 | }, 43 | "dependencies": { 44 | "uuid": "^8.3.2" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/neo4j/src/utils.ts: -------------------------------------------------------------------------------- 1 | import type { Session } from "neo4j-driver" 2 | import { isInt, integer } from "neo4j-driver" 3 | 4 | // https://github.com/honeinc/is-iso-date/blob/master/index.js 5 | const isoDateRE = 6 | /(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))/ 7 | 8 | function isDate(value: any) { 9 | return value && isoDateRE.test(value) && !isNaN(Date.parse(value)) 10 | } 11 | 12 | export const format = { 13 | /** Takes a plain old JavaScript object and turns it into a Neo4j compatible object */ 14 | to(object: Record) { 15 | const newObject: Record = {} 16 | for (const key in object) { 17 | const value = object[key] 18 | if (value instanceof Date) newObject[key] = value.toISOString() 19 | else newObject[key] = value 20 | } 21 | return newObject 22 | }, 23 | /** Takes a Neo4j object and returns a plain old JavaScript object */ 24 | from>(object?: Record): T | null { 25 | const newObject: Record = {} 26 | if (!object) return null 27 | for (const key in object) { 28 | const value = object[key] 29 | if (isDate(value)) { 30 | newObject[key] = new Date(value) 31 | } else if (isInt(value)) { 32 | if (integer.inSafeRange(value)) newObject[key] = value.toNumber() 33 | else newObject[key] = value.toString() 34 | } else { 35 | newObject[key] = value 36 | } 37 | } 38 | 39 | return newObject as T 40 | }, 41 | } 42 | 43 | export function client(session: Session) { 44 | return { 45 | /** Reads values from the database */ 46 | async read(statement: string, values?: any): Promise { 47 | const result = await session.readTransaction((tx) => 48 | tx.run(statement, values) 49 | ) 50 | 51 | return format.from(result?.records[0]?.get(0)) ?? null 52 | }, 53 | /** 54 | * Reads/writes values from/to the database. 55 | * Properties are available under `$data` 56 | */ 57 | async write(statement: string, values: T): Promise { 58 | const result = await session.writeTransaction((tx) => 59 | tx.run(statement, { data: format.to(values) }) 60 | ) 61 | 62 | return format.from(result?.records[0]?.toObject()) 63 | }, 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /packages/neo4j/tests/index.test.ts: -------------------------------------------------------------------------------- 1 | import * as neo4j from "neo4j-driver" 2 | import { runBasicTests } from "../../../basic-tests" 3 | import statements from "./resources/statements" 4 | 5 | import { Neo4jAdapter, format } from "../src" 6 | 7 | const driver = neo4j.driver( 8 | "bolt://localhost", 9 | neo4j.auth.basic("neo4j", "password") 10 | ) 11 | 12 | const neo4jSession = driver.session() 13 | 14 | runBasicTests({ 15 | adapter: Neo4jAdapter(neo4jSession), 16 | db: { 17 | async connect() { 18 | for await (const statement of statements.split(";")) { 19 | if (!statement.length) return 20 | await neo4jSession.writeTransaction((tx) => tx.run(statement)) 21 | } 22 | }, 23 | async disconnect() { 24 | await neo4jSession.writeTransaction((tx) => 25 | tx.run( 26 | `MATCH (n) 27 | DETACH DELETE n 28 | RETURN count(n)` 29 | ) 30 | ) 31 | await neo4jSession.close() 32 | await driver.close() 33 | }, 34 | async user(id) { 35 | const result = await neo4jSession.readTransaction((tx) => 36 | tx.run(`MATCH (u:User) RETURN u`, { id }) 37 | ) 38 | return format.from(result?.records[0]?.get("u")?.properties) 39 | }, 40 | 41 | async session(sessionToken: any) { 42 | const result = await neo4jSession.readTransaction((tx) => 43 | tx.run( 44 | `MATCH (u:User)-[:HAS_SESSION]->(s:Session) 45 | RETURN s, u.id AS userId`, 46 | { sessionToken } 47 | ) 48 | ) 49 | const session = result?.records[0]?.get("s")?.properties 50 | const userId = result?.records[0]?.get("userId") 51 | 52 | if (!session || session.userId || !userId) return null 53 | 54 | return { ...format.from(session), userId } 55 | }, 56 | 57 | async account(provider_providerAccountId) { 58 | const result = await neo4jSession.readTransaction((tx) => 59 | tx.run( 60 | `MATCH (u:User)-[:HAS_ACCOUNT]->(a:Account) 61 | RETURN a, u.id AS userId`, 62 | provider_providerAccountId 63 | ) 64 | ) 65 | 66 | const account = result?.records[0]?.get("a")?.properties 67 | const userId = result?.records[0]?.get("userId") 68 | 69 | if (!account || account.userId || !userId) return null 70 | 71 | return { ...format.from(account), userId } 72 | }, 73 | 74 | async verificationToken(identifier_token) { 75 | const result = await neo4jSession.readTransaction((tx) => 76 | tx.run( 77 | `MATCH (v:VerificationToken) 78 | RETURN v`, 79 | identifier_token 80 | ) 81 | ) 82 | 83 | return format.from(result?.records[0]?.get("v")?.properties) 84 | }, 85 | }, 86 | }) 87 | -------------------------------------------------------------------------------- /packages/neo4j/tests/resources/statements.ts: -------------------------------------------------------------------------------- 1 | // Constraints and indexes - relevant to Neo4j Community Edition. 2 | export default ` 3 | CREATE CONSTRAINT user_id_constraint IF NOT EXISTS 4 | ON (u:User) ASSERT u.id IS UNIQUE; 5 | 6 | CREATE INDEX user_id_index IF NOT EXISTS 7 | FOR (u:User) ON (u.id); 8 | 9 | CREATE INDEX user_email_index IF NOT EXISTS 10 | FOR (u:User) ON (u.email); 11 | 12 | CREATE CONSTRAINT session_session_token_constraint IF NOT EXISTS 13 | ON (s:Session) ASSERT s.sessionToken IS UNIQUE; 14 | 15 | CREATE INDEX session_session_token_index IF NOT EXISTS 16 | FOR (s:Session) ON (s.sessionToken); 17 | 18 | CREATE INDEX account_provider_index IF NOT EXISTS 19 | FOR (a:Account) ON (a.provider); 20 | 21 | CREATE INDEX account_provider_account_id_index IF NOT EXISTS 22 | FOR (a:Account) ON (a.providerAccountId); 23 | 24 | CREATE INDEX verification_token_identifier_index IF NOT EXISTS 25 | FOR (v:VerificationToken) ON (v.identifier); 26 | 27 | CREATE INDEX verification_token_token_index IF NOT EXISTS 28 | FOR (v:VerificationToken) ON (v.token);` 29 | -------------------------------------------------------------------------------- /packages/neo4j/tests/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" 4 | NEO4J_USER=neo4j 5 | NEO4J_PASS=password 6 | CONTAINER_NAME=next-auth-neo4j-test-e 7 | JEST_WATCH=false 8 | 9 | # Is the watch flag passed to the script? 10 | while getopts w flag 11 | do 12 | case "${flag}" in 13 | w) JEST_WATCH=true;; 14 | *) continue;; 15 | esac 16 | done 17 | 18 | # Start db 19 | docker run -d --rm \ 20 | -e NEO4J_AUTH=${NEO4J_USER}/${NEO4J_PASS} \ 21 | -e TEST_NEO4J_USER=${NEO4J_USER} \ 22 | -e TEST_NEO4J_PASS=${NEO4J_PASS} \ 23 | --name "${CONTAINER_NAME}" \ 24 | -p7474:7474 -p7687:7687 \ 25 | neo4j:4.2.0 26 | 27 | # For debug or testing it may be useful to use neo4j enterprise edition. 28 | # Use the lines below in the docker run statement. 29 | # -e NEO4J_ACCEPT_LICENSE_AGREEMENT=yes \ 30 | # neo4j:4.2.0-enterprise 31 | 32 | echo "Waiting 5 sec for db to start..." && sleep 5 33 | 34 | if $JEST_WATCH; then 35 | # Run jest in watch mode 36 | npx jest tests --watch 37 | # Only stop the container after jest has been quit 38 | docker stop "${CONTAINER_NAME}" 39 | else 40 | # Always stop container, but exit with 1 when tests are failing 41 | if npx jest tests --detectOpenHandles --forceExit;then 42 | docker stop "${CONTAINER_NAME}" 43 | else 44 | docker stop "${CONTAINER_NAME}" && exit 1 45 | fi 46 | fi 47 | -------------------------------------------------------------------------------- /packages/neo4j/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "outDir": "dist" 6 | }, 7 | "exclude": ["tests", "dist"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/pouchdb/.gitignore: -------------------------------------------------------------------------------- 1 | .pouchdb -------------------------------------------------------------------------------- /packages/pouchdb/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [0.1.3](https://github.com/nextauthjs/adapters/compare/@next-auth/pouchdb-adapter@0.1.2...@next-auth/pouchdb-adapter@0.1.3) (2021-08-17) 7 | 8 | **Note:** Version bump only for package @next-auth/pouchdb-adapter 9 | 10 | ## [0.1.2](https://github.com/nextauthjs/adapters/compare/@next-auth/pouchdb-adapter@0.1.1...@next-auth/pouchdb-adapter@0.1.2) (2021-07-02) 11 | 12 | **Note:** Version bump only for package @next-auth/pouchdb-adapter 13 | 14 | ## [0.1.1](https://github.com/nextauthjs/adapters/compare/@next-auth/pouchdb-adapter@0.1.0...@next-auth/pouchdb-adapter@0.1.1) (2021-06-30) 15 | 16 | **Note:** Version bump only for package @next-auth/pouchdb-adapter 17 | -------------------------------------------------------------------------------- /packages/pouchdb/README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 |      4 |

PouchDB Adapter - NextAuth.js

5 |

6 | Open Source. Full Stack. Own Your Data. 7 |

8 |

9 | CI Test 10 | Bundle Size 11 | @next-auth/pouchdb-adapter Version 12 |

13 |

14 | 15 | ## Overview 16 | 17 | This is the PouchDB Adapter for [`next-auth`](https://next-auth.js.org). This package can only be used in conjunction with the primary `next-auth` package. It is not a standalone package. 18 | 19 | Depending on your architecture you can use PouchDB's http adapter to reach any database compliant with the CouchDB protocol (CouchDB, Cloudant, ...) or use any other PouchDB compatible adapter (leveldb, in-memory, ...) 20 | 21 | ## Getting Started 22 | 23 | 1. Install `next-auth` and `@next-auth/pouchdb-adapter`, as well as `pouchdb`. 24 | 25 | > **Prerequesite**: Your PouchDB instance MUST provide the `pouchdb-find` plugin since it is used internally by the adapter to build and manage indexes 26 | 27 | ```js 28 | npm install next-auth @next-auth/pouchdb-adapter pouchdb 29 | ``` 30 | 31 | 2. Add this adapter to your `pages/api/[...nextauth].js` next-auth configuration object. 32 | 33 | ```js 34 | import NextAuth from "next-auth" 35 | import Providers from "next-auth/providers" 36 | import { PouchDBAdapter } from "@next-auth/pouchdb-adapter" 37 | import PouchDB from "pouchdb" 38 | 39 | // Setup your PouchDB instance and database 40 | PouchDB.plugin(require("pouchdb-adapter-leveldb")) // Or any other PouchDB-compliant adapter 41 | .plugin(require("pouchdb-find")) // Don't forget the `pouchdb-find` plugin 42 | 43 | const pouchdb = new PouchDB("auth_db", { adapter: "leveldb" }) 44 | 45 | // For more information on each option (and a full list of options) go to 46 | // https://next-auth.js.org/configuration/options 47 | export default NextAuth({ 48 | // https://next-auth.js.org/configuration/providers 49 | providers: [ 50 | Providers.Google({ 51 | clientId: process.env.GOOGLE_ID, 52 | clientSecret: process.env.GOOGLE_SECRET, 53 | }), 54 | ], 55 | adapter: PouchDBAdapter(pouchdb), 56 | // ... 57 | }) 58 | ``` 59 | 60 | ## Advanced 61 | 62 | ### Memory-First Caching Strategy 63 | 64 | If you need to boost your authentication layer performance, you may use PouchDB's powerful sync features and various adapters, to build a memory-first caching strategy. 65 | 66 | Use an in-memory PouchDB as your main authentication database, and synchronize it with any other persisted PouchDB. You may do a one way, one-off replication at startup from the persisted PouchDB into the in-memory PouchDB, then two-way, continuous, retriable sync. 67 | 68 | This will probably not improve performance much in a serverless environment for various reasons such as concurrency, function startup time increases, etc. 69 | 70 | For more details, please see https://pouchdb.com/api.html#sync 71 | 72 | ## Contributing 73 | 74 | We're open to all community contributions! If you'd like to contribute in any way, please read our [Contributing Guide](https://github.com/nextauthjs/adapters/blob/main/CONTRIBUTING.md). 75 | 76 | ## License 77 | 78 | ISC 79 | -------------------------------------------------------------------------------- /packages/pouchdb/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require("../../jest.config") 2 | -------------------------------------------------------------------------------- /packages/pouchdb/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /packages/pouchdb/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@next-auth/pouchdb-adapter", 3 | "version": "0.1.3", 4 | "description": "PouchDB adapter for next-auth.", 5 | "homepage": "https://next-auth.js.org", 6 | "repository": "https://github.com/nextauthjs/adapters", 7 | "bugs": { 8 | "url": "https://github.com/nextauthjs/next-auth/issues" 9 | }, 10 | "author": "jpbourgeon (https://github.com/jpbourgeon)", 11 | "main": "dist/index.js", 12 | "license": "ISC", 13 | "keywords": [ 14 | "next-auth", 15 | "next.js", 16 | "oauth", 17 | "pouchdb" 18 | ], 19 | "private": false, 20 | "publishConfig": { 21 | "access": "public" 22 | }, 23 | "scripts": { 24 | "build": "tsc", 25 | "tdd": "jest --watch", 26 | "test": "jest" 27 | }, 28 | "files": [ 29 | "README.md", 30 | "dist" 31 | ], 32 | "peerDependencies": { 33 | "next-auth": "^3.23.3", 34 | "pouchdb": "^7.2.2", 35 | "pouchdb-find": "^7.2.2" 36 | }, 37 | "dependencies": { 38 | "crypto": "^1.0.1", 39 | "ulid": "^2.3.0" 40 | }, 41 | "devDependencies": { 42 | "@types/pouchdb": "^6.4.0", 43 | "pouchdb": "^7.2.2", 44 | "pouchdb-adapter-memory": "^7.2.2", 45 | "pouchdb-find": "^7.2.2" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/pouchdb/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "outDir": "dist", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "exclude": ["tests", "dist"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/prisma/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [1.0.1](https://github.com/nextauthjs/adapters/compare/@next-auth/prisma-adapter@1.0.0...@next-auth/prisma-adapter@1.0.1) (2021-12-06) 7 | 8 | **Note:** Version bump only for package @next-auth/prisma-adapter 9 | 10 | ## [0.5.4](https://github.com/nextauthjs/adapters/compare/@next-auth/prisma-adapter@0.5.3...@next-auth/prisma-adapter@0.5.4) (2021-08-17) 11 | 12 | **Note:** Version bump only for package @next-auth/prisma-adapter 13 | 14 | ## [0.5.3](https://github.com/nextauthjs/adapters/compare/@next-auth/prisma-adapter@0.5.2...@next-auth/prisma-adapter@0.5.3) (2021-07-11) 15 | 16 | ### Bug Fixes 17 | 18 | - **prisma:** remove spread profile object ([#166](https://github.com/nextauthjs/adapters/issues/166)) ([d33dfb0](https://github.com/nextauthjs/adapters/commit/d33dfb0ea20667004831e5ac41e718008f233025)) 19 | 20 | ## [0.5.2](https://github.com/nextauthjs/adapters/compare/@next-auth/prisma-adapter@0.5.1...@next-auth/prisma-adapter@0.5.2) (2021-07-02) 21 | 22 | **Note:** Version bump only for package @next-auth/prisma-adapter 23 | 24 | ## [0.5.1](https://github.com/nextauthjs/adapters/compare/@next-auth/prisma-adapter@0.5.0...@next-auth/prisma-adapter@0.5.1) (2021-06-30) 25 | 26 | **Note:** Version bump only for package @next-auth/prisma-adapter 27 | -------------------------------------------------------------------------------- /packages/prisma/README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 |      4 |

Prisma Adapter - NextAuth.js

5 |

6 | Open Source. Full Stack. Own Your Data. 7 |

8 |

9 | CI Test 10 | Bundle Size 11 | @next-auth/prisma-adapter Version 12 |

13 |

14 | 15 | ## Overview 16 | 17 | This is the Prisma Adapter for [`next-auth`](https://next-auth.js.org). This package can only be used in conjunction with the primary `next-auth` package. It is not a standalone package. 18 | 19 | You can find the Prisma schema in the docs at [next-auth.js.org/adapters/prisma](https://next-auth.js.org/adapters/prisma). 20 | 21 | ## Getting Started 22 | 23 | 1. Install `next-auth` and `@next-auth/prisma-adapter` as well as `prisma` and `@prisma/client`. 24 | 25 | ```js 26 | npm install next-auth @next-auth/prisma-adapter @prisma/client 27 | npm install --save-dev prisma 28 | ``` 29 | 30 | 2. Add this adapter to your `pages/api/[...nextauth].js` next-auth configuration object. 31 | 32 | ```js 33 | import NextAuth from "next-auth" 34 | import { PrismaAdapter } from "@next-auth/prisma-adapter" 35 | import * as Prisma from "@prisma/client" 36 | 37 | const prisma = new Prisma.PrismaClient() 38 | 39 | // For more information on each option (and a full list of options) go to 40 | // https://next-auth.js.org/configuration/options 41 | export default NextAuth({ 42 | // https://next-auth.js.org/configuration/providers 43 | providers: [], 44 | adapter: PrismaAdapter(prisma) 45 | ... 46 | }) 47 | ``` 48 | 49 | ## Contributing 50 | 51 | We're open to all community contributions! If you'd like to contribute in any way, please read our [Contributing Guide](https://github.com/nextauthjs/adapters/blob/main/CONTRIBUTING.md). 52 | 53 | ## License 54 | 55 | ISC 56 | -------------------------------------------------------------------------------- /packages/prisma/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require("../../jest.config") 2 | -------------------------------------------------------------------------------- /packages/prisma/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/prisma/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@next-auth/prisma-adapter", 3 | "version": "1.0.1", 4 | "description": "Prisma adapter for next-auth.", 5 | "homepage": "https://next-auth.js.org", 6 | "repository": "https://github.com/nextauthjs/adapters", 7 | "bugs": { 8 | "url": "https://github.com/nextauthjs/next-auth/issues" 9 | }, 10 | "author": "William Luke", 11 | "main": "dist/index.js", 12 | "license": "ISC", 13 | "keywords": [ 14 | "next-auth", 15 | "next.js", 16 | "oauth", 17 | "prisma" 18 | ], 19 | "private": false, 20 | "publishConfig": { 21 | "access": "public" 22 | }, 23 | "scripts": { 24 | "clean": "rm -rf ./prisma/migrations && rm ./prisma/dev.db*", 25 | "init:default": "prisma migrate dev --name init --skip-seed", 26 | "init:custom": "prisma migrate dev --name init-custom --schema ./prisma/custom.prisma", 27 | "test:default": "yarn init:default && jest", 28 | "test:custom": "yarn init:custom && CUSTOM_MODEL=1 jest", 29 | "test": "yarn test:default && yarn test:custom", 30 | "build": "prisma generate && tsc", 31 | "studio": "prisma studio" 32 | }, 33 | "files": [ 34 | "README.md", 35 | "dist" 36 | ], 37 | "peerDependencies": { 38 | "@prisma/client": ">=2.26.0 || >=3", 39 | "next-auth": "^4.0.1" 40 | }, 41 | "devDependencies": { 42 | "@prisma/client": "^3.0.2", 43 | "prisma": "^3.0.2" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/prisma/prisma/custom.prisma: -------------------------------------------------------------------------------- 1 | datasource db { 2 | provider = "sqlite" 3 | url = "file:./dev.db" 4 | } 5 | 6 | generator client { 7 | provider = "prisma-client-js" 8 | } 9 | 10 | model User { 11 | id String @id @default(cuid()) 12 | name String? 13 | email String? @unique 14 | emailVerified DateTime? 15 | image String? 16 | phone String? 17 | role String? 18 | accounts Account[] 19 | sessions Session[] 20 | } 21 | 22 | model Account { 23 | id String @id @default(cuid()) 24 | userId String 25 | type String 26 | provider String 27 | providerAccountId String 28 | refresh_token String? 29 | access_token String? 30 | expires_at Int? 31 | token_type String? 32 | scope String? 33 | id_token String? 34 | session_state String? 35 | 36 | user User @relation(fields: [userId], references: [id], onDelete: Cascade) 37 | 38 | @@unique([provider, providerAccountId]) 39 | } 40 | 41 | model Session { 42 | id String @id @default(cuid()) 43 | sessionToken String @unique 44 | userId String 45 | expires DateTime 46 | user User @relation(fields: [userId], references: [id], onDelete: Cascade) 47 | } 48 | 49 | model VerificationToken { 50 | identifier String 51 | token String @unique 52 | expires DateTime 53 | 54 | @@unique([identifier, token]) 55 | } 56 | -------------------------------------------------------------------------------- /packages/prisma/prisma/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (i.e. Git) 3 | provider = "sqlite" -------------------------------------------------------------------------------- /packages/prisma/prisma/schema.prisma: -------------------------------------------------------------------------------- 1 | datasource db { 2 | provider = "sqlite" 3 | url = "file:./dev.db" 4 | } 5 | 6 | generator client { 7 | provider = "prisma-client-js" 8 | } 9 | 10 | model User { 11 | id String @id @default(cuid()) 12 | name String? 13 | email String? @unique 14 | emailVerified DateTime? 15 | image String? 16 | accounts Account[] 17 | sessions Session[] 18 | } 19 | 20 | model Account { 21 | id String @id @default(cuid()) 22 | userId String 23 | type String 24 | provider String 25 | providerAccountId String 26 | refresh_token String? 27 | access_token String? 28 | expires_at Int? 29 | token_type String? 30 | scope String? 31 | id_token String? 32 | session_state String? 33 | 34 | user User @relation(fields: [userId], references: [id], onDelete: Cascade) 35 | 36 | @@unique([provider, providerAccountId]) 37 | } 38 | 39 | model Session { 40 | id String @id @default(cuid()) 41 | sessionToken String @unique 42 | userId String 43 | expires DateTime 44 | user User @relation(fields: [userId], references: [id], onDelete: Cascade) 45 | } 46 | 47 | model VerificationToken { 48 | identifier String 49 | token String @unique 50 | expires DateTime 51 | 52 | @@unique([identifier, token]) 53 | } 54 | -------------------------------------------------------------------------------- /packages/prisma/src/index.ts: -------------------------------------------------------------------------------- 1 | import type { PrismaClient, Prisma } from "@prisma/client" 2 | import type { Adapter } from "next-auth/adapters" 3 | 4 | export function PrismaAdapter(p: PrismaClient): Adapter { 5 | return { 6 | createUser: (data) => p.user.create({ data }), 7 | getUser: (id) => p.user.findUnique({ where: { id } }), 8 | getUserByEmail: (email) => p.user.findUnique({ where: { email } }), 9 | async getUserByAccount(provider_providerAccountId) { 10 | const account = await p.account.findUnique({ 11 | where: { provider_providerAccountId }, 12 | select: { user: true }, 13 | }) 14 | return account?.user ?? null 15 | }, 16 | updateUser: (data) => p.user.update({ where: { id: data.id }, data }), 17 | deleteUser: (id) => p.user.delete({ where: { id } }), 18 | linkAccount: (data) => p.account.create({ data }) as any, 19 | unlinkAccount: (provider_providerAccountId) => 20 | p.account.delete({ where: { provider_providerAccountId } }) as any, 21 | async getSessionAndUser(sessionToken) { 22 | const userAndSession = await p.session.findUnique({ 23 | where: { sessionToken }, 24 | include: { user: true }, 25 | }) 26 | if (!userAndSession) return null 27 | const { user, ...session } = userAndSession 28 | return { user, session } 29 | }, 30 | createSession: (data) => p.session.create({ data }), 31 | updateSession: (data) => 32 | p.session.update({ data, where: { sessionToken: data.sessionToken } }), 33 | deleteSession: (sessionToken) => 34 | p.session.delete({ where: { sessionToken } }), 35 | createVerificationToken: (data) => p.verificationToken.create({ data }), 36 | async useVerificationToken(identifier_token) { 37 | try { 38 | return await p.verificationToken.delete({ where: { identifier_token } }) 39 | } catch (error) { 40 | // If token already used/deleted, just return null 41 | // https://www.prisma.io/docs/reference/api-reference/error-reference#p2025 42 | if ((error as Prisma.PrismaClientKnownRequestError).code === "P2025") 43 | return null 44 | throw error 45 | } 46 | }, 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/prisma/tests/index.test.ts: -------------------------------------------------------------------------------- 1 | import { runBasicTests } from "../../../basic-tests" 2 | import { PrismaClient } from "@prisma/client" 3 | import { PrismaAdapter } from "../src" 4 | const prisma = new PrismaClient() 5 | 6 | runBasicTests({ 7 | adapter: PrismaAdapter(prisma), 8 | db: { 9 | connect: async () => { 10 | await Promise.all([ 11 | prisma.user.deleteMany({}), 12 | prisma.account.deleteMany({}), 13 | prisma.session.deleteMany({}), 14 | prisma.verificationToken.deleteMany({}), 15 | ]) 16 | }, 17 | disconnect: async () => { 18 | await Promise.all([ 19 | prisma.user.deleteMany({}), 20 | prisma.account.deleteMany({}), 21 | prisma.session.deleteMany({}), 22 | prisma.verificationToken.deleteMany({}), 23 | ]) 24 | await prisma.$disconnect() 25 | }, 26 | verificationToken: (identifier_token) => 27 | prisma.verificationToken.findUnique({ 28 | where: { identifier_token }, 29 | }), 30 | user: (id) => prisma.user.findUnique({ where: { id } }), 31 | account: (provider_providerAccountId) => 32 | prisma.account.findUnique({ 33 | where: { provider_providerAccountId }, 34 | }), 35 | session: (sessionToken) => 36 | prisma.session.findUnique({ where: { sessionToken } }), 37 | }, 38 | }) 39 | -------------------------------------------------------------------------------- /packages/prisma/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "outDir": "dist" 6 | }, 7 | "exclude": ["tests", "dist"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/sequelize/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [1.0.2](https://github.com/nextauthjs/adapters/compare/@next-auth/sequelize-adapter@1.0.1...@next-auth/sequelize-adapter@1.0.2) (2022-01-23) 7 | 8 | ### Bug Fixes 9 | 10 | - **sequelize:** prevent indexes duplication ([#375](https://github.com/nextauthjs/adapters/issues/375)) ([c6642b5](https://github.com/nextauthjs/adapters/commit/c6642b51955dec382b1bc50722163444d5bfeb2d)) 11 | 12 | ## [1.0.1](https://github.com/nextauthjs/adapters/compare/@next-auth/sequelize-adapter@1.0.0...@next-auth/sequelize-adapter@1.0.1) (2021-12-06) 13 | 14 | **Note:** Version bump only for package @next-auth/sequelize-adapter 15 | -------------------------------------------------------------------------------- /packages/sequelize/README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 |      4 |

Sequelize Adapter - NextAuth.js

5 |

6 | Open Source. Full Stack. Own Your Data. 7 |

8 |

9 | CI Test 10 | Bundle Size 11 | @next-auth/sequelize-adapter Version 12 |

13 |

14 | 15 | ## Overview 16 | 17 | This is the Sequelize Adapter for [`next-auth`](https://next-auth.js.org). This package can only be used in conjunction with the primary `next-auth` package. It is not a standalone package. 18 | 19 | You can find the Sequelize schema in the docs at [next-auth.js.org/adapters/sequelize](https://next-auth.js.org/adapters/sequelize). 20 | 21 | ## Getting Started 22 | 23 | 1. Install `next-auth` and `@next-auth/sequelize-adapter` as well as `sequelize` and your [database driver](https://sequelize.org/master/manual/getting-started.html) of choice. 24 | 25 | ```js 26 | npm install next-auth @next-auth/sequelize-adapter sequelize sqlite3 27 | npm install --save-dev sequelize 28 | ``` 29 | 30 | 2. Add this adapter to your `pages/api/[...nextauth].js` next-auth configuration object. 31 | 32 | ```js 33 | import NextAuth from "next-auth" 34 | import SequelizeAdapter from "@next-auth/sequelize-adapter" 35 | import Sequelize from 'sequelize' 36 | 37 | const sequelize = new Sequelize("sqlite::memory:") 38 | 39 | // For more information on each option (and a full list of options) go to 40 | // https://next-auth.js.org/configuration/options 41 | export default NextAuth({ 42 | ... 43 | adapter: SequelizeAdapter(sequelize) 44 | ... 45 | }) 46 | ``` 47 | 48 | ## Updating the database schema 49 | 50 | In development, the sequelize adapter will create the necessary tables, foreign keys and indexes in your database. In production, synchronization is disabled. Best practice is to create the [required tables](https://next-auth.js.org/adapters/models) in your database via [migrations](https://sequelize.org/master/manual/migrations.html). 51 | 52 | In development, if you do not want the adapter to automatically create tables, you are able to pass `{ synchronize: false }` as the second option to `SequelizeAdapter` to disable this behavior: 53 | 54 | ```js 55 | import NextAuth from "next-auth" 56 | import SequelizeAdapter from "@next-auth/sequelize-adapter" 57 | import Sequelize from 'sequelize' 58 | 59 | const sequelize = new Sequelize("sqlite::memory:") 60 | 61 | export default NextAuth({ 62 | ... 63 | adapter: SequelizeAdapter(sequelize, { synchronize: false }) 64 | ... 65 | }) 66 | ``` 67 | 68 | ## Using custom models 69 | 70 | Sequelize models are option to customization like so: 71 | 72 | ```js 73 | import NextAuth from "next-auth" 74 | import SequelizeAdapter, { models } from "@next-auth/sequelize-adapter" 75 | import Sequelize, { DataTypes } from 'sequelize' 76 | 77 | const sequelize = new Sequelize("sqlite::memory:") 78 | 79 | export default NextAuth({ 80 | ... 81 | adapter: SequelizeAdapter(sequelize, { 82 | models: { 83 | User: sequelize.define('user', { ...models.User, phoneNumber: DataTypes.STRING }) 84 | } 85 | }) 86 | ... 87 | }) 88 | ``` 89 | 90 | ## Contributing 91 | 92 | We're open to all community contributions! If you'd like to contribute in any way, please read our [Contributing Guide](https://github.com/nextauthjs/adapters/blob/main/CONTRIBUTING.md). 93 | 94 | ## License 95 | 96 | ISC 97 | -------------------------------------------------------------------------------- /packages/sequelize/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require("../../jest.config") 2 | -------------------------------------------------------------------------------- /packages/sequelize/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/sequelize/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@next-auth/sequelize-adapter", 3 | "version": "1.0.2", 4 | "description": "Sequelize adapter for next-auth.", 5 | "homepage": "https://next-auth.js.org", 6 | "repository": "https://github.com/nextauthjs/adapters", 7 | "bugs": { 8 | "url": "https://github.com/nextauthjs/adapters/issues" 9 | }, 10 | "author": "github.com/luke-j", 11 | "main": "dist/index.js", 12 | "license": "ISC", 13 | "keywords": [ 14 | "next-auth", 15 | "next.js", 16 | "oauth", 17 | "sequelize" 18 | ], 19 | "private": false, 20 | "publishConfig": { 21 | "access": "public" 22 | }, 23 | "scripts": { 24 | "test": "jest", 25 | "build": "tsc" 26 | }, 27 | "files": [ 28 | "README.md", 29 | "dist" 30 | ], 31 | "peerDependencies": { 32 | "next-auth": "^4.0.1", 33 | "sequelize": "^6.6.5" 34 | }, 35 | "devDependencies": { 36 | "sequelize": "^6.6.5" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/sequelize/src/models.ts: -------------------------------------------------------------------------------- 1 | import { DataTypes } from "sequelize" 2 | 3 | export const Account = { 4 | id: { 5 | type: DataTypes.UUID, 6 | defaultValue: DataTypes.UUIDV4, 7 | primaryKey: true, 8 | }, 9 | type: { type: DataTypes.STRING, allowNull: false }, 10 | provider: { type: DataTypes.STRING, allowNull: false }, 11 | providerAccountId: { type: DataTypes.STRING, allowNull: false }, 12 | refresh_token: { type: DataTypes.STRING }, 13 | access_token: { type: DataTypes.STRING }, 14 | expires_at: { type: DataTypes.INTEGER }, 15 | token_type: { type: DataTypes.STRING }, 16 | scope: { type: DataTypes.STRING }, 17 | id_token: { type: DataTypes.STRING }, 18 | session_state: { type: DataTypes.STRING }, 19 | userId: { type: DataTypes.UUID }, 20 | } 21 | 22 | export const User = { 23 | id: { 24 | type: DataTypes.UUID, 25 | defaultValue: DataTypes.UUIDV4, 26 | primaryKey: true, 27 | }, 28 | name: { type: DataTypes.STRING }, 29 | email: { type: DataTypes.STRING, unique: 'email' }, 30 | emailVerified: { type: DataTypes.DATE }, 31 | image: { type: DataTypes.STRING }, 32 | } 33 | 34 | export const Session = { 35 | id: { 36 | type: DataTypes.UUID, 37 | defaultValue: DataTypes.UUIDV4, 38 | primaryKey: true, 39 | }, 40 | expires: { type: DataTypes.DATE, allowNull: false }, 41 | sessionToken: { type: DataTypes.STRING, unique: 'sessionToken', allowNull: false }, 42 | userId: { type: DataTypes.UUID }, 43 | } 44 | 45 | export const VerificationToken = { 46 | token: { type: DataTypes.STRING, primaryKey: true }, 47 | identifier: { type: DataTypes.STRING, allowNull: false }, 48 | expires: { type: DataTypes.DATE, allowNull: false }, 49 | } 50 | -------------------------------------------------------------------------------- /packages/sequelize/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "outDir": "dist" 6 | }, 7 | "exclude": ["tests", "dist"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/typeorm-legacy/.dockerignore: -------------------------------------------------------------------------------- 1 | # Exclude directories we don't need from Docker context to improve build time 2 | node_modules 3 | www 4 | src -------------------------------------------------------------------------------- /packages/typeorm-legacy/.gitignore: -------------------------------------------------------------------------------- 1 | # Misc 2 | .DS_Store 3 | 4 | .env 5 | .env.local 6 | .env.development.local 7 | .env.test.local 8 | .env.production.local 9 | 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Dependencies 15 | node_modules 16 | 17 | # Build dirs 18 | .next 19 | /build 20 | /dist 21 | /www/build 22 | 23 | # Generated files 24 | .docusaurus 25 | .cache-loader 26 | .next 27 | 28 | # VS 29 | /.vs/slnx.sqlite-journal 30 | /.vs/slnx.sqlite 31 | /.vs 32 | .vscode 33 | 34 | # GitHub Actions runner 35 | /actions-runner 36 | /_work -------------------------------------------------------------------------------- /packages/typeorm-legacy/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [1.0.1](https://github.com/nextauthjs/adapters/compare/@next-auth/typeorm-legacy-adapter@1.0.0...@next-auth/typeorm-legacy-adapter@1.0.1) (2021-12-06) 7 | 8 | **Note:** Version bump only for package @next-auth/typeorm-legacy-adapter 9 | 10 | ## [0.1.4](https://github.com/nextauthjs/adapters/compare/@next-auth/typeorm-legacy-adapter@0.1.3...@next-auth/typeorm-legacy-adapter@0.1.4) (2021-08-17) 11 | 12 | **Note:** Version bump only for package @next-auth/typeorm-legacy-adapter 13 | 14 | ## [0.1.3](https://github.com/nextauthjs/adapters/compare/@next-auth/typeorm-legacy-adapter@0.1.2...@next-auth/typeorm-legacy-adapter@0.1.3) (2021-07-11) 15 | 16 | **Note:** Version bump only for package @next-auth/typeorm-legacy-adapter 17 | 18 | ## [0.1.2](https://github.com/nextauthjs/adapters/compare/@next-auth/typeorm-legacy-adapter@0.1.1...@next-auth/typeorm-legacy-adapter@0.1.2) (2021-07-02) 19 | 20 | **Note:** Version bump only for package @next-auth/typeorm-legacy-adapter 21 | 22 | ## [0.1.1](https://github.com/nextauthjs/adapters/compare/@next-auth/typeorm-legacy-adapter@0.1.0...@next-auth/typeorm-legacy-adapter@0.1.1) (2021-06-30) 23 | 24 | **Note:** Version bump only for package @next-auth/typeorm-legacy-adapter 25 | -------------------------------------------------------------------------------- /packages/typeorm-legacy/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require("../../jest.config") 2 | -------------------------------------------------------------------------------- /packages/typeorm-legacy/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nextauthjs/adapters/1237a5372004951eccb37c5f7481e5fcb62415b6/packages/typeorm-legacy/logo.png -------------------------------------------------------------------------------- /packages/typeorm-legacy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@next-auth/typeorm-legacy-adapter", 3 | "version": "1.0.1", 4 | "description": "TypeORM (legacy) adapter for next-auth.", 5 | "homepage": "https://next-auth.js.org", 6 | "repository": "https://github.com/nextauthjs/adapters", 7 | "bugs": { 8 | "url": "https://github.com/nextauthjs/adapters/issues" 9 | }, 10 | "author": "Iain Collins", 11 | "contributors": [ 12 | "Balázs Orbán " 13 | ], 14 | "main": "dist/index.js", 15 | "files": [ 16 | "README.md", 17 | "dist" 18 | ], 19 | "license": "ISC", 20 | "keywords": [ 21 | "next-auth", 22 | "next.js", 23 | "oauth", 24 | "typeorm" 25 | ], 26 | "private": false, 27 | "publishConfig": { 28 | "access": "public" 29 | }, 30 | "scripts": { 31 | "build": "tsc", 32 | "init:db": "tests/init.sh", 33 | "test:containers": "tests/test.sh", 34 | "test": "tests/test.sh", 35 | "mysql": "yarn init:db && tests/mysql/test.sh", 36 | "postgres": "yarn init:db && tests/postgresql/test.sh", 37 | "sqlite": "tests/sqlite/test.sh" 38 | }, 39 | "devDependencies": { 40 | "mssql": "^7.2.1", 41 | "mysql": "^2.18.1", 42 | "pg": "^8.7.1", 43 | "sqlite3": "^5.0.2", 44 | "typeorm": "^0.2.37", 45 | "typeorm-naming-strategies": "^2.0.0" 46 | }, 47 | "peerDependencies": { 48 | "mssql": "^6.2.1 || 7", 49 | "mysql": "^2.18.1", 50 | "next-auth": "^4.0.1", 51 | "pg": "^8.2.1", 52 | "sqlite3": "^5.0.2", 53 | "typeorm": "^0.2.31" 54 | }, 55 | "peerDependenciesMeta": { 56 | "mysql": { 57 | "optional": true 58 | }, 59 | "mssql": { 60 | "optional": true 61 | }, 62 | "pg": { 63 | "optional": true 64 | }, 65 | "sqlite3": { 66 | "optional": true 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /packages/typeorm-legacy/src/entities.ts: -------------------------------------------------------------------------------- 1 | import type { ValueTransformer } from "typeorm" 2 | import { 3 | Entity, 4 | PrimaryGeneratedColumn, 5 | Column, 6 | ManyToOne, 7 | OneToMany, 8 | } from "typeorm" 9 | 10 | const transformer: Record<"date" | "bigint", ValueTransformer> = { 11 | date: { 12 | from: (date: string | null) => date && new Date(parseInt(date, 10)), 13 | to: (date?: Date) => date?.valueOf().toString(), 14 | }, 15 | bigint: { 16 | from: (bigInt: string | null) => bigInt && parseInt(bigInt, 10), 17 | to: (bigInt?: number) => bigInt?.toString(), 18 | }, 19 | } 20 | 21 | @Entity({ name: "users" }) 22 | export class UserEntity { 23 | @PrimaryGeneratedColumn("uuid") 24 | id!: string 25 | 26 | @Column({ type: "varchar", nullable: true }) 27 | name!: string | null 28 | 29 | @Column({ type: "varchar", nullable: true, unique: true }) 30 | email!: string | null 31 | 32 | @Column({ type: "varchar", nullable: true, transformer: transformer.date }) 33 | emailVerified!: string | null 34 | 35 | @Column({ type: "varchar", nullable: true }) 36 | image!: string | null 37 | 38 | @OneToMany(() => SessionEntity, (session) => session.userId) 39 | sessions!: SessionEntity[] 40 | 41 | @OneToMany(() => AccountEntity, (account) => account.userId) 42 | accounts!: AccountEntity[] 43 | } 44 | 45 | @Entity({ name: "accounts" }) 46 | export class AccountEntity { 47 | @PrimaryGeneratedColumn("uuid") 48 | id!: string 49 | 50 | @Column({ type: "uuid" }) 51 | userId!: string 52 | 53 | @Column() 54 | type!: string 55 | 56 | @Column() 57 | provider!: string 58 | 59 | @Column() 60 | providerAccountId!: string 61 | 62 | @Column({ type: "varchar", nullable: true }) 63 | refresh_token!: string 64 | 65 | @Column({ type: "varchar", nullable: true }) 66 | access_token!: string | null 67 | 68 | @Column({ 69 | nullable: true, 70 | type: "bigint", 71 | transformer: transformer.bigint, 72 | }) 73 | expires_at!: number | null 74 | 75 | @Column({ type: "varchar", nullable: true }) 76 | token_type!: string | null 77 | 78 | @Column({ type: "varchar", nullable: true }) 79 | scope!: string | null 80 | 81 | @Column({ type: "varchar", nullable: true }) 82 | id_token!: string | null 83 | 84 | @Column({ type: "varchar", nullable: true }) 85 | session_state!: string | null 86 | 87 | @ManyToOne(() => UserEntity, (user) => user.accounts, { 88 | createForeignKeyConstraints: true, 89 | }) 90 | user!: UserEntity 91 | } 92 | 93 | @Entity({ name: "sessions" }) 94 | export class SessionEntity { 95 | @PrimaryGeneratedColumn("uuid") 96 | id!: string 97 | 98 | @Column({ unique: true }) 99 | sessionToken!: string 100 | 101 | @Column({ type: "uuid" }) 102 | userId!: string 103 | 104 | @Column({ transformer: transformer.date }) 105 | expires!: string 106 | 107 | @ManyToOne(() => UserEntity, (user) => user.sessions) 108 | user!: UserEntity 109 | } 110 | 111 | @Entity({ name: "verification_tokens" }) 112 | export class VerificationTokenEntity { 113 | @PrimaryGeneratedColumn("uuid") 114 | id!: string 115 | 116 | @Column() 117 | token!: string 118 | 119 | @Column() 120 | identifier!: string 121 | 122 | @Column({ transformer: transformer.date }) 123 | expires!: string 124 | } 125 | -------------------------------------------------------------------------------- /packages/typeorm-legacy/src/utils.ts: -------------------------------------------------------------------------------- 1 | import type { Connection, ConnectionOptions } from "typeorm" 2 | import * as defaultEntities from "./entities" 3 | 4 | /** Ensure configOrString is normalized to an object. */ 5 | export function parseConnectionConfig( 6 | configOrString: string | ConnectionOptions 7 | ): ConnectionOptions { 8 | if (typeof configOrString !== "string") { 9 | return { 10 | ...configOrString, 11 | entities: Object.values(configOrString.entities ?? defaultEntities), 12 | } 13 | } 14 | 15 | // If the input is URL string, automatically convert the string to an object 16 | // to make configuration easier (in most use cases). 17 | // 18 | // TypeORM accepts connection string as a 'url' option, but unfortunately 19 | // not for all databases (e.g. SQLite) or for all options, so we handle 20 | // parsing it in this function. 21 | try { 22 | const parsedUrl = new URL(configOrString) 23 | const config: any = { 24 | entities: Object.values(defaultEntities), 25 | } 26 | 27 | if (parsedUrl.protocol.startsWith("mongodb+srv")) { 28 | // Special case handling is required for mongodb+srv with TypeORM 29 | config.type = "mongodb" 30 | config.url = configOrString.replace(/\?(.*)$/, "") 31 | config.useNewUrlParser = true 32 | } else { 33 | config.type = parsedUrl.protocol.replace(/:$/, "") 34 | config.host = parsedUrl.hostname 35 | config.port = Number(parsedUrl.port) 36 | config.username = parsedUrl.username 37 | config.password = parsedUrl.password 38 | config.database = parsedUrl.pathname 39 | .replace(/^\//, "") 40 | .replace(/\?(.*)$/, "") 41 | config.options = {} 42 | } 43 | 44 | // This option is recommended by mongodb 45 | if (config.type === "mongodb") { 46 | config.useUnifiedTopology = true 47 | } 48 | 49 | // Prevents warning about deprecated option (sets default value) 50 | if (config.type === "mssql") { 51 | config.options.enableArithAbort = true 52 | } 53 | 54 | if (parsedUrl.search) { 55 | parsedUrl.search 56 | .replace(/^\?/, "") 57 | .split("&") 58 | .forEach((keyValuePair) => { 59 | let [key, value] = keyValuePair.split("=") as any 60 | // Converts true/false strings to actual boolean values 61 | if (value === "true") { 62 | value = true 63 | } 64 | if (value === "false") { 65 | value = false 66 | } 67 | config[key] = value 68 | }) 69 | } 70 | 71 | return config 72 | } catch (error) { 73 | // If URL parsing fails for any reason, try letting TypeORM handle it 74 | return { url: configOrString } as any 75 | } 76 | } 77 | 78 | function entitiesChanged( 79 | prevEntities: any[] | undefined, 80 | newEntities: any[] 81 | ): boolean { 82 | if (prevEntities?.length !== newEntities?.length) return true 83 | 84 | for (let i = 0; i < prevEntities?.length; i++) { 85 | if (prevEntities[i] !== newEntities[i]) return true 86 | } 87 | 88 | return false 89 | } 90 | 91 | export async function updateConnectionEntities( 92 | connection: Connection, 93 | entities: any[] 94 | ) { 95 | if (!entitiesChanged(connection.options.entities, entities)) return 96 | 97 | // @ts-expect-error 98 | connection.options.entities = entities 99 | 100 | // @ts-expect-error 101 | connection.buildMetadatas() 102 | 103 | if (connection.options.synchronize !== false) { 104 | console.warn( 105 | "[next-auth][warn][adapter_typeorm_updating_entities]", 106 | "\nhttps://next-auth.js.org/warnings#adapter_typeorm_updating_entities" 107 | ) 108 | await connection.synchronize() 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /packages/typeorm-legacy/tests/custom-entities.ts: -------------------------------------------------------------------------------- 1 | import type { ValueTransformer } from "typeorm" 2 | import { 3 | Entity, 4 | PrimaryGeneratedColumn, 5 | Column, 6 | ManyToOne, 7 | OneToMany, 8 | } from "typeorm" 9 | 10 | const transformer: Record<"date" | "bigint", ValueTransformer> = { 11 | date: { 12 | from: (date: string | null) => date && new Date(parseInt(date, 10)), 13 | to: (date?: Date) => date?.valueOf().toString(), 14 | }, 15 | bigint: { 16 | from: (bigInt: string | null) => bigInt && parseInt(bigInt, 10), 17 | to: (bigInt?: number) => bigInt?.toString(), 18 | }, 19 | } 20 | 21 | @Entity({ name: "users" }) 22 | export class UserEntity { 23 | @PrimaryGeneratedColumn("uuid") 24 | id!: string 25 | 26 | @Column({ type: "varchar", nullable: true }) 27 | name!: string | null 28 | 29 | @Column({ type: "varchar", nullable: true, unique: true }) 30 | email!: string | null 31 | 32 | @Column({ type: "varchar", nullable: true, transformer: transformer.date }) 33 | emailVerified!: string | null 34 | 35 | @Column({ type: "varchar", nullable: true }) 36 | role!: string | null 37 | 38 | @Column({ type: "varchar", nullable: true }) 39 | phone!: string | null 40 | 41 | @Column({ type: "varchar", nullable: true }) 42 | image!: string | null 43 | 44 | @OneToMany(() => SessionEntity, (session) => session.userId) 45 | sessions!: SessionEntity[] 46 | 47 | @OneToMany(() => AccountEntity, (account) => account.userId) 48 | accounts!: AccountEntity[] 49 | } 50 | 51 | @Entity({ name: "accounts" }) 52 | export class AccountEntity { 53 | @PrimaryGeneratedColumn("uuid") 54 | id!: string 55 | 56 | @Column({ type: "uuid" }) 57 | userId!: string 58 | 59 | @Column() 60 | type!: string 61 | 62 | @Column() 63 | provider!: string 64 | 65 | @Column() 66 | providerAccountId!: string 67 | 68 | @Column({ type: "varchar", nullable: true }) 69 | refresh_token!: string 70 | 71 | @Column({ type: "varchar", nullable: true }) 72 | access_token!: string | null 73 | 74 | @Column({ 75 | nullable: true, 76 | type: "bigint", 77 | transformer: transformer.bigint, 78 | }) 79 | expires_at!: number | null 80 | 81 | @Column({ type: "varchar", nullable: true }) 82 | token_type!: string | null 83 | 84 | @Column({ type: "varchar", nullable: true }) 85 | scope!: string | null 86 | 87 | @Column({ type: "varchar", nullable: true }) 88 | id_token!: string | null 89 | 90 | @Column({ type: "varchar", nullable: true }) 91 | session_state!: string | null 92 | 93 | @ManyToOne(() => UserEntity, (user) => user.accounts, { 94 | createForeignKeyConstraints: true, 95 | }) 96 | user!: UserEntity 97 | } 98 | 99 | @Entity({ name: "sessions" }) 100 | export class SessionEntity { 101 | @PrimaryGeneratedColumn("uuid") 102 | id!: string 103 | 104 | @Column({ unique: true }) 105 | sessionToken!: string 106 | 107 | @Column({ type: "uuid" }) 108 | userId!: string 109 | 110 | @Column({ transformer: transformer.date }) 111 | expires!: string 112 | 113 | @ManyToOne(() => UserEntity, (user) => user.sessions) 114 | user!: UserEntity 115 | } 116 | 117 | @Entity({ name: "verification_tokens" }) 118 | export class VerificationTokenEntity { 119 | @PrimaryGeneratedColumn("uuid") 120 | id!: string 121 | 122 | @Column() 123 | token!: string 124 | 125 | @Column() 126 | identifier!: string 127 | 128 | @Column({ transformer: transformer.date }) 129 | expires!: string 130 | } 131 | -------------------------------------------------------------------------------- /packages/typeorm-legacy/tests/helpers.ts: -------------------------------------------------------------------------------- 1 | import type { ConnectionOptions } from "typeorm" 2 | import { ConnectionManager } from "typeorm" 3 | import type { TestOptions } from "../../../basic-tests" 4 | import * as defaultEntities from "../src/entities" 5 | import { parseConnectionConfig } from "../src/utils" 6 | 7 | export { defaultEntities } 8 | 9 | /** Set up Test Database */ 10 | export function db( 11 | config: string | ConnectionOptions, 12 | entities: typeof defaultEntities = defaultEntities 13 | ): TestOptions["db"] { 14 | const connection = new ConnectionManager().create({ 15 | ...parseConnectionConfig(config), 16 | entities: Object.values(entities), 17 | }) 18 | 19 | const m = connection.manager 20 | return { 21 | connect: async () => await connection.connect(), 22 | disconnect: async () => await connection.close(), 23 | async user(id) { 24 | const user = await m.findOne(entities.UserEntity, id) 25 | return user ?? null 26 | }, 27 | async account(provider_providerAccountId) { 28 | const account = await m.findOne( 29 | entities.AccountEntity, 30 | provider_providerAccountId 31 | ) 32 | return account ?? null 33 | }, 34 | async session(sessionToken) { 35 | const session = await m.findOne(entities.SessionEntity, { sessionToken }) 36 | return session ?? null 37 | }, 38 | async verificationToken(token_identifier) { 39 | const verificationToken = await m.findOne( 40 | entities.VerificationTokenEntity, 41 | token_identifier 42 | ) 43 | if (!verificationToken) return null 44 | const { id: _, ...rest } = verificationToken 45 | return rest 46 | }, 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/typeorm-legacy/tests/index.test.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error 2 | // @ts-ignore 3 | import { parseConnectionString } from "../src/lib/config" 4 | 5 | const connectionString = "mysql://root:password@localhost:3306/next-auth" 6 | 7 | test("could parse connection string", () => { 8 | expect(parseConnectionString(connectionString)).toEqual( 9 | expect.objectContaining({ 10 | type: "mysql", 11 | host: "localhost", 12 | port: 3306, 13 | username: "root", 14 | password: "password", 15 | database: "next-auth", 16 | }) 17 | ) 18 | }) 19 | -------------------------------------------------------------------------------- /packages/typeorm-legacy/tests/init.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Init PostgreSQL container 4 | echo "Initializing container for PostgreSQL tests" 5 | 6 | PGUSER=nextauth 7 | PGDATABASE=nextauth 8 | PGPORT=5432 9 | PG_CONTAINER_NAME=next-auth-postgres-test 10 | 11 | docker run -d --rm \ 12 | -e POSTGRES_USER=${PGUSER} \ 13 | -e POSTGRES_DB=${PGDATABASE} \ 14 | -e POSTGRES_HOST_AUTH_METHOD=trust \ 15 | --name "${PG_CONTAINER_NAME}" \ 16 | -p ${PGPORT}:5432 \ 17 | postgres:13.3 18 | 19 | # Init MyDQL container 20 | echo "Initializing container for MySQL tests" 21 | 22 | MYSQL_DATABASE=next-auth 23 | MYSQL_ROOT_PASSWORD=password 24 | MYSQL_CONTAINER_NAME=next-auth-mysql-test 25 | 26 | docker run -d --rm \ 27 | -e MYSQL_DATABASE=${MYSQL_DATABASE} \ 28 | -e MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} \ 29 | --name "${MYSQL_CONTAINER_NAME}" \ 30 | -p 3306:3306 \ 31 | mysql:8 \ 32 | --default-authentication-plugin=mysql_native_password 33 | 34 | 35 | -------------------------------------------------------------------------------- /packages/typeorm-legacy/tests/mysql/index.custom.test.ts: -------------------------------------------------------------------------------- 1 | import { runBasicTests } from "../../../../basic-tests" 2 | import { TypeORMLegacyAdapter } from "../../src" 3 | import * as entities from "../custom-entities" 4 | import { db } from "../helpers" 5 | import { SnakeNamingStrategy } from "typeorm-naming-strategies" 6 | 7 | import type { ConnectionOptions } from "typeorm" 8 | 9 | const mysqlConfig: ConnectionOptions = { 10 | type: "mysql" as const, 11 | host: "localhost", 12 | port: 3306, 13 | username: "root", 14 | password: "password", 15 | database: "next-auth", 16 | synchronize: true, 17 | namingStrategy: new SnakeNamingStrategy(), 18 | } 19 | 20 | runBasicTests({ 21 | adapter: TypeORMLegacyAdapter(mysqlConfig, { 22 | entities, 23 | }), 24 | db: db(mysqlConfig, entities), 25 | }) 26 | -------------------------------------------------------------------------------- /packages/typeorm-legacy/tests/mysql/index.test.ts: -------------------------------------------------------------------------------- 1 | import { runBasicTests } from "../../../../basic-tests" 2 | import { TypeORMLegacyAdapter } from "../../src" 3 | import { db } from "../helpers" 4 | 5 | const mysqlConfig = { 6 | type: "mysql" as const, 7 | host: "localhost", 8 | port: 3306, 9 | username: "root", 10 | password: "password", 11 | database: "next-auth", 12 | synchronize: true, 13 | } 14 | 15 | runBasicTests({ 16 | adapter: TypeORMLegacyAdapter(mysqlConfig), 17 | db: db(mysqlConfig), 18 | }) 19 | -------------------------------------------------------------------------------- /packages/typeorm-legacy/tests/mysql/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | WAIT=20 4 | echo "Waiting ${WAIT} sec for MySQL db to be up..." 5 | sleep ${WAIT} 6 | 7 | set -eu 8 | 9 | echo "Started running MySQL tests with default models." 10 | jest tests/mysql/index.test.ts 11 | echo "Finished running MySQL tests with default models." 12 | 13 | echo "Started running MySQL tests with custom models." 14 | CUSTOM_MODEL=1 jest tests/mysql/index.custom.test.ts 15 | echo "Finished running MySQL tests with custom models." 16 | -------------------------------------------------------------------------------- /packages/typeorm-legacy/tests/postgresql/index.custom.test.ts: -------------------------------------------------------------------------------- 1 | import { runBasicTests } from "../../../../basic-tests" 2 | import { TypeORMLegacyAdapter } from "../../src" 3 | import * as entities from "../custom-entities" 4 | import { db } from "../helpers" 5 | 6 | const postgresConfig = 7 | "postgres://nextauth:password@localhost:5432/nextauth?synchronize=true" 8 | 9 | runBasicTests({ 10 | adapter: TypeORMLegacyAdapter(postgresConfig, { 11 | entities, 12 | }), 13 | db: db(postgresConfig, entities), 14 | }) 15 | -------------------------------------------------------------------------------- /packages/typeorm-legacy/tests/postgresql/index.test.ts: -------------------------------------------------------------------------------- 1 | import { runBasicTests } from "../../../../basic-tests" 2 | import { TypeORMLegacyAdapter } from "../../src" 3 | import { db } from "../helpers" 4 | 5 | const postgresConfig = 6 | "postgres://nextauth:password@localhost:5432/nextauth?synchronize=true" 7 | 8 | runBasicTests({ 9 | adapter: TypeORMLegacyAdapter(postgresConfig), 10 | db: db(postgresConfig), 11 | }) 12 | -------------------------------------------------------------------------------- /packages/typeorm-legacy/tests/postgresql/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | WAIT=10 4 | echo "Waiting ${WAIT} sec for PostgreSQL db to be up..." 5 | sleep ${WAIT} 6 | 7 | set -eu 8 | 9 | echo "Started running PostgreSQL tests with default models." 10 | jest tests/postgresql/index.test.ts 11 | echo "Finished running PostgreSQL tests with default models." 12 | 13 | echo "Started running PostgreSQL tests with custom models." 14 | CUSTOM_MODEL=1 jest tests/postgresql/index.custom.test.ts 15 | echo "Finished running PostgreSQL tests with custom models." 16 | -------------------------------------------------------------------------------- /packages/typeorm-legacy/tests/sqlite/index.custom.test.ts: -------------------------------------------------------------------------------- 1 | import { runBasicTests } from "../../../../basic-tests" 2 | import { TypeORMLegacyAdapter } from "../../src" 3 | import * as entities from "../custom-entities" 4 | import { db } from "../helpers" 5 | 6 | const sqliteConfig = { 7 | type: "sqlite" as const, 8 | name: "next-auth-test-memory", 9 | database: "./tests/sqlite/dev.db", 10 | synchronize: true, 11 | } 12 | 13 | runBasicTests({ 14 | adapter: TypeORMLegacyAdapter(sqliteConfig, { 15 | entities, 16 | }), 17 | db: db(sqliteConfig, entities), 18 | }) 19 | -------------------------------------------------------------------------------- /packages/typeorm-legacy/tests/sqlite/index.test.ts: -------------------------------------------------------------------------------- 1 | import { runBasicTests } from "../../../../basic-tests" 2 | import { TypeORMLegacyAdapter } from "../../src" 3 | import { db } from "../helpers" 4 | import { SnakeNamingStrategy } from "typeorm-naming-strategies" 5 | 6 | import type { ConnectionOptions } from "typeorm" 7 | 8 | const sqliteConfig: ConnectionOptions = { 9 | type: "sqlite" as const, 10 | name: "next-auth-test-memory", 11 | database: "./tests/sqlite/dev.db", 12 | synchronize: true, 13 | namingStrategy: new SnakeNamingStrategy(), 14 | } 15 | 16 | runBasicTests({ 17 | adapter: TypeORMLegacyAdapter(sqliteConfig), 18 | db: db(sqliteConfig), 19 | }) 20 | -------------------------------------------------------------------------------- /packages/typeorm-legacy/tests/sqlite/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu 4 | 5 | rm -f tests/sqlite/dev.db 6 | 7 | echo "Started running SQLite tests with default models." 8 | jest tests/sqlite/index.test.ts 9 | echo "Finished running SQLite tests with default models." 10 | 11 | rm -f tests/sqlite/dev.db 12 | 13 | echo "Started running SQLite tests with custom models." 14 | CUSTOM_MODEL=1 jest tests/sqlite/index.custom.test.ts 15 | echo "Finished running SQLite tests with custom models." 16 | -------------------------------------------------------------------------------- /packages/typeorm-legacy/tests/test.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | # 3 | # Run parallel commands and fail if any of them fails. 4 | # shellcheck disable=SC2046 5 | # Based on: https://gist.github.com/mjambon/79adfc5cf6b11252e78b75df50793f24#gistcomment-3861511 6 | 7 | set -eu 8 | 9 | pids=() 10 | 11 | ./tests/init.sh 12 | 13 | ./tests/sqlite/test.sh & pids+=($!) 14 | 15 | ./tests/postgresql/test.sh & pids+=($!) 16 | 17 | ./tests/mysql/test.sh & pids+=($!) 18 | 19 | for _ in "${pids[@]}"; do 20 | if wait -n; then 21 | : 22 | else 23 | status=$? 24 | echo "One of the subprocesses exited with nonzero status $status. Aborting." 25 | for pid in "${pids[@]}"; do 26 | # Send a termination signal to all the children, and ignore errors 27 | # due to children that no longer exist. 28 | kill "$pid" 2> /dev/null || : 29 | done 30 | docker kill $(docker ps -q) 31 | exit "$status" 32 | fi 33 | done 34 | 35 | docker kill $(docker ps -q) 36 | -------------------------------------------------------------------------------- /packages/typeorm-legacy/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "outDir": "dist", 6 | "experimentalDecorators": true, 7 | "emitDecoratorMetadata": true, 8 | "stripInternal": true 9 | }, 10 | "exclude": ["tests", "dist"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/upstash-redis/.env.example: -------------------------------------------------------------------------------- 1 | UPSTASH_REDIS_URL= 2 | UPSTASH_REDIS_KEY= -------------------------------------------------------------------------------- /packages/upstash-redis/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # 1.1.0 (2022-01-17) 7 | 8 | ### Bug Fixes 9 | 10 | - **upstash-redis:** expose environment variables in workflow ([#373](https://github.com/nextauthjs/adapters/issues/373)) ([fcee362](https://github.com/nextauthjs/adapters/commit/fcee36227fec4e42e818f104b06a3030838790da)) 11 | - **upstash-redis:** fix deployment ([c2df2c8](https://github.com/nextauthjs/adapters/commit/c2df2c86b53f4e42a2bc1051256701ec7cc08fbd)) 12 | 13 | ### Features 14 | 15 | - **upstash-redis:** add upstash-redis adapter ([#341](https://github.com/nextauthjs/adapters/issues/341)) ([f4a8464](https://github.com/nextauthjs/adapters/commit/f4a84644296f545c1dac16519337a6dc7718c88c)) 16 | -------------------------------------------------------------------------------- /packages/upstash-redis/README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 |      4 |

Upstash Redis Adapter - NextAuth.js

5 |

6 | Open Source. Full Stack. Own Your Data. 7 |

8 |

9 | CI Test 10 | Bundle Size 11 | @next-auth/upstash-adapter Version 12 |

13 |

14 | 15 | ## Overview 16 | 17 | This is the Upstash Redis adapter for [`next-auth`](https://next-auth.js.org). This package can only be used in conjunction with the primary `next-auth` and `@upstash/redis` packages. It is not a standalone package. 18 | 19 | ## Getting Started 20 | 21 | 1. Install `next-auth` and `@next-auth/upstash-redis-adapter` as well as `@upstash/redis` via NPM. 22 | 23 | ```js 24 | npm install next-auth @next-auth/upstash-redis-adapter @upstash/redis 25 | ``` 26 | 27 | 2. Add the follwing code to your `pages/api/[...nextauth].js` next-auth configuration object. 28 | 29 | ```js 30 | import NextAuth from "next-auth" 31 | import { UpstashRedisAdapter } from "@next-auth/upstash-adapter" 32 | import upstashRedisClient from "@upstash/redis" 33 | 34 | const redis = upstashRedisClient("UPSTASH_REDIS_REST_URL", "UPSTASH_REDIS_REST_TOKEN") 35 | 36 | // For more information on each option (and a full list of options) go to 37 | // https://next-auth.js.org/configuration/options 38 | export default NextAuth({ 39 | ... 40 | adapter: UpstashRedisAdapter(redis) 41 | ... 42 | }) 43 | ``` 44 | 45 | ## Using Multiple Apps with a Single Upstash Redis Instance 46 | 47 | The Upstash free-tier allows for only one Redis instance. If you have multiple Next-Auth connected apps using this instance, you need different key prefixes for every app. 48 | 49 | You can change the prefixes by passing an `options` object as the second argument to the adapter factory function. 50 | 51 | The default values for this object are: 52 | 53 | ```js 54 | const defaultOptions = { 55 | baseKeyPrefix: "", 56 | accountKeyPrefix: "user:account:", 57 | accountByUserIdPrefix: "user:account:by-user-id:", 58 | emailKeyPrefix: "user:email:", 59 | sessionKeyPrefix: "user:session:", 60 | sessionByUserIdKeyPrefix: "user:session:by-user-id:", 61 | userKeyPrefix: "user:", 62 | verificationTokenKeyPrefix: "user:token:", 63 | } 64 | ``` 65 | 66 | Usually changing the `baseKeyPrefix` should be enough for this scenario, but for more custom setups, you can also change the prefixes of every single key. 67 | 68 | Example: 69 | 70 | ```js 71 | export default NextAuth({ 72 | ... 73 | adapter: UpstashRedisAdapter(redis, {baseKeyPrefix: "app2:"}) 74 | ... 75 | }) 76 | ``` 77 | 78 | ## Contributing 79 | 80 | We're open to all community contributions! If you'd like to contribute in any way, please read our [Contributing Guide](https://github.com/nextauthjs/adapters/blob/main/CONTRIBUTING.md). 81 | 82 | ## License 83 | 84 | ISC 85 | -------------------------------------------------------------------------------- /packages/upstash-redis/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require("../../jest.config") 2 | -------------------------------------------------------------------------------- /packages/upstash-redis/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/upstash-redis/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@next-auth/upstash-redis-adapter", 3 | "version": "1.1.0", 4 | "description": "Upstash adapter for next-auth. It uses Upstash's connectionless (HTTP based) Redis client.", 5 | "homepage": "https://next-auth.js.org", 6 | "repository": "https://github.com/nextauthjs/adapters", 7 | "bugs": { 8 | "url": "https://github.com/nextauthjs/adapters/issues" 9 | }, 10 | "author": "github.com/kay-is", 11 | "main": "dist/index.js", 12 | "license": "ISC", 13 | "keywords": [ 14 | "next-auth", 15 | "next.js", 16 | "oauth", 17 | "upstash", 18 | "redis" 19 | ], 20 | "private": false, 21 | "publishConfig": { 22 | "access": "public" 23 | }, 24 | "scripts": { 25 | "test": "jest", 26 | "build": "tsc" 27 | }, 28 | "files": [ 29 | "README.md", 30 | "dist" 31 | ], 32 | "peerDependencies": { 33 | "@upstash/redis": "^0.2.1", 34 | "next-auth": "^4.0.1" 35 | }, 36 | "devDependencies": { 37 | "@upstash/redis": "^0.2.1", 38 | "dotenv": "^10.0.0" 39 | }, 40 | "dependencies": { 41 | "uuid": "^8.3.2" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/upstash-redis/tests/index.test.ts: -------------------------------------------------------------------------------- 1 | import upstashRedisClient from "@upstash/redis" 2 | import { runBasicTests } from "../../../basic-tests" 3 | import { reviveFromJson, UpstashRedisAdapter } from "../src" 4 | import "dotenv/config" 5 | 6 | const client = upstashRedisClient( 7 | process.env.UPSTASH_REDIS_URL, 8 | process.env.UPSTASH_REDIS_KEY 9 | ) 10 | 11 | runBasicTests({ 12 | adapter: UpstashRedisAdapter(client, { baseKeyPrefix: "testApp:" }), 13 | db: { 14 | async user(id: string) { 15 | const { data } = await client.get(`testApp:user:${id}`) 16 | return reviveFromJson(data) 17 | }, 18 | async account({ provider, providerAccountId }) { 19 | const { data } = await client.get( 20 | `testApp:user:account:${provider}:${providerAccountId}` 21 | ) 22 | return reviveFromJson(data) 23 | }, 24 | async session(sessionToken) { 25 | const { data } = await client.get(`testApp:user:session:${sessionToken}`) 26 | return reviveFromJson(data) 27 | }, 28 | async verificationToken(where) { 29 | const { data } = await client.get( 30 | `testApp:user:token:${where.identifier}` 31 | ) 32 | return reviveFromJson(data) 33 | }, 34 | }, 35 | }) 36 | -------------------------------------------------------------------------------- /packages/upstash-redis/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "outDir": "dist" 6 | }, 7 | "exclude": ["tests", "dist"] 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2019", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "strict": true, 7 | "esModuleInterop": true, 8 | "skipLibCheck": true, 9 | "forceConsistentCasingInFileNames": true 10 | } 11 | } 12 | --------------------------------------------------------------------------------