mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
Compare commits
1 Commits
@next-auth
...
@next-auth
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46304838af |
40
.eslintrc.js
40
.eslintrc.js
@@ -1,40 +0,0 @@
|
|||||||
const path = require("path")
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
parser: "@typescript-eslint/parser",
|
|
||||||
overrides: [
|
|
||||||
{
|
|
||||||
files: ["*.ts", "*.tsx"],
|
|
||||||
extends: ["standard-with-typescript", "prettier"],
|
|
||||||
rules: {
|
|
||||||
camelcase: "off",
|
|
||||||
"@typescript-eslint/naming-convention": "off",
|
|
||||||
"@typescript-eslint/strict-boolean-expressions": "off",
|
|
||||||
"@typescript-eslint/explicit-function-return-type": "off",
|
|
||||||
"@typescript-eslint/restrict-template-expressions": "off",
|
|
||||||
},
|
|
||||||
|
|
||||||
parserOptions: {
|
|
||||||
project: [
|
|
||||||
path.resolve(__dirname, "./packages/**/tsconfig.eslint.json"),
|
|
||||||
path.resolve(__dirname, "./apps/**/tsconfig.json"),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
extends: ["prettier"],
|
|
||||||
globals: {
|
|
||||||
localStorage: "readonly",
|
|
||||||
location: "readonly",
|
|
||||||
fetch: "readonly",
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
camelcase: "off",
|
|
||||||
},
|
|
||||||
plugins: ["jest"],
|
|
||||||
env: {
|
|
||||||
"jest/globals": true,
|
|
||||||
},
|
|
||||||
ignorePatterns: [".eslintrc.js"],
|
|
||||||
}
|
|
||||||
15
.github/CODEOWNERS
vendored
15
.github/CODEOWNERS
vendored
@@ -1,11 +1,4 @@
|
|||||||
# Learn how to add code owners here:
|
/types/ @balazsorban44 @lluia
|
||||||
# https://help.github.com/en/articles/about-code-owners
|
/docs/ @balazsorban44 @ndom91
|
||||||
|
/adapters/ @balazsorban44 @ndom91
|
||||||
* @balazsorban44
|
/__tests__/ @lluia
|
||||||
.github @ThangHuuVu
|
|
||||||
/apps/ @lluia @ndom91 @ThangHuuVu
|
|
||||||
/docs/ @lluia @ndom91
|
|
||||||
/packages/ @ThangHuuVu
|
|
||||||
/packages/adapter-*/ @ndom91
|
|
||||||
/**/*test* @lluia
|
|
||||||
/**/*type* @lluia
|
|
||||||
|
|||||||
1
.github/ISSUE_TEMPLATE/1_bug_framework.yml
vendored
1
.github/ISSUE_TEMPLATE/1_bug_framework.yml
vendored
@@ -5,7 +5,6 @@ body:
|
|||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
**NOTE:** Issues that are potentially security related should be reported to us by following the [Security guidelines](https://next-auth.js.org/security) rather than on GitHub.
|
|
||||||
Thanks for taking the time to fill out this issue after reading/searching through the [documentation](https://next-auth.js.org) first!
|
Thanks for taking the time to fill out this issue after reading/searching through the [documentation](https://next-auth.js.org) first!
|
||||||
Is this your first time contributing? Check out this video: https://www.youtube.com/watch?v=cuoNzXFLitc
|
Is this your first time contributing? Check out this video: https://www.youtube.com/watch?v=cuoNzXFLitc
|
||||||
|
|
||||||
|
|||||||
2
.github/ISSUE_TEMPLATE/2_bug_provider.yml
vendored
2
.github/ISSUE_TEMPLATE/2_bug_provider.yml
vendored
@@ -5,7 +5,6 @@ body:
|
|||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
**NOTE:** Issues that are potentially security related should be reported to us by following the [Security guidelines](https://next-auth.js.org/security) rather than on GitHub.
|
|
||||||
Thanks for taking the time to fill out this [Provider](https://next-auth.js.org/providers/overview) related issue!
|
Thanks for taking the time to fill out this [Provider](https://next-auth.js.org/providers/overview) related issue!
|
||||||
Is this your first time contributing? Check out this video: https://www.youtube.com/watch?v=cuoNzXFLitc
|
Is this your first time contributing? Check out this video: https://www.youtube.com/watch?v=cuoNzXFLitc
|
||||||
|
|
||||||
@@ -68,7 +67,6 @@ body:
|
|||||||
- "Slack"
|
- "Slack"
|
||||||
- "Spotify"
|
- "Spotify"
|
||||||
- "Strava"
|
- "Strava"
|
||||||
- "Todoist"
|
|
||||||
- "Trakt"
|
- "Trakt"
|
||||||
- "Twitch"
|
- "Twitch"
|
||||||
- "Twitter"
|
- "Twitter"
|
||||||
|
|||||||
3
.github/ISSUE_TEMPLATE/3_bug_adapter.yml
vendored
3
.github/ISSUE_TEMPLATE/3_bug_adapter.yml
vendored
@@ -5,7 +5,6 @@ body:
|
|||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
**NOTE:** Issues that are potentially security related should be reported to us by following the [Security guidelines](https://next-auth.js.org/security) rather than on GitHub.
|
|
||||||
Thanks for taking the time to fill out this [Adapter](https://next-auth.js.org/adapters/overview) related issue!
|
Thanks for taking the time to fill out this [Adapter](https://next-auth.js.org/adapters/overview) related issue!
|
||||||
Is this your first time contributing? Check out this video: https://www.youtube.com/watch?v=cuoNzXFLitc
|
Is this your first time contributing? Check out this video: https://www.youtube.com/watch?v=cuoNzXFLitc
|
||||||
|
|
||||||
@@ -31,10 +30,8 @@ body:
|
|||||||
- "@next-auth/pouchdb-adapter"
|
- "@next-auth/pouchdb-adapter"
|
||||||
- "@next-auth/prisma-adapter"
|
- "@next-auth/prisma-adapter"
|
||||||
- "@next-auth/sequelize-adapter"
|
- "@next-auth/sequelize-adapter"
|
||||||
- "@next-auth/supabase-adapter"
|
|
||||||
- "@next-auth/typeorm-legacy-adapter"
|
- "@next-auth/typeorm-legacy-adapter"
|
||||||
- "@next-auth/upstash-redis-adapter"
|
- "@next-auth/upstash-redis-adapter"
|
||||||
- "@next-auth/xata-adapter"
|
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
|||||||
1
.github/ISSUE_TEMPLATE/5_feature_request.yml
vendored
1
.github/ISSUE_TEMPLATE/5_feature_request.yml
vendored
@@ -9,7 +9,6 @@ body:
|
|||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
**NOTE:** Issues that are potentially security related should be reported to us by following the [Security guidelines](https://next-auth.js.org/security) rather than on GitHub.
|
|
||||||
Thank you very much for reaching out to us regarding the awesome feature that you believe should be included in the NextAuth.js library.
|
Thank you very much for reaching out to us regarding the awesome feature that you believe should be included in the NextAuth.js library.
|
||||||
|
|
||||||
_NOTE: Feature requests are converted to [discussions (Ideas 💡)](https://github.com/nextauthjs/next-auth/discussions/categories/ideas). Make sure your idea hasn't been asked yet, and upvote the existing one before opening a new instead._
|
_NOTE: Feature requests are converted to [discussions (Ideas 💡)](https://github.com/nextauthjs/next-auth/discussions/categories/ideas). Make sure your idea hasn't been asked yet, and upvote the existing one before opening a new instead._
|
||||||
|
|||||||
1
.github/ISSUE_TEMPLATE/6_typescript.yml
vendored
1
.github/ISSUE_TEMPLATE/6_typescript.yml
vendored
@@ -17,7 +17,6 @@ body:
|
|||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
**NOTE:** Issues that are potentially security related should be reported to us by following the [Security guidelines](https://next-auth.js.org/security) rather than on GitHub.
|
|
||||||
Make sure you [link]() to external documentation if necessary and provide inline code examples like so:
|
Make sure you [link]() to external documentation if necessary and provide inline code examples like so:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
|||||||
1
.github/ISSUE_TEMPLATE/7_question.yml
vendored
1
.github/ISSUE_TEMPLATE/7_question.yml
vendored
@@ -9,7 +9,6 @@ body:
|
|||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
**NOTE:** Issues that are potentially security related should be reported to us by following the [Security guidelines](https://next-auth.js.org/security) rather than on GitHub.
|
|
||||||
We are glad that you have a question about this library. Please provide the following information:
|
We are glad that you have a question about this library. Please provide the following information:
|
||||||
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
|||||||
12
.github/PULL_REQUEST_TEMPLATE.md
vendored
12
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -5,14 +5,9 @@ Please fill out the information below to expedite the review and (hopefully)
|
|||||||
merge of your pull request!
|
merge of your pull request!
|
||||||
-->
|
-->
|
||||||
|
|
||||||
> _NOTE_:
|
|
||||||
>
|
|
||||||
> - It's a good idea to open an issue first to discuss potential changes.
|
|
||||||
> - Please make sure that you are _NOT_ opening a PR to fix a potential security vulnerability. Instead, please follow the [Security guidelines](../Security.md) to disclose the issue to us confidentially.
|
|
||||||
|
|
||||||
## ☕️ Reasoning
|
## ☕️ Reasoning
|
||||||
|
|
||||||
<!-- What changes are being made? What feature/bug is being fixed here? -->
|
What changes are being made? What feature/bug is being fixed here?
|
||||||
|
|
||||||
## 🧢 Checklist
|
## 🧢 Checklist
|
||||||
|
|
||||||
@@ -28,7 +23,6 @@ Fixes: INSERT_ISSUE_LINK_HERE
|
|||||||
|
|
||||||
## 📌 Resources
|
## 📌 Resources
|
||||||
|
|
||||||
- [Security guidelines](../Security.md)
|
- [Contributing guidelines](./CONTRIBUTING.md)
|
||||||
- [Contributing guidelines](../CONTRIBUTING.md)
|
- [Code of conduct](./CODE_OF_CONDUCT.md)
|
||||||
- [Code of conduct](../CODE_OF_CONDUCT.md)
|
|
||||||
- [Contributing to Open Source](https://kcd.im/pull-request)
|
- [Contributing to Open Source](https://kcd.im/pull-request)
|
||||||
|
|||||||
6
.github/issue-labeler.yml
vendored
6
.github/issue-labeler.yml
vendored
@@ -30,14 +30,8 @@ prisma:
|
|||||||
sequelize:
|
sequelize:
|
||||||
- "@next-auth/sequelize-adapter"
|
- "@next-auth/sequelize-adapter"
|
||||||
|
|
||||||
supabase:
|
|
||||||
- "@next-auth/supabase-adapter"
|
|
||||||
|
|
||||||
typeorm-legacy:
|
typeorm-legacy:
|
||||||
- "@next-auth/typeorm-legacy-adapter"
|
- "@next-auth/typeorm-legacy-adapter"
|
||||||
|
|
||||||
upstash-redis:
|
upstash-redis:
|
||||||
- "@next-auth/upstash-redis-adapter"
|
- "@next-auth/upstash-redis-adapter"
|
||||||
|
|
||||||
xata:
|
|
||||||
- "@next-auth/xata-adapter"
|
|
||||||
|
|||||||
8
.github/pr-labeler.yml
vendored
8
.github/pr-labeler.yml
vendored
@@ -10,7 +10,7 @@ providers:
|
|||||||
|
|
||||||
adapters:
|
adapters:
|
||||||
- packages/next-auth/src/adapters.ts
|
- packages/next-auth/src/adapters.ts
|
||||||
- packages/adapter-*/**
|
- packages/*-adapter/**
|
||||||
|
|
||||||
dgraph:
|
dgraph:
|
||||||
- packages/adapter-dgraph/**
|
- packages/adapter-dgraph/**
|
||||||
@@ -42,18 +42,12 @@ prisma:
|
|||||||
sequelize:
|
sequelize:
|
||||||
- packages/adapter-sequelize/**
|
- packages/adapter-sequelize/**
|
||||||
|
|
||||||
supabase:
|
|
||||||
- packages/adapter-supabase/**
|
|
||||||
|
|
||||||
typeorm-legacy:
|
typeorm-legacy:
|
||||||
- packages/adapter-typeorm-legacy/**
|
- packages/adapter-typeorm-legacy/**
|
||||||
|
|
||||||
upstash-redis:
|
upstash-redis:
|
||||||
- packages/adapter-upstash-redis/**
|
- packages/adapter-upstash-redis/**
|
||||||
|
|
||||||
xata:
|
|
||||||
- packages/adapter-xata/**
|
|
||||||
|
|
||||||
core:
|
core:
|
||||||
- packages/next-auth/src/**/*
|
- packages/next-auth/src/**/*
|
||||||
|
|
||||||
|
|||||||
2
.github/version-pr/action.yml
vendored
2
.github/version-pr/action.yml
vendored
@@ -4,5 +4,5 @@ outputs:
|
|||||||
version:
|
version:
|
||||||
description: "npm package version"
|
description: "npm package version"
|
||||||
runs:
|
runs:
|
||||||
using: "node16"
|
using: "node12"
|
||||||
main: "index.js"
|
main: "index.js"
|
||||||
|
|||||||
8
.github/workflows/codeql-analysis.yml
vendored
8
.github/workflows/codeql-analysis.yml
vendored
@@ -2,7 +2,7 @@ name: Code Analysis
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [beta, next]
|
branches: [main, beta, next]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
schedule:
|
schedule:
|
||||||
@@ -18,10 +18,10 @@ jobs:
|
|||||||
language: ["javascript"]
|
language: ["javascript"]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v2
|
uses: github/codeql-action/init@v1
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v2
|
uses: github/codeql-action/analyze@v1
|
||||||
|
|||||||
2
.github/workflows/label-issue.yml
vendored
2
.github/workflows/label-issue.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
|||||||
name: Triage
|
name: Triage
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: github/issue-labeler@v2.5
|
- uses: github/issue-labeler@v2.4.1
|
||||||
with:
|
with:
|
||||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
configuration-path: ".github/issue-labeler.yml"
|
configuration-path: ".github/issue-labeler.yml"
|
||||||
|
|||||||
2
.github/workflows/label-pr.yml
vendored
2
.github/workflows/label-pr.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
name: Triage
|
name: Triage
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/labeler@v4
|
- uses: actions/labeler@v3
|
||||||
with:
|
with:
|
||||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
configuration-path: ".github/pr-labeler.yml"
|
configuration-path: ".github/pr-labeler.yml"
|
||||||
|
|||||||
37
.github/workflows/release.yml
vendored
37
.github/workflows/release.yml
vendored
@@ -15,17 +15,17 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Init
|
- name: Init
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v2.2.4
|
uses: pnpm/action-setup@v2.2.1
|
||||||
with:
|
with:
|
||||||
version: 7.5.1
|
version: 6.32.8
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 16
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
@@ -49,17 +49,17 @@ jobs:
|
|||||||
environment: Production
|
environment: Production
|
||||||
steps:
|
steps:
|
||||||
- name: Init
|
- name: Init
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v2.2.4
|
uses: pnpm/action-setup@v2.2.1
|
||||||
with:
|
with:
|
||||||
version: 7.5.1
|
version: 6.32.8
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 16
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
@@ -69,8 +69,7 @@ jobs:
|
|||||||
git config --global user.name "Balázs Orbán"
|
git config --global user.name "Balázs Orbán"
|
||||||
pnpm release
|
pnpm release
|
||||||
env:
|
env:
|
||||||
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
|
||||||
NPM_TOKEN_PKG: ${{ secrets.NPM_TOKEN_PKG }}
|
NPM_TOKEN_PKG: ${{ secrets.NPM_TOKEN_PKG }}
|
||||||
NPM_TOKEN_ORG: ${{ secrets.NPM_TOKEN_ORG }}
|
NPM_TOKEN_ORG: ${{ secrets.NPM_TOKEN_ORG }}
|
||||||
release-pr:
|
release-pr:
|
||||||
@@ -81,15 +80,15 @@ jobs:
|
|||||||
environment: Preview
|
environment: Preview
|
||||||
steps:
|
steps:
|
||||||
- name: Init
|
- name: Init
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v2.2.4
|
uses: pnpm/action-setup@v2.2.1
|
||||||
with:
|
with:
|
||||||
version: 7.5.1
|
version: 6.32.8
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 16
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
@@ -104,15 +103,11 @@ jobs:
|
|||||||
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> .npmrc
|
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> .npmrc
|
||||||
pnpm publish --no-git-checks --access public --tag experimental
|
pnpm publish --no-git-checks --access public --tag experimental
|
||||||
env:
|
env:
|
||||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN_PKG }}
|
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
- name: Comment version on PR
|
- name: Comment version on PR
|
||||||
uses: NejcZdovc/comment-pr@v2
|
uses: NejcZdovc/comment-pr@v1
|
||||||
with:
|
with:
|
||||||
message:
|
message: "🎉 Experimental release [published on npm](https://www.npmjs.com/package/next-auth/v/${{ env.VERSION }})!\n\n```sh\nnpm i next-auth@${{ env.VERSION }}\n```\n```sh\nyarn add next-auth@${{ env.VERSION }}\n```"
|
||||||
"🎉 Experimental release [published 📦️ on npm](https://npmjs.com/package/next-auth/v/${{ env.VERSION }})!\n \
|
|
||||||
```sh\npnpm add next-auth@${{ env.VERSION }}\n```\n \
|
|
||||||
```sh\nyarn add next-auth@${{ env.VERSION }}\n```\n \
|
|
||||||
```sh\nnpm i next-auth@${{ env.VERSION }}\n```"
|
|
||||||
env:
|
env:
|
||||||
VERSION: ${{ steps.determine-version.outputs.version }}
|
VERSION: ${{ steps.determine-version.outputs.version }}
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
2
.github/workflows/sync-examples.yml
vendored
2
.github/workflows/sync-examples.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
- name: Run GitHub File Sync
|
- name: Run GitHub File Sync
|
||||||
# Can update to v1 when https://github.com/BetaHuhn/repo-file-sync-action/issues/168 is resolved
|
# Can update to v1 when https://github.com/BetaHuhn/repo-file-sync-action/issues/168 is resolved
|
||||||
uses: BetaHuhn/repo-file-sync-action@v1.16.5
|
uses: BetaHuhn/repo-file-sync-action@v1.16.5
|
||||||
|
|||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -30,12 +30,11 @@ packages/next-auth/providers
|
|||||||
packages/next-auth/src/providers/oauth-types.ts
|
packages/next-auth/src/providers/oauth-types.ts
|
||||||
packages/next-auth/client
|
packages/next-auth/client
|
||||||
packages/next-auth/css
|
packages/next-auth/css
|
||||||
packages/next-auth/utils
|
packages/next-auth/lib
|
||||||
packages/next-auth/core
|
packages/next-auth/core
|
||||||
packages/next-auth/jwt
|
packages/next-auth/jwt
|
||||||
packages/next-auth/react
|
packages/next-auth/react
|
||||||
packages/next-auth/adapters.d.ts
|
packages/next-auth/adapters.d.ts
|
||||||
packages/next-auth/adapters.js
|
|
||||||
packages/next-auth/index.d.ts
|
packages/next-auth/index.d.ts
|
||||||
packages/next-auth/index.js
|
packages/next-auth/index.js
|
||||||
packages/next-auth/next
|
packages/next-auth/next
|
||||||
@@ -45,7 +44,6 @@ packages/next-auth/middleware.js
|
|||||||
# Development app
|
# Development app
|
||||||
apps/dev/src/css
|
apps/dev/src/css
|
||||||
apps/dev/prisma/migrations
|
apps/dev/prisma/migrations
|
||||||
apps/dev/typeorm
|
|
||||||
|
|
||||||
# VS
|
# VS
|
||||||
/.vs/slnx.sqlite-journal
|
/.vs/slnx.sqlite-journal
|
||||||
@@ -65,7 +63,6 @@ dev.db*
|
|||||||
packages/adapter-prisma/prisma/dev.db
|
packages/adapter-prisma/prisma/dev.db
|
||||||
packages/adapter-prisma/prisma/migrations
|
packages/adapter-prisma/prisma/migrations
|
||||||
db.sqlite
|
db.sqlite
|
||||||
packages/adapter-supabase/supabase/.branches
|
|
||||||
|
|
||||||
# Tests
|
# Tests
|
||||||
coverage
|
coverage
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ further defined and clarified by project maintainers.
|
|||||||
## Enforcement
|
## Enforcement
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
reported by contacting hi@thvu.dev, info@balazsorban.com, yo@ndo.dev and me@iaincollins.com.
|
reported by contacting me@iaincollins.com or info@balazsorban.com and yo@ndo.dev.
|
||||||
All complaints will be reviewed and investigated and will result in a response
|
All complaints will be reviewed and investigated and will result in a response
|
||||||
that is deemed necessary and appropriate to the circumstances. The project team
|
that is deemed necessary and appropriate to the circumstances. The project team
|
||||||
is obligated to maintain confidentiality with regard to the reporter of an
|
is obligated to maintain confidentiality with regard to the reporter of an
|
||||||
|
|||||||
@@ -12,14 +12,12 @@ Please raise any significant new functionality or breaking change an issue for d
|
|||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
Before contributing, we recommend you read the [Tour de Source: NextAuth.js](https://sourcegraph.com/notebooks/Tm90ZWJvb2s6MTc2MQ==) post to become more familiar with the libraries inner workings.
|
|
||||||
|
|
||||||
### Pull Requests
|
### Pull Requests
|
||||||
|
|
||||||
- The latest changes are always in `main`, so please make your Pull Request against that branch.
|
- The latest changes are always in `main`, so please make your Pull Request against that branch.
|
||||||
- Pull Requests should be raised for any change
|
- Pull Requests should be raised for any change
|
||||||
- Pull Requests need approval of a [core contributor](https://next-auth.js.org/contributors#core-team) before merging
|
- Pull Requests need approval of a [core contributor](https://next-auth.js.org/contributors#core-team) before merging
|
||||||
- We use ESLint/Prettier for linting/formatting, so please run `pnpm lint:fix` before committing to make resolving conflicts easier (VSCode users, check out [this ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) and [this Prettier extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) to fix lint and formatting issues in development)
|
- We use ESLint/Prettier for linting/formatting, so please run `yarn lint:fix` before committing to make resolving conflicts easier (VSCode users, check out [this ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) and [this Prettier extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) to fix lint and formatting issues in development)
|
||||||
- We encourage you to test your changes, and if you have the opportunity, please make those tests part of the Pull Request
|
- We encourage you to test your changes, and if you have the opportunity, please make those tests part of the Pull Request
|
||||||
- If you add new functionality, please provide the corresponding documentation as well and make it part of the Pull Request
|
- If you add new functionality, please provide the corresponding documentation as well and make it part of the Pull Request
|
||||||
|
|
||||||
@@ -28,6 +26,7 @@ Before contributing, we recommend you read the [Tour de Source: NextAuth.js](htt
|
|||||||
|
|
||||||
A quick guide on how to setup _next-auth_ locally to work on it and test out any changes:
|
A quick guide on how to setup _next-auth_ locally to work on it and test out any changes:
|
||||||
|
|
||||||
|
|
||||||
1. Clone the repo:
|
1. Clone the repo:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -35,21 +34,13 @@ git clone git@github.com:nextauthjs/next-auth.git
|
|||||||
cd next-auth
|
cd next-auth
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Set up the correct pnpm version, using [Corepack](https://nodejs.org/api/corepack.html). Run the following in the project'a root:
|
1. Install packages. Developing requires Node.js v16:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
corepack enable pnpm
|
yarn
|
||||||
```
|
```
|
||||||
|
|
||||||
(Now, if you run `pnpm --version`, it should print the same verion as the `packageManager` property in the [`package.json` file](https://github.com/nextauthjs/next-auth/blob/main/package.json))
|
3. Populate `.env.local`:
|
||||||
|
|
||||||
3. Install packages. Developing requires Node.js v18:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
pnpm install
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Populate `.env.local`:
|
|
||||||
|
|
||||||
Copy `apps/dev/.env.local.example` to `apps/dev/.env.local`, and add your env variables for each provider you want to test.
|
Copy `apps/dev/.env.local.example` to `apps/dev/.env.local`, and add your env variables for each provider you want to test.
|
||||||
|
|
||||||
@@ -61,12 +52,11 @@ cp .env.local.example .env.local
|
|||||||
> NOTE: You can add any environment variables to .env.local that you would like to use in your dev app.
|
> NOTE: You can add any environment variables to .env.local that you would like to use in your dev app.
|
||||||
> You can find the next-auth config under`apps/dev/pages/api/auth/[...nextauth].js`.
|
> You can find the next-auth config under`apps/dev/pages/api/auth/[...nextauth].js`.
|
||||||
|
|
||||||
5. Start the developer application/server:
|
4. Start the developer application/server:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
pnpm dev
|
yarn dev:app
|
||||||
```
|
```
|
||||||
|
|
||||||
Your developer application will be available on `http://localhost:3000`
|
Your developer application will be available on `http://localhost:3000`
|
||||||
|
|
||||||
That's it! 🎉
|
That's it! 🎉
|
||||||
@@ -75,7 +65,7 @@ If you need an example project to link to, you can use [next-auth-example](https
|
|||||||
|
|
||||||
#### Hot reloading
|
#### Hot reloading
|
||||||
|
|
||||||
When running `pnpm dev`, you start a Next.js developer server on `http://localhost:3000`, which includes hot reloading out-of-the-box. Make changes on any of the files in `src` and see the changes immediately.
|
When running `yarn dev:app`, you start a Next.js developer server on `http://localhost:3000`, which includes hot reloading out of the box. Make changes on any of the files in `src` and see the changes immediately.
|
||||||
|
|
||||||
> NOTE: When working on CSS, you will have to manually refresh the page after changes. The reason for this is our pages using CSS are server-side rendered (using API routes). (Improving this through a PR is very welcome!)
|
> NOTE: When working on CSS, you will have to manually refresh the page after changes. The reason for this is our pages using CSS are server-side rendered (using API routes). (Improving this through a PR is very welcome!)
|
||||||
|
|
||||||
@@ -85,9 +75,8 @@ When running `pnpm dev`, you start a Next.js developer server on `http://localho
|
|||||||
|
|
||||||
If you think your custom provider might be useful to others, we encourage you to open a PR and add it to the built-in list so others can discover it much more easily! You only need to add two changes:
|
If you think your custom provider might be useful to others, we encourage you to open a PR and add it to the built-in list so others can discover it much more easily! You only need to add two changes:
|
||||||
|
|
||||||
1. Add your config: [`src/providers/{provider}.js`](https://github.com/nextauthjs/next-auth/tree/main/packages/next-auth/src/providers) (Make sure you use a named default export, like `export default function YourProvider`!)
|
1. Add your config: [`src/providers/{provider}.js`](https://github.com/nextauthjs/next-auth/tree/main/src/providers) (Make sure you use a named default export, like `export default function YourProvider`!)
|
||||||
2. Add provider documentation: [`www/docs/providers/{provider}.md`](https://github.com/nextauthjs/next-auth/tree/main/www/docs/providers)
|
2. Add provider documentation: [`www/docs/providers/{provider}.md`](https://github.com/nextauthjs/next-auth/tree/main/www/docs/providers)
|
||||||
3. Add provider logo svgs, like `google-dark.svg` (dark mode) and `google.svg` (light mode) to the `/packages/next-auth/provider-logos/` directory. Don't forget to set the provider's styling options in the `provider.style` config object.
|
|
||||||
|
|
||||||
That's it! 🎉 Others will be able to discover this provider much more easily now!
|
That's it! 🎉 Others will be able to discover this provider much more easily now!
|
||||||
|
|
||||||
@@ -99,13 +88,13 @@ If you would like to contribute to an existing database adapter or help create a
|
|||||||
|
|
||||||
#### Testing
|
#### Testing
|
||||||
|
|
||||||
Tests can be run with `pnpm test`.
|
Tests can be run with `yarn test`.
|
||||||
|
|
||||||
Automated tests are currently crude and limited in functionality, but improvements are in development.
|
Automated tests are currently crude and limited in functionality, but improvements are in development.
|
||||||
|
|
||||||
## For maintainers
|
## For maintainers
|
||||||
|
|
||||||
We use [a custom script](https://github.com/nextauthjs/next-auth/blob/main/scripts/release/index.ts) together with [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0) to automate releases. This makes the maintenance process easier and less error-prone. Please study the "Conventional Commits" site to understand how to write a good commit message.
|
We use [a custom script](https://github.com/nextauthjs/next-auth/tree/main/scripts/index.ts) together with [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0) to automate releases. This makes the maintenance process easier and less error-prone. Please study the "Conventional Commits" site to understand how to write a good commit message.
|
||||||
|
|
||||||
When accepting Pull Requests, make sure the following:
|
When accepting Pull Requests, make sure the following:
|
||||||
|
|
||||||
@@ -114,9 +103,9 @@ When accepting Pull Requests, make sure the following:
|
|||||||
- Rewrite the commit message to conform to the `Conventional Commits` style.
|
- Rewrite the commit message to conform to the `Conventional Commits` style.
|
||||||
- Using `fix` releases a patch (x.x.1)
|
- Using `fix` releases a patch (x.x.1)
|
||||||
- Using `feat` releases a minor (x.1.x)
|
- Using `feat` releases a minor (x.1.x)
|
||||||
- Using `feat` when `BREAKING CHANGE` is present in the commit message releases a major (1.x.x)
|
- Using `feat` when `BREAKING CHANGE` is present in the commit messgae releases a major (1.x.x)
|
||||||
- Optionally link issues the PR will resolve (You can add "close" in front of the issue numbers to close the issues automatically, when the PR is merged. `semantic-release` will also comment back to connected issues and PRs, notifying the users that a feature is added/bug fixed, etc.)
|
- Optionally link issues the PR will resolve (You can add "close" in front of the issue numbers to close the issues automatically, when the PR is merged. `semantic-release` will also comment back to connected issues and PRs, notifying the users that a feature is added/bug fixed, etc.)
|
||||||
|
|
||||||
### Skipping a release
|
### Skipping a release
|
||||||
|
|
||||||
If a commit contains `[skip release]` in their message, it will be excluded from the commit analysis and won't participate in the release type determination. This is useful, if the PR being merged should not trigger a new `npm` release.
|
If a commit contains `[skip release]` in their message will be excluded from the commit analysis and won't participate in the release type determination. This is useful, if the PR being merged should not trigger a new `npm` release.
|
||||||
@@ -13,9 +13,9 @@ If you contact us regarding a serious issue:
|
|||||||
- We will disclose the issue (and credit you, with your consent) once a fix to resolve the issue has been released.
|
- We will disclose the issue (and credit you, with your consent) once a fix to resolve the issue has been released.
|
||||||
- If 90 days has elapsed and we still don't have a fix, we will disclose the issue publicly.
|
- If 90 days has elapsed and we still don't have a fix, we will disclose the issue publicly.
|
||||||
|
|
||||||
The best way to report an issue is by contacting us via email at hi@thvu.dev, info@balazsorban.com, yo@ndo.dev and me@iaincollins.com, or raise a public issue requesting someone get in touch with you via whatever means you prefer for more details. (Please do not disclose sensitive details publicly at this stage.)
|
The best way to report an issue is by contacting us via email at info@balazsorban.com or me@iaincollins.com and yo@ndo.dev, or raise a public issue requesting someone get in touch with you via whatever means you prefer for more details. (Please do not disclose sensitive details publicly at this stage.)
|
||||||
|
|
||||||
> For less serious issues (e.g. RFC compliance for unsupported flows or potential issues that may cause a problem in the future) it is appropriate to submit these publicly as bug reports or feature requests or to raise a question to open a discussion around them.
|
> For less serious issues (e.g. RFC compliance for unsupported flows or potential issues that may cause a problem in the future) it is appropriate to submit these these publically as bug reports or feature requests or to raise a question to open a discussion around them.
|
||||||
|
|
||||||
## Supported Versions
|
## Supported Versions
|
||||||
|
|
||||||
|
|||||||
@@ -47,12 +47,6 @@ EMAIL_FROM=user@gmail.com
|
|||||||
# MongoDB: DATABASE_URL=mongodb://nextauth:password@127.0.0.1:27017/nextauth?synchronize=true
|
# MongoDB: DATABASE_URL=mongodb://nextauth:password@127.0.0.1:27017/nextauth?synchronize=true
|
||||||
DATABASE_URL=
|
DATABASE_URL=
|
||||||
|
|
||||||
WIKIMEDIA_ID=
|
BOXYHQSAML_ISSUER="https://jackson-demo.boxyhq.com"
|
||||||
WIKIMEDIA_SECRET=
|
BOXYHQSAML_ID="tenant=boxyhq.com&product=saml-demo.boxyhq.com"
|
||||||
|
BOXYHQSAML_SECRET="dummy"
|
||||||
# Supabase Example Configuration
|
|
||||||
# Supabase Example Configuration
|
|
||||||
# NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321
|
|
||||||
# SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSJ9.vI9obAHOGyVVKa3pD--kJlyxp-Z2zV9UUMAhKpNLAcU
|
|
||||||
# SUPABASE_JWT_SECRET=super-secret-jwt-token-with-at-least-32-characters-long
|
|
||||||
# NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24ifQ.625_WdcF3KHqz5amU0x2X5WWHP-OEs_4qj0ssLNHzTs
|
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
export default function RootLayout({
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
children: React.ReactNode
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<html>
|
|
||||||
<head></head>
|
|
||||||
<body>{children}</body>
|
|
||||||
</html>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import { unstable_getServerSession } from "next-auth/next"
|
|
||||||
|
|
||||||
export default async function Page() {
|
|
||||||
const session = await unstable_getServerSession()
|
|
||||||
return <pre>{JSON.stringify(session, null, 2)}</pre>
|
|
||||||
}
|
|
||||||
@@ -17,7 +17,9 @@ export default function Footer() {
|
|||||||
<a href="https://github.com/nextauthjs/next-auth-example">GitHub</a>
|
<a href="https://github.com/nextauthjs/next-auth-example">GitHub</a>
|
||||||
</li>
|
</li>
|
||||||
<li className={styles.navItem}>
|
<li className={styles.navItem}>
|
||||||
<Link href="/policy">Policy</Link>
|
<Link href="/policy">
|
||||||
|
<a>Policy</a>
|
||||||
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className={styles.navItem}>
|
<li className={styles.navItem}>
|
||||||
<em>{packageJSON.version}</em>
|
<em>{packageJSON.version}</em>
|
||||||
|
|||||||
@@ -64,37 +64,49 @@ export default function Header() {
|
|||||||
<nav>
|
<nav>
|
||||||
<ul className={styles.navItems}>
|
<ul className={styles.navItems}>
|
||||||
<li className={styles.navItem}>
|
<li className={styles.navItem}>
|
||||||
<Link href="/">Home</Link>
|
<Link href="/">
|
||||||
|
<a>Home</a>
|
||||||
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className={styles.navItem}>
|
<li className={styles.navItem}>
|
||||||
<Link href="/client">Client</Link>
|
<Link href="/client">
|
||||||
|
<a>Client</a>
|
||||||
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className={styles.navItem}>
|
<li className={styles.navItem}>
|
||||||
<Link href="/server">Server</Link>
|
<Link href="/server">
|
||||||
|
<a>Server</a>
|
||||||
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className={styles.navItem}>
|
<li className={styles.navItem}>
|
||||||
<Link href="/protected">Protected</Link>
|
<Link href="/protected">
|
||||||
|
<a>Protected</a>
|
||||||
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className={styles.navItem}>
|
<li className={styles.navItem}>
|
||||||
<Link href="/protected-ssr">Protected(SSR)</Link>
|
<Link href="/protected-ssr">
|
||||||
|
<a>Protected(SSR)</a>
|
||||||
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className={styles.navItem}>
|
<li className={styles.navItem}>
|
||||||
<Link href="/api-example">API</Link>
|
<Link href="/api-example">
|
||||||
|
<a>API</a>
|
||||||
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className={styles.navItem}>
|
<li className={styles.navItem}>
|
||||||
<Link href="/credentials">Credentials</Link>
|
<Link href="/credentials">
|
||||||
|
<a>Credentials</a>
|
||||||
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className={styles.navItem}>
|
<li className={styles.navItem}>
|
||||||
<Link href="/email">Email</Link>
|
<Link href="/email">
|
||||||
|
<a>Email</a>
|
||||||
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className={styles.navItem}>
|
<li className={styles.navItem}>
|
||||||
<Link href="/middleware-protected">Middleware protected</Link>
|
<Link href="/middleware-protected">
|
||||||
</li>
|
<a>Middleware protected</a>
|
||||||
<li className={styles.navItem}>
|
</Link>
|
||||||
<Link href="/supabase-client-rls">Supabase RLS</Link>
|
|
||||||
</li>
|
|
||||||
<li className={styles.navItem}>
|
|
||||||
<Link href="/supabase-ssr">Supabase RLS(SSR)</Link>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ module.exports = {
|
|||||||
config.experiments = { ...config.experiments, topLevelAwait: true }
|
config.experiments = { ...config.experiments, topLevelAwait: true }
|
||||||
return config
|
return config
|
||||||
},
|
},
|
||||||
experimental: { appDir: true },
|
|
||||||
typescript: { ignoreBuildErrors: true },
|
typescript: { ignoreBuildErrors: true },
|
||||||
|
experimental: { externalDir: true },
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,10 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"clean": "rm -rf .next",
|
"clean": "rm -rf .next",
|
||||||
"dev": "next dev",
|
"copy:css": "cpx \"../../packages/next-auth/css/**/*\" src/css --watch",
|
||||||
"lint": "next lint",
|
"watch:css": "cd ../../packages/next-auth && pnpm watch:css",
|
||||||
|
"dev": "concurrently \"pnpm dev:next\" \"pnpm watch:css\" \"pnpm copy:css\"",
|
||||||
|
"dev:next": "next dev",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"email": "fake-smtp-server",
|
"email": "fake-smtp-server",
|
||||||
@@ -14,28 +16,21 @@
|
|||||||
},
|
},
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@next-auth/fauna-adapter": "workspace:*",
|
"@next-auth/fauna-adapter": "^1.0.1",
|
||||||
"@next-auth/prisma-adapter": "workspace:*",
|
"@next-auth/prisma-adapter": "^1.0.1",
|
||||||
"@next-auth/supabase-adapter": "workspace:*",
|
"@prisma/client": "^3.10.0",
|
||||||
"@next-auth/typeorm-legacy-adapter": "workspace:*",
|
"cpx": "^1.5.0",
|
||||||
"@prisma/client": "^3",
|
"fake-smtp-server": "^0.8.0",
|
||||||
"@supabase/supabase-js": "^2.0.5",
|
"faunadb": "^4.4.1",
|
||||||
"faunadb": "^4",
|
"next": "^12.1.0",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"nodemailer": "^6.7.2",
|
||||||
"next": "13.0.2",
|
"react": "^17.0.2",
|
||||||
"next-auth": "workspace:*",
|
"react-dom": "^17.0.2"
|
||||||
"nodemailer": "^6",
|
|
||||||
"react": "^18",
|
|
||||||
"react-dom": "^18"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jsonwebtoken": "^8.5.5",
|
"@types/react": "^17.0.37",
|
||||||
"@types/react": "^18.0.15",
|
"@types/react-dom": "^17.0.11",
|
||||||
"@types/react-dom": "^18.0.6",
|
"concurrently": "^7.1.0",
|
||||||
"fake-smtp-server": "^0.8.0",
|
"prisma": "^3.10.0"
|
||||||
"pg": "^8.7.3",
|
|
||||||
"prisma": "^3",
|
|
||||||
"sqlite3": "^5.0.8",
|
|
||||||
"typeorm": "0.3.7"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,159 +1,218 @@
|
|||||||
import NextAuth from "next-auth"
|
import NextAuth, { NextAuthOptions } from "next-auth"
|
||||||
import type { NextAuthOptions } from "next-auth"
|
// import EmailProvider from "next-auth/providers/email"
|
||||||
import jwt from "jsonwebtoken"
|
import GitHubProvider from "next-auth/providers/github"
|
||||||
|
import Auth0Provider from "next-auth/providers/auth0"
|
||||||
// Providers
|
import KeycloakProvider from "next-auth/providers/keycloak"
|
||||||
import Apple from "next-auth/providers/apple"
|
import TwitterProvider, {
|
||||||
import Auth0 from "next-auth/providers/auth0"
|
TwitterLegacy as TwitterLegacyProvider,
|
||||||
import AzureAD from "next-auth/providers/azure-ad"
|
} from "next-auth/providers/twitter"
|
||||||
import AzureB2C from "next-auth/providers/azure-ad-b2c"
|
import CredentialsProvider from "next-auth/providers/credentials"
|
||||||
import BoxyHQSAML from "next-auth/providers/boxyhq-saml"
|
import IDS4Provider from "next-auth/providers/identity-server4"
|
||||||
import Cognito from "next-auth/providers/cognito"
|
|
||||||
import Credentials from "next-auth/providers/credentials"
|
|
||||||
import Discord from "next-auth/providers/discord"
|
|
||||||
import DuendeIDS6 from "next-auth/providers/duende-identity-server6"
|
|
||||||
import Email from "next-auth/providers/email"
|
|
||||||
import Facebook from "next-auth/providers/facebook"
|
|
||||||
import Foursquare from "next-auth/providers/foursquare"
|
|
||||||
import Freshbooks from "next-auth/providers/freshbooks"
|
|
||||||
import GitHub from "next-auth/providers/github"
|
|
||||||
import Gitlab from "next-auth/providers/gitlab"
|
|
||||||
import Google from "next-auth/providers/google"
|
|
||||||
import IDS4 from "next-auth/providers/identity-server4"
|
|
||||||
import Instagram from "next-auth/providers/instagram"
|
|
||||||
import Keycloak from "next-auth/providers/keycloak"
|
|
||||||
import Line from "next-auth/providers/line"
|
|
||||||
import LinkedIn from "next-auth/providers/linkedin"
|
|
||||||
import Mailchimp from "next-auth/providers/mailchimp"
|
|
||||||
import Okta from "next-auth/providers/okta"
|
|
||||||
import Osu from "next-auth/providers/osu"
|
|
||||||
import Patreon from "next-auth/providers/patreon"
|
|
||||||
import Slack from "next-auth/providers/slack"
|
|
||||||
import Spotify from "next-auth/providers/spotify"
|
|
||||||
import Trakt from "next-auth/providers/trakt"
|
|
||||||
import Twitch from "next-auth/providers/twitch"
|
import Twitch from "next-auth/providers/twitch"
|
||||||
import Twitter, { TwitterLegacy } from "next-auth/providers/twitter"
|
import GoogleProvider from "next-auth/providers/google"
|
||||||
import Vk from "next-auth/providers/vk"
|
import FacebookProvider from "next-auth/providers/facebook"
|
||||||
import Wikimedia from "next-auth/providers/wikimedia"
|
import FoursquareProvider from "next-auth/providers/foursquare"
|
||||||
import WorkOS from "next-auth/providers/workos"
|
// import FreshbooksProvider from "next-auth/providers/freshbooks"
|
||||||
|
import GitlabProvider from "next-auth/providers/gitlab"
|
||||||
|
import InstagramProvider from "next-auth/providers/instagram"
|
||||||
|
import LineProvider from "next-auth/providers/line"
|
||||||
|
import LinkedInProvider from "next-auth/providers/linkedin"
|
||||||
|
import MailchimpProvider from "next-auth/providers/mailchimp"
|
||||||
|
import DiscordProvider from "next-auth/providers/discord"
|
||||||
|
import AzureADProvider from "next-auth/providers/azure-ad"
|
||||||
|
import SpotifyProvider from "next-auth/providers/spotify"
|
||||||
|
import CognitoProvider from "next-auth/providers/cognito"
|
||||||
|
import SlackProvider from "next-auth/providers/slack"
|
||||||
|
import Okta from "next-auth/providers/okta"
|
||||||
|
import AzureB2C from "next-auth/providers/azure-ad-b2c"
|
||||||
|
import OsuProvider from "next-auth/providers/osu"
|
||||||
|
import AppleProvider from "next-auth/providers/apple"
|
||||||
|
import PatreonProvider from "next-auth/providers/patreon"
|
||||||
|
import TraktProvider from "next-auth/providers/trakt"
|
||||||
|
import WorkOSProvider from "next-auth/providers/workos"
|
||||||
|
import BoxyHQSAMLProvider from "next-auth/providers/boxyhq-saml"
|
||||||
|
|
||||||
// Adapters
|
// import { PrismaAdapter } from "@next-auth/prisma-adapter"
|
||||||
import { PrismaClient } from "@prisma/client"
|
// import { PrismaClient } from "@prisma/client"
|
||||||
import { PrismaAdapter } from "@next-auth/prisma-adapter"
|
// const prisma = new PrismaClient()
|
||||||
import { Client as FaunaClient } from "faunadb"
|
// const adapter = PrismaAdapter(prisma)
|
||||||
import { FaunaAdapter } from "@next-auth/fauna-adapter"
|
|
||||||
import { TypeORMLegacyAdapter } from "@next-auth/typeorm-legacy-adapter"
|
|
||||||
import { SupabaseAdapter } from "@next-auth/supabase-adapter"
|
|
||||||
|
|
||||||
// Add an adapter you want to test here.
|
// import { Client as FaunaClient } from "faunadb"
|
||||||
const adapters = {
|
// import { FaunaAdapter } from "@next-auth/fauna-adapter"
|
||||||
prisma() {
|
|
||||||
const client = globalThis.prisma || new PrismaClient()
|
|
||||||
if (process.env.NODE_ENV !== "production") globalThis.prisma = client
|
|
||||||
return PrismaAdapter(client)
|
|
||||||
},
|
|
||||||
typeorm() {
|
|
||||||
return TypeORMLegacyAdapter({
|
|
||||||
type: "sqlite",
|
|
||||||
name: "next-auth-test-memory",
|
|
||||||
database: "./typeorm/dev.db",
|
|
||||||
synchronize: true,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
fauna() {
|
|
||||||
const client =
|
|
||||||
globalThis.fauna ||
|
|
||||||
new FaunaClient({
|
|
||||||
secret: process.env.FAUNA_SECRET,
|
|
||||||
domain: process.env.FAUNA_DOMAIN,
|
|
||||||
})
|
|
||||||
if (process.env.NODE_ENV !== "production") global.fauna = client
|
|
||||||
return FaunaAdapter(client)
|
|
||||||
},
|
|
||||||
supabase() {
|
|
||||||
return SupabaseAdapter({
|
|
||||||
url: process.env.NEXT_PUBLIC_SUPABASE_URL,
|
|
||||||
secret: process.env.SUPABASE_SERVICE_ROLE_KEY,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
noop() {
|
|
||||||
return undefined
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// const client = new FaunaClient({
|
||||||
|
// secret: process.env.FAUNA_SECRET,
|
||||||
|
// domain: process.env.FAUNA_DOMAIN,
|
||||||
|
// })
|
||||||
|
// const adapter = FaunaAdapter(client)
|
||||||
export const authOptions: NextAuthOptions = {
|
export const authOptions: NextAuthOptions = {
|
||||||
adapter: adapters.noop(),
|
// adapter,
|
||||||
callbacks: {
|
providers: [
|
||||||
async session({ session, user }) {
|
// E-mail
|
||||||
// NOTE: this is needed when using Supabase with RLS. Otherwise this callback can be removed.
|
// Start fake e-mail server with `npm run start:email`
|
||||||
const signingSecret = process.env.SUPABASE_JWT_SECRET
|
// EmailProvider({
|
||||||
if (signingSecret) {
|
// server: {
|
||||||
const payload = {
|
// host: "127.0.0.1",
|
||||||
aud: "authenticated",
|
// auth: null,
|
||||||
exp: Math.floor(new Date(session.expires).getTime() / 1000),
|
// secure: false,
|
||||||
sub: user.id,
|
// port: 1025,
|
||||||
email: user.email,
|
// tls: { rejectUnauthorized: false },
|
||||||
role: "authenticated",
|
// },
|
||||||
}
|
// }),
|
||||||
session.supabaseAccessToken = jwt.sign(payload, signingSecret)
|
// Credentials
|
||||||
}
|
CredentialsProvider({
|
||||||
return session
|
name: "Credentials",
|
||||||
|
credentials: {
|
||||||
|
password: { label: "Password", type: "password" },
|
||||||
},
|
},
|
||||||
|
async authorize(credentials) {
|
||||||
|
if (credentials.password === "pw") {
|
||||||
|
return {
|
||||||
|
name: "Fill Murray",
|
||||||
|
email: "bill@fillmurray.com",
|
||||||
|
image: "https://www.fillmurray.com/64/64",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
},
|
},
|
||||||
debug: process.env.NODE_ENV !== "production",
|
}),
|
||||||
|
// OAuth 1
|
||||||
|
// TwitterLegacyProvider({
|
||||||
|
// clientId: process.env.TWITTER_LEGACY_ID,
|
||||||
|
// clientSecret: process.env.TWITTER_LEGACY_SECRET,
|
||||||
|
// }),
|
||||||
|
// OAuth 2 / OIDC
|
||||||
|
TwitterProvider({
|
||||||
|
// Opt-in to the new Twitter API for now. Should be default in the future.
|
||||||
|
version: "2.0",
|
||||||
|
clientId: process.env.TWITTER_ID,
|
||||||
|
clientSecret: process.env.TWITTER_SECRET,
|
||||||
|
}),
|
||||||
|
GitHubProvider({
|
||||||
|
clientId: process.env.GITHUB_ID,
|
||||||
|
clientSecret: process.env.GITHUB_SECRET,
|
||||||
|
}),
|
||||||
|
Auth0Provider({
|
||||||
|
clientId: process.env.AUTH0_ID,
|
||||||
|
clientSecret: process.env.AUTH0_SECRET,
|
||||||
|
issuer: process.env.AUTH0_ISSUER,
|
||||||
|
}),
|
||||||
|
KeycloakProvider({
|
||||||
|
clientId: process.env.KEYCLOAK_ID,
|
||||||
|
clientSecret: process.env.KEYCLOAK_SECRET,
|
||||||
|
issuer: process.env.KEYCLOAK_ISSUER,
|
||||||
|
}),
|
||||||
|
Twitch({
|
||||||
|
clientId: process.env.TWITCH_ID,
|
||||||
|
clientSecret: process.env.TWITCH_SECRET,
|
||||||
|
}),
|
||||||
|
GoogleProvider({
|
||||||
|
clientId: process.env.GOOGLE_ID,
|
||||||
|
clientSecret: process.env.GOOGLE_SECRET,
|
||||||
|
}),
|
||||||
|
FacebookProvider({
|
||||||
|
clientId: process.env.FACEBOOK_ID,
|
||||||
|
clientSecret: process.env.FACEBOOK_SECRET,
|
||||||
|
}),
|
||||||
|
FoursquareProvider({
|
||||||
|
clientId: process.env.FOURSQUARE_ID,
|
||||||
|
clientSecret: process.env.FOURSQUARE_SECRET,
|
||||||
|
}),
|
||||||
|
// FreshbooksProvider({
|
||||||
|
// clientId: process.env.FRESHBOOKS_ID,
|
||||||
|
// clientSecret: process.env.FRESHBOOKS_SECRET,
|
||||||
|
// }),
|
||||||
|
GitlabProvider({
|
||||||
|
clientId: process.env.GITLAB_ID,
|
||||||
|
clientSecret: process.env.GITLAB_SECRET,
|
||||||
|
}),
|
||||||
|
InstagramProvider({
|
||||||
|
clientId: process.env.INSTAGRAM_ID,
|
||||||
|
clientSecret: process.env.INSTAGRAM_SECRET,
|
||||||
|
}),
|
||||||
|
LineProvider({
|
||||||
|
clientId: process.env.LINE_ID,
|
||||||
|
clientSecret: process.env.LINE_SECRET,
|
||||||
|
}),
|
||||||
|
LinkedInProvider({
|
||||||
|
clientId: process.env.LINKEDIN_ID,
|
||||||
|
clientSecret: process.env.LINKEDIN_SECRET,
|
||||||
|
}),
|
||||||
|
MailchimpProvider({
|
||||||
|
clientId: process.env.MAILCHIMP_ID,
|
||||||
|
clientSecret: process.env.MAILCHIMP_SECRET,
|
||||||
|
}),
|
||||||
|
IDS4Provider({
|
||||||
|
clientId: process.env.IDS4_ID,
|
||||||
|
clientSecret: process.env.IDS4_SECRET,
|
||||||
|
issuer: process.env.IDS4_ISSUER,
|
||||||
|
}),
|
||||||
|
DiscordProvider({
|
||||||
|
clientId: process.env.DISCORD_ID,
|
||||||
|
clientSecret: process.env.DISCORD_SECRET,
|
||||||
|
}),
|
||||||
|
AzureADProvider({
|
||||||
|
clientId: process.env.AZURE_AD_CLIENT_ID,
|
||||||
|
clientSecret: process.env.AZURE_AD_CLIENT_SECRET,
|
||||||
|
tenantId: process.env.AZURE_AD_TENANT_ID,
|
||||||
|
profilePhotoSize: 48,
|
||||||
|
}),
|
||||||
|
SpotifyProvider({
|
||||||
|
clientId: process.env.SPOTIFY_ID,
|
||||||
|
clientSecret: process.env.SPOTIFY_SECRET,
|
||||||
|
}),
|
||||||
|
CognitoProvider({
|
||||||
|
clientId: process.env.COGNITO_ID,
|
||||||
|
clientSecret: process.env.COGNITO_SECRET,
|
||||||
|
issuer: process.env.COGNITO_ISSUER,
|
||||||
|
}),
|
||||||
|
Okta({
|
||||||
|
clientId: process.env.OKTA_ID,
|
||||||
|
clientSecret: process.env.OKTA_SECRET,
|
||||||
|
issuer: process.env.OKTA_ISSUER,
|
||||||
|
}),
|
||||||
|
SlackProvider({
|
||||||
|
clientId: process.env.SLACK_ID,
|
||||||
|
clientSecret: process.env.SLACK_SECRET,
|
||||||
|
}),
|
||||||
|
AzureB2C({
|
||||||
|
clientId: process.env.AZURE_B2C_ID,
|
||||||
|
clientSecret: process.env.AZURE_B2C_SECRET,
|
||||||
|
tenantId: process.env.AZURE_B2C_TENANT_ID,
|
||||||
|
primaryUserFlow: process.env.AZURE_B2C_PRIMARY_USER_FLOW,
|
||||||
|
}),
|
||||||
|
OsuProvider({
|
||||||
|
clientId: process.env.OSU_CLIENT_ID,
|
||||||
|
clientSecret: process.env.OSU_CLIENT_SECRET,
|
||||||
|
}),
|
||||||
|
AppleProvider({
|
||||||
|
clientId: process.env.APPLE_ID,
|
||||||
|
clientSecret: process.env.APPLE_SECRET,
|
||||||
|
}),
|
||||||
|
PatreonProvider({
|
||||||
|
clientId: process.env.PATREON_ID,
|
||||||
|
clientSecret: process.env.PATREON_SECRET,
|
||||||
|
}),
|
||||||
|
TraktProvider({
|
||||||
|
clientId: process.env.TRAKT_ID,
|
||||||
|
clientSecret: process.env.TRAKT_SECRET,
|
||||||
|
}),
|
||||||
|
WorkOSProvider({
|
||||||
|
clientId: process.env.WORKOS_ID,
|
||||||
|
clientSecret: process.env.WORKOS_SECRET,
|
||||||
|
}),
|
||||||
|
BoxyHQSAMLProvider({
|
||||||
|
issuer: process.env.BOXYHQSAML_ISSUER,
|
||||||
|
clientId: process.env.BOXYHQSAML_ID,
|
||||||
|
clientSecret: process.env.BOXYHQSAML_SECRET,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
debug: true,
|
||||||
theme: {
|
theme: {
|
||||||
|
colorScheme: "auto",
|
||||||
logo: "https://next-auth.js.org/img/logo/logo-sm.png",
|
logo: "https://next-auth.js.org/img/logo/logo-sm.png",
|
||||||
brandColor: "#1786fb",
|
brandColor: "#1786fb",
|
||||||
},
|
},
|
||||||
providers: [
|
|
||||||
Credentials({
|
|
||||||
credentials: { password: { label: "Password", type: "password" } },
|
|
||||||
async authorize(credentials) {
|
|
||||||
if (credentials.password !== "pw") return null
|
|
||||||
return { name: "Fill Murray", email: "bill@fillmurray.com", image: "https://www.fillmurray.com/64/64" }
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
Apple({ clientId: process.env.APPLE_ID, clientSecret: process.env.APPLE_SECRET }),
|
|
||||||
Auth0({ clientId: process.env.AUTH0_ID, clientSecret: process.env.AUTH0_SECRET, issuer: process.env.AUTH0_ISSUER }),
|
|
||||||
AzureAD({ clientId: process.env.AZURE_AD_CLIENT_ID, clientSecret: process.env.AZURE_AD_CLIENT_SECRET, tenantId: process.env.AZURE_AD_TENANT_ID }),
|
|
||||||
AzureB2C({ clientId: process.env.AZURE_B2C_ID, clientSecret: process.env.AZURE_B2C_SECRET, issuer: process.env.AZURE_B2C_ISSUER }),
|
|
||||||
BoxyHQSAML({ issuer: "https://jackson-demo.boxyhq.com", clientId: "tenant=boxyhq.com&product=saml-demo.boxyhq.com", clientSecret: "dummy" }),
|
|
||||||
Cognito({ clientId: process.env.COGNITO_ID, clientSecret: process.env.COGNITO_SECRET, issuer: process.env.COGNITO_ISSUER }),
|
|
||||||
Discord({ clientId: process.env.DISCORD_ID, clientSecret: process.env.DISCORD_SECRET }),
|
|
||||||
DuendeIDS6({ clientId: "interactive.confidential", clientSecret: "secret", issuer: "https://demo.duendesoftware.com" }),
|
|
||||||
Facebook({ clientId: process.env.FACEBOOK_ID, clientSecret: process.env.FACEBOOK_SECRET }),
|
|
||||||
Foursquare({ clientId: process.env.FOURSQUARE_ID, clientSecret: process.env.FOURSQUARE_SECRET }),
|
|
||||||
Freshbooks({ clientId: process.env.FRESHBOOKS_ID, clientSecret: process.env.FRESHBOOKS_SECRET }),
|
|
||||||
GitHub({ clientId: process.env.GITHUB_ID, clientSecret: process.env.GITHUB_SECRET }),
|
|
||||||
Gitlab({ clientId: process.env.GITLAB_ID, clientSecret: process.env.GITLAB_SECRET }),
|
|
||||||
Google({ clientId: process.env.GOOGLE_ID, clientSecret: process.env.GOOGLE_SECRET }),
|
|
||||||
IDS4({ clientId: process.env.IDS4_ID, clientSecret: process.env.IDS4_SECRET, issuer: process.env.IDS4_ISSUER }),
|
|
||||||
Instagram({ clientId: process.env.INSTAGRAM_ID, clientSecret: process.env.INSTAGRAM_SECRET }),
|
|
||||||
Keycloak({ clientId: process.env.KEYCLOAK_ID, clientSecret: process.env.KEYCLOAK_SECRET, issuer: process.env.KEYCLOAK_ISSUER }),
|
|
||||||
Line({ clientId: process.env.LINE_ID, clientSecret: process.env.LINE_SECRET }),
|
|
||||||
LinkedIn({ clientId: process.env.LINKEDIN_ID, clientSecret: process.env.LINKEDIN_SECRET }),
|
|
||||||
Mailchimp({ clientId: process.env.MAILCHIMP_ID, clientSecret: process.env.MAILCHIMP_SECRET }),
|
|
||||||
Okta({ clientId: process.env.OKTA_ID, clientSecret: process.env.OKTA_SECRET, issuer: process.env.OKTA_ISSUER }),
|
|
||||||
Osu({ clientId: process.env.OSU_CLIENT_ID, clientSecret: process.env.OSU_CLIENT_SECRET }),
|
|
||||||
Patreon({ clientId: process.env.PATREON_ID, clientSecret: process.env.PATREON_SECRET }),
|
|
||||||
Slack({ clientId: process.env.SLACK_ID, clientSecret: process.env.SLACK_SECRET }),
|
|
||||||
Spotify({ clientId: process.env.SPOTIFY_ID, clientSecret: process.env.SPOTIFY_SECRET }),
|
|
||||||
Trakt({ clientId: process.env.TRAKT_ID, clientSecret: process.env.TRAKT_SECRET }),
|
|
||||||
Twitch({ clientId: process.env.TWITCH_ID, clientSecret: process.env.TWITCH_SECRET }),
|
|
||||||
Twitter({ version: "2.0", clientId: process.env.TWITTER_ID, clientSecret: process.env.TWITTER_SECRET }),
|
|
||||||
TwitterLegacy({ clientId: process.env.TWITTER_LEGACY_ID, clientSecret: process.env.TWITTER_LEGACY_SECRET }),
|
|
||||||
Vk({ clientId: process.env.VK_ID, clientSecret: process.env.VK_SECRET }),
|
|
||||||
Wikimedia({ clientId: process.env.WIKIMEDIA_ID, clientSecret: process.env.WIKIMEDIA_SECRET }),
|
|
||||||
WorkOS({ clientId: process.env.WORKOS_ID, clientSecret: process.env.WORKOS_SECRET }),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
if (authOptions.adapter) {
|
|
||||||
authOptions.providers.unshift(
|
|
||||||
// NOTE: You can start a fake e-mail server with `pnpm email`
|
|
||||||
// and then go to `http://localhost:1080` in the browser
|
|
||||||
Email({ server: "smtp://127.0.0.1:1025?tls.rejectUnauthorized=false" })
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NextAuth(authOptions)
|
export default NextAuth(authOptions)
|
||||||
|
|||||||
@@ -2,6 +2,6 @@
|
|||||||
import { getToken } from "next-auth/jwt"
|
import { getToken } from "next-auth/jwt"
|
||||||
|
|
||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
const token = await getToken({ req })
|
const token = await getToken({ req, secret: process.env.SECRET })
|
||||||
res.send(JSON.stringify(token, null, 2))
|
res.send(JSON.stringify(token, null, 2))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
// This is an example of to protect an API route
|
// This is an example of to protect an API route
|
||||||
import { unstable_getServerSession } from "next-auth/next"
|
import { getSession } from "next-auth/react"
|
||||||
import { authOptions } from "../auth/[...nextauth]"
|
|
||||||
|
|
||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
const session = await unstable_getServerSession(req, res, authOptions)
|
const session = await getSession({ req })
|
||||||
|
|
||||||
if (session) {
|
if (session) {
|
||||||
res.send({
|
res.send({
|
||||||
content:
|
content:
|
||||||
"This is protected content. You can access this content because you are signed in.",
|
"This is protected content. You can access this content because you are signed in.",
|
||||||
session,
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
res.send({
|
res.send({
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// This is an example of how to access a session from an API route
|
// This is an example of how to access a session from an API route
|
||||||
import { unstable_getServerSession } from "next-auth/next"
|
import { getSession } from "next-auth/react"
|
||||||
import { authOptions } from "../auth/[...nextauth]"
|
|
||||||
|
|
||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
const session = await unstable_getServerSession(req, res, authOptions)
|
const session = await getSession({ req })
|
||||||
res.json(session)
|
res.send(JSON.stringify(session, null, 2))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
// This is an example of how to query data from Supabase with RLS.
|
|
||||||
// Learn more about Row Levele Security (RLS): https://supabase.com/docs/guides/auth/row-level-security
|
|
||||||
import { unstable_getServerSession } from "next-auth/next"
|
|
||||||
import { authOptions } from "../auth/[...nextauth]"
|
|
||||||
import { createClient } from "@supabase/supabase-js"
|
|
||||||
|
|
||||||
export default async (req, res) => {
|
|
||||||
const session = await unstable_getServerSession(req, res, authOptions)
|
|
||||||
|
|
||||||
if (!session)
|
|
||||||
return res.send(JSON.stringify({ error: "No session!" }, null, 2))
|
|
||||||
|
|
||||||
const { supabaseAccessToken } = session
|
|
||||||
|
|
||||||
const supabase = createClient(
|
|
||||||
process.env.NEXT_PUBLIC_SUPABASE_URL,
|
|
||||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
|
|
||||||
{
|
|
||||||
global: {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${supabaseAccessToken}`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
// Now you can query with RLS enabled.
|
|
||||||
const { data, error } = await supabase.from("users").select("*")
|
|
||||||
|
|
||||||
res.send(JSON.stringify({ supabaseAccessToken, data, error }, null, 2))
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
export { default } from "next-auth/middleware"
|
export { default } from "next-auth/middleware"
|
||||||
|
|
||||||
export const config = { matcher: ["/middleware-protected"] }
|
|
||||||
|
|
||||||
// Other ways to use this middleware
|
// Other ways to use this middleware
|
||||||
|
|
||||||
// import withAuth from "next-auth/middleware"
|
// import withAuth from "next-auth/middleware"
|
||||||
@@ -30,11 +28,12 @@ export const config = { matcher: ["/middleware-protected"] }
|
|||||||
// export default withAuth(
|
// export default withAuth(
|
||||||
// function middleware(req, ev) {
|
// function middleware(req, ev) {
|
||||||
// console.log(req, ev)
|
// console.log(req, ev)
|
||||||
|
// return undefined // NOTE: `NextMiddleware` should allow returning `void`
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
// callbacks: {
|
// callbacks: {
|
||||||
// authorized: ({ token }) => token.name === "Balázs Orbán",
|
// authorized: ({ token }) => token.name === "Balázs Orbán",
|
||||||
// },
|
// }
|
||||||
// }
|
// }
|
||||||
// )
|
// )
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// This is an example of how to protect content using server rendering
|
// This is an example of how to protect content using server rendering
|
||||||
import { unstable_getServerSession } from "next-auth/next"
|
import { getServerSession } from "next-auth/next"
|
||||||
import { authOptions } from "./api/auth/[...nextauth]"
|
import { authOptions } from "./api/auth/[...nextauth]"
|
||||||
import Layout from "../components/layout"
|
import Layout from "../components/layout"
|
||||||
import AccessDenied from "../components/access-denied"
|
import AccessDenied from "../components/access-denied"
|
||||||
@@ -26,11 +26,7 @@ export default function Page({ content, session }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getServerSideProps(context) {
|
export async function getServerSideProps(context) {
|
||||||
const session = await unstable_getServerSession(
|
const session = await getServerSession(context, authOptions)
|
||||||
context.req,
|
|
||||||
context.res,
|
|
||||||
authOptions
|
|
||||||
)
|
|
||||||
let content = null
|
let content = null
|
||||||
|
|
||||||
if (session) {
|
if (session) {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { unstable_getServerSession } from "next-auth/next"
|
import { getSession } from "next-auth/react"
|
||||||
import Layout from "../components/layout"
|
import Layout from "../components/layout"
|
||||||
import { authOptions } from './api/auth/[...nextauth]';
|
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
// As this page uses Server Side Rendering, the `session` will be already
|
// As this page uses Server Side Rendering, the `session` will be already
|
||||||
@@ -12,17 +11,13 @@ export default function Page() {
|
|||||||
<Layout>
|
<Layout>
|
||||||
<h1>Server Side Rendering</h1>
|
<h1>Server Side Rendering</h1>
|
||||||
<p>
|
<p>
|
||||||
This page uses the <strong>unstable_getServerSession()</strong> method
|
This page uses the universal <strong>getSession()</strong> method in{" "}
|
||||||
in <strong>getServerSideProps()</strong>.
|
<strong>getServerSideProps()</strong>.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Using <strong>unstable_getServerSession()</strong> in{" "}
|
Using <strong>getSession()</strong> in{" "}
|
||||||
<strong>getServerSideProps()</strong> is currently the recommended
|
<strong>getServerSideProps()</strong> is the recommended approach if you
|
||||||
approach, although the API may still change, if you need to support
|
need to support Server Side Rendering with authentication.
|
||||||
Server Side Rendering with authentication.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Using <strong>getSession()</strong> is still recommended on the client.
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
The advantage of Server Side Rendering is this page does not require
|
The advantage of Server Side Rendering is this page does not require
|
||||||
@@ -40,11 +35,7 @@ export default function Page() {
|
|||||||
export async function getServerSideProps(context) {
|
export async function getServerSideProps(context) {
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
session: await unstable_getServerSession(
|
session: await getSession(context),
|
||||||
context.req,
|
|
||||||
context.res,
|
|
||||||
authOptions
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
import Layout from "../components/layout"
|
|
||||||
import { useState, useEffect } from "react"
|
|
||||||
import { useSession } from "next-auth/react"
|
|
||||||
import { createClient } from "@supabase/supabase-js"
|
|
||||||
|
|
||||||
export default function Page() {
|
|
||||||
const { data: session } = useSession()
|
|
||||||
const [data, setData] = useState(null)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (session) {
|
|
||||||
console.log(session)
|
|
||||||
// User is logged in, let's fetch their data.
|
|
||||||
const { supabaseAccessToken } = session
|
|
||||||
const supabase = createClient(
|
|
||||||
process.env.NEXT_PUBLIC_SUPABASE_URL,
|
|
||||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
|
|
||||||
{
|
|
||||||
global: {
|
|
||||||
headers: { Authorization: `Bearer ${supabaseAccessToken}` },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
// Fetch data with RLS enabled.
|
|
||||||
supabase
|
|
||||||
.from("users")
|
|
||||||
.select("*")
|
|
||||||
.then(({ data }) => setData(data))
|
|
||||||
}
|
|
||||||
}, [session])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Layout>
|
|
||||||
<h1>Fetch Data from Supabase with RLS</h1>
|
|
||||||
<h2>Client-side data fetching with RLS:</h2>
|
|
||||||
<pre>{JSON.stringify(data, null, 2)}</pre>
|
|
||||||
<h2>API Example</h2>
|
|
||||||
<p>
|
|
||||||
You can also use Supabase in API routes. See the code in the
|
|
||||||
`/pages/api/examples/supabase-rls.js` file.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<em>You must be signed in to see responses.</em>
|
|
||||||
</p>
|
|
||||||
<p>/api/examples/supabase-rls</p>
|
|
||||||
<iframe src="/api/examples/supabase-rls" />
|
|
||||||
</Layout>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
// This is an example of how to protect content using server rendering
|
|
||||||
// and fetching data from Supabase with RLS enabled.
|
|
||||||
import { unstable_getServerSession } from "next-auth/next"
|
|
||||||
import { authOptions } from "./api/auth/[...nextauth]"
|
|
||||||
import { createClient } from "@supabase/supabase-js"
|
|
||||||
import Layout from "../components/layout"
|
|
||||||
import AccessDenied from "../components/access-denied"
|
|
||||||
|
|
||||||
export default function Page({ data, session }) {
|
|
||||||
// If no session exists, display access denied message
|
|
||||||
if (!session) {
|
|
||||||
return (
|
|
||||||
<Layout>
|
|
||||||
<AccessDenied />
|
|
||||||
</Layout>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If session exists, display content
|
|
||||||
return (
|
|
||||||
<Layout>
|
|
||||||
<h1>Protected Page</h1>
|
|
||||||
<p>Data fetched during SSR from Supabase with RSL enabled:</p>
|
|
||||||
<pre>{JSON.stringify(data, null, 2)}</pre>
|
|
||||||
</Layout>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getServerSideProps(context) {
|
|
||||||
const session = await unstable_getServerSession(
|
|
||||||
context.req,
|
|
||||||
context.res,
|
|
||||||
authOptions
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!session)
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
session,
|
|
||||||
data: null,
|
|
||||||
error: "No session",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const { supabaseAccessToken } = session
|
|
||||||
|
|
||||||
const supabase = createClient(
|
|
||||||
process.env.NEXT_PUBLIC_SUPABASE_URL,
|
|
||||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
|
|
||||||
{
|
|
||||||
global: {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${supabaseAccessToken}`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
// Now you can query with RLS enabled.
|
|
||||||
const { data, error } = await supabase.from("users").select("*")
|
|
||||||
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
session,
|
|
||||||
data,
|
|
||||||
error,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,7 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "esnext",
|
"target": "esnext",
|
||||||
"lib": [
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
"dom",
|
|
||||||
"dom.iterable",
|
|
||||||
"esnext"
|
|
||||||
],
|
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"strict": false,
|
"strict": false,
|
||||||
@@ -19,20 +15,11 @@
|
|||||||
"incremental": true,
|
"incremental": true,
|
||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"plugins": [
|
"paths": {
|
||||||
{
|
"next-auth": ["../../packages/next-auth/src"],
|
||||||
"name": "next"
|
"next-auth/*": ["../../packages/next-auth/src/*"]
|
||||||
}
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"include": [
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||||
"next-env.d.ts",
|
"exclude": ["node_modules", "jest.config.js"]
|
||||||
"**/*.ts",
|
|
||||||
"**/*.tsx",
|
|
||||||
".next/types/**/*.ts"
|
|
||||||
],
|
|
||||||
"exclude": [
|
|
||||||
"node_modules",
|
|
||||||
"jest.config.js"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
20
apps/dev/types/nextauth.d.ts
vendored
20
apps/dev/types/nextauth.d.ts
vendored
@@ -1,20 +0,0 @@
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
import NextAuth from "next-auth"
|
|
||||||
|
|
||||||
declare module "next-auth" {
|
|
||||||
/**
|
|
||||||
* Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
|
|
||||||
*/
|
|
||||||
interface Session {
|
|
||||||
// A JWT which can be used as Authorization header with supabase-js for RLS.
|
|
||||||
supabaseAccessToken?: string
|
|
||||||
user: {
|
|
||||||
/** The user's postal address. */
|
|
||||||
address: string
|
|
||||||
} & User
|
|
||||||
}
|
|
||||||
|
|
||||||
interface User {
|
|
||||||
foo: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -65,6 +65,7 @@ You **can** skip configuring a database and come back to it later if you want.
|
|||||||
For more information about setting up a database, please check out the following links:
|
For more information about setting up a database, please check out the following links:
|
||||||
|
|
||||||
* Docs: [next-auth.js.org/adapters/overview](https://next-auth.js.org/adapters/overview)
|
* Docs: [next-auth.js.org/adapters/overview](https://next-auth.js.org/adapters/overview)
|
||||||
|
* Adapters Repo: [nextauthjs/adapters](https://github.com/nextauthjs/adapters)
|
||||||
|
|
||||||
### 3. Configure Authentication Providers
|
### 3. Configure Authentication Providers
|
||||||
|
|
||||||
|
|||||||
@@ -12,9 +12,9 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dotenv": "^16.0.0",
|
"dotenv": "^16.0.0",
|
||||||
"gatsby": "next",
|
"gatsby": "next",
|
||||||
"next-auth": "latest",
|
"next-auth": "^4.2.1",
|
||||||
"react": "^18",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^18"
|
"react-dom": "^17.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vercel": "^23.1.2"
|
"vercel": "^23.1.2"
|
||||||
|
|||||||
104
apps/example-nextjs/.gitignore
vendored
104
apps/example-nextjs/.gitignore
vendored
@@ -1,20 +1,110 @@
|
|||||||
.DS_Store
|
# Logs
|
||||||
|
|
||||||
node_modules/
|
|
||||||
logs
|
logs
|
||||||
*.log
|
*.log
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
lerna-debug.log*
|
lerna-debug.log*
|
||||||
.yarn-integrity
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# TypeScript v1 declaration files
|
||||||
|
typings/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
.npm
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
.eslintcache
|
.eslintcache
|
||||||
|
|
||||||
*.tsbuildinfo
|
# Microbundle cache
|
||||||
next-env.d.ts
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variables file
|
||||||
|
.env
|
||||||
|
.env.test
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
.next
|
.next
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
# Comment in the public line in if your project uses Gatsby and *not* Next.js
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
||||||
|
|
||||||
.vercel
|
.vercel
|
||||||
.env*.local
|
.now
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ You **can** skip configuring a database and come back to it later if you want.
|
|||||||
For more information about setting up a database, please check out the following links:
|
For more information about setting up a database, please check out the following links:
|
||||||
|
|
||||||
* Docs: [next-auth.js.org/adapters/overview](https://next-auth.js.org/adapters/overview)
|
* Docs: [next-auth.js.org/adapters/overview](https://next-auth.js.org/adapters/overview)
|
||||||
|
* Adapters Repo: [nextauthjs/adapters](https://github.com/nextauthjs/adapters)
|
||||||
|
|
||||||
### 3. Configure Authentication Providers
|
### 3. Configure Authentication Providers
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ export default function Footer() {
|
|||||||
<a href="https://github.com/nextauthjs/next-auth-example">GitHub</a>
|
<a href="https://github.com/nextauthjs/next-auth-example">GitHub</a>
|
||||||
</li>
|
</li>
|
||||||
<li className={styles.navItem}>
|
<li className={styles.navItem}>
|
||||||
<Link href="/policy">Policy</Link>
|
<Link href="/policy">
|
||||||
|
<a>Policy</a>
|
||||||
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className={styles.navItem}>
|
<li className={styles.navItem}>
|
||||||
<em>next-auth@{packageJSON.dependencies["next-auth"]}</em>
|
<em>next-auth@{packageJSON.dependencies["next-auth"]}</em>
|
||||||
|
|||||||
@@ -67,25 +67,39 @@ export default function Header() {
|
|||||||
<nav>
|
<nav>
|
||||||
<ul className={styles.navItems}>
|
<ul className={styles.navItems}>
|
||||||
<li className={styles.navItem}>
|
<li className={styles.navItem}>
|
||||||
<Link href="/">Home</Link>
|
<Link href="/">
|
||||||
|
<a>Home</a>
|
||||||
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className={styles.navItem}>
|
<li className={styles.navItem}>
|
||||||
<Link href="/client">Client</Link>
|
<Link href="/client">
|
||||||
|
<a>Client</a>
|
||||||
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className={styles.navItem}>
|
<li className={styles.navItem}>
|
||||||
<Link href="/server">Server</Link>
|
<Link href="/server">
|
||||||
|
<a>Server</a>
|
||||||
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className={styles.navItem}>
|
<li className={styles.navItem}>
|
||||||
<Link href="/protected">Protected</Link>
|
<Link href="/protected">
|
||||||
|
<a>Protected</a>
|
||||||
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className={styles.navItem}>
|
<li className={styles.navItem}>
|
||||||
<Link href="/api-example">API</Link>
|
<Link href="/api-example">
|
||||||
|
<a>API</a>
|
||||||
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className={styles.navItem}>
|
<li className={styles.navItem}>
|
||||||
<Link href="/admin">Admin</Link>
|
<Link href="/admin">
|
||||||
|
<a>Admin</a>
|
||||||
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className={styles.navItem}>
|
<li className={styles.navItem}>
|
||||||
<Link href="/me">Me</Link>
|
<Link href="/me">
|
||||||
|
<a>Me</a>
|
||||||
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
import Header from "./header"
|
import Header from "./header"
|
||||||
import Footer from "./footer"
|
import Footer from "./footer"
|
||||||
import type { ReactNode } from "react"
|
import type { ReactChildren } from "react"
|
||||||
|
|
||||||
export default function Layout({ children }: { children: ReactNode }) {
|
interface Props {
|
||||||
|
children: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Layout({ children }: Props) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header />
|
<Header />
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
import { withAuth } from "next-auth/middleware"
|
|
||||||
|
|
||||||
// More on how NextAuth.js middleware works: https://next-auth.js.org/configuration/nextjs#middleware
|
|
||||||
export default withAuth({
|
|
||||||
callbacks: {
|
|
||||||
authorized({ req, token }) {
|
|
||||||
// `/admin` requires admin role
|
|
||||||
if (req.nextUrl.pathname === "/admin") {
|
|
||||||
return token?.userRole === "admin"
|
|
||||||
}
|
|
||||||
// `/me` only requires the user to be logged in
|
|
||||||
return !!token
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
export const config = { matcher: ["/admin", "/me"] }
|
|
||||||
@@ -1,15 +1,19 @@
|
|||||||
{
|
{
|
||||||
|
"name": "next-auth-example",
|
||||||
|
"version": "0.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "An example project for NextAuth.js with Next.js",
|
"description": "An example project for NextAuth.js",
|
||||||
"repository": "https://github.com/nextauthjs/next-auth-example.git",
|
"repository": "https://github.com/nextauthjs/next-auth-example.git",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/nextauthjs/next-auth/issues"
|
"url": "https://github.com/nextauthjs/next-auth/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://next-auth-example.vercel.app",
|
"homepage": "https://next-auth-example.vercel.app",
|
||||||
|
"main": "",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next",
|
"dev": "next",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start"
|
"start": "next start",
|
||||||
|
"types": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"author": "Iain Collins <me@iaincollins.com>",
|
"author": "Iain Collins <me@iaincollins.com>",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
@@ -17,16 +21,20 @@
|
|||||||
"Nico Domino <yo@ndo.dev>",
|
"Nico Domino <yo@ndo.dev>",
|
||||||
"Lluis Agusti <hi@llu.lu>"
|
"Lluis Agusti <hi@llu.lu>"
|
||||||
],
|
],
|
||||||
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"next": "latest",
|
"next": "^12.0.11-canary.4",
|
||||||
"next-auth": "latest",
|
"next-auth": "latest",
|
||||||
"nodemailer": "^6",
|
"nodemailer": "^6.6.3",
|
||||||
"react": "^18.2.0",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^18.2.0"
|
"react-dom": "^17.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^17",
|
"@types/node": "^17.0.14",
|
||||||
"@types/react": "^18.0.15",
|
"@types/react": "^17.0.39",
|
||||||
"typescript": "^4"
|
"typescript": "^4.5.5"
|
||||||
|
},
|
||||||
|
"prettier": {
|
||||||
|
"semi": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,12 @@
|
|||||||
import { SessionProvider } from "next-auth/react"
|
import { SessionProvider } from "next-auth/react"
|
||||||
import "./styles.css"
|
|
||||||
|
|
||||||
import type { AppProps } from "next/app"
|
import type { AppProps } from "next/app"
|
||||||
import type { Session } from "next-auth"
|
import "./styles.css"
|
||||||
|
|
||||||
// Use of the <SessionProvider> is mandatory to allow components that call
|
// Use of the <SessionProvider> is mandatory to allow components that call
|
||||||
// `useSession()` anywhere in your application to access the `session` object.
|
// `useSession()` anywhere in your application to access the `session` object.
|
||||||
export default function App({
|
export default function App({ Component, pageProps }: AppProps) {
|
||||||
Component,
|
|
||||||
pageProps: { session, ...pageProps },
|
|
||||||
}: AppProps<{ session: Session }>) {
|
|
||||||
return (
|
return (
|
||||||
<SessionProvider session={session}>
|
<SessionProvider session={pageProps.session} refetchInterval={0}>
|
||||||
<Component {...pageProps} />
|
<Component {...pageProps} />
|
||||||
</SessionProvider>
|
</SessionProvider>
|
||||||
)
|
)
|
||||||
|
|||||||
8
apps/example-nextjs/pages/admin/_middleware.ts
Normal file
8
apps/example-nextjs/pages/admin/_middleware.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { withAuth } from "next-auth/middleware"
|
||||||
|
|
||||||
|
// More on how NextAuth.js middleware works: https://next-auth.js.org/configuration/nextjs#middleware
|
||||||
|
export default withAuth({
|
||||||
|
callbacks: {
|
||||||
|
authorized: ({ token }) => token?.userRole === "admin",
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import Layout from "../components/layout"
|
import Layout from "../../components/layout"
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
return (
|
return (
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import NextAuth, { NextAuthOptions } from "next-auth"
|
import NextAuth from "next-auth"
|
||||||
import GoogleProvider from "next-auth/providers/google"
|
import GoogleProvider from "next-auth/providers/google"
|
||||||
import FacebookProvider from "next-auth/providers/facebook"
|
import FacebookProvider from "next-auth/providers/facebook"
|
||||||
import GithubProvider from "next-auth/providers/github"
|
import GithubProvider from "next-auth/providers/github"
|
||||||
@@ -9,7 +9,7 @@ import Auth0Provider from "next-auth/providers/auth0"
|
|||||||
|
|
||||||
// For more information on each option (and a full list of options) go to
|
// For more information on each option (and a full list of options) go to
|
||||||
// https://next-auth.js.org/configuration/options
|
// https://next-auth.js.org/configuration/options
|
||||||
export const authOptions: NextAuthOptions = {
|
export default NextAuth({
|
||||||
// https://next-auth.js.org/configuration/providers/oauth
|
// https://next-auth.js.org/configuration/providers/oauth
|
||||||
providers: [
|
providers: [
|
||||||
/* EmailProvider({
|
/* EmailProvider({
|
||||||
@@ -60,6 +60,4 @@ export const authOptions: NextAuthOptions = {
|
|||||||
return token
|
return token
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
|
|
||||||
export default NextAuth(authOptions)
|
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
// This is an example of how to read a JSON Web Token from an API route
|
// This is an example of how to read a JSON Web Token from an API route
|
||||||
import { getToken } from "next-auth/jwt"
|
import { getToken } from "next-auth/jwt"
|
||||||
|
|
||||||
import type { NextApiRequest, NextApiResponse } from "next"
|
import type { NextApiRequest, NextApiResponse } from "next"
|
||||||
|
|
||||||
export default async function handler(
|
const secret = process.env.NEXTAUTH_SECRET
|
||||||
req: NextApiRequest,
|
|
||||||
res: NextApiResponse
|
export default async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
) {
|
const token = await getToken({ req, secret })
|
||||||
// If you don't have the NEXTAUTH_SECRET environment variable set,
|
|
||||||
// you will have to pass your secret as `secret` to `getToken`
|
|
||||||
const token = await getToken({ req })
|
|
||||||
res.send(JSON.stringify(token, null, 2))
|
res.send(JSON.stringify(token, null, 2))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,18 @@
|
|||||||
// This is an example of to protect an API route
|
// This is an example of to protect an API route
|
||||||
import { unstable_getServerSession } from "next-auth/next"
|
import { getSession } from "next-auth/react"
|
||||||
import { authOptions } from "../auth/[...nextauth]"
|
|
||||||
|
|
||||||
import type { NextApiRequest, NextApiResponse } from "next"
|
import type { NextApiRequest, NextApiResponse } from "next"
|
||||||
|
|
||||||
export default async function handler(
|
export default async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
req: NextApiRequest,
|
const session = await getSession({ req })
|
||||||
res: NextApiResponse
|
|
||||||
) {
|
|
||||||
const session = await unstable_getServerSession(req, res, authOptions)
|
|
||||||
|
|
||||||
if (session) {
|
if (session) {
|
||||||
return res.send({
|
res.send({
|
||||||
content:
|
content:
|
||||||
"This is protected content. You can access this content because you are signed in.",
|
"This is protected content. You can access this content because you are signed in.",
|
||||||
})
|
})
|
||||||
}
|
} else {
|
||||||
|
|
||||||
res.send({
|
res.send({
|
||||||
error: "You must be signed in to view the protected content on this page.",
|
error: "You must be signed in to view the protected content on this page.",
|
||||||
})
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
// This is an example of how to access a session from an API route
|
// This is an example of how to access a session from an API route
|
||||||
import { unstable_getServerSession } from "next-auth"
|
import { getSession } from "next-auth/react"
|
||||||
import { authOptions } from "../auth/[...nextauth]"
|
|
||||||
|
|
||||||
import type { NextApiRequest, NextApiResponse } from "next"
|
import type { NextApiRequest, NextApiResponse } from "next"
|
||||||
|
|
||||||
export default async function handler(
|
export default async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
req: NextApiRequest,
|
const session = await getSession({ req })
|
||||||
res: NextApiResponse
|
|
||||||
) {
|
|
||||||
const session = await unstable_getServerSession(req, res, authOptions)
|
|
||||||
res.send(JSON.stringify(session, null, 2))
|
res.send(JSON.stringify(session, null, 2))
|
||||||
}
|
}
|
||||||
|
|||||||
2
apps/example-nextjs/pages/me/_middleware.ts
Normal file
2
apps/example-nextjs/pages/me/_middleware.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// More on how NextAuth.js middleware works: https://next-auth.js.org/configuration/nextjs#middleware
|
||||||
|
export { default } from "next-auth/middleware"
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useSession } from "next-auth/react"
|
import { useSession } from "next-auth/react"
|
||||||
import Layout from "../components/layout"
|
import Layout from "../../components/layout"
|
||||||
|
|
||||||
export default function MePage() {
|
export default function MePage() {
|
||||||
const { data } = useSession()
|
const { data } = useSession()
|
||||||
@@ -4,7 +4,8 @@ import Layout from "../components/layout"
|
|||||||
import AccessDenied from "../components/access-denied"
|
import AccessDenied from "../components/access-denied"
|
||||||
|
|
||||||
export default function ProtectedPage() {
|
export default function ProtectedPage() {
|
||||||
const { data: session } = useSession()
|
const { data: session, status } = useSession()
|
||||||
|
const loading = status === "loading"
|
||||||
const [content, setContent] = useState()
|
const [content, setContent] = useState()
|
||||||
|
|
||||||
// Fetch content from protected route
|
// Fetch content from protected route
|
||||||
@@ -19,6 +20,8 @@ export default function ProtectedPage() {
|
|||||||
fetchData()
|
fetchData()
|
||||||
}, [session])
|
}, [session])
|
||||||
|
|
||||||
|
// When rendering client side don't display anything until loading is complete
|
||||||
|
if (typeof window !== "undefined" && loading) return null
|
||||||
|
|
||||||
// If no session exists, display access denied message
|
// If no session exists, display access denied message
|
||||||
if (!session) {
|
if (!session) {
|
||||||
|
|||||||
@@ -1,22 +1,24 @@
|
|||||||
import { unstable_getServerSession } from "next-auth/next"
|
import { useSession, getSession } from "next-auth/react"
|
||||||
import { authOptions } from "./api/auth/[...nextauth]"
|
|
||||||
import Layout from "../components/layout"
|
import Layout from "../components/layout"
|
||||||
|
import type { NextPageContext } from "next"
|
||||||
|
|
||||||
import type { GetServerSidePropsContext } from "next"
|
export default function ServerSidePage() {
|
||||||
import type { Session } from "next-auth"
|
|
||||||
|
|
||||||
export default function ServerSidePage({ session }: { session: Session }) {
|
|
||||||
// As this page uses Server Side Rendering, the `session` will be already
|
// As this page uses Server Side Rendering, the `session` will be already
|
||||||
// populated on render without needing to go through a loading stage.
|
// populated on render without needing to go through a loading stage.
|
||||||
|
// This is possible because of the shared context configured in `_app.js` that
|
||||||
|
// is used by `useSession()`.
|
||||||
|
const { data: session, status } = useSession()
|
||||||
|
const loading = status === "loading"
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<h1>Server Side Rendering</h1>
|
<h1>Server Side Rendering</h1>
|
||||||
<p>
|
<p>
|
||||||
This page uses the <strong>unstable_getServerSession()</strong> method
|
This page uses the universal <strong>getSession()</strong> method in{" "}
|
||||||
in <strong>getServerSideProps()</strong>.
|
<strong>getServerSideProps()</strong>.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Using <strong>unstable_getServerSession()</strong> in{" "}
|
Using <strong>getSession()</strong> in{" "}
|
||||||
<strong>getServerSideProps()</strong> is the recommended approach if you
|
<strong>getServerSideProps()</strong> is the recommended approach if you
|
||||||
need to support Server Side Rendering with authentication.
|
need to support Server Side Rendering with authentication.
|
||||||
</p>
|
</p>
|
||||||
@@ -28,20 +30,15 @@ export default function ServerSidePage({ session }: { session: Session }) {
|
|||||||
The disadvantage of Server Side Rendering is that this page is slower to
|
The disadvantage of Server Side Rendering is that this page is slower to
|
||||||
render.
|
render.
|
||||||
</p>
|
</p>
|
||||||
<pre>{JSON.stringify(session, null, 2)}</pre>
|
|
||||||
</Layout>
|
</Layout>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export the `session` prop to use sessions with Server Side Rendering
|
// Export the `session` prop to use sessions with Server Side Rendering
|
||||||
export async function getServerSideProps(context: GetServerSidePropsContext) {
|
export async function getServerSideProps(context: NextPageContext) {
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
session: await unstable_getServerSession(
|
session: await getSession(context),
|
||||||
context.req,
|
|
||||||
context.res,
|
|
||||||
authOptions
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
indent_size = 2
|
|
||||||
indent_style = space
|
|
||||||
end_of_line = lf
|
|
||||||
charset = utf-8
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
insert_final_newline = true
|
|
||||||
|
|
||||||
[*.md]
|
|
||||||
trim_trailing_whitespace = false
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
dist
|
|
||||||
node_modules
|
|
||||||
tsconfig.json
|
|
||||||
package.json
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": [
|
|
||||||
"@nuxtjs/eslint-config-typescript"
|
|
||||||
],
|
|
||||||
"rules": {
|
|
||||||
"@typescript-eslint/no-unused-vars": [
|
|
||||||
"off"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
52
apps/playground-nuxt/.gitignore
vendored
52
apps/playground-nuxt/.gitignore
vendored
@@ -1,52 +0,0 @@
|
|||||||
# Dependencies
|
|
||||||
node_modules
|
|
||||||
|
|
||||||
# Logs
|
|
||||||
*.log*
|
|
||||||
|
|
||||||
# Temp directories
|
|
||||||
.temp
|
|
||||||
.tmp
|
|
||||||
.cache
|
|
||||||
|
|
||||||
# Yarn
|
|
||||||
**/.yarn/cache
|
|
||||||
**/.yarn/*state*
|
|
||||||
|
|
||||||
# Generated dirs
|
|
||||||
dist
|
|
||||||
|
|
||||||
# Nuxt
|
|
||||||
.nuxt
|
|
||||||
.output
|
|
||||||
.vercel_build_output
|
|
||||||
.build-*
|
|
||||||
.env
|
|
||||||
.netlify
|
|
||||||
|
|
||||||
# Env
|
|
||||||
.env
|
|
||||||
|
|
||||||
# Testing
|
|
||||||
reports
|
|
||||||
coverage
|
|
||||||
*.lcov
|
|
||||||
.nyc_output
|
|
||||||
|
|
||||||
# VSCode
|
|
||||||
.vscode
|
|
||||||
|
|
||||||
# Intellij idea
|
|
||||||
*.iml
|
|
||||||
.idea
|
|
||||||
|
|
||||||
# OSX
|
|
||||||
.DS_Store
|
|
||||||
.AppleDouble
|
|
||||||
.LSOverride
|
|
||||||
.AppleDB
|
|
||||||
.AppleDesktop
|
|
||||||
Network Trash Folder
|
|
||||||
Temporary Items
|
|
||||||
.apdisk
|
|
||||||
.vercel
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
imports.autoImport=false
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
# NextAuth + Nuxt 3 Playground
|
|
||||||
|
|
||||||
NextAuth.js is committed to bringing easy authentication to other frameworks. [#2294](https://github.com/nextauthjs/next-auth/issues/2294)
|
|
||||||
|
|
||||||
Nuxt 3 support with NextAuth.js is currently experimental. This directory contains a minimal, proof-of-concept application. Parts of this is expected to be abstracted away into a package like` @next-auth/nuxt.`
|
|
||||||
|
|
||||||
This package uses Nuxt's [module starter](https://github.com/nuxt/starter/tree/module).
|
|
||||||
|
|
||||||
Demo: https://next-auth-nuxt-demo.vercel.app
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
### Add the module to the modules section of `nuxt.config.ts`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineNuxtConfig({
|
|
||||||
// temporary module name.
|
|
||||||
modules: ['next-auth-nuxt'],
|
|
||||||
// https://v3.nuxtjs.org/migration/runtime-config#runtime-config
|
|
||||||
runtimeConfig: {
|
|
||||||
secret: process.env.NEXTAUTH_SECRET
|
|
||||||
github: {
|
|
||||||
clientId: process.env.GITHUB_CLIENT_ID,
|
|
||||||
clientSecret: process.env.GITHUB_CLIENT_SECRET
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// https://v3.nuxtjs.org/guide/concepts/esm#aliasing-libraries
|
|
||||||
// Fix for GithubProvider (or whichever provider you choose) is not a function error in Vite
|
|
||||||
alias: {
|
|
||||||
'next-auth/providers/github': 'node_modules/next-auth/providers/github.js'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Add API route
|
|
||||||
|
|
||||||
To add `NextAuth.js` to a project create a file called `[...].ts` in `server/api/auth`. This contains the dynamic route handler for NextAuth.js which will also contain all of your global NextAuth.js configurations.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// ~/server/api/auth/[...].ts
|
|
||||||
import { NextAuthNuxtHandler } from 'next-auth-nuxt/handler'
|
|
||||||
import GithubProvider from 'next-auth/providers/github'
|
|
||||||
|
|
||||||
const runtimeConfig = useRuntimeConfig()
|
|
||||||
|
|
||||||
export const authOptions = {
|
|
||||||
secret: runtimeConfig.secret,
|
|
||||||
providers: [
|
|
||||||
GithubProvider({
|
|
||||||
clientId: runtimeConfig.github.clientId,
|
|
||||||
clientSecret: runtimeConfig.github.clientSecret
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
export default NextAuthNuxtHandler(authOptions)
|
|
||||||
```
|
|
||||||
|
|
||||||
All requests to `/api/auth/*` (`signIn`, `callback`, `signOut`, etc.) will automatically be handled by NextAuth.js.
|
|
||||||
|
|
||||||
### Frontend - Add Vue Composable
|
|
||||||
|
|
||||||
The `useSession()` Vue Composable is the easiest way to check if someone is signed in.
|
|
||||||
|
|
||||||
```html
|
|
||||||
<script setup lang="ts">
|
|
||||||
const { data: session } = useSession()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div v-if="session">
|
|
||||||
Signed in as {{ session.user.email }} <br />
|
|
||||||
<button @click="signOut">Sign out</button>
|
|
||||||
</div>
|
|
||||||
<div v-else>
|
|
||||||
Not signed in <br />
|
|
||||||
<button @click="signIn">Sign in</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Backend - API Route
|
|
||||||
|
|
||||||
To protect an API Route, you can use the `getServerSession()` method.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { getServerSession } from 'next-auth-nuxt/handler'
|
|
||||||
import { authOptions } from '~/server/api/auth/[...]'
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
const session = await getServerSession(event, authOptions)
|
|
||||||
|
|
||||||
if (session) {
|
|
||||||
return {
|
|
||||||
content: 'This is protected content. You can access this content because you are signed in.'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
error: 'You must be signed in to view the protected content on this page.'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
- Run `pnpm dev:generate` to generate type stubs.
|
|
||||||
- Use `pnpm dev` to start `playground` in development mode.
|
|
||||||
1
apps/playground-nuxt/client.d.ts
vendored
1
apps/playground-nuxt/client.d.ts
vendored
@@ -1 +0,0 @@
|
|||||||
export * from './dist/runtime/client'
|
|
||||||
1
apps/playground-nuxt/handler.d.ts
vendored
1
apps/playground-nuxt/handler.d.ts
vendored
@@ -1 +0,0 @@
|
|||||||
export * from './dist/runtime/server/handler'
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "next-auth-nuxt",
|
|
||||||
"type": "module",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"packageManager": "pnpm@7.1.1",
|
|
||||||
"license": "MIT",
|
|
||||||
"main": "./dist/module.cjs",
|
|
||||||
"types": "./dist/types.d.ts",
|
|
||||||
"exports": {
|
|
||||||
".": {
|
|
||||||
"import": "./dist/module.mjs",
|
|
||||||
"require": "./dist/module.cjs"
|
|
||||||
},
|
|
||||||
"./handler": {
|
|
||||||
"import": "./dist/runtime/server/handler.mjs",
|
|
||||||
"types": "./dist/runtime/server/handler.d.ts"
|
|
||||||
},
|
|
||||||
"./client": {
|
|
||||||
"import": "./dist/runtime/client/index.mjs",
|
|
||||||
"types": "./dist/runtime/client/index.d.ts"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"dist",
|
|
||||||
"handler.d.ts",
|
|
||||||
"client.d.ts"
|
|
||||||
],
|
|
||||||
"scripts": {
|
|
||||||
"prepack": "nuxt-module-build",
|
|
||||||
"dev": "pnpm prepack && nuxi dev playground",
|
|
||||||
"dev:build": "nuxi build playground",
|
|
||||||
"dev:build:vercel": "NITRO_PRESET=vercel nuxi build playground",
|
|
||||||
"dev:prepare": "nuxt-module-build --stub && nuxi prepare playground"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@nuxt/kit": "^3.0.0-rc.13",
|
|
||||||
"h3": "^0.8.6",
|
|
||||||
"next-auth": "^4.16.2",
|
|
||||||
"pathe": "^0.3.9"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@nuxt/module-builder": "^0.2.0",
|
|
||||||
"@nuxt/schema": "^3.0.0-rc.12",
|
|
||||||
"@nuxtjs/eslint-config-typescript": "^11.0.0",
|
|
||||||
"eslint": "^8.26.0",
|
|
||||||
"nuxt": "^3.0.0-rc.13",
|
|
||||||
"next-auth-nuxt": "workspace:*"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
GITHUB_CLIENT_ID=
|
|
||||||
GITHUB_CLIENT_SECRET=
|
|
||||||
NEXTAUTH_URL=
|
|
||||||
NEXTAUTH_SECRET=
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<Header />
|
|
||||||
<NuxtPage />
|
|
||||||
<Footer />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font-family: -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
|
|
||||||
padding: 0 1rem 1rem 1rem;
|
|
||||||
max-width: 680px;
|
|
||||||
margin: 0 auto;
|
|
||||||
background: #fff;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
li,
|
|
||||||
p {
|
|
||||||
line-height: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
iframe {
|
|
||||||
background: #ccc;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
height: 10rem;
|
|
||||||
width: 100%;
|
|
||||||
border-radius: .5rem;
|
|
||||||
filter: invert(1);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<h1>Access Denied</h1>
|
|
||||||
<p>
|
|
||||||
<a href="/api/auth/signin">You must be signed in to view this page</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
<template>
|
|
||||||
<footer class="fotter">
|
|
||||||
<hr>
|
|
||||||
<ul class="navItems">
|
|
||||||
<li class="navItem">
|
|
||||||
<a href="https://github.com/nextauthjs/next-auth/tree/main/apps/playground-nuxt">Demo GitHub</a>
|
|
||||||
</li>
|
|
||||||
<li class="navItem">
|
|
||||||
<a href="https://next-auth.js.org">Next.js Documentation</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</footer>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.footer {
|
|
||||||
margin-top: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navItems {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
padding: 0;
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navItem {
|
|
||||||
display: inline-block;
|
|
||||||
margin-right: 1rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,155 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { useSession, signIn, signOut, computed } from '#imports'
|
|
||||||
|
|
||||||
const { data: session, status } = useSession()
|
|
||||||
const loading = computed(() => status.value === 'loading')
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<header>
|
|
||||||
<div class="signedInStatus">
|
|
||||||
<p :class="['nojs-show', !session && loading ? 'loading' : 'loaded']">
|
|
||||||
<template v-if="session">
|
|
||||||
<span v-if="session.user?.image" :style="{ backgroundImage: `url(${session.user.image})` }" class="avatar" />
|
|
||||||
<span class="signedInText">
|
|
||||||
<small>Signed in as</small><br>
|
|
||||||
<strong>{{ session.user?.email || session.user?.name }}</strong>
|
|
||||||
</span>
|
|
||||||
<a href="/api/auth/signout" class="button" @click.prevent="signOut">Sign out</a>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<span class="notSignedInText">You are not signed in</span>
|
|
||||||
<a href="/api/auth/signin" class="buttonPrimary" @click.prevent="signIn">Sign in</a>
|
|
||||||
</template>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<nav>
|
|
||||||
<ul class="navItems">
|
|
||||||
<li class="navItem">
|
|
||||||
<NuxtLink to="/">
|
|
||||||
Home
|
|
||||||
</NuxtLink>
|
|
||||||
</li>
|
|
||||||
<li class="navItem">
|
|
||||||
<NuxtLink to="/client">
|
|
||||||
Client
|
|
||||||
</NuxtLink>
|
|
||||||
</li>
|
|
||||||
<li class="navItem">
|
|
||||||
<NuxtLink to="/server">
|
|
||||||
Server
|
|
||||||
</NuxtLink>
|
|
||||||
</li>
|
|
||||||
<li class="navItem">
|
|
||||||
<NuxtLink to="/protected">
|
|
||||||
Protected
|
|
||||||
</NuxtLink>
|
|
||||||
</li>
|
|
||||||
<li class="navItem">
|
|
||||||
<NuxtLink to="/api-example">
|
|
||||||
API
|
|
||||||
</NuxtLink>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.nojs-show {
|
|
||||||
opacity: 1;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.signedInStatus {
|
|
||||||
display: block;
|
|
||||||
min-height: 4rem;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading,
|
|
||||||
.loaded {
|
|
||||||
position: relative;
|
|
||||||
top: 0;
|
|
||||||
opacity: 1;
|
|
||||||
overflow: hidden;
|
|
||||||
border-radius: 0 0 .6rem .6rem;
|
|
||||||
padding: .6rem 1rem;
|
|
||||||
margin: 0;
|
|
||||||
background-color: rgba(0,0,0,.05);
|
|
||||||
transition: all 0.2s ease-in;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading {
|
|
||||||
top: -2rem;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.signedInText,
|
|
||||||
.notSignedInText {
|
|
||||||
position: absolute;
|
|
||||||
padding-top: .8rem;
|
|
||||||
left: 1rem;
|
|
||||||
right: 6.5rem;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
display: inherit;
|
|
||||||
z-index: 1;
|
|
||||||
line-height: 1.3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.signedInText {
|
|
||||||
padding-top: 0rem;
|
|
||||||
left: 4.6rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
border-radius: 2rem;
|
|
||||||
float: left;
|
|
||||||
height: 2.8rem;
|
|
||||||
width: 2.8rem;
|
|
||||||
background-color: white;
|
|
||||||
background-size: cover;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button,
|
|
||||||
.buttonPrimary {
|
|
||||||
float: right;
|
|
||||||
margin-right: -.4rem;
|
|
||||||
font-weight: 500;
|
|
||||||
border-radius: .3rem;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 1rem;
|
|
||||||
line-height: 1.4rem;
|
|
||||||
padding: .7rem .8rem;
|
|
||||||
position: relative;
|
|
||||||
z-index: 10;
|
|
||||||
background-color: transparent;
|
|
||||||
color: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttonPrimary {
|
|
||||||
background-color: #346df1;
|
|
||||||
border-color: #346df1;
|
|
||||||
color: #fff;
|
|
||||||
text-decoration: none;
|
|
||||||
padding: .7rem 1.4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttonPrimary:hover {
|
|
||||||
box-shadow: inset 0 0 5rem rgba(0,0,0,0.2)
|
|
||||||
}
|
|
||||||
|
|
||||||
.navItems {
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
padding: 0;
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navItem {
|
|
||||||
display: inline-block;
|
|
||||||
margin-right: 1rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import MyModule from '../src/module'
|
|
||||||
|
|
||||||
export default defineNuxtConfig({
|
|
||||||
modules: [
|
|
||||||
MyModule
|
|
||||||
],
|
|
||||||
// https://v3.nuxtjs.org/migration/runtime-config#runtime-config
|
|
||||||
runtimeConfig: {
|
|
||||||
secret: process.env.NEXTAUTH_SECRET,
|
|
||||||
github: {
|
|
||||||
clientId: process.env.GITHUB_CLIENT_ID,
|
|
||||||
clientSecret: process.env.GITHUB_CLIENT_SECRET
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// https://v3.nuxtjs.org/guide/concepts/esm#aliasing-libraries
|
|
||||||
// Fix for GithubProvider is not a function error in Vite
|
|
||||||
alias: {
|
|
||||||
'next-auth/providers/github': 'node_modules/next-auth/providers/github.js'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "playground",
|
|
||||||
"private": true
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<h1>API Example</h1>
|
|
||||||
<p>The examples below show responses from the example API endpoints.</p>
|
|
||||||
<p>
|
|
||||||
<em>You must be signed in to see responses.</em>
|
|
||||||
</p>
|
|
||||||
<h2>Session</h2>
|
|
||||||
<p>/api/examples/session</p>
|
|
||||||
<iframe src="/api/examples/session" />
|
|
||||||
<h2>JSON Web Token</h2>
|
|
||||||
<p>/api/examples/jwt</p>
|
|
||||||
<iframe src="/api/examples/jwt" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<h1>Client Side Rendering</h1>
|
|
||||||
<p>
|
|
||||||
This page uses the <strong>useSession()</strong> Vue Composable in the <strong><Header/></strong> component.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
The <strong>useSession()</strong> Vue Composable is easy to use and allows pages to render very quickly.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
The advantage of this approach is that session state is shared between pages by using a provided session via <strong>Vue Plugin</strong> so
|
|
||||||
that navigation between pages using <strong>useSession()</strong> is very fast.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
The disadvantage of <strong>useSession()</strong> is that it requires client side JavaScript.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<h1>Nuxt 3 + NextAuth.js Example</h1>
|
|
||||||
<p>
|
|
||||||
This is an example site to demonstrate how to use <a href="https://v3.nuxtjs.org/">Nuxt 3</a> with <a href="https://next-auth.js.org">NextAuth.js</a> for authentication.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { useSession, useFetch, useLazyFetch } from '#imports'
|
|
||||||
import AccessDenied from '~/components/AccessDenied.vue'
|
|
||||||
|
|
||||||
const { data: session } = useSession()
|
|
||||||
const { data } = await useLazyFetch('/api/examples/protected', {
|
|
||||||
server: false
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<AccessDenied v-if="!session" />
|
|
||||||
<template v-else>
|
|
||||||
<h1>Protected Page</h1>
|
|
||||||
<p><strong>{{ data?.content || "\u00a0" }}</strong></p>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { useFetch } from '#imports'
|
|
||||||
|
|
||||||
await useFetch('/api/examples/session')
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<h1>Server Side Rendering</h1>
|
|
||||||
<p>
|
|
||||||
This page uses the <strong>getServerSession()</strong> method inside an api route and is fetched using the <strong>useFetch()</strong> composable.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Using <strong>getServerSession()</strong> is the recommended approach if you need to
|
|
||||||
support Server Side Rendering with authentication.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
The advantage of Server Side Rendering is this page does not require client side JavaScript.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
The disadvantage of Server Side Rendering is that this page is slower to render.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import { NextAuthNuxtHandler } from 'next-auth-nuxt/handler'
|
|
||||||
import GithubProvider from 'next-auth/providers/github'
|
|
||||||
import type { NextAuthOptions } from 'next-auth'
|
|
||||||
|
|
||||||
const runtimeConfig = useRuntimeConfig()
|
|
||||||
|
|
||||||
export const authOptions: NextAuthOptions = {
|
|
||||||
secret: runtimeConfig.secret,
|
|
||||||
providers: [
|
|
||||||
GithubProvider({
|
|
||||||
clientId: runtimeConfig.github.clientId,
|
|
||||||
clientSecret: runtimeConfig.github.clientSecret
|
|
||||||
})
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
export default NextAuthNuxtHandler(authOptions)
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import { getToken } from 'next-auth/jwt'
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
// @ts-expect-error: cookies property is not present in h3
|
|
||||||
event.req.cookies = parseCookies(event)
|
|
||||||
const token = await getToken({
|
|
||||||
req: event.req
|
|
||||||
})
|
|
||||||
return token
|
|
||||||
})
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import { getServerSession } from 'next-auth-nuxt/handler'
|
|
||||||
import { authOptions } from '../auth/[...]'
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
const session = await getServerSession(event, authOptions)
|
|
||||||
|
|
||||||
if (session) {
|
|
||||||
return {
|
|
||||||
content: 'This is protected content. You can access this content because you are signed in.'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
error: 'You must be signed in to view the protected content on this page.'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import { getServerSession } from 'next-auth-nuxt/handler'
|
|
||||||
import { authOptions } from '../auth/[...]'
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
const session = await getServerSession(event, authOptions)
|
|
||||||
return session
|
|
||||||
})
|
|
||||||
6386
apps/playground-nuxt/pnpm-lock.yaml
generated
6386
apps/playground-nuxt/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,2 +0,0 @@
|
|||||||
packages:
|
|
||||||
- playground
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
import { fileURLToPath } from 'url'
|
|
||||||
import { addImports, addPlugin, defineNuxtModule, extendViteConfig } from '@nuxt/kit'
|
|
||||||
import { resolve } from 'pathe'
|
|
||||||
|
|
||||||
export interface ModuleOptions {
|
|
||||||
}
|
|
||||||
|
|
||||||
export default defineNuxtModule<ModuleOptions>({
|
|
||||||
meta: {
|
|
||||||
name: 'next-auth-nuxt',
|
|
||||||
configKey: 'auth'
|
|
||||||
},
|
|
||||||
defaults: {
|
|
||||||
},
|
|
||||||
async setup (_options, nuxt) {
|
|
||||||
const runtimeDir = fileURLToPath(new URL('./runtime', import.meta.url))
|
|
||||||
nuxt.options.build.transpile.push(runtimeDir)
|
|
||||||
|
|
||||||
addPlugin(resolve(runtimeDir, 'plugin.client'))
|
|
||||||
|
|
||||||
// Composables are auto-imported in client.
|
|
||||||
const client = resolve(runtimeDir, 'client')
|
|
||||||
await addImports([
|
|
||||||
{ name: 'getSession', from: client },
|
|
||||||
{ name: 'getCsrfToken', from: client },
|
|
||||||
{ name: 'getProviders', from: client },
|
|
||||||
{ name: 'signIn', from: client },
|
|
||||||
{ name: 'signOut', from: client },
|
|
||||||
{ name: 'useSession', from: client }
|
|
||||||
])
|
|
||||||
|
|
||||||
// We can safely expose this to client.
|
|
||||||
extendViteConfig((config) => {
|
|
||||||
config.define = config.define || {}
|
|
||||||
config.define['process.env.NEXTAUTH_URL'] = JSON.stringify(process.env.NEXTAUTH_URL)
|
|
||||||
config.define['process.env.NEXTAUTH_URL_INTERNAL'] = JSON.stringify(process.env.NEXTAUTH_URL_INTERNAL)
|
|
||||||
config.define['process.env.VERCEL_URL'] = JSON.stringify(process.env.VERCEL_URL)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@@ -1,369 +0,0 @@
|
|||||||
import type { NextAuthClientConfig } from 'next-auth/client/_utils'
|
|
||||||
import type { Plugin, Ref } from 'vue'
|
|
||||||
import { ref, reactive, computed, inject, toRefs } from 'vue'
|
|
||||||
import { BroadcastChannel, apiBaseUrl, fetchData, now } from 'next-auth/client/_utils'
|
|
||||||
import type { Session } from 'next-auth'
|
|
||||||
import type {
|
|
||||||
BuiltInProviderType,
|
|
||||||
RedirectableProviderType
|
|
||||||
} from 'next-auth/providers'
|
|
||||||
import type { H3EventContext } from 'h3'
|
|
||||||
import parseUrl from '../lib/parse-url'
|
|
||||||
import _logger, { proxyLogger } from '../lib/logger'
|
|
||||||
import type {
|
|
||||||
ClientSafeProvider,
|
|
||||||
LiteralUnion,
|
|
||||||
SessionProviderProps,
|
|
||||||
SignInAuthorizationParams,
|
|
||||||
SignInOptions,
|
|
||||||
SignInResponse,
|
|
||||||
SignOutParams,
|
|
||||||
SignOutResponse
|
|
||||||
} from '../types'
|
|
||||||
|
|
||||||
// This behaviour mirrors the default behaviour for getting the site name that
|
|
||||||
// happens server side in server/index.js
|
|
||||||
// 1. An empty value is legitimate when the code is being invoked client side as
|
|
||||||
// relative URLs are valid in that context and so defaults to empty.
|
|
||||||
// 2. When invoked server side the value is picked up from an environment
|
|
||||||
// variable and defaults to 'http://localhost:3000'.
|
|
||||||
const __NEXTAUTH: NextAuthClientConfig = {
|
|
||||||
baseUrl: parseUrl(process.env.NEXTAUTH_URL ?? process.env.VERCEL_URL).origin,
|
|
||||||
basePath: parseUrl(process.env.NEXTAUTH_URL).path,
|
|
||||||
baseUrlServer: parseUrl(
|
|
||||||
process.env.NEXTAUTH_URL_INTERNAL ??
|
|
||||||
process.env.NEXTAUTH_URL ??
|
|
||||||
process.env.VERCEL_URL
|
|
||||||
).origin,
|
|
||||||
basePathServer: parseUrl(
|
|
||||||
process.env.NEXTAUTH_URL_INTERNAL ?? process.env.NEXTAUTH_URL
|
|
||||||
).path,
|
|
||||||
_lastSync: 0,
|
|
||||||
_session: undefined,
|
|
||||||
_getSession: () => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CtxOrReq {
|
|
||||||
req?: H3EventContext['req']
|
|
||||||
event?: { req: H3EventContext['req'] }
|
|
||||||
}
|
|
||||||
|
|
||||||
export type GetSessionParams = CtxOrReq & {
|
|
||||||
event?: 'storage' | 'timer' | 'hidden' | string
|
|
||||||
triggerEvent?: boolean
|
|
||||||
broadcast?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
const logger = proxyLogger(_logger, __NEXTAUTH.basePath)
|
|
||||||
|
|
||||||
const broadcast = BroadcastChannel()
|
|
||||||
|
|
||||||
function isServer () {
|
|
||||||
return (process as any).server
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getSession (params?: GetSessionParams) {
|
|
||||||
const session = await fetchData<Session>(
|
|
||||||
'session',
|
|
||||||
__NEXTAUTH,
|
|
||||||
logger,
|
|
||||||
params
|
|
||||||
)
|
|
||||||
if (params?.broadcast ?? true) { broadcast.post({ event: 'session', data: { trigger: 'getSession' } }) }
|
|
||||||
|
|
||||||
return session
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current Cross Site Request Forgery Token (CSRF Token)
|
|
||||||
* required to make POST requests (e.g. for signing in and signing out).
|
|
||||||
* You likely only need to use this if you are not using the built-in
|
|
||||||
* `signIn()` and `signOut()` methods.
|
|
||||||
*
|
|
||||||
* [Documentation](https://next-auth.js.org/getting-started/client#getcsrftoken)
|
|
||||||
*/
|
|
||||||
export async function getCsrfToken (params?: CtxOrReq) {
|
|
||||||
const response = await fetchData<{ csrfToken: string }>(
|
|
||||||
'csrf',
|
|
||||||
__NEXTAUTH,
|
|
||||||
logger,
|
|
||||||
params
|
|
||||||
)
|
|
||||||
return response?.csrfToken
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* It calls `/api/auth/providers` and returns
|
|
||||||
* a list of the currently configured authentication providers.
|
|
||||||
* It can be useful if you are creating a dynamic custom sign in page.
|
|
||||||
*
|
|
||||||
* [Documentation](https://next-auth.js.org/getting-started/client#getproviders)
|
|
||||||
*/
|
|
||||||
export async function getProviders () {
|
|
||||||
return await fetchData<
|
|
||||||
Record<LiteralUnion<BuiltInProviderType>, ClientSafeProvider>
|
|
||||||
>('providers', __NEXTAUTH, logger)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Client-side method to initiate a signin flow
|
|
||||||
* or send the user to the signin page listing all possible providers.
|
|
||||||
* Automatically adds the CSRF token to the request.
|
|
||||||
*
|
|
||||||
* [Documentation](https://next-auth.js.org/getting-started/client#signin)
|
|
||||||
*/
|
|
||||||
export async function signIn<
|
|
||||||
P extends RedirectableProviderType | undefined = undefined,
|
|
||||||
> (
|
|
||||||
provider?: LiteralUnion<BuiltInProviderType>,
|
|
||||||
options?: SignInOptions,
|
|
||||||
authorizationParams?: SignInAuthorizationParams
|
|
||||||
): Promise<
|
|
||||||
P extends RedirectableProviderType ? SignInResponse | undefined : undefined
|
|
||||||
> {
|
|
||||||
const { callbackUrl = window.location.href, redirect = true } = options ?? {}
|
|
||||||
|
|
||||||
const baseUrl = apiBaseUrl(__NEXTAUTH)
|
|
||||||
const providers = await getProviders()
|
|
||||||
|
|
||||||
if (!providers) {
|
|
||||||
window.location.href = `${baseUrl}/error`
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!provider || !(provider in providers)) {
|
|
||||||
window.location.href = `${baseUrl}/signin?${new URLSearchParams({
|
|
||||||
callbackUrl
|
|
||||||
})}`
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const isCredentials = providers[provider].type === 'credentials'
|
|
||||||
const isEmail = providers[provider].type === 'email'
|
|
||||||
const isSupportingReturn = isCredentials || isEmail
|
|
||||||
|
|
||||||
const signInUrl = `${baseUrl}/${
|
|
||||||
isCredentials ? 'callback' : 'signin'
|
|
||||||
}/${provider}`
|
|
||||||
|
|
||||||
const _signInUrl = `${signInUrl}?${new URLSearchParams(authorizationParams)}`
|
|
||||||
|
|
||||||
const res = await fetch(_signInUrl, {
|
|
||||||
method: 'post',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/x-www-form-urlencoded'
|
|
||||||
},
|
|
||||||
// @ts-expect-error: Internal
|
|
||||||
body: new URLSearchParams({
|
|
||||||
...options,
|
|
||||||
csrfToken: await getCsrfToken(),
|
|
||||||
callbackUrl,
|
|
||||||
json: true
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const data = await res.json()
|
|
||||||
|
|
||||||
if (redirect || !isSupportingReturn) {
|
|
||||||
const url = data.url ?? callbackUrl
|
|
||||||
window.location.href = url
|
|
||||||
// If url contains a hash, the browser does not reload the page. We reload manually
|
|
||||||
if (url.includes('#')) { window.location.reload() }
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const error = new URL(data.url).searchParams.get('error')
|
|
||||||
|
|
||||||
if (res.ok) { await __NEXTAUTH._getSession({ event: 'storage' }) }
|
|
||||||
|
|
||||||
return {
|
|
||||||
error,
|
|
||||||
status: res.status,
|
|
||||||
ok: res.ok,
|
|
||||||
url: error ? null : data.url
|
|
||||||
} as any
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signs the user out, by removing the session cookie.
|
|
||||||
* Automatically adds the CSRF token to the request.
|
|
||||||
*
|
|
||||||
* [Documentation](https://next-auth.js.org/getting-started/client#signout)
|
|
||||||
*/
|
|
||||||
export async function signOut<R extends boolean = true> (
|
|
||||||
options?: SignOutParams<R>
|
|
||||||
): Promise<R extends true ? undefined : SignOutResponse> {
|
|
||||||
const { callbackUrl = window.location.href } = options ?? {}
|
|
||||||
const baseUrl = apiBaseUrl(__NEXTAUTH)
|
|
||||||
const fetchOptions = {
|
|
||||||
method: 'post',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/x-www-form-urlencoded'
|
|
||||||
},
|
|
||||||
// @ts-expect-error: Internal
|
|
||||||
body: new URLSearchParams({
|
|
||||||
csrfToken: await getCsrfToken(),
|
|
||||||
callbackUrl,
|
|
||||||
json: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const res = await fetch(`${baseUrl}/signout`, fetchOptions)
|
|
||||||
const data = await res.json()
|
|
||||||
|
|
||||||
broadcast.post({ event: 'session', data: { trigger: 'signout' } })
|
|
||||||
|
|
||||||
if (options?.redirect ?? true) {
|
|
||||||
const url = data.url ?? callbackUrl
|
|
||||||
window.location.href = url
|
|
||||||
// If url contains a hash, the browser does not reload the page. We reload manually
|
|
||||||
if (url.includes('#')) { window.location.reload() }
|
|
||||||
// @ts-expect-error: Internal
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await __NEXTAUTH._getSession({ event: 'storage' })
|
|
||||||
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SessionProviderPlugin (options: SessionProviderProps): Plugin {
|
|
||||||
return {
|
|
||||||
install (app) {
|
|
||||||
const { basePath } = options
|
|
||||||
|
|
||||||
if (basePath) { __NEXTAUTH.basePath = basePath }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If session was `null`, there was an attempt to fetch it,
|
|
||||||
* but it failed, but we still treat it as a valid initial value.
|
|
||||||
*/
|
|
||||||
const hasInitialSession = options.session !== undefined
|
|
||||||
|
|
||||||
/** If session was passed, initialize as already synced */
|
|
||||||
__NEXTAUTH._lastSync = hasInitialSession ? now() : 0
|
|
||||||
|
|
||||||
if (hasInitialSession) { __NEXTAUTH._session = options.session }
|
|
||||||
|
|
||||||
const session = ref(options.session)
|
|
||||||
|
|
||||||
/** If session was passed, initialize as not loading */
|
|
||||||
const loading = ref(!hasInitialSession)
|
|
||||||
|
|
||||||
__NEXTAUTH._getSession = async ({ event } = {}) => {
|
|
||||||
try {
|
|
||||||
const storageEvent = event === 'storage'
|
|
||||||
// We should always update if we don't have a client session yet
|
|
||||||
// or if there are events from other tabs/windows
|
|
||||||
if (storageEvent || __NEXTAUTH._session === undefined) {
|
|
||||||
__NEXTAUTH._lastSync = now()
|
|
||||||
__NEXTAUTH._session = await getSession({
|
|
||||||
broadcast: !storageEvent
|
|
||||||
})
|
|
||||||
session.value = __NEXTAUTH._session
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
// If there is no time defined for when a session should be considered
|
|
||||||
// stale, then it's okay to use the value we have until an event is
|
|
||||||
// triggered which updates it
|
|
||||||
!event ||
|
|
||||||
// If the client doesn't have a session then we don't need to call
|
|
||||||
// the server to check if it does (if they have signed in via another
|
|
||||||
// tab or window that will come through as a "stroage" event
|
|
||||||
// event anyway)
|
|
||||||
__NEXTAUTH._session === null ||
|
|
||||||
// Bail out early if the client session is not stale yet
|
|
||||||
now() < __NEXTAUTH._lastSync
|
|
||||||
) { return }
|
|
||||||
|
|
||||||
// An event or session staleness occurred, update the client session.
|
|
||||||
__NEXTAUTH._lastSync = now()
|
|
||||||
__NEXTAUTH._session = await getSession()
|
|
||||||
session.value = __NEXTAUTH._session
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('CLIENT_SESSION_ERROR', error as Error)
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
__NEXTAUTH._getSession()
|
|
||||||
|
|
||||||
const { refetchOnWindowFocus = true } = options
|
|
||||||
|
|
||||||
// Listen for when the page is visible, if the user switches tabs
|
|
||||||
// and makes our tab visible again, re-fetch the session, but only if
|
|
||||||
// this feature is not disabled.
|
|
||||||
const visibilityHandler = () => {
|
|
||||||
if (refetchOnWindowFocus && document.visibilityState === 'visible') { __NEXTAUTH._getSession({ event: 'visibilitychange' }) }
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('visibilitychange', visibilityHandler, false)
|
|
||||||
|
|
||||||
const unsubscribeFromBroadcast = broadcast.receive(() =>
|
|
||||||
__NEXTAUTH._getSession({ event: 'storage' })
|
|
||||||
)
|
|
||||||
|
|
||||||
const { refetchInterval } = options
|
|
||||||
let refetchIntervalTimer: NodeJS.Timer
|
|
||||||
|
|
||||||
if (refetchInterval) {
|
|
||||||
refetchIntervalTimer = setInterval(() => {
|
|
||||||
if (__NEXTAUTH._session) { __NEXTAUTH._getSession({ event: 'poll' }) }
|
|
||||||
}, refetchInterval * 1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
const originalUnmount = app.unmount
|
|
||||||
app.unmount = function nextAuthUnmount () {
|
|
||||||
document.removeEventListener('visibilitychange', visibilityHandler, false)
|
|
||||||
unsubscribeFromBroadcast?.()
|
|
||||||
clearInterval(refetchIntervalTimer)
|
|
||||||
__NEXTAUTH._lastSync = 0
|
|
||||||
__NEXTAUTH._session = undefined
|
|
||||||
__NEXTAUTH._getSession = () => {}
|
|
||||||
originalUnmount()
|
|
||||||
}
|
|
||||||
|
|
||||||
const status = computed(() => loading.value ? 'loading' : session.value ? 'authenticated' : 'unauthenticated')
|
|
||||||
const value = reactive({
|
|
||||||
data: session,
|
|
||||||
status
|
|
||||||
})
|
|
||||||
|
|
||||||
app.provide('SessionKey', value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Vue Composable that gives you access
|
|
||||||
* to the logged in user's session data.
|
|
||||||
*
|
|
||||||
* [Documentation](https://next-auth.js.org/getting-started/client#usesession)
|
|
||||||
*/
|
|
||||||
export function useSession (): {
|
|
||||||
data: Ref<SessionProviderProps['session']>;
|
|
||||||
status: Ref<string>;
|
|
||||||
} {
|
|
||||||
if (typeof window === 'undefined') {
|
|
||||||
return {
|
|
||||||
data: ref(null),
|
|
||||||
status: ref('loading')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const value = inject<{
|
|
||||||
data: SessionProviderProps['session']
|
|
||||||
status: string
|
|
||||||
}>('SessionKey')
|
|
||||||
if (!value) {
|
|
||||||
throw new Error('Could not resolve provided session value')
|
|
||||||
}
|
|
||||||
const { data, status } = toRefs(value)
|
|
||||||
|
|
||||||
return {
|
|
||||||
data,
|
|
||||||
status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
import type { Adapter } from 'next-auth/adapters'
|
|
||||||
import type { EventCallbacks, LoggerInstance } from 'next-auth'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Same as the default `Error`, but it is JSON serializable.
|
|
||||||
* @source https://iaincollins.medium.com/error-handling-in-javascript-a6172ccdf9af
|
|
||||||
*/
|
|
||||||
export class UnknownError extends Error {
|
|
||||||
code: string
|
|
||||||
constructor (error: Error | string) {
|
|
||||||
super((error as Error)?.message ?? error)
|
|
||||||
this.name = 'UnknownError'
|
|
||||||
this.code = (error as any).code
|
|
||||||
if (error instanceof Error) { this.stack = error.stack }
|
|
||||||
}
|
|
||||||
|
|
||||||
toJSON () {
|
|
||||||
return {
|
|
||||||
name: this.name,
|
|
||||||
message: this.message,
|
|
||||||
stack: this.stack
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class OAuthCallbackError extends UnknownError {
|
|
||||||
name = 'OAuthCallbackError'
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Thrown when an Email address is already associated with an account
|
|
||||||
* but the user is trying an OAuth account that is not linked to it.
|
|
||||||
*/
|
|
||||||
export class AccountNotLinkedError extends UnknownError {
|
|
||||||
name = 'AccountNotLinkedError'
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MissingAPIRoute extends UnknownError {
|
|
||||||
name = 'MissingAPIRouteError'
|
|
||||||
code = 'MISSING_NEXTAUTH_API_ROUTE_ERROR'
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MissingSecret extends UnknownError {
|
|
||||||
name = 'MissingSecretError'
|
|
||||||
code = 'NO_SECRET'
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MissingAuthorize extends UnknownError {
|
|
||||||
name = 'MissingAuthorizeError'
|
|
||||||
code = 'CALLBACK_CREDENTIALS_HANDLER_ERROR'
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MissingAdapter extends UnknownError {
|
|
||||||
name = 'MissingAdapterError'
|
|
||||||
code = 'EMAIL_REQUIRES_ADAPTER_ERROR'
|
|
||||||
}
|
|
||||||
|
|
||||||
export class UnsupportedStrategy extends UnknownError {
|
|
||||||
name = 'UnsupportedStrategyError'
|
|
||||||
code = 'CALLBACK_CREDENTIALS_JWT_ERROR'
|
|
||||||
}
|
|
||||||
|
|
||||||
type Method = (...args: any[]) => Promise<any>
|
|
||||||
|
|
||||||
export function upperSnake (s: string) {
|
|
||||||
return s.replace(/([A-Z])/g, '_$1').toUpperCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
export function capitalize (s: string) {
|
|
||||||
return `${s[0].toUpperCase()}${s.slice(1)}`
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wraps an object of methods and adds error handling.
|
|
||||||
*/
|
|
||||||
export function eventsErrorHandler (
|
|
||||||
methods: Partial<EventCallbacks>,
|
|
||||||
logger: LoggerInstance
|
|
||||||
): Partial<EventCallbacks> {
|
|
||||||
return Object.keys(methods).reduce<any>((acc, name) => {
|
|
||||||
acc[name] = async (...args: any[]) => {
|
|
||||||
try {
|
|
||||||
const method: Method = methods[name as keyof Method]
|
|
||||||
return await method(...args)
|
|
||||||
} catch (e) {
|
|
||||||
logger.error(`${upperSnake(name)}_EVENT_ERROR`, e as Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return acc
|
|
||||||
}, {})
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Handles adapter induced errors. */
|
|
||||||
export function adapterErrorHandler (
|
|
||||||
adapter: Adapter | undefined,
|
|
||||||
logger: LoggerInstance
|
|
||||||
): Adapter | undefined {
|
|
||||||
if (!adapter) { return }
|
|
||||||
|
|
||||||
return Object.keys(adapter).reduce<any>((acc, name) => {
|
|
||||||
acc[name] = async (...args: any[]) => {
|
|
||||||
try {
|
|
||||||
logger.debug(`adapter_${name}`, { args })
|
|
||||||
const method: Method = adapter[name as keyof Method]
|
|
||||||
return await method(...args)
|
|
||||||
} catch (error) {
|
|
||||||
logger.error(`adapter_error_${name}`, error as Error)
|
|
||||||
const e = new UnknownError(error as Error)
|
|
||||||
e.name = `${capitalize(name)}Error`
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return acc
|
|
||||||
}, {})
|
|
||||||
}
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
import { UnknownError } from './errors'
|
|
||||||
|
|
||||||
// TODO: better typing
|
|
||||||
/** Makes sure that error is always serializable */
|
|
||||||
function formatError (o: unknown): unknown {
|
|
||||||
if (o instanceof Error && !(o instanceof UnknownError)) { return { message: o.message, stack: o.stack, name: o.name } }
|
|
||||||
|
|
||||||
if (hasErrorProperty(o)) {
|
|
||||||
o.error = formatError(o.error) as Error
|
|
||||||
o.message = o.message ?? o.error.message
|
|
||||||
}
|
|
||||||
return o
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasErrorProperty (
|
|
||||||
x: unknown
|
|
||||||
): x is { error: Error; [key: string]: unknown } {
|
|
||||||
return !!(x as any)?.error
|
|
||||||
}
|
|
||||||
|
|
||||||
export type WarningCode =
|
|
||||||
| 'NEXTAUTH_URL'
|
|
||||||
| 'NO_SECRET'
|
|
||||||
| 'TWITTER_OAUTH_2_BETA'
|
|
||||||
| 'DEBUG_ENABLED'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Override any of the methods, and the rest will use the default logger.
|
|
||||||
*
|
|
||||||
* [Documentation](https://next-auth.js.org/configuration/options#logger)
|
|
||||||
*/
|
|
||||||
export interface LoggerInstance extends Record<string, Function> {
|
|
||||||
warn: (code: WarningCode) => void
|
|
||||||
error: (
|
|
||||||
code: string,
|
|
||||||
/**
|
|
||||||
* Either an instance of (JSON serializable) Error
|
|
||||||
* or an object that contains some debug information.
|
|
||||||
* (Error is still available through `metadata.error`)
|
|
||||||
*/
|
|
||||||
metadata: Error | { error: Error; [key: string]: unknown }
|
|
||||||
) => void
|
|
||||||
debug: (code: string, metadata: unknown) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
const _logger: LoggerInstance = {
|
|
||||||
error (code, metadata) {
|
|
||||||
metadata = formatError(metadata) as Error
|
|
||||||
console.error(
|
|
||||||
`[next-auth][error][${code}]`,
|
|
||||||
`\nhttps://next-auth.js.org/errors#${code.toLowerCase()}`,
|
|
||||||
metadata.message,
|
|
||||||
metadata
|
|
||||||
)
|
|
||||||
},
|
|
||||||
warn (code) {
|
|
||||||
console.warn(
|
|
||||||
`[next-auth][warn][${code}]`,
|
|
||||||
`\nhttps://next-auth.js.org/warnings#${code.toLowerCase()}`
|
|
||||||
)
|
|
||||||
},
|
|
||||||
debug (code, metadata) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(`[next-auth][debug][${code}]`, metadata)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Override the built-in logger with user's implementation.
|
|
||||||
* Any `undefined` level will use the default logger.
|
|
||||||
*/
|
|
||||||
export function setLogger (
|
|
||||||
newLogger: Partial<LoggerInstance> = {},
|
|
||||||
debug?: boolean
|
|
||||||
) {
|
|
||||||
// Turn off debug logging if `debug` isn't set to `true`
|
|
||||||
if (!debug) { _logger.debug = () => {} }
|
|
||||||
|
|
||||||
if (newLogger.error) { _logger.error = newLogger.error }
|
|
||||||
if (newLogger.warn) { _logger.warn = newLogger.warn }
|
|
||||||
if (newLogger.debug) { _logger.debug = newLogger.debug }
|
|
||||||
}
|
|
||||||
|
|
||||||
export default _logger
|
|
||||||
|
|
||||||
/** Serializes client-side log messages and sends them to the server */
|
|
||||||
export function proxyLogger (
|
|
||||||
logger: LoggerInstance = _logger,
|
|
||||||
basePath?: string
|
|
||||||
): LoggerInstance {
|
|
||||||
try {
|
|
||||||
if (typeof window === 'undefined') { return logger }
|
|
||||||
|
|
||||||
const clientLogger: Record<string, unknown> = {}
|
|
||||||
for (const level in logger) {
|
|
||||||
clientLogger[level] = (code: string, metadata: Error) => {
|
|
||||||
_logger[level](code, metadata) // Logs to console
|
|
||||||
|
|
||||||
if (level === 'error') {
|
|
||||||
metadata = formatError(metadata) as Error
|
|
||||||
}(metadata as any).client = true
|
|
||||||
const url = `${basePath}/_log`
|
|
||||||
const body = new URLSearchParams({ level, code, ...(metadata as any) })
|
|
||||||
if (navigator.sendBeacon) { return navigator.sendBeacon(url, body) }
|
|
||||||
|
|
||||||
return fetch(url, { method: 'POST', body, keepalive: true })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return clientLogger as unknown as LoggerInstance
|
|
||||||
} catch {
|
|
||||||
return _logger
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
export interface InternalUrl {
|
|
||||||
/** @default "http://localhost:3000" */
|
|
||||||
origin: string
|
|
||||||
/** @default "localhost:3000" */
|
|
||||||
host: string
|
|
||||||
/** @default "/api/auth" */
|
|
||||||
path: string
|
|
||||||
/** @default "http://localhost:3000/api/auth" */
|
|
||||||
base: string
|
|
||||||
/** @default "http://localhost:3000/api/auth" */
|
|
||||||
toString: () => string
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns an `URL` like object to make requests/redirects from server-side */
|
|
||||||
export default function parseUrl (url?: string): InternalUrl {
|
|
||||||
const defaultUrl = new URL('http://localhost:3000/api/auth')
|
|
||||||
|
|
||||||
if (url && !url.startsWith('http')) { url = `https://${url}` }
|
|
||||||
|
|
||||||
const _url = new URL(url ?? defaultUrl)
|
|
||||||
const path = (_url.pathname === '/' ? defaultUrl.pathname : _url.pathname)
|
|
||||||
// Remove trailing slash
|
|
||||||
.replace(/\/$/, '')
|
|
||||||
|
|
||||||
const base = `${_url.origin}${path}`
|
|
||||||
|
|
||||||
return {
|
|
||||||
origin: _url.origin,
|
|
||||||
host: _url.host,
|
|
||||||
path,
|
|
||||||
base,
|
|
||||||
toString: () => base
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
// @ts-expect-error: Nuxt auto-import
|
|
||||||
import { defineNuxtPlugin } from '#app'
|
|
||||||
import { SessionProviderPlugin } from './client'
|
|
||||||
|
|
||||||
export default defineNuxtPlugin((nuxtApp) => {
|
|
||||||
nuxtApp.vueApp.use(SessionProviderPlugin({}))
|
|
||||||
})
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
import type { NextAuthAction, NextAuthOptions, Session } from 'next-auth'
|
|
||||||
import type { RequestInternal } from 'next-auth/core'
|
|
||||||
import { NextAuthHandler } from 'next-auth/core'
|
|
||||||
import {
|
|
||||||
appendHeader,
|
|
||||||
defineEventHandler,
|
|
||||||
isMethod,
|
|
||||||
sendRedirect,
|
|
||||||
setCookie,
|
|
||||||
readBody,
|
|
||||||
parseCookies,
|
|
||||||
getQuery
|
|
||||||
} from 'h3'
|
|
||||||
import type { H3Event } from 'h3'
|
|
||||||
|
|
||||||
export function NextAuthNuxtHandler (options: NextAuthOptions) {
|
|
||||||
return defineEventHandler(async (event) => {
|
|
||||||
// Catch-all route params in Nuxt goes to the underscore property
|
|
||||||
const nextauth = event.context.params._.split('/')
|
|
||||||
|
|
||||||
const req: RequestInternal | Request = {
|
|
||||||
host: process.env.NEXTAUTH_URL,
|
|
||||||
body: undefined,
|
|
||||||
query: getQuery(event),
|
|
||||||
headers: event.req.headers,
|
|
||||||
method: event.req.method,
|
|
||||||
cookies: parseCookies(event),
|
|
||||||
action: nextauth[0] as NextAuthAction,
|
|
||||||
providerId: nextauth[1],
|
|
||||||
error: nextauth[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isMethod(event, 'POST')) {
|
|
||||||
req.body = await readBody(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await NextAuthHandler({
|
|
||||||
req,
|
|
||||||
options
|
|
||||||
})
|
|
||||||
|
|
||||||
const { headers, cookies, body, redirect, status = 200 } = response
|
|
||||||
event.res.statusCode = status
|
|
||||||
|
|
||||||
headers?.forEach((header) => {
|
|
||||||
appendHeader(event, header.key, header.value)
|
|
||||||
})
|
|
||||||
|
|
||||||
cookies?.forEach((cookie) => {
|
|
||||||
setCookie(event, cookie.name, cookie.value, cookie.options)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (redirect) {
|
|
||||||
if (isMethod(event, 'POST')) {
|
|
||||||
const body = await readBody(event)
|
|
||||||
if (body?.json !== 'true') { await sendRedirect(event, redirect, 302) }
|
|
||||||
|
|
||||||
return {
|
|
||||||
url: redirect
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
await sendRedirect(event, redirect, 302)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return body
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getServerSession (
|
|
||||||
event: H3Event,
|
|
||||||
options: NextAuthOptions
|
|
||||||
): Promise<Session | null> {
|
|
||||||
options.secret = process.env.NEXTAUTH_SECRET
|
|
||||||
|
|
||||||
const session = await NextAuthHandler<Session>({
|
|
||||||
req: {
|
|
||||||
host: process.env.NEXTAUTH_URL,
|
|
||||||
action: 'session',
|
|
||||||
method: 'GET',
|
|
||||||
cookies: parseCookies(event),
|
|
||||||
headers: event.req.headers
|
|
||||||
},
|
|
||||||
options
|
|
||||||
})
|
|
||||||
|
|
||||||
const { body } = session
|
|
||||||
|
|
||||||
if (body && Object.keys(body).length) {
|
|
||||||
return body
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
import type { Session } from 'next-auth'
|
|
||||||
import type { BuiltInProviderType, ProviderType } from 'next-auth/providers'
|
|
||||||
|
|
||||||
export interface UseSessionOptions<R extends boolean> {
|
|
||||||
required: R
|
|
||||||
/** Defaults to `signIn` */
|
|
||||||
onUnauthenticated?: () => void
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Util type that matches some strings literally, but allows any other string as well.
|
|
||||||
* @source https://github.com/microsoft/TypeScript/issues/29729#issuecomment-832522611
|
|
||||||
*/
|
|
||||||
export type LiteralUnion<T extends U, U = string> =
|
|
||||||
| T
|
|
||||||
| (U & Record<never, never>)
|
|
||||||
|
|
||||||
export interface ClientSafeProvider {
|
|
||||||
id: LiteralUnion<BuiltInProviderType>
|
|
||||||
name: string
|
|
||||||
type: ProviderType
|
|
||||||
signinUrl: string
|
|
||||||
callbackUrl: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SignInOptions extends Record<string, unknown> {
|
|
||||||
/**
|
|
||||||
* Defaults to the current URL.
|
|
||||||
* @docs https://next-auth.js.org/getting-started/client#specifying-a-callbackurl
|
|
||||||
*/
|
|
||||||
callbackUrl?: string
|
|
||||||
/** @docs https://next-auth.js.org/getting-started/client#using-the-redirect-false-option */
|
|
||||||
redirect?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SignInResponse {
|
|
||||||
error: string | undefined
|
|
||||||
status: number
|
|
||||||
ok: boolean
|
|
||||||
url: string | null
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Match `inputType` of `new URLSearchParams(inputType)` */
|
|
||||||
export type SignInAuthorizationParams =
|
|
||||||
| string
|
|
||||||
| string[][]
|
|
||||||
| Record<string, string>
|
|
||||||
| URLSearchParams
|
|
||||||
|
|
||||||
/** @docs https://next-auth.js.org/getting-started/client#using-the-redirect-false-option-1 */
|
|
||||||
export interface SignOutResponse {
|
|
||||||
url: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SignOutParams<R extends boolean = true> {
|
|
||||||
/** @docs https://next-auth.js.org/getting-started/client#specifying-a-callbackurl-1 */
|
|
||||||
callbackUrl?: string
|
|
||||||
/** @docs https://next-auth.js.org/getting-started/client#using-the-redirect-false-option-1 */
|
|
||||||
redirect?: R
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @docs: https://next-auth.js.org/getting-started/client#options */
|
|
||||||
export interface SessionProviderProps {
|
|
||||||
// children: React.ReactNode
|
|
||||||
session?: Session | null
|
|
||||||
baseUrl?: string
|
|
||||||
basePath?: string
|
|
||||||
/**
|
|
||||||
* A time interval (in seconds) after which the session will be re-fetched.
|
|
||||||
* If set to `0` (default), the session is not polled.
|
|
||||||
*/
|
|
||||||
refetchInterval?: number
|
|
||||||
/**
|
|
||||||
* `SessionProvider` automatically refetches the session when the user switches between windows.
|
|
||||||
* This option activates this behaviour if set to `true` (default).
|
|
||||||
*/
|
|
||||||
refetchOnWindowFocus?: boolean
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
// https://v3.nuxtjs.org/concepts/typescript
|
|
||||||
"extends": "./playground/.nuxt/tsconfig.json"
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
GITHUB_CLIENT_ID=
|
VITE_GITHUB_CLIENT_ID=
|
||||||
GITHUB_CLIENT_SECRET=
|
VITE_GITHUB_CLIENT_SECRET=
|
||||||
NEXTAUTH_SECRET=
|
VITE_NEXTAUTH_URL=
|
||||||
PUBLIC_NEXTAUTH_URL=http://localhost:5173
|
VITE_NEXTAUTH_SECRET=
|
||||||
@@ -7,7 +7,7 @@ module.exports = {
|
|||||||
"prettier",
|
"prettier",
|
||||||
],
|
],
|
||||||
plugins: ["svelte3", "@typescript-eslint"],
|
plugins: ["svelte3", "@typescript-eslint"],
|
||||||
ignorePatterns: ["*.cjs", "build/**/*"],
|
ignorePatterns: ["*.cjs"],
|
||||||
overrides: [{ files: ["*.svelte"], processor: "svelte3/svelte3" }],
|
overrides: [{ files: ["*.svelte"], processor: "svelte3/svelte3" }],
|
||||||
settings: {
|
settings: {
|
||||||
"svelte3/typescript": () => require("typescript"),
|
"svelte3/typescript": () => require("typescript"),
|
||||||
|
|||||||
@@ -4,71 +4,84 @@ NextAuth.js is committed to bringing easy authentication to other frameworks. ht
|
|||||||
|
|
||||||
SvelteKit support with NextAuth.js is currently experimental. This directory contains a minimal, proof-of-concept application. Parts of this is expected to be abstracted away into a package like `@next-auth/sveltekit`
|
SvelteKit support with NextAuth.js is currently experimental. This directory contains a minimal, proof-of-concept application. Parts of this is expected to be abstracted away into a package like `@next-auth/sveltekit`
|
||||||
|
|
||||||
## Running this Demo
|
|
||||||
|
|
||||||
- Copy `.env.example` to `.env`
|
|
||||||
- In `.env`, set `GITHUB_CLIENT_ID` and `GITHUB_CLIENT_SECRET`
|
|
||||||
- See [https://docs.github.com/en/developers/apps/building-oauth-apps/creating-an-oauth-app](https://docs.github.com/en/developers/apps/building-oauth-apps/creating-an-oauth-app))
|
|
||||||
- When creating the OAuth app, set "Homepage URL" to `http://localhost:5173` and Authorization callack URL to `http://localhost:5173/api/auth/callback/github`
|
|
||||||
- In `.env`, set `NEXTAUTH_SECRET` to any random string
|
|
||||||
- Build and run the application: `yarn build && yarn start`
|
|
||||||
|
|
||||||
## Existing Project
|
## Existing Project
|
||||||
|
|
||||||
### Add API Route
|
### Add API route
|
||||||
|
|
||||||
To add NextAuth.js to a project create a file called `[...nextauth]/+server.js` in routes/api/auth. This contains the dynamic route handler for NextAuth.js which will also contain all of your global NextAuth.js configurations.
|
To add NextAuth.js to a project create a file called `[...nextauth].js` in routes/api/auth. This contains the dynamic route handler for NextAuth.js which will also contain all of your global NextAuth.js configurations.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { NextAuth, options } from "$lib/next-auth"
|
import NextAuth from "$lib"
|
||||||
|
import GithubProvider from "next-auth/providers/github"
|
||||||
|
|
||||||
export const { GET, POST } = NextAuth(options)
|
const nextAuthOptions = {
|
||||||
|
// Configure one or more authentication providers
|
||||||
|
providers: [
|
||||||
|
GithubProvider({
|
||||||
|
clientId: import.meta.env.VITE_GITHUB_CLIENT_ID,
|
||||||
|
clientSecret: import.meta.env.VITE_GITHUB_CLIENT_SECRET,
|
||||||
|
}),
|
||||||
|
// ...add more providers here
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
export const { get, post } = NextAuth(nextAuthOptions)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Add [hook](https://kit.svelte.dev/docs/hooks)
|
### Add [hook](https://kit.svelte.dev/docs/hooks)
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import type { Handle } from "@sveltejs/kit"
|
import { getServerSession } from "$lib"
|
||||||
import { getServerSession, options as nextAuthOptions } from "$lib/next-auth"
|
import GithubProvider from "next-auth/providers/github"
|
||||||
|
|
||||||
export const handle: Handle = async function handle({
|
const nextAuthOptions = {
|
||||||
event,
|
providers: [
|
||||||
resolve,
|
GithubProvider({
|
||||||
}): Promise<Response> {
|
clientId: import.meta.env.VITE_GITHUB_CLIENT_ID,
|
||||||
|
clientSecret: import.meta.env.VITE_GITHUB_CLIENT_SECRET,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function handle({ event, resolve }) {
|
||||||
const session = await getServerSession(event.request, nextAuthOptions)
|
const session = await getServerSession(event.request, nextAuthOptions)
|
||||||
event.locals.session = session
|
event.locals.session = session
|
||||||
|
|
||||||
return resolve(event)
|
return resolve(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getSession(event) {
|
||||||
|
return event.locals.session || {}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Load Session from Primary Layout
|
### Protecting a route
|
||||||
|
|
||||||
```ts
|
```html
|
||||||
// src/lib/routes/+layout.server.ts
|
<script context="module">
|
||||||
import type { LayoutServerLoad } from "./$types"
|
export async function load({ session }) {
|
||||||
|
const { user } = session
|
||||||
|
|
||||||
export const load: LayoutServerLoad = ({ locals }) => {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
session: locals.session,
|
status: 302,
|
||||||
|
redirect: "/",
|
||||||
}
|
}
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Protecting a Route
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// src/lib/routes/protected/+page.ts
|
|
||||||
import { redirect } from "@sveltejs/kit"
|
|
||||||
import type { PageLoad } from "./$types"
|
|
||||||
|
|
||||||
export const load: PageLoad = async ({ parent }) => {
|
|
||||||
const { session } = await parent()
|
|
||||||
if (!session?.user) {
|
|
||||||
throw redirect(302, "/")
|
|
||||||
}
|
}
|
||||||
return {}
|
|
||||||
}
|
return {
|
||||||
|
props: {
|
||||||
|
session,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export let session
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<p>Session expiry: {session.expires}</p>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Packaging lib
|
## Packaging lib
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user