mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
Compare commits
42 Commits
@next-auth
...
next-auth@
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
462cca1087 | ||
|
|
ab48fcfe5b | ||
|
|
fe7aaeded8 | ||
|
|
c53c09ea5c | ||
|
|
4bcba45294 | ||
|
|
eb5a9bad9d | ||
|
|
9a6d95c17c | ||
|
|
5b2fc7b570 | ||
|
|
6f459225fa | ||
|
|
f38ee19a8a | ||
|
|
38a03ed7d8 | ||
|
|
e1eb684cc6 | ||
|
|
777b7b2f23 | ||
|
|
6132c3fa75 | ||
|
|
94beef77e6 | ||
|
|
490d59dd17 | ||
|
|
26a8c5fc6d | ||
|
|
e26ec74720 | ||
|
|
d13997e140 | ||
|
|
d6efda077d | ||
|
|
0a4b99de3b | ||
|
|
2d2dfecc9d | ||
|
|
2a2c3d7a45 | ||
|
|
82786ac440 | ||
|
|
dfe3e02132 | ||
|
|
92b38ed740 | ||
|
|
97feae7916 | ||
|
|
24945895e9 | ||
|
|
6deccf610f | ||
|
|
f770b90219 | ||
|
|
87f4786917 | ||
|
|
191ef06471 | ||
|
|
75e6d8f0aa | ||
|
|
17999edd30 | ||
|
|
54b1845e58 | ||
|
|
879faf9fab | ||
|
|
3e3c36891e | ||
|
|
ac5d8a9795 | ||
|
|
965c6267e2 | ||
|
|
bfc429d20b | ||
|
|
2d8e910a19 | ||
|
|
d16e04848e |
29
.eslintrc.js
29
.eslintrc.js
@@ -3,10 +3,27 @@ const path = require("path")
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: "@typescript-eslint/parser",
|
||||
parserOptions: {
|
||||
project: [path.resolve(__dirname, "./packages/**/tsconfig.eslint.json")],
|
||||
},
|
||||
extends: ["standard-with-typescript", "prettier"],
|
||||
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",
|
||||
@@ -14,10 +31,6 @@ module.exports = {
|
||||
},
|
||||
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",
|
||||
},
|
||||
plugins: ["jest"],
|
||||
env: {
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/1_bug_framework.yml
vendored
1
.github/ISSUE_TEMPLATE/1_bug_framework.yml
vendored
@@ -5,6 +5,7 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
**NOTE:** Issues that are potentially security related should be reported to us by following the [Security guidelines](https://next-auth.js.org/security) rather than on GitHub.
|
||||
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
|
||||
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/2_bug_provider.yml
vendored
1
.github/ISSUE_TEMPLATE/2_bug_provider.yml
vendored
@@ -5,6 +5,7 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
**NOTE:** Issues that are potentially security related should be reported to us by following the [Security guidelines](https://next-auth.js.org/security) rather than on GitHub.
|
||||
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
|
||||
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/3_bug_adapter.yml
vendored
1
.github/ISSUE_TEMPLATE/3_bug_adapter.yml
vendored
@@ -5,6 +5,7 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
**NOTE:** Issues that are potentially security related should be reported to us by following the [Security guidelines](https://next-auth.js.org/security) rather than on GitHub.
|
||||
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
|
||||
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/5_feature_request.yml
vendored
1
.github/ISSUE_TEMPLATE/5_feature_request.yml
vendored
@@ -9,6 +9,7 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
**NOTE:** Issues that are potentially security related should be reported to us by following the [Security guidelines](https://next-auth.js.org/security) rather than on GitHub.
|
||||
Thank you very much for reaching out to us regarding the awesome feature that you believe should be included in the NextAuth.js library.
|
||||
|
||||
_NOTE: Feature requests are converted to [discussions (Ideas 💡)](https://github.com/nextauthjs/next-auth/discussions/categories/ideas). Make sure your idea hasn't been asked yet, and upvote the existing one before opening a new instead._
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/6_typescript.yml
vendored
1
.github/ISSUE_TEMPLATE/6_typescript.yml
vendored
@@ -17,6 +17,7 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
**NOTE:** Issues that are potentially security related should be reported to us by following the [Security guidelines](https://next-auth.js.org/security) rather than on GitHub.
|
||||
Make sure you [link]() to external documentation if necessary and provide inline code examples like so:
|
||||
|
||||
```js
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/7_question.yml
vendored
1
.github/ISSUE_TEMPLATE/7_question.yml
vendored
@@ -9,6 +9,7 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
**NOTE:** Issues that are potentially security related should be reported to us by following the [Security guidelines](https://next-auth.js.org/security) rather than on GitHub.
|
||||
We are glad that you have a question about this library. Please provide the following information:
|
||||
|
||||
- type: textarea
|
||||
|
||||
12
.github/PULL_REQUEST_TEMPLATE.md
vendored
12
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -5,9 +5,14 @@ Please fill out the information below to expedite the review and (hopefully)
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
@@ -23,6 +28,7 @@ Fixes: INSERT_ISSUE_LINK_HERE
|
||||
|
||||
## 📌 Resources
|
||||
|
||||
- [Contributing guidelines](./CONTRIBUTING.md)
|
||||
- [Code of conduct](./CODE_OF_CONDUCT.md)
|
||||
- [Security guidelines](../Security.md)
|
||||
- [Contributing guidelines](../CONTRIBUTING.md)
|
||||
- [Code of conduct](../CODE_OF_CONDUCT.md)
|
||||
- [Contributing to Open Source](https://kcd.im/pull-request)
|
||||
|
||||
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@@ -2,7 +2,7 @@ name: Code Analysis
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, beta, next]
|
||||
branches: [beta, next]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
schedule:
|
||||
|
||||
@@ -55,7 +55,7 @@ further defined and clarified by project maintainers.
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting info@balazsorban.com, yo@ndo.dev, thvu@hey.com and me@iaincollins.com.
|
||||
reported by contacting hi@thvu.dev, info@balazsorban.com, yo@ndo.dev and me@iaincollins.com.
|
||||
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
|
||||
is obligated to maintain confidentiality with regard to the reporter of an
|
||||
|
||||
@@ -26,7 +26,6 @@ Anyone can be a contributor. Either you found a typo, or you have an awesome fea
|
||||
|
||||
A quick guide on how to setup _next-auth_ locally to work on it and test out any changes:
|
||||
|
||||
|
||||
1. Clone the repo:
|
||||
|
||||
```sh
|
||||
@@ -34,13 +33,21 @@ git clone git@github.com:nextauthjs/next-auth.git
|
||||
cd next-auth
|
||||
```
|
||||
|
||||
1. Install packages. Developing requires Node.js v16:
|
||||
2. Set up the correct pnpm version, using [Corepack](https://nodejs.org/api/corepack.html). Run the following in the project'a root:
|
||||
|
||||
```sh
|
||||
corepack enable pnpm
|
||||
```
|
||||
|
||||
(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. Install packages. Developing requires Node.js v16:
|
||||
|
||||
```sh
|
||||
pnpm install
|
||||
```
|
||||
|
||||
3. Populate `.env.local`:
|
||||
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.
|
||||
|
||||
@@ -52,11 +59,12 @@ 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.
|
||||
> You can find the next-auth config under`apps/dev/pages/api/auth/[...nextauth].js`.
|
||||
|
||||
4. Start the developer application/server:
|
||||
5. Start the developer application/server:
|
||||
|
||||
```sh
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
Your developer application will be available on `http://localhost:3000`
|
||||
|
||||
That's it! 🎉
|
||||
|
||||
@@ -13,7 +13,7 @@ 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.
|
||||
- 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 info@balazsorban.com, yo@ndo.dev, thvu@hey.com 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 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.)
|
||||
|
||||
> 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.
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"scripts": {
|
||||
"clean": "rm -rf .next",
|
||||
"dev": "next dev",
|
||||
"lint": "next lint",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"email": "fake-smtp-server",
|
||||
@@ -18,7 +19,7 @@
|
||||
"@next-auth/typeorm-legacy-adapter": "workspace:*",
|
||||
"@prisma/client": "^3",
|
||||
"faunadb": "^4",
|
||||
"next": "12.2.0",
|
||||
"next": "12.3.1",
|
||||
"next-auth": "workspace:*",
|
||||
"nodemailer": "^6",
|
||||
"react": "^18",
|
||||
|
||||
@@ -18,6 +18,7 @@ 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 Hubspot from "next-auth/providers/hubspot"
|
||||
import IDS4 from "next-auth/providers/identity-server4"
|
||||
import Instagram from "next-auth/providers/instagram"
|
||||
import Keycloak from "next-auth/providers/keycloak"
|
||||
@@ -35,47 +36,37 @@ import Twitter, { TwitterLegacy } from "next-auth/providers/twitter"
|
||||
import Vk from "next-auth/providers/vk"
|
||||
import Wikimedia from "next-auth/providers/wikimedia"
|
||||
import WorkOS from "next-auth/providers/workos"
|
||||
import Zitadel from "next-auth/providers/zitadel"
|
||||
|
||||
// Adapters
|
||||
import { PrismaClient } from "@prisma/client"
|
||||
import { PrismaAdapter } from "@next-auth/prisma-adapter"
|
||||
import { Client as FaunaClient } from "faunadb"
|
||||
import { FaunaAdapter } from "@next-auth/fauna-adapter"
|
||||
import { TypeORMLegacyAdapter } from "@next-auth/typeorm-legacy-adapter"
|
||||
|
||||
// Add an adapter you want to test here.
|
||||
const adapters = {
|
||||
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)
|
||||
},
|
||||
noop() {
|
||||
return undefined
|
||||
},
|
||||
}
|
||||
// // Prisma
|
||||
// import { PrismaClient } from "@prisma/client"
|
||||
// import { PrismaAdapter } from "@next-auth/prisma-adapter"
|
||||
// const client = globalThis.prisma || new PrismaClient()
|
||||
// if (process.env.NODE_ENV !== "production") globalThis.prisma = client
|
||||
// const adapter = PrismaAdapter(client)
|
||||
|
||||
// // Fauna
|
||||
// import { Client as FaunaClient } from "faunadb"
|
||||
// import { FaunaAdapter } from "@next-auth/fauna-adapter"
|
||||
// const opts = { secret: process.env.FAUNA_SECRET, domain: process.env.FAUNA_DOMAIN }
|
||||
// const client = globalThis.fauna || new FaunaClient(opts)
|
||||
// if (process.env.NODE_ENV !== "production") globalThis.fauna = client
|
||||
// const adapter = FaunaAdapter(client)
|
||||
|
||||
// // TypeORM
|
||||
// import { TypeORMLegacyAdapter } from "@next-auth/typeorm-legacy-adapter"
|
||||
// const adapter = TypeORMLegacyAdapter({
|
||||
// type: "sqlite",
|
||||
// name: "next-auth-test-memory",
|
||||
// database: "./typeorm/dev.db",
|
||||
// synchronize: true,
|
||||
// })
|
||||
|
||||
export const authOptions: NextAuthOptions = {
|
||||
adapter: adapters.noop(),
|
||||
debug: true,
|
||||
// adapter,
|
||||
debug: process.env.NODE_ENV !== "production",
|
||||
theme: {
|
||||
logo: "https://next-auth.js.org/img/logo/logo-sm.png",
|
||||
brandColor: "#1786fb",
|
||||
@@ -102,6 +93,7 @@ export const authOptions: NextAuthOptions = {
|
||||
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 }),
|
||||
Hubspot({ clientId: process.env.HUBSPOT_ID, clientSecret: process.env.HUBSPOT_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 }),
|
||||
@@ -120,6 +112,7 @@ export const authOptions: NextAuthOptions = {
|
||||
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 }),
|
||||
Zitadel({ issuer: process.env.ZITADEL_ISSUER, clientId: process.env.ZITADEL_CLIENT_ID, clientSecret: process.env.ZITADEL_CLIENT_SECRET }),
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ export default async (req, res) => {
|
||||
res.send({
|
||||
content:
|
||||
"This is protected content. You can access this content because you are signed in.",
|
||||
session,
|
||||
})
|
||||
} else {
|
||||
res.send({
|
||||
|
||||
18
apps/dev/types/nextauth.d.ts
vendored
Normal file
18
apps/dev/types/nextauth.d.ts
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
// 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 {
|
||||
user: {
|
||||
/** The user's postal address. */
|
||||
address: string
|
||||
} & User
|
||||
}
|
||||
|
||||
interface User {
|
||||
foo: string
|
||||
}
|
||||
}
|
||||
104
apps/example-nextjs/.gitignore
vendored
104
apps/example-nextjs/.gitignore
vendored
@@ -1,110 +1,20 @@
|
||||
# Logs
|
||||
.DS_Store
|
||||
|
||||
node_modules/
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# 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
|
||||
.yarn-integrity
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
.now
|
||||
.env.local
|
||||
|
||||
.DS_Store
|
||||
.env*.local
|
||||
@@ -2,12 +2,16 @@ import { SessionProvider } from "next-auth/react"
|
||||
import "./styles.css"
|
||||
|
||||
import type { AppProps } from "next/app"
|
||||
import type { Session } from "next-auth"
|
||||
|
||||
// Use of the <SessionProvider> is mandatory to allow components that call
|
||||
// `useSession()` anywhere in your application to access the `session` object.
|
||||
export default function App({ Component, pageProps }: AppProps) {
|
||||
export default function App({
|
||||
Component,
|
||||
pageProps: { session, ...pageProps },
|
||||
}: AppProps<{ session: Session }>) {
|
||||
return (
|
||||
<SessionProvider session={pageProps.session} refetchInterval={0}>
|
||||
<SessionProvider session={session}>
|
||||
<Component {...pageProps} />
|
||||
</SessionProvider>
|
||||
)
|
||||
|
||||
@@ -4,8 +4,7 @@ import Layout from "../components/layout"
|
||||
import AccessDenied from "../components/access-denied"
|
||||
|
||||
export default function ProtectedPage() {
|
||||
const { data: session, status } = useSession()
|
||||
const loading = status === "loading"
|
||||
const { data: session } = useSession()
|
||||
const [content, setContent] = useState()
|
||||
|
||||
// Fetch content from protected route
|
||||
@@ -19,9 +18,7 @@ export default function ProtectedPage() {
|
||||
}
|
||||
fetchData()
|
||||
}, [session])
|
||||
|
||||
// When rendering client side don't display anything until loading is complete
|
||||
if (typeof window !== "undefined" && loading) return null
|
||||
|
||||
|
||||
// If no session exists, display access denied message
|
||||
if (!session) {
|
||||
|
||||
@@ -7,7 +7,7 @@ module.exports = {
|
||||
"prettier",
|
||||
],
|
||||
plugins: ["svelte3", "@typescript-eslint"],
|
||||
ignorePatterns: ["*.cjs"],
|
||||
ignorePatterns: ["*.cjs", "build/**/*"],
|
||||
overrides: [{ files: ["*.svelte"], processor: "svelte3/svelte3" }],
|
||||
settings: {
|
||||
"svelte3/typescript": () => require("typescript"),
|
||||
|
||||
@@ -4,6 +4,15 @@ 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`
|
||||
|
||||
## 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
|
||||
|
||||
### Add API Route
|
||||
|
||||
@@ -6,14 +6,16 @@
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"start": "HOST=127.0.0.1 PORT=5173 ORIGIN=http://localhost:5173 node ./build",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"lint": "prettier --check . && eslint .",
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-auto": "1.0.0-next.66",
|
||||
"@sveltejs/kit": "1.0.0-next.443",
|
||||
"@sveltejs/adapter-auto": "^1.0.0-next.80",
|
||||
"@sveltejs/adapter-node": "1.0.0-next.96",
|
||||
"@sveltejs/kit": "1.0.0-next.511",
|
||||
"@types/cookie": "^0.5.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.35.1",
|
||||
"@typescript-eslint/parser": "^5.35.1",
|
||||
@@ -27,7 +29,7 @@
|
||||
"svelte-preprocess": "^4.10.7",
|
||||
"tslib": "^2.4.0",
|
||||
"typescript": "~4.8.2",
|
||||
"vite": "^2.9.13"
|
||||
"vite": "^3.1.0"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body>
|
||||
<div>%sveltekit.body%</div>
|
||||
</body>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body>
|
||||
<div>%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -6,7 +6,9 @@ export const handle: Handle = async function handle({
|
||||
resolve,
|
||||
}): Promise<Response> {
|
||||
const session = await getServerSession(event.request, nextAuthOptions)
|
||||
event.locals.session = session
|
||||
if (session) {
|
||||
event.locals.session = session
|
||||
}
|
||||
|
||||
return resolve(event)
|
||||
}
|
||||
@@ -11,8 +11,8 @@ import {
|
||||
NEXTAUTH_SECRET,
|
||||
} from "$env/static/private"
|
||||
import { PUBLIC_NEXTAUTH_URL } from "$env/static/public"
|
||||
import getFormBody from "./utils/get-form-body"
|
||||
|
||||
// @ts-expect-error import is exported on .default during SSR
|
||||
const github = GithubProvider?.default || GithubProvider
|
||||
|
||||
export const options: NextAuthOptions = {
|
||||
@@ -24,9 +24,11 @@ export const options: NextAuthOptions = {
|
||||
],
|
||||
}
|
||||
|
||||
const toSvelteKitResponse = async (
|
||||
const toSvelteKitResponse = async <
|
||||
T extends string | any[] | Record<string, any>
|
||||
>(
|
||||
request: Request,
|
||||
nextAuthResponse: NextAuthResponse<unknown>
|
||||
nextAuthResponse: NextAuthResponse<T>
|
||||
): Promise<Response> => {
|
||||
const { cookies, redirect } = nextAuthResponse
|
||||
|
||||
@@ -50,14 +52,14 @@ const toSvelteKitResponse = async (
|
||||
let status = nextAuthResponse.status || 200
|
||||
|
||||
if (redirect) {
|
||||
let formData = null
|
||||
let formData: FormData | null = null
|
||||
try {
|
||||
formData = await request.formData()
|
||||
formData = getFormBody(formData)
|
||||
} catch {
|
||||
// no formData passed
|
||||
}
|
||||
if (formData?.json !== "true") {
|
||||
const { json } = Object.fromEntries(formData ?? [])
|
||||
if (json !== "true") {
|
||||
status = 302
|
||||
headers.set("Location", redirect)
|
||||
} else {
|
||||
@@ -79,17 +81,16 @@ const SKNextAuthHandler = async (
|
||||
options: NextAuthOptions
|
||||
): Promise<Response> => {
|
||||
const [action, provider] = params.nextauth!.split("/")
|
||||
let body = undefined
|
||||
let body: FormData | undefined
|
||||
try {
|
||||
body = await request.formData()
|
||||
body = getFormBody(body)
|
||||
} catch {
|
||||
// no formData passed
|
||||
}
|
||||
options.secret = NEXTAUTH_SECRET
|
||||
const req: RequestInternal = {
|
||||
host: PUBLIC_NEXTAUTH_URL,
|
||||
body,
|
||||
body: Object.fromEntries(body ?? []),
|
||||
query: Object.fromEntries(url.searchParams),
|
||||
headers: request.headers,
|
||||
method: request.method,
|
||||
@@ -135,8 +136,8 @@ export const getServerSession = async (
|
||||
export const NextAuth = (
|
||||
options: NextAuthOptions
|
||||
): {
|
||||
GET: (event) => Promise<unknown>
|
||||
POST: (event) => Promise<unknown>
|
||||
GET: (event: ServerLoadEvent) => Promise<unknown>
|
||||
POST: (event: ServerLoadEvent) => Promise<unknown>
|
||||
} => ({
|
||||
GET: (event) => SKNextAuthHandler(event, options),
|
||||
POST: (event) => SKNextAuthHandler(event, options),
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
// https://dev.to/danawoodman/getting-form-body-data-in-your-sveltekit-endpoints-4a85
|
||||
export default function getFormBody(
|
||||
body: FormData | null
|
||||
): Record<string, any> {
|
||||
if (!body) return {}
|
||||
|
||||
// @ts-expect-error: Entries property type missing
|
||||
return [...body.entries()].reduce((data, [k, v]) => {
|
||||
const value = v
|
||||
if (k in data)
|
||||
data[k] = Array.isArray(data[k]) ? [...data[k], value] : [data[k], value]
|
||||
else data[k] = value
|
||||
return data
|
||||
}, {})
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import adapter from "@sveltejs/adapter-auto"
|
||||
import adapter from "@sveltejs/adapter-node" // or use https://github.com/sveltejs/kit/tree/master/packages/adapter-auto
|
||||
import preprocess from "svelte-preprocess"
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
@@ -6,14 +6,8 @@ const config = {
|
||||
// Consult https://github.com/sveltejs/svelte-preprocess
|
||||
// for more information about preprocessors
|
||||
preprocess: preprocess(),
|
||||
|
||||
kit: {
|
||||
adapter: adapter(),
|
||||
|
||||
// Override http methods in the Todo forms
|
||||
methodOverride: {
|
||||
allowed: ["PATCH", "DELETE"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -10,14 +10,19 @@
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@cloudflare/workers-types@^3.14.0":
|
||||
version "3.15.0"
|
||||
resolved "https://registry.yarnpkg.com/@cloudflare/workers-types/-/workers-types-3.15.0.tgz#b8b17f923181c61220880aaa8ef30d75d59a1219"
|
||||
integrity sha512-meL/Afy5qdIsgfdnlbVfcYUh/YjHk23EWUvgmULf6iDrDbrBcd+fse2os3CC7rxSfScdP1OqJVTHgRSEjUm/Pw==
|
||||
version "3.16.0"
|
||||
resolved "https://registry.yarnpkg.com/@cloudflare/workers-types/-/workers-types-3.16.0.tgz#011658ca3f9810373e0eb4a2b5d6cabe4848d8d6"
|
||||
integrity sha512-gaBUSaKS65mN3iKZEgichbXYEmAa/pXkc5Gbt+1BptYphdGkj09ggdsiE4w8g0F/uI1g36QaTKrzVnBAWMipvQ==
|
||||
|
||||
"@esbuild/linux-loong64@0.14.54":
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz#de2a4be678bd4d0d1ffbb86e6de779cde5999028"
|
||||
integrity sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==
|
||||
"@esbuild/android-arm@0.15.10":
|
||||
version "0.15.10"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.15.10.tgz#a5f9432eb221afc243c321058ef25fe899886892"
|
||||
integrity sha512-FNONeQPy/ox+5NBkcSbYJxoXj9GWu8gVGJTVmUyoOCKQFDTrHVKgNSzChdNt0I8Aj/iKcsDf2r9BFwv+FSNUXg==
|
||||
|
||||
"@esbuild/linux-loong64@0.15.10":
|
||||
version "0.15.10"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.15.10.tgz#78a42897c2cf8db9fd5f1811f7590393b77774c7"
|
||||
integrity sha512-w0Ou3Z83LOYEkwaui2M8VwIp+nLi/NA60lBLMvaJ+vXVMcsARYdEzLNE7RSm4+lSg4zq4d7fAVuzk7PNQ5JFgg==
|
||||
|
||||
"@eslint/eslintrc@^1.3.1":
|
||||
version "1.3.1"
|
||||
@@ -82,9 +87,9 @@
|
||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||
|
||||
"@mapbox/node-pre-gyp@^1.0.5":
|
||||
version "1.0.9"
|
||||
resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.9.tgz#09a8781a3a036151cdebbe8719d6f8b25d4058bc"
|
||||
integrity sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw==
|
||||
version "1.0.10"
|
||||
resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz#8e6735ccebbb1581e5a7e652244cadc8a844d03c"
|
||||
integrity sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==
|
||||
dependencies:
|
||||
detect-libc "^2.0.0"
|
||||
https-proxy-agent "^5.0.0"
|
||||
@@ -127,6 +132,47 @@
|
||||
resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1"
|
||||
integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==
|
||||
|
||||
"@rollup/plugin-commonjs@^22.0.1":
|
||||
version "22.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-22.0.2.tgz#ee8ca8415cda30d383b4096aad5222435b4b69b6"
|
||||
integrity sha512-//NdP6iIwPbMTcazYsiBMbJW7gfmpHom33u1beiIoHDEM0Q9clvtQB1T0efvMqHeKsGohiHo97BCPCkBXdscwg==
|
||||
dependencies:
|
||||
"@rollup/pluginutils" "^3.1.0"
|
||||
commondir "^1.0.1"
|
||||
estree-walker "^2.0.1"
|
||||
glob "^7.1.6"
|
||||
is-reference "^1.2.1"
|
||||
magic-string "^0.25.7"
|
||||
resolve "^1.17.0"
|
||||
|
||||
"@rollup/plugin-json@^4.1.0":
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-4.1.0.tgz#54e09867ae6963c593844d8bd7a9c718294496f3"
|
||||
integrity sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==
|
||||
dependencies:
|
||||
"@rollup/pluginutils" "^3.0.8"
|
||||
|
||||
"@rollup/plugin-node-resolve@^14.1.0":
|
||||
version "14.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-14.1.0.tgz#f2fa475405cd7fed6420bf438fe393f988a9bc96"
|
||||
integrity sha512-5G2niJroNCz/1zqwXtk0t9+twOSDlG00k1Wfd7bkbbXmwg8H8dvgHdIWAun53Ps/rckfvOC7scDBjuGFg5OaWw==
|
||||
dependencies:
|
||||
"@rollup/pluginutils" "^3.1.0"
|
||||
"@types/resolve" "1.17.1"
|
||||
deepmerge "^4.2.2"
|
||||
is-builtin-module "^3.1.0"
|
||||
is-module "^1.0.0"
|
||||
resolve "^1.19.0"
|
||||
|
||||
"@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.1.0":
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b"
|
||||
integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==
|
||||
dependencies:
|
||||
"@types/estree" "0.0.39"
|
||||
estree-walker "^1.0.1"
|
||||
picomatch "^2.2.2"
|
||||
|
||||
"@rollup/pluginutils@^4.2.1":
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d"
|
||||
@@ -135,77 +181,96 @@
|
||||
estree-walker "^2.0.1"
|
||||
picomatch "^2.2.2"
|
||||
|
||||
"@sveltejs/adapter-auto@1.0.0-next.66":
|
||||
version "1.0.0-next.66"
|
||||
resolved "https://registry.yarnpkg.com/@sveltejs/adapter-auto/-/adapter-auto-1.0.0-next.66.tgz#93a6de02a7a4ebef11160bf8d012ef349839656e"
|
||||
integrity sha512-p78AQaSDHkLS5EFGqCF2xrLHMjKxx6wTLUvnP26cu2llh/VV4NihQ0rheVNgPWL+tGZpVznhrUG8fWmJxPciug==
|
||||
"@sveltejs/adapter-auto@^1.0.0-next.80":
|
||||
version "1.0.0-next.80"
|
||||
resolved "https://registry.yarnpkg.com/@sveltejs/adapter-auto/-/adapter-auto-1.0.0-next.80.tgz#15d331bbfca89cb39b719c3086d134cc1a7227b1"
|
||||
integrity sha512-352WoZr9fQgxJqgNENvxRr2gsA+wTF6V9AVaQaaatDYd3RVEBaXTYOOalFaRLSa25mRUJaLYP2aaliqczMl23g==
|
||||
dependencies:
|
||||
"@sveltejs/adapter-cloudflare" "1.0.0-next.32"
|
||||
"@sveltejs/adapter-netlify" "1.0.0-next.72"
|
||||
"@sveltejs/adapter-vercel" "1.0.0-next.68"
|
||||
"@sveltejs/adapter-cloudflare" "1.0.0-next.38"
|
||||
"@sveltejs/adapter-netlify" "1.0.0-next.78"
|
||||
"@sveltejs/adapter-vercel" "1.0.0-next.77"
|
||||
|
||||
"@sveltejs/adapter-cloudflare@1.0.0-next.32":
|
||||
version "1.0.0-next.32"
|
||||
resolved "https://registry.yarnpkg.com/@sveltejs/adapter-cloudflare/-/adapter-cloudflare-1.0.0-next.32.tgz#2e164e50786cf50d907f79a1d9ec8e0ab72a99df"
|
||||
integrity sha512-tzkUsdQlBk9xUjcGUOBYos4HKaeaXvz9v4TQ1QS2yIHEtL5xvMEDPZ94/DB2gPL4LZCnYbdY2lsy5HCsoN0hkQ==
|
||||
"@sveltejs/adapter-cloudflare@1.0.0-next.38":
|
||||
version "1.0.0-next.38"
|
||||
resolved "https://registry.yarnpkg.com/@sveltejs/adapter-cloudflare/-/adapter-cloudflare-1.0.0-next.38.tgz#b4c6e7314de79f2a3ab1d8e2f88274a8c224e325"
|
||||
integrity sha512-N6jdTomRZkdKlcNoguwYD7lpdXSt0beIyUJsp0MS/YLm/4gI83y698zFYInFKJ9t5e6DAnuEBSAXcg568z2oFA==
|
||||
dependencies:
|
||||
"@cloudflare/workers-types" "^3.14.0"
|
||||
esbuild "^0.14.48"
|
||||
esbuild "^0.15.7"
|
||||
worktop "0.8.0-next.14"
|
||||
|
||||
"@sveltejs/adapter-netlify@1.0.0-next.72":
|
||||
version "1.0.0-next.72"
|
||||
resolved "https://registry.yarnpkg.com/@sveltejs/adapter-netlify/-/adapter-netlify-1.0.0-next.72.tgz#5fe42a4944de476baa089cdc7f525ae6037585c9"
|
||||
integrity sha512-g570hYAMkgrJfo/TRg3DZFmlR7bNFHECFPOMgc8R+f28ROap/nXA8ICbiSBF7+zJ5JXvJbqHGjERSsyhEq+59g==
|
||||
"@sveltejs/adapter-netlify@1.0.0-next.78":
|
||||
version "1.0.0-next.78"
|
||||
resolved "https://registry.yarnpkg.com/@sveltejs/adapter-netlify/-/adapter-netlify-1.0.0-next.78.tgz#a18e750ac9d146afb308c527649f24c40b746d59"
|
||||
integrity sha512-Yyn/j/0QcLK3Db442ducLUZmyvkO74j7Gdcwu9xN0fQN3kBlCJP9Itx5o4SySrPFGc4Q8cLJ5ELNg+mWduLBAA==
|
||||
dependencies:
|
||||
"@iarna/toml" "^2.2.5"
|
||||
esbuild "^0.14.48"
|
||||
esbuild "^0.15.7"
|
||||
set-cookie-parser "^2.4.8"
|
||||
tiny-glob "^0.2.9"
|
||||
|
||||
"@sveltejs/adapter-vercel@1.0.0-next.68":
|
||||
version "1.0.0-next.68"
|
||||
resolved "https://registry.yarnpkg.com/@sveltejs/adapter-vercel/-/adapter-vercel-1.0.0-next.68.tgz#ccd0707f02d8aff9185b373a484366d961f58753"
|
||||
integrity sha512-ImM+fDwGkVaf920Wzh284nfAfu/WoPXCpMwog0kveIODVgCozbpJY55fO860LccqdS0YDyeFqOUrZJCqcYNx4w==
|
||||
"@sveltejs/adapter-node@1.0.0-next.96":
|
||||
version "1.0.0-next.96"
|
||||
resolved "https://registry.yarnpkg.com/@sveltejs/adapter-node/-/adapter-node-1.0.0-next.96.tgz#3f071a0677f787f9e8b2157b087b5403c4df4b6c"
|
||||
integrity sha512-tIHaRolUYy2PiHl4RUWaOsYxEjK5lN9501qzCzFbYr/uoLnZcnPGSXNJICwX0AX9AUkV6cvkZey6bLbUQcwH0Q==
|
||||
dependencies:
|
||||
"@vercel/nft" "^0.21.0"
|
||||
esbuild "^0.14.48"
|
||||
"@rollup/plugin-commonjs" "^22.0.1"
|
||||
"@rollup/plugin-json" "^4.1.0"
|
||||
"@rollup/plugin-node-resolve" "^14.1.0"
|
||||
rollup "^2.78.1"
|
||||
|
||||
"@sveltejs/kit@1.0.0-next.443":
|
||||
version "1.0.0-next.443"
|
||||
resolved "https://registry.yarnpkg.com/@sveltejs/kit/-/kit-1.0.0-next.443.tgz#5797a3c21d839562b3e96d19151beefa957dbf37"
|
||||
integrity sha512-Stdjwj6iL/u40O/+20UKTWtCBYxYaKwX2nRV5devrGk5hvq6/FbAqw+azwoWKvNoz3PGtrSmHIyeLcAx8iKQcw==
|
||||
"@sveltejs/adapter-vercel@1.0.0-next.77":
|
||||
version "1.0.0-next.77"
|
||||
resolved "https://registry.yarnpkg.com/@sveltejs/adapter-vercel/-/adapter-vercel-1.0.0-next.77.tgz#be9bfa2b46fd63723893d37ecdacbb1d735515c3"
|
||||
integrity sha512-r4MqtP+lzx83HfcvI8PU0Yxzmxt6WQq9nzZETLboJouJzhSBUFIN5RmNZfEn6nNIlUwZbGQUEK/FxsRnnxI/Ig==
|
||||
dependencies:
|
||||
"@sveltejs/vite-plugin-svelte" "^1.0.1"
|
||||
"@vercel/nft" "^0.22.0"
|
||||
esbuild "^0.15.7"
|
||||
|
||||
"@sveltejs/kit@1.0.0-next.511":
|
||||
version "1.0.0-next.511"
|
||||
resolved "https://registry.yarnpkg.com/@sveltejs/kit/-/kit-1.0.0-next.511.tgz#d045d0737ec93d21de5a27b6ca619daf79f8e4f7"
|
||||
integrity sha512-A/fxd4qHWDD07Mjo6qEEEfsBEkoj5C4/dPSzx6xPUoWmPvRPhU8t+P0oMc8BOn5YHOhPDq3coH8bmafbh73zKg==
|
||||
dependencies:
|
||||
"@sveltejs/vite-plugin-svelte" "^1.0.5"
|
||||
"@types/cookie" "^0.5.1"
|
||||
cookie "^0.5.0"
|
||||
devalue "^2.0.1"
|
||||
devalue "^4.0.0"
|
||||
kleur "^4.1.4"
|
||||
magic-string "^0.26.2"
|
||||
mime "^3.0.0"
|
||||
node-fetch "^3.2.4"
|
||||
sade "^1.8.1"
|
||||
set-cookie-parser "^2.4.8"
|
||||
sirv "^2.0.2"
|
||||
tiny-glob "^0.2.9"
|
||||
undici "^5.8.1"
|
||||
undici "^5.11.0"
|
||||
|
||||
"@sveltejs/vite-plugin-svelte@^1.0.1":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.3.tgz#f760677e03074b9f42335dbde33ba988c95d1e67"
|
||||
integrity sha512-0Qu51m2W9RBlxWPp8i31KJpnqmjWMOne8vAzgmOX6ZM9uX+/RAv6BNhEMcNoP5MsyLjyW1ZTCiJoaZZ5EeqpFg==
|
||||
"@sveltejs/vite-plugin-svelte@^1.0.5":
|
||||
version "1.0.9"
|
||||
resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.9.tgz#b19bde0ba1929a06205888e94475595e3ac0c258"
|
||||
integrity sha512-+SDrAnT7TDi8sdj4OfD2SC4s9DNrpNVBrue8fT2PmKks9Ddu0JIfSeX91wXZb/1xHz4EkGb+rli8GTRI0yGOjg==
|
||||
dependencies:
|
||||
"@rollup/pluginutils" "^4.2.1"
|
||||
debug "^4.3.4"
|
||||
deepmerge "^4.2.2"
|
||||
kleur "^4.1.5"
|
||||
magic-string "^0.26.2"
|
||||
svelte-hmr "^0.14.12"
|
||||
magic-string "^0.26.5"
|
||||
svelte-hmr "^0.15.0"
|
||||
|
||||
"@types/cookie@^0.5.1":
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.5.1.tgz#b29aa1f91a59f35e29ff8f7cb24faf1a3a750554"
|
||||
integrity sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==
|
||||
|
||||
"@types/estree@*":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2"
|
||||
integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==
|
||||
|
||||
"@types/estree@0.0.39":
|
||||
version "0.0.39"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
|
||||
integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
|
||||
|
||||
"@types/json-schema@^7.0.9":
|
||||
version "7.0.9"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
|
||||
@@ -221,6 +286,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/pug/-/pug-2.0.6.tgz#f830323c88172e66826d0bde413498b61054b5a6"
|
||||
integrity sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==
|
||||
|
||||
"@types/resolve@1.17.1":
|
||||
version "1.17.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6"
|
||||
integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/sass@^1.16.0":
|
||||
version "1.43.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/sass/-/sass-1.43.1.tgz#86bb0168e9e881d7dade6eba16c9ed6d25dc2f68"
|
||||
@@ -309,10 +381,10 @@
|
||||
"@typescript-eslint/types" "5.36.1"
|
||||
eslint-visitor-keys "^3.3.0"
|
||||
|
||||
"@vercel/nft@^0.21.0":
|
||||
version "0.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@vercel/nft/-/nft-0.21.0.tgz#e0715b1997cd7021a7c7c48b584ef2295fd4b810"
|
||||
integrity sha512-hFCAETfI5cG8l5iAiLhMC2bReC5K7SIybzrxGorv+eGspIbIFsVw7Vg85GovXm/LxA08pIDrAlrhR6GN36XB/Q==
|
||||
"@vercel/nft@^0.22.0":
|
||||
version "0.22.1"
|
||||
resolved "https://registry.yarnpkg.com/@vercel/nft/-/nft-0.22.1.tgz#0d91d2a21e3a7f0b23ce1550da9870eac4942828"
|
||||
integrity sha512-lYYZIoxRurqDOSoVIdBicGnpUIpfyaS5qVjdPq+EfI285WqtZK3NK/dyCkiyBul+X2U2OEhRyeMdXPCHGJbohw==
|
||||
dependencies:
|
||||
"@mapbox/node-pre-gyp" "^1.0.5"
|
||||
acorn "^8.6.0"
|
||||
@@ -443,6 +515,18 @@ buffer-crc32@^0.2.5:
|
||||
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
|
||||
integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
|
||||
|
||||
builtin-modules@^3.3.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6"
|
||||
integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==
|
||||
|
||||
busboy@^1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893"
|
||||
integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==
|
||||
dependencies:
|
||||
streamsearch "^1.1.0"
|
||||
|
||||
callsites@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
||||
@@ -493,6 +577,11 @@ color-support@^1.1.2:
|
||||
resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2"
|
||||
integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
|
||||
|
||||
commondir@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
|
||||
integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==
|
||||
|
||||
concat-map@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
@@ -522,11 +611,6 @@ cross-spawn@^7.0.2:
|
||||
shebang-command "^2.0.0"
|
||||
which "^2.0.1"
|
||||
|
||||
data-uri-to-buffer@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz#b5db46aea50f6176428ac05b73be39a57701a64b"
|
||||
integrity sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==
|
||||
|
||||
debug@4, debug@^4.3.4:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||
@@ -566,10 +650,10 @@ detect-libc@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd"
|
||||
integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==
|
||||
|
||||
devalue@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/devalue/-/devalue-2.0.1.tgz#5d368f9adc0928e47b77eea53ca60d2f346f9762"
|
||||
integrity sha512-I2TiqT5iWBEyB8GRfTDP0hiLZ0YeDJZ+upDxjBfOC2lebO5LezQMv7QvIUTzdb64jQyAKLf1AHADtGN+jw6v8Q==
|
||||
devalue@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/devalue/-/devalue-4.0.0.tgz#efeadbc922894d468ad903029d4a7e65e37f4952"
|
||||
integrity sha512-w25siwXyuMUqMr7jPlEjyNCp1vn0Jzj/fNg3qVt/r/Dpe8HjESh2V92L0jmh3uq4iJt0BvjH+Azk1pQzkcnDWA==
|
||||
|
||||
dir-glob@^3.0.1:
|
||||
version "3.0.1"
|
||||
@@ -595,132 +679,133 @@ es6-promise@^3.1.2:
|
||||
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613"
|
||||
integrity sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=
|
||||
|
||||
esbuild-android-64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz#505f41832884313bbaffb27704b8bcaa2d8616be"
|
||||
integrity sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==
|
||||
esbuild-android-64@0.15.10:
|
||||
version "0.15.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.15.10.tgz#8a59a84acbf2eca96996cadc35642cf055c494f0"
|
||||
integrity sha512-UI7krF8OYO1N7JYTgLT9ML5j4+45ra3amLZKx7LO3lmLt1Ibn8t3aZbX5Pu4BjWiqDuJ3m/hsvhPhK/5Y/YpnA==
|
||||
|
||||
esbuild-android-arm64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz#8ce69d7caba49646e009968fe5754a21a9871771"
|
||||
integrity sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==
|
||||
esbuild-android-arm64@0.15.10:
|
||||
version "0.15.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.15.10.tgz#f453851dc1d8c5409a38cf7613a33852faf4915d"
|
||||
integrity sha512-EOt55D6xBk5O05AK8brXUbZmoFj4chM8u3riGflLa6ziEoVvNjRdD7Cnp82NHQGfSHgYR06XsPI8/sMuA/cUwg==
|
||||
|
||||
esbuild-darwin-64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz#24ba67b9a8cb890a3c08d9018f887cc221cdda25"
|
||||
integrity sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==
|
||||
esbuild-darwin-64@0.15.10:
|
||||
version "0.15.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.15.10.tgz#778bd29c8186ff47b176c8af58c08cf0fb8e6b86"
|
||||
integrity sha512-hbDJugTicqIm+WKZgp208d7FcXcaK8j2c0l+fqSJ3d2AzQAfjEYDRM3Z2oMeqSJ9uFxyj/muSACLdix7oTstRA==
|
||||
|
||||
esbuild-darwin-arm64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz#3f7cdb78888ee05e488d250a2bdaab1fa671bf73"
|
||||
integrity sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==
|
||||
esbuild-darwin-arm64@0.15.10:
|
||||
version "0.15.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.10.tgz#b30bbefb46dc3c5d4708b0435e52f6456578d6df"
|
||||
integrity sha512-M1t5+Kj4IgSbYmunf2BB6EKLkWUq+XlqaFRiGOk8bmBapu9bCDrxjf4kUnWn59Dka3I27EiuHBKd1rSO4osLFQ==
|
||||
|
||||
esbuild-freebsd-64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz#09250f997a56ed4650f3e1979c905ffc40bbe94d"
|
||||
integrity sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==
|
||||
esbuild-freebsd-64@0.15.10:
|
||||
version "0.15.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.10.tgz#ab301c5f6ded5110dbdd611140bef1a7c2e99236"
|
||||
integrity sha512-KMBFMa7C8oc97nqDdoZwtDBX7gfpolkk6Bcmj6YFMrtCMVgoU/x2DI1p74DmYl7CSS6Ppa3xgemrLrr5IjIn0w==
|
||||
|
||||
esbuild-freebsd-arm64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz#bafb46ed04fc5f97cbdb016d86947a79579f8e48"
|
||||
integrity sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==
|
||||
esbuild-freebsd-arm64@0.15.10:
|
||||
version "0.15.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.10.tgz#a5b09b867a6ff49110f52343b6f12265db63d43f"
|
||||
integrity sha512-m2KNbuCX13yQqLlbSojFMHpewbn8wW5uDS6DxRpmaZKzyq8Dbsku6hHvh2U+BcLwWY4mpgXzFUoENEf7IcioGg==
|
||||
|
||||
esbuild-linux-32@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz#e2a8c4a8efdc355405325033fcebeb941f781fe5"
|
||||
integrity sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==
|
||||
esbuild-linux-32@0.15.10:
|
||||
version "0.15.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.15.10.tgz#5282fe9915641caf9c8070e4ba2c3e16d358f837"
|
||||
integrity sha512-guXrwSYFAvNkuQ39FNeV4sNkNms1bLlA5vF1H0cazZBOLdLFIny6BhT+TUbK/hdByMQhtWQ5jI9VAmPKbVPu1w==
|
||||
|
||||
esbuild-linux-64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz#de5fdba1c95666cf72369f52b40b03be71226652"
|
||||
integrity sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==
|
||||
esbuild-linux-64@0.15.10:
|
||||
version "0.15.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.15.10.tgz#f3726e85a00149580cb19f8abfabcbb96f5d52bb"
|
||||
integrity sha512-jd8XfaSJeucMpD63YNMO1JCrdJhckHWcMv6O233bL4l6ogQKQOxBYSRP/XLWP+6kVTu0obXovuckJDcA0DKtQA==
|
||||
|
||||
esbuild-linux-arm64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz#dae4cd42ae9787468b6a5c158da4c84e83b0ce8b"
|
||||
integrity sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==
|
||||
esbuild-linux-arm64@0.15.10:
|
||||
version "0.15.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.10.tgz#2f0056e9d5286edb0185b56655caa8c574d8dbe7"
|
||||
integrity sha512-GByBi4fgkvZFTHFDYNftu1DQ1GzR23jws0oWyCfhnI7eMOe+wgwWrc78dbNk709Ivdr/evefm2PJiUBMiusS1A==
|
||||
|
||||
esbuild-linux-arm@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz#a2c1dff6d0f21dbe8fc6998a122675533ddfcd59"
|
||||
integrity sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==
|
||||
esbuild-linux-arm@0.15.10:
|
||||
version "0.15.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.15.10.tgz#40a9270da3c8ffa32cf72e24a79883e323dff08d"
|
||||
integrity sha512-6N8vThLL/Lysy9y4Ex8XoLQAlbZKUyExCWyayGi2KgTBelKpPgj6RZnUaKri0dHNPGgReJriKVU6+KDGQwn10A==
|
||||
|
||||
esbuild-linux-mips64le@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz#d9918e9e4cb972f8d6dae8e8655bf9ee131eda34"
|
||||
integrity sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==
|
||||
esbuild-linux-mips64le@0.15.10:
|
||||
version "0.15.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.10.tgz#90ce1c4ee0202edb4ac69807dea77f7e5804abc4"
|
||||
integrity sha512-BxP+LbaGVGIdQNJUNF7qpYjEGWb0YyHVSKqYKrn+pTwH/SiHUxFyJYSP3pqkku61olQiSBnSmWZ+YUpj78Tw7Q==
|
||||
|
||||
esbuild-linux-ppc64le@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz#3f9a0f6d41073fb1a640680845c7de52995f137e"
|
||||
integrity sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==
|
||||
esbuild-linux-ppc64le@0.15.10:
|
||||
version "0.15.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.10.tgz#782837ae7bd5b279178106c9dd801755a21fabdf"
|
||||
integrity sha512-LoSQCd6498PmninNgqd/BR7z3Bsk/mabImBWuQ4wQgmQEeanzWd5BQU2aNi9mBURCLgyheuZS6Xhrw5luw3OkQ==
|
||||
|
||||
esbuild-linux-riscv64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz#618853c028178a61837bc799d2013d4695e451c8"
|
||||
integrity sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==
|
||||
esbuild-linux-riscv64@0.15.10:
|
||||
version "0.15.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.10.tgz#d7420d806ece5174f24f4634303146f915ab4207"
|
||||
integrity sha512-Lrl9Cr2YROvPV4wmZ1/g48httE8z/5SCiXIyebiB5N8VT7pX3t6meI7TQVHw/wQpqP/AF4SksDuFImPTM7Z32Q==
|
||||
|
||||
esbuild-linux-s390x@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz#d1885c4c5a76bbb5a0fe182e2c8c60eb9e29f2a6"
|
||||
integrity sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==
|
||||
esbuild-linux-s390x@0.15.10:
|
||||
version "0.15.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.10.tgz#21fdf0cb3494a7fb520a71934e4dffce67fe47be"
|
||||
integrity sha512-ReP+6q3eLVVP2lpRrvl5EodKX7EZ1bS1/z5j6hsluAlZP5aHhk6ghT6Cq3IANvvDdscMMCB4QEbI+AjtvoOFpA==
|
||||
|
||||
esbuild-netbsd-64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz#69ae917a2ff241b7df1dbf22baf04bd330349e81"
|
||||
integrity sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==
|
||||
esbuild-netbsd-64@0.15.10:
|
||||
version "0.15.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.10.tgz#6c06b3107e3df53de381e6299184d4597db0440f"
|
||||
integrity sha512-iGDYtJCMCqldMskQ4eIV+QSS/CuT7xyy9i2/FjpKvxAuCzrESZXiA1L64YNj6/afuzfBe9i8m/uDkFHy257hTw==
|
||||
|
||||
esbuild-openbsd-64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz#db4c8495287a350a6790de22edea247a57c5d47b"
|
||||
integrity sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==
|
||||
esbuild-openbsd-64@0.15.10:
|
||||
version "0.15.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.10.tgz#4daef5f5d8e74bbda53b65160029445d582570cf"
|
||||
integrity sha512-ftMMIwHWrnrYnvuJQRJs/Smlcb28F9ICGde/P3FUTCgDDM0N7WA0o9uOR38f5Xe2/OhNCgkjNeb7QeaE3cyWkQ==
|
||||
|
||||
esbuild-sunos-64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz#54287ee3da73d3844b721c21bc80c1dc7e1bf7da"
|
||||
integrity sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==
|
||||
esbuild-sunos-64@0.15.10:
|
||||
version "0.15.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.15.10.tgz#5fe7bef267a02f322fd249a8214d0274937388a7"
|
||||
integrity sha512-mf7hBL9Uo2gcy2r3rUFMjVpTaGpFJJE5QTDDqUFf1632FxteYANffDZmKbqX0PfeQ2XjUDE604IcE7OJeoHiyg==
|
||||
|
||||
esbuild-windows-32@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz#f8aaf9a5667630b40f0fb3aa37bf01bbd340ce31"
|
||||
integrity sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==
|
||||
esbuild-windows-32@0.15.10:
|
||||
version "0.15.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.15.10.tgz#48e3dde25ab0135579a288b30ab6ddef6d1f0b28"
|
||||
integrity sha512-ttFVo+Cg8b5+qHmZHbEc8Vl17kCleHhLzgT8X04y8zudEApo0PxPg9Mz8Z2cKH1bCYlve1XL8LkyXGFjtUYeGg==
|
||||
|
||||
esbuild-windows-64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz#bf54b51bd3e9b0f1886ffdb224a4176031ea0af4"
|
||||
integrity sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==
|
||||
esbuild-windows-64@0.15.10:
|
||||
version "0.15.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.15.10.tgz#387a9515bef3fee502d277a5d0a2db49a4ecda05"
|
||||
integrity sha512-2H0gdsyHi5x+8lbng3hLbxDWR7mKHWh5BXZGKVG830KUmXOOWFE2YKJ4tHRkejRduOGDrBvHBriYsGtmTv3ntA==
|
||||
|
||||
esbuild-windows-arm64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz#937d15675a15e4b0e4fafdbaa3a01a776a2be982"
|
||||
integrity sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==
|
||||
esbuild-windows-arm64@0.15.10:
|
||||
version "0.15.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.10.tgz#5a6fcf2fa49e895949bf5495cf088ab1b43ae879"
|
||||
integrity sha512-S+th4F+F8VLsHLR0zrUcG+Et4hx0RKgK1eyHc08kztmLOES8BWwMiaGdoW9hiXuzznXQ0I/Fg904MNbr11Nktw==
|
||||
|
||||
esbuild@^0.14.27, esbuild@^0.14.48:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.54.tgz#8b44dcf2b0f1a66fc22459943dccf477535e9aa2"
|
||||
integrity sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==
|
||||
esbuild@^0.15.7, esbuild@^0.15.9:
|
||||
version "0.15.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.15.10.tgz#85c2f8446e9b1fe04fae68daceacba033eedbd42"
|
||||
integrity sha512-N7wBhfJ/E5fzn/SpNgX+oW2RLRjwaL8Y0ezqNqhjD6w0H2p0rDuEz2FKZqpqLnO8DCaWumKe8dsC/ljvVSSxng==
|
||||
optionalDependencies:
|
||||
"@esbuild/linux-loong64" "0.14.54"
|
||||
esbuild-android-64 "0.14.54"
|
||||
esbuild-android-arm64 "0.14.54"
|
||||
esbuild-darwin-64 "0.14.54"
|
||||
esbuild-darwin-arm64 "0.14.54"
|
||||
esbuild-freebsd-64 "0.14.54"
|
||||
esbuild-freebsd-arm64 "0.14.54"
|
||||
esbuild-linux-32 "0.14.54"
|
||||
esbuild-linux-64 "0.14.54"
|
||||
esbuild-linux-arm "0.14.54"
|
||||
esbuild-linux-arm64 "0.14.54"
|
||||
esbuild-linux-mips64le "0.14.54"
|
||||
esbuild-linux-ppc64le "0.14.54"
|
||||
esbuild-linux-riscv64 "0.14.54"
|
||||
esbuild-linux-s390x "0.14.54"
|
||||
esbuild-netbsd-64 "0.14.54"
|
||||
esbuild-openbsd-64 "0.14.54"
|
||||
esbuild-sunos-64 "0.14.54"
|
||||
esbuild-windows-32 "0.14.54"
|
||||
esbuild-windows-64 "0.14.54"
|
||||
esbuild-windows-arm64 "0.14.54"
|
||||
"@esbuild/android-arm" "0.15.10"
|
||||
"@esbuild/linux-loong64" "0.15.10"
|
||||
esbuild-android-64 "0.15.10"
|
||||
esbuild-android-arm64 "0.15.10"
|
||||
esbuild-darwin-64 "0.15.10"
|
||||
esbuild-darwin-arm64 "0.15.10"
|
||||
esbuild-freebsd-64 "0.15.10"
|
||||
esbuild-freebsd-arm64 "0.15.10"
|
||||
esbuild-linux-32 "0.15.10"
|
||||
esbuild-linux-64 "0.15.10"
|
||||
esbuild-linux-arm "0.15.10"
|
||||
esbuild-linux-arm64 "0.15.10"
|
||||
esbuild-linux-mips64le "0.15.10"
|
||||
esbuild-linux-ppc64le "0.15.10"
|
||||
esbuild-linux-riscv64 "0.15.10"
|
||||
esbuild-linux-s390x "0.15.10"
|
||||
esbuild-netbsd-64 "0.15.10"
|
||||
esbuild-openbsd-64 "0.15.10"
|
||||
esbuild-sunos-64 "0.15.10"
|
||||
esbuild-windows-32 "0.15.10"
|
||||
esbuild-windows-64 "0.15.10"
|
||||
esbuild-windows-arm64 "0.15.10"
|
||||
|
||||
escape-string-regexp@^4.0.0:
|
||||
version "4.0.0"
|
||||
@@ -858,6 +943,11 @@ estree-walker@^0.6.1:
|
||||
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362"
|
||||
integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==
|
||||
|
||||
estree-walker@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700"
|
||||
integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==
|
||||
|
||||
esutils@^2.0.2:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
|
||||
@@ -896,14 +986,6 @@ fastq@^1.6.0:
|
||||
dependencies:
|
||||
reusify "^1.0.4"
|
||||
|
||||
fetch-blob@^3.1.2, fetch-blob@^3.1.4:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9"
|
||||
integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==
|
||||
dependencies:
|
||||
node-domexception "^1.0.0"
|
||||
web-streams-polyfill "^3.0.3"
|
||||
|
||||
file-entry-cache@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
|
||||
@@ -944,13 +1026,6 @@ flatted@^3.1.0:
|
||||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3"
|
||||
integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==
|
||||
|
||||
formdata-polyfill@^4.0.10:
|
||||
version "4.0.10"
|
||||
resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423"
|
||||
integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==
|
||||
dependencies:
|
||||
fetch-blob "^3.1.2"
|
||||
|
||||
fs-minipass@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
|
||||
@@ -1019,6 +1094,18 @@ glob@^7.1.3:
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
glob@^7.1.6:
|
||||
version "7.2.3"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
|
||||
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
minimatch "^3.1.1"
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
globals@^13.15.0:
|
||||
version "13.17.0"
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-13.17.0.tgz#902eb1e680a41da93945adbdcb5a9f361ba69bd4"
|
||||
@@ -1126,10 +1213,17 @@ is-binary-path@~2.1.0:
|
||||
dependencies:
|
||||
binary-extensions "^2.0.0"
|
||||
|
||||
is-core-module@^2.8.1:
|
||||
version "2.8.1"
|
||||
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211"
|
||||
integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==
|
||||
is-builtin-module@^3.1.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.0.tgz#bb0310dfe881f144ca83f30100ceb10cf58835e0"
|
||||
integrity sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw==
|
||||
dependencies:
|
||||
builtin-modules "^3.3.0"
|
||||
|
||||
is-core-module@^2.9.0:
|
||||
version "2.10.0"
|
||||
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed"
|
||||
integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==
|
||||
dependencies:
|
||||
has "^1.0.3"
|
||||
|
||||
@@ -1150,11 +1244,23 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
|
||||
dependencies:
|
||||
is-extglob "^2.1.1"
|
||||
|
||||
is-module@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
|
||||
integrity sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==
|
||||
|
||||
is-number@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
|
||||
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
|
||||
|
||||
is-reference@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7"
|
||||
integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==
|
||||
dependencies:
|
||||
"@types/estree" "*"
|
||||
|
||||
isexe@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||
@@ -1233,6 +1339,13 @@ magic-string@^0.26.2:
|
||||
dependencies:
|
||||
sourcemap-codec "^1.4.8"
|
||||
|
||||
magic-string@^0.26.5:
|
||||
version "0.26.6"
|
||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.26.6.tgz#b61e417c9f40b7b53bf7e73c0a803258e20d25ee"
|
||||
integrity sha512-6d+3bFybzyQFJYSoRsl9ZC0wheze8M1LrQC7tNMRqXR4izUTDOLMd9BtSuExK9iAukFh+s5K0WAhc/dlQ+HKYA==
|
||||
dependencies:
|
||||
sourcemap-codec "^1.4.8"
|
||||
|
||||
make-dir@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
|
||||
@@ -1271,7 +1384,7 @@ min-indent@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
|
||||
integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
|
||||
|
||||
minimatch@^3.0.4, minimatch@^3.1.2:
|
||||
minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
||||
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
|
||||
@@ -1350,11 +1463,6 @@ next-auth@latest:
|
||||
preact-render-to-string "^5.1.19"
|
||||
uuid "^8.3.2"
|
||||
|
||||
node-domexception@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5"
|
||||
integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==
|
||||
|
||||
node-fetch@^2.6.7:
|
||||
version "2.6.7"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
|
||||
@@ -1362,15 +1470,6 @@ node-fetch@^2.6.7:
|
||||
dependencies:
|
||||
whatwg-url "^5.0.0"
|
||||
|
||||
node-fetch@^3.2.4:
|
||||
version "3.2.10"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.2.10.tgz#e8347f94b54ae18b57c9c049ef641cef398a85c8"
|
||||
integrity sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA==
|
||||
dependencies:
|
||||
data-uri-to-buffer "^4.0.0"
|
||||
fetch-blob "^3.1.4"
|
||||
formdata-polyfill "^4.0.10"
|
||||
|
||||
node-gyp-build@^4.2.2:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40"
|
||||
@@ -1503,10 +1602,10 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3, picomatc
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
||||
|
||||
postcss@^8.4.13:
|
||||
version "8.4.16"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.16.tgz#33a1d675fac39941f5f445db0de4db2b6e01d43c"
|
||||
integrity sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==
|
||||
postcss@^8.4.16:
|
||||
version "8.4.17"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.17.tgz#f87863ec7cd353f81f7ab2dec5d67d861bbb1be5"
|
||||
integrity sha512-UNxNOLQydcOFi41yHNMcKRZ39NeXlr8AxGuZJsdub8vIb12fHzcq37DTU/QtbI6WLxNg2gF9Z+8qtRwTj1UI1Q==
|
||||
dependencies:
|
||||
nanoid "^3.3.4"
|
||||
picocolors "^1.0.0"
|
||||
@@ -1595,12 +1694,12 @@ resolve-from@^5.0.0:
|
||||
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
|
||||
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
|
||||
|
||||
resolve@^1.22.0:
|
||||
version "1.22.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198"
|
||||
integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==
|
||||
resolve@^1.17.0, resolve@^1.19.0, resolve@^1.22.1:
|
||||
version "1.22.1"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
|
||||
integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
|
||||
dependencies:
|
||||
is-core-module "^2.8.1"
|
||||
is-core-module "^2.9.0"
|
||||
path-parse "^1.0.7"
|
||||
supports-preserve-symlinks-flag "^1.0.0"
|
||||
|
||||
@@ -1630,10 +1729,17 @@ rollup-pluginutils@^2.8.2:
|
||||
dependencies:
|
||||
estree-walker "^0.6.1"
|
||||
|
||||
rollup@^2.59.0:
|
||||
version "2.67.3"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.67.3.tgz#3f04391fc296f807d067c9081d173e0a33dbd37e"
|
||||
integrity sha512-G/x1vUwbGtP6O5ZM8/sWr8+p7YfZhI18pPqMRtMYMWSbHjKZ/ajHGiM+GWNTlWyOR0EHIdT8LHU+Z4ciIZ1oBw==
|
||||
rollup@^2.78.1:
|
||||
version "2.79.1"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7"
|
||||
integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
rollup@~2.78.0:
|
||||
version "2.78.1"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.78.1.tgz#52fe3934d9c83cb4f7c4cb5fb75d88591be8648f"
|
||||
integrity sha512-VeeCgtGi4P+o9hIg+xz4qQpRl6R401LWEXBmxYKOV4zlF82lyhgh2hTZnheFUbANE8l2A41F458iwj2vEYaXJg==
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
@@ -1672,9 +1778,9 @@ semver@^6.0.0:
|
||||
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
|
||||
|
||||
semver@^7.3.5:
|
||||
version "7.3.5"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
|
||||
integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
|
||||
version "7.3.8"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
|
||||
integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
@@ -1746,6 +1852,11 @@ sourcemap-codec@^1.3.0, sourcemap-codec@^1.4.4, sourcemap-codec@^1.4.8:
|
||||
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
|
||||
integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
|
||||
|
||||
streamsearch@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764"
|
||||
integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
|
||||
|
||||
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
@@ -1807,10 +1918,10 @@ svelte-check@^2.8.1:
|
||||
svelte-preprocess "^4.0.0"
|
||||
typescript "*"
|
||||
|
||||
svelte-hmr@^0.14.12:
|
||||
version "0.14.12"
|
||||
resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.14.12.tgz#a127aec02f1896500b10148b2d4d21ddde39973f"
|
||||
integrity sha512-4QSW/VvXuqVcFZ+RhxiR8/newmwOCTlbYIezvkeN6302YFRE8cXy0naamHcjz8Y9Ce3ITTZtrHrIL0AGfyo61w==
|
||||
svelte-hmr@^0.15.0:
|
||||
version "0.15.0"
|
||||
resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.15.0.tgz#c8304b5dd33f006415329d91470761d19417a324"
|
||||
integrity sha512-Aw21SsyoohyVn4yiKXWPNCSW2DQNH/76kvUnE9kpt4h9hcg9tfyQc6xshx9hzgMfGF0kVx0EGD8oBMWSnATeOg==
|
||||
|
||||
svelte-preprocess@^4.0.0:
|
||||
version "4.10.3"
|
||||
@@ -1922,10 +2033,12 @@ typescript@~4.8.2:
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.2.tgz#e3b33d5ccfb5914e4eeab6699cf208adee3fd790"
|
||||
integrity sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==
|
||||
|
||||
undici@^5.8.1:
|
||||
version "5.10.0"
|
||||
resolved "https://registry.yarnpkg.com/undici/-/undici-5.10.0.tgz#dd9391087a90ccfbd007568db458674232ebf014"
|
||||
integrity sha512-c8HsD3IbwmjjbLvoZuRI26TZic+TSEe8FPMLLOkN1AfYRhdjnKBU6yL+IwcSCbdZiX4e5t0lfMDLDCqj4Sq70g==
|
||||
undici@^5.11.0:
|
||||
version "5.11.0"
|
||||
resolved "https://registry.yarnpkg.com/undici/-/undici-5.11.0.tgz#1db25f285821828fc09d3804b9e2e934ae86fc13"
|
||||
integrity sha512-oWjWJHzFet0Ow4YZBkyiJwiK5vWqEYoH7BINzJAJOLedZ++JpAlCbUktW2GQ2DS2FpKmxD/JMtWUUWl1BtghGw==
|
||||
dependencies:
|
||||
busboy "^1.6.0"
|
||||
|
||||
uri-js@^4.2.2:
|
||||
version "4.4.1"
|
||||
@@ -1944,23 +2057,18 @@ uuid@^8.3.2:
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
||||
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||
|
||||
vite@^2.9.13:
|
||||
version "2.9.13"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-2.9.13.tgz#859cb5d4c316c0d8c6ec9866045c0f7858ca6abc"
|
||||
integrity sha512-AsOBAaT0AD7Mhe8DuK+/kE4aWYFMx/i0ZNi98hJclxb4e0OhQcZYUrvLjIaQ8e59Ui7txcvKMiJC1yftqpQoDw==
|
||||
vite@^3.1.0:
|
||||
version "3.1.6"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-3.1.6.tgz#4c6db3000326342c918204a42a130fb3ffed2414"
|
||||
integrity sha512-qMXIwnehvvcK5XfJiXQUiTxoYAEMKhM+jqCY6ZSTKFBKu1hJnAKEzP3AOcnTerI0cMZYAaJ4wpW1wiXLMDt4mA==
|
||||
dependencies:
|
||||
esbuild "^0.14.27"
|
||||
postcss "^8.4.13"
|
||||
resolve "^1.22.0"
|
||||
rollup "^2.59.0"
|
||||
esbuild "^0.15.9"
|
||||
postcss "^8.4.16"
|
||||
resolve "^1.22.1"
|
||||
rollup "~2.78.0"
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
web-streams-polyfill@^3.0.3:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6"
|
||||
integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==
|
||||
|
||||
webidl-conversions@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
|
||||
|
||||
@@ -12,15 +12,28 @@ npm install next-auth @prisma/client @next-auth/prisma-adapter
|
||||
npm install prisma --save-dev
|
||||
```
|
||||
|
||||
Create a file with your Prisma Client:
|
||||
|
||||
```typescript title="lib/prismadb.ts"
|
||||
import { PrismaClient } from "@prisma/client"
|
||||
|
||||
declare global {
|
||||
var prisma: PrismaClient | undefined
|
||||
}
|
||||
|
||||
const client = globalThis.prisma || new PrismaClient()
|
||||
if (process.env.NODE_ENV !== "production") globalThis.prisma = client
|
||||
|
||||
export default client
|
||||
```
|
||||
|
||||
Configure your NextAuth.js to use the Prisma Adapter:
|
||||
|
||||
```javascript title="pages/api/auth/[...nextauth].js"
|
||||
import NextAuth from "next-auth"
|
||||
import GoogleProvider from "next-auth/providers/google"
|
||||
import { PrismaAdapter } from "@next-auth/prisma-adapter"
|
||||
import { PrismaClient } from "@prisma/client"
|
||||
|
||||
const prisma = new PrismaClient()
|
||||
import prisma from "../../../lib/prismadb"
|
||||
|
||||
export default NextAuth({
|
||||
adapter: PrismaAdapter(prisma),
|
||||
|
||||
@@ -112,15 +112,16 @@ Requests to `/api/auth/signin`, `/api/auth/session` and calls to `getSession()`,
|
||||
- As with database persisted session expiry times, token expiry time is extended whenever a session is active.
|
||||
- The arguments _user_, _account_, _profile_ and _isNewUser_ are only passed the first time this callback is called on a new session, after the user signs in. In subsequent calls, only `token` will be available.
|
||||
|
||||
The contents _user_, _account_, _profile_ and _isNewUser_ will vary depending on the provider and on if you are using a database or not. You can persist data such as User ID, OAuth Access Token in this token. To make it available in the browser, check out the [`session()` callback](#session-callback) as well.
|
||||
The contents _user_, _account_, _profile_ and _isNewUser_ will vary depending on the provider and if you are using a database. You can persist data such as User ID, OAuth Access Token in this token, see the example below for `access_token` and `user.id`. To expose it on the client side, check out the [`session()` callback](#session-callback) as well.
|
||||
|
||||
```js title="pages/api/auth/[...nextauth].js"
|
||||
...
|
||||
callbacks: {
|
||||
async jwt({ token, account }) {
|
||||
// Persist the OAuth access_token to the token right after signin
|
||||
async jwt({ token, account, profile }) {
|
||||
// Persist the OAuth access_token and or the user id to the token right after signin
|
||||
if (account) {
|
||||
token.accessToken = account.access_token
|
||||
token.id = profile.id
|
||||
}
|
||||
return token
|
||||
}
|
||||
@@ -134,7 +135,7 @@ Use an if branch to check for the existence of parameters (apart from `token`).
|
||||
|
||||
## Session callback
|
||||
|
||||
The session callback is called whenever a session is checked. By default, only a subset of the token is returned for increased security. If you want to make something available you added to the token through the `jwt()` callback, you have to explicitly forward it here to make it available to the client.
|
||||
The session callback is called whenever a session is checked. By default, **only a subset of the token is returned for increased security**. If you want to make something available you added to the token (like `access_token` and `user.id` from above) via the `jwt()` callback, you have to explicitly forward it here to make it available to the client.
|
||||
|
||||
e.g. `getSession()`, `useSession()`, `/api/auth/session`
|
||||
|
||||
@@ -145,8 +146,10 @@ e.g. `getSession()`, `useSession()`, `/api/auth/session`
|
||||
...
|
||||
callbacks: {
|
||||
async session({ session, token, user }) {
|
||||
// Send properties to the client, like an access_token from a provider.
|
||||
// Send properties to the client, like an access_token and user id from a provider.
|
||||
session.accessToken = token.accessToken
|
||||
session.user.id = token.id
|
||||
|
||||
return session
|
||||
}
|
||||
}
|
||||
@@ -155,7 +158,7 @@ callbacks: {
|
||||
|
||||
:::tip
|
||||
When using JSON Web Tokens the `jwt()` callback is invoked before the `session()` callback, so anything you add to the
|
||||
JSON Web Token will be immediately available in the session callback, like for example an `access_token` from a provider.
|
||||
JSON Web Token will be immediately available in the session callback, like for example an `access_token` or `id` from a provider.
|
||||
:::
|
||||
|
||||
:::warning
|
||||
|
||||
@@ -13,12 +13,12 @@ When deploying to production, set the `NEXTAUTH_URL` environment variable to the
|
||||
NEXTAUTH_URL=https://example.com
|
||||
```
|
||||
|
||||
If your Next.js application uses a custom base path, specify the route to the API endpoint in full. More informations about the usage of custom base path [here](/getting-started/client#custom-base-path).
|
||||
If your Next.js application uses a custom base path, specify the route to the API endpoint in full. More information about the usage of custom base path [here](/getting-started/client#custom-base-path).
|
||||
|
||||
_e.g. `NEXTAUTH_URL=https://example.com/custom-route/api/auth`_
|
||||
|
||||
:::tip
|
||||
When you're using a custom base path, you will need to pass the `basePath` page prop to the `<SessionProvider>`. More informations [here](/getting-started/client#custom-base-path).
|
||||
When you're using a custom base path, you will need to pass the `basePath` page prop to the `<SessionProvider>`. More information [here](/getting-started/client#custom-base-path).
|
||||
:::
|
||||
|
||||
:::note
|
||||
@@ -114,6 +114,12 @@ session: {
|
||||
// Use it to limit write operations. Set to 0 to always update the database.
|
||||
// Note: This option is ignored if using JSON Web Tokens
|
||||
updateAge: 24 * 60 * 60, // 24 hours
|
||||
|
||||
// The session token is usually either a random UUID or string, however if you
|
||||
// need a more customized session token string, you can define your own generate function.
|
||||
generateSessionToken: () => {
|
||||
return randomUUID?.() ?? randomBytes(32).toString("hex")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -156,7 +156,7 @@ interface OAuthConfig {
|
||||
*/
|
||||
id: string
|
||||
version: string
|
||||
profile(profile: P, tokens: TokenSet): Awaitable<User & { id: string }>
|
||||
profile(profile: P, tokens: TokenSet): Awaitable<User>
|
||||
checks?: ChecksType | ChecksType[]
|
||||
clientId: string
|
||||
clientSecret: string
|
||||
|
||||
@@ -136,7 +136,7 @@ The `callbackUrl` provided was either invalid or not defined. See [specifying a
|
||||
|
||||
#### JWT_SESSION_ERROR
|
||||
|
||||
JWKKeySupport: the key does not support HS512 verify algorithm
|
||||
JWTKeySupport: the key does not support HS512 verify algorithm
|
||||
|
||||
The algorithm used for generating your key isn't listed as supported. You can generate a HS512 key using
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ export default function Component() {
|
||||
`useSession()` returns an object containing two values: `data` and `status`:
|
||||
|
||||
- **`data`**: This can be three values: [`Session`](https://github.com/nextauthjs/next-auth/blob/8ff4b260143458c5d8a16b80b11d1b93baa0690f/types/index.d.ts#L437-L444) / `undefined` / `null`.
|
||||
- when the session hasn't been fetched yet, `data` will `undefined`
|
||||
- when the session hasn't been fetched yet, `data` will be `undefined`
|
||||
- in case it failed to retrieve the session, `data` will be `null`
|
||||
- in case of success, `data` will be [`Session`](https://github.com/nextauthjs/next-auth/blob/8ff4b260143458c5d8a16b80b11d1b93baa0690f/types/index.d.ts#L437-L444).
|
||||
- **`status`**: enum mapping to three possible session states: `"loading" | "authenticated" | "unauthenticated"`
|
||||
|
||||
@@ -50,7 +50,7 @@ providers: [
|
||||
// You can pass any HTML attribute to the <input> tag through the object.
|
||||
credentials: {
|
||||
username: { label: "Username", type: "text", placeholder: "jsmith" },
|
||||
password: { label: "Password", type: "password" }
|
||||
password: { label: "Password", type: "password" }
|
||||
},
|
||||
async authorize(credentials, req) {
|
||||
// Add logic here to look up the user from the credentials supplied
|
||||
|
||||
37
docs/docs/providers/pinterest.md
Normal file
37
docs/docs/providers/pinterest.md
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
id: pinterest
|
||||
title: Pinterest
|
||||
---
|
||||
|
||||
## Documentation
|
||||
|
||||
https://developers.pinterest.com/docs/getting-started/authentication/
|
||||
|
||||
## Configuration
|
||||
|
||||
https://developers.pinterest.com/apps/
|
||||
|
||||
## Options
|
||||
|
||||
The **Pinterest Provider** comes with a set of default options:
|
||||
|
||||
- [Pinterest Provider options](https://github.com/nextauthjs/next-auth/blob/main/packages/next-auth/src/providers/pinterest.ts)
|
||||
|
||||
You can override any of the options to suit your own use case.
|
||||
|
||||
## Example
|
||||
|
||||
```ts
|
||||
import PinterestProvider from "next-auth/providers/pinterest"
|
||||
...
|
||||
providers: [
|
||||
PinterestProvider({
|
||||
clientId: process.env.PINTEREST_ID,
|
||||
clientSecret: process.env.PINTEREST_SECRET
|
||||
})
|
||||
]
|
||||
...
|
||||
|
||||
:::tip
|
||||
To use in production, make sure the app has standard API access and not trial access
|
||||
:::
|
||||
87
docs/docs/providers/zitadel.md
Normal file
87
docs/docs/providers/zitadel.md
Normal file
@@ -0,0 +1,87 @@
|
||||
---
|
||||
id: zitadel
|
||||
title: Zitadel
|
||||
---
|
||||
|
||||
## Documentation
|
||||
|
||||
https://docs.zitadel.com/docs/apis/openidoauth/endpoints
|
||||
|
||||
## Configuration
|
||||
|
||||
https://docs.zitadel.com/docs/guides/integrate/oauth-recommended-flows
|
||||
|
||||
The Redirect URIs used when creating the credentials must include your full domain and end in the callback path. For example:
|
||||
|
||||
- For production: `https://{YOUR_DOMAIN}/api/auth/callback/zitadel`
|
||||
- For development: `http://localhost:3000/api/auth/callback/zitadel`
|
||||
|
||||
Make sure to enable **dev mode** in ZITADEL console to allow redirects for local development.
|
||||
|
||||
## Options
|
||||
|
||||
The **ZITADEL Provider** comes with a set of default options:
|
||||
|
||||
- [ZITADEL Provider options](https://github.com/nextauthjs/next-auth/blob/main/packages/next-auth/src/providers/zitadel.ts)
|
||||
|
||||
You can override any of the options to suit your own use case.
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
import ZitadelProvider from "next-auth/providers/zitadel";
|
||||
...
|
||||
providers: [
|
||||
ZitadelProvider({
|
||||
issuer: process.env.ZITADEL_ISSUER,
|
||||
clientId: process.env.ZITADEL_CLIENT_ID,
|
||||
clientSecret: process.env.ZITADEL_CLIENT_SECRET,
|
||||
})
|
||||
]
|
||||
...
|
||||
```
|
||||
|
||||
If you need access to ZITADEL APIs or need additional information, make sure to add the corresponding scopes.
|
||||
|
||||
To get the full list of supported claims take a look [here](https://docs.zitadel.com/docs/apis/openidoauth/endpoints).
|
||||
|
||||
```js
|
||||
const options = {
|
||||
...
|
||||
providers: [
|
||||
ZitadelProvider({
|
||||
clientId: process.env.ZITADEL_CLIENT_ID,
|
||||
authorization: {
|
||||
params: {
|
||||
scope: `openid email profile urn:zitadel:iam:org:project:id:${process.env.ZITADEL_PROJECT_ID}:aud`
|
||||
}
|
||||
}
|
||||
})
|
||||
],
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::tip
|
||||
ZITADEL also returns a `email_verified` boolean property in the profile.
|
||||
|
||||
You can use this property to restrict access to people with verified accounts.
|
||||
|
||||
```js
|
||||
const options = {
|
||||
...
|
||||
callbacks: {
|
||||
async signIn({ account, profile }) {
|
||||
if (account.provider === "zitadel") {
|
||||
return profile.email_verified;
|
||||
}
|
||||
return true; // Do different verification for other providers that don't have `email_verified`
|
||||
},
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
@@ -16,7 +16,7 @@ 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.
|
||||
- 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 info@balazsorban.com, yo@ndo.dev, thvu@hey.com 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 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.)
|
||||
|
||||
:::note
|
||||
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.
|
||||
|
||||
@@ -105,6 +105,11 @@ This tutorial covers:
|
||||
|
||||
## Database
|
||||
|
||||
#### [Create a NextAuth.js Custom Adapter with HarperDB & Next.js](https://spacejelly.dev/posts/how-to-create-a-nextauth-js-custom-adapter-with-harperdb-next-js/) <svg xmlns="http://www.w3.org/2000/svg" style={{ marginLeft: '5px', marginBottom:'-6px'}} height="20" width="20" fill="none" viewBox="0 0 24 24" stroke="currentColor"><title>External</title> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" /> </svg>
|
||||
|
||||
- Use a custom database in a Custom Adapter for persisted NextAuth.js sessions using HarperDB as an example.
|
||||
- Video tutorial also available: <https://www.youtube.com/watch?v=pu7xBv7sZ8s>
|
||||
|
||||
#### [Using NextAuth.js with Prisma and PlanetScale serverless databases](https://github.com/planetscale/nextjs-planetscale-starter) <svg xmlns="http://www.w3.org/2000/svg" style={{ marginLeft: '5px', marginBottom:'-6px'}} height="20" width="20" fill="none" viewBox="0 0 24 24" stroke="currentColor"><title>External</title> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" /> </svg>
|
||||
|
||||
- How to set up a PlanetScale database to fetch and store user / account data with the Prisma adapter.
|
||||
|
||||
@@ -42,7 +42,7 @@ export default function Page() {
|
||||
|
||||
### Next.js (Middleware)
|
||||
|
||||
With NextAuth.js 4.2.0 and Next.js 12, you can now protect your pages via the middleware pattern more easily. If you would like to protect all pages, you can create a `_middleware.js` file in your root `pages` directory which looks like this:
|
||||
With NextAuth.js 4.2.0 and Next.js 12, you can now protect your pages via the middleware pattern more easily. If you would like to protect all pages, you can create a `middleware.js` file in your root `pages` directory which looks like this:
|
||||
|
||||
```js title="/middleware.js"
|
||||
export { default } from "next-auth/middleware"
|
||||
@@ -60,6 +60,12 @@ For the time being, the `withAuth` middleware only supports `"jwt"` as [session
|
||||
|
||||
More details can be found [here](https://next-auth.js.org/configuration/nextjs#middleware).
|
||||
|
||||
:::tip
|
||||
To inclue all `dashboard` nested routes (sub pages like `/dashboard/settings`, `/dashboard/profile`) you can pass `matcher: "/dashboard/:path*"` to `config`.
|
||||
|
||||
For other patterns check out the [Next.js Middleware documentation](https://nextjs.org/docs/advanced-features/middleware#matcher).
|
||||
:::
|
||||
|
||||
### Server Side
|
||||
|
||||
You can protect server side rendered pages using the `unstable_getServerSession` method. This is different from the old `getSession()` method, in that it does not do an extra fetch out over the internet to confirm data from itself, increasing performance significantly.
|
||||
|
||||
14
package.json
14
package.json
@@ -4,21 +4,21 @@
|
||||
"private": true,
|
||||
"repository": "https://github.com/nextauthjs/next-auth.git",
|
||||
"scripts": {
|
||||
"build:app": "turbo run build --filter=next-auth-app --include-dependencies",
|
||||
"build:app": "turbo run build --filter=next-auth-app",
|
||||
"build": "turbo run build --filter=next-auth --filter=@next-auth/* --no-deps",
|
||||
"lint": "turbo run lint --filter=!next-auth-docs --parallel",
|
||||
"test": "turbo run test --concurrency=1 --filter=!@next-auth/pouchdb-adapter --filter=!@next-auth/upstash-redis-adapter --filter=!next-auth-* --filter=[HEAD^1]",
|
||||
"clean": "turbo run clean --no-cache",
|
||||
"dev:app": "turbo run dev --parallel --continue --filter=next-auth-app...",
|
||||
"dev:db": "turbo run dev --parallel --continue --filter=next-auth-app...",
|
||||
"dev": "turbo run dev --parallel --continue --filter=next-auth-app... --filter=!./packages/adapter-*",
|
||||
"dev:docs": "turbo run dev --filter=next-auth-docs",
|
||||
"dev": "pnpm dev:app",
|
||||
"email": "cd apps/dev && pnpm email",
|
||||
"release": "release",
|
||||
"version:pr": "node ./config/version-pr"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@actions/core": "^1.6.0",
|
||||
"@balazsorban/monorepo-release": "0.0.4",
|
||||
"@balazsorban/monorepo-release": "0.0.5",
|
||||
"@types/jest": "^28.1.3",
|
||||
"@types/node": "^17.0.25",
|
||||
"@typescript-eslint/eslint-plugin": "^5.10.2",
|
||||
@@ -31,13 +31,13 @@
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^6.0.0",
|
||||
"husky": "^7.0.4",
|
||||
"prettier": "2.4.1",
|
||||
"prettier": "2.7.1",
|
||||
"pretty-quick": "^3.1.2",
|
||||
"semver": "7.3.5",
|
||||
"stream-to-array": "2.3.0",
|
||||
"ts-node": "10.5.0",
|
||||
"turbo": "1.3.1",
|
||||
"typescript": "4.7.4"
|
||||
"typescript": "4.8.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.19.0 || ^14.15.0 || ^16.13.0"
|
||||
@@ -54,7 +54,7 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"packageManager": "pnpm@7.5.1",
|
||||
"packageManager": "pnpm@7.13.3",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@next-auth/dynamodb-adapter",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
"version": "1.0.4",
|
||||
"version": "1.0.5",
|
||||
"description": "AWS DynamoDB adapter for next-auth.",
|
||||
"keywords": [
|
||||
"next-auth",
|
||||
@@ -43,4 +43,4 @@
|
||||
"jest": "^27.4.3",
|
||||
"next-auth": "workspace:*"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@ import type {
|
||||
BatchWriteCommandInput,
|
||||
DynamoDBDocument,
|
||||
} from "@aws-sdk/lib-dynamodb"
|
||||
import type { Account } from "next-auth"
|
||||
import type {
|
||||
Adapter,
|
||||
AdapterSession,
|
||||
AdapterAccount,
|
||||
AdapterUser,
|
||||
VerificationToken,
|
||||
} from "next-auth/adapters"
|
||||
@@ -86,7 +86,7 @@ export function DynamoDBAdapter(
|
||||
})
|
||||
if (!data.Items?.length) return null
|
||||
|
||||
const accounts = data.Items[0] as Account
|
||||
const accounts = data.Items[0] as AdapterAccount
|
||||
const res = await client.get({
|
||||
TableName,
|
||||
Key: {
|
||||
@@ -174,7 +174,7 @@ export function DynamoDBAdapter(
|
||||
":gsi1sk": `ACCOUNT#${providerAccountId}`,
|
||||
},
|
||||
})
|
||||
const account = format.from<Account>(data.Items?.[0])
|
||||
const account = format.from<AdapterAccount>(data.Items?.[0])
|
||||
if (!account) return
|
||||
await client.delete({
|
||||
TableName,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/firebase-adapter",
|
||||
"version": "1.0.1",
|
||||
"version": "1.0.2",
|
||||
"description": "Firebase adapter for next-auth.",
|
||||
"homepage": "https://next-auth.js.org",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
@@ -43,4 +43,4 @@
|
||||
"jest": "^27.4.3",
|
||||
"next-auth": "workspace:*"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,17 +15,18 @@ import {
|
||||
where,
|
||||
connectFirestoreEmulator,
|
||||
} from "firebase/firestore"
|
||||
import type { Account } from "next-auth"
|
||||
|
||||
import type {
|
||||
Adapter,
|
||||
AdapterSession,
|
||||
AdapterUser,
|
||||
AdapterAccount,
|
||||
AdapterSession,
|
||||
VerificationToken,
|
||||
} from "next-auth/adapters"
|
||||
|
||||
import { getConverter } from "./converter"
|
||||
|
||||
type IndexableObject = Record<string, unknown>
|
||||
export type IndexableObject = Record<string, unknown>
|
||||
|
||||
export interface FirestoreAdapterOptions {
|
||||
emulator?: {
|
||||
@@ -50,13 +51,13 @@ export function FirestoreAdapter({
|
||||
}
|
||||
|
||||
const Users = collection(db, "users").withConverter(
|
||||
getConverter<AdapterUser>()
|
||||
getConverter<AdapterUser & IndexableObject>()
|
||||
)
|
||||
const Sessions = collection(db, "sessions").withConverter(
|
||||
getConverter<AdapterSession & IndexableObject>()
|
||||
)
|
||||
const Accounts = collection(db, "accounts").withConverter(
|
||||
getConverter<Account>()
|
||||
getConverter<AdapterAccount>()
|
||||
)
|
||||
const VerificationTokens = collection(db, "verificationTokens").withConverter(
|
||||
getConverter<VerificationToken & IndexableObject>({ excludeId: true })
|
||||
|
||||
@@ -14,7 +14,7 @@ connectFirestoreEmulator(firestore, 'localhost', 8080);
|
||||
|
||||
type IndexableObject = Record<string, unknown>;
|
||||
|
||||
const Users = collection(firestore, 'users').withConverter(getConverter<AdapterUser>());
|
||||
const Users = collection(firestore, 'users').withConverter(getConverter<AdapterUser & IndexableObject>());
|
||||
const Sessions = collection(firestore, 'sessions').withConverter(getConverter<AdapterSession & IndexableObject>());
|
||||
const Accounts = collection(firestore, 'accounts').withConverter(getConverter<Account>());
|
||||
const VerificationTokens = collection(firestore, 'verificationTokens').withConverter(getConverter<VerificationToken & IndexableObject>({ excludeId: true }));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/mikro-orm-adapter",
|
||||
"version": "3.0.0",
|
||||
"version": "3.0.1",
|
||||
"description": "MikroORM adapter for next-auth.",
|
||||
"homepage": "https://next-auth.js.org",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
@@ -50,4 +50,4 @@
|
||||
"jest": {
|
||||
"preset": "@next-auth/adapter-test/jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,17 +5,16 @@ import {
|
||||
Unique,
|
||||
PrimaryKey,
|
||||
Entity,
|
||||
Enum,
|
||||
OneToMany,
|
||||
Collection,
|
||||
ManyToOne,
|
||||
types,
|
||||
} from "@mikro-orm/core"
|
||||
|
||||
import type { DefaultAccount } from "next-auth"
|
||||
import type {
|
||||
AdapterSession,
|
||||
AdapterUser,
|
||||
AdapterAccount,
|
||||
AdapterSession,
|
||||
VerificationToken as AdapterVerificationToken,
|
||||
} from "next-auth/adapters"
|
||||
import type { ProviderType } from "next-auth/providers"
|
||||
@@ -35,7 +34,7 @@ export class User implements RemoveIndex<AdapterUser> {
|
||||
|
||||
@Property({ type: types.string, nullable: true })
|
||||
@Unique()
|
||||
email?: string
|
||||
email: string = ""
|
||||
|
||||
@Property({ type: types.datetime, nullable: true })
|
||||
emailVerified: Date | null = null
|
||||
@@ -44,7 +43,7 @@ export class User implements RemoveIndex<AdapterUser> {
|
||||
image?: string
|
||||
|
||||
@OneToMany({
|
||||
entity: 'Session',
|
||||
entity: "Session",
|
||||
mappedBy: (session: Session) => session.user,
|
||||
hidden: true,
|
||||
orphanRemoval: true,
|
||||
@@ -52,7 +51,7 @@ export class User implements RemoveIndex<AdapterUser> {
|
||||
sessions = new Collection<Session, object>(this)
|
||||
|
||||
@OneToMany({
|
||||
entity: 'Account',
|
||||
entity: "Account",
|
||||
mappedBy: (account: Account) => account.user,
|
||||
hidden: true,
|
||||
orphanRemoval: true,
|
||||
@@ -67,7 +66,7 @@ export class Session implements AdapterSession {
|
||||
id: string = randomUUID()
|
||||
|
||||
@ManyToOne({
|
||||
entity: 'User',
|
||||
entity: "User",
|
||||
hidden: true,
|
||||
onDelete: "cascade",
|
||||
})
|
||||
@@ -76,7 +75,7 @@ export class Session implements AdapterSession {
|
||||
@Property({ type: types.string, persist: false })
|
||||
userId!: string
|
||||
|
||||
@Property({ type: 'Date' })
|
||||
@Property({ type: "Date" })
|
||||
expires!: Date
|
||||
|
||||
@Property({ type: types.string })
|
||||
@@ -86,13 +85,13 @@ export class Session implements AdapterSession {
|
||||
|
||||
@Entity()
|
||||
@Unique({ properties: ["provider", "providerAccountId"] })
|
||||
export class Account implements RemoveIndex<DefaultAccount> {
|
||||
export class Account implements RemoveIndex<AdapterAccount> {
|
||||
@PrimaryKey()
|
||||
@Property({ type: types.string })
|
||||
id: string = randomUUID()
|
||||
|
||||
@ManyToOne({
|
||||
entity: 'User',
|
||||
entity: "User",
|
||||
hidden: true,
|
||||
onDelete: "cascade",
|
||||
})
|
||||
@@ -139,7 +138,7 @@ export class VerificationToken implements AdapterVerificationToken {
|
||||
@Property({ type: types.string })
|
||||
token!: string
|
||||
|
||||
@Property({ type: 'Date' })
|
||||
@Property({ type: "Date" })
|
||||
expires!: Date
|
||||
|
||||
@Property({ type: types.string })
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import { Options, types } from "@mikro-orm/core"
|
||||
import type { SqliteDriver } from "@mikro-orm/sqlite"
|
||||
import { MikroORM, wrap } from "@mikro-orm/core"
|
||||
import { runBasicTests } from "@next-auth/adapter-test"
|
||||
import { MikroOrmAdapter, defaultEntities } from "../src"
|
||||
import {
|
||||
Cascade,
|
||||
@@ -11,8 +8,12 @@ import {
|
||||
PrimaryKey,
|
||||
Property,
|
||||
Unique,
|
||||
MikroORM,
|
||||
wrap,
|
||||
Options,
|
||||
types,
|
||||
} from "@mikro-orm/core"
|
||||
import { randomUUID } from "@next-auth/adapter-test"
|
||||
import { randomUUID, runBasicTests } from "@next-auth/adapter-test"
|
||||
|
||||
@Entity()
|
||||
export class User implements defaultEntities.User {
|
||||
@@ -25,16 +26,16 @@ export class User implements defaultEntities.User {
|
||||
|
||||
@Property({ type: types.string, nullable: true })
|
||||
@Unique()
|
||||
email?: string
|
||||
email: string = ""
|
||||
|
||||
@Property({ type: 'Date', nullable: true })
|
||||
@Property({ type: "Date", nullable: true })
|
||||
emailVerified: Date | null = null
|
||||
|
||||
@Property({ type: types.string, nullable: true })
|
||||
image?: string
|
||||
|
||||
@OneToMany({
|
||||
entity: 'Session',
|
||||
entity: "Session",
|
||||
mappedBy: (session: defaultEntities.Session) => session.user,
|
||||
hidden: true,
|
||||
orphanRemoval: true,
|
||||
@@ -43,7 +44,7 @@ export class User implements defaultEntities.User {
|
||||
sessions = new Collection<defaultEntities.Session>(this)
|
||||
|
||||
@OneToMany({
|
||||
entity: 'Account',
|
||||
entity: "Account",
|
||||
mappedBy: (account: defaultEntities.Account) => account.user,
|
||||
hidden: true,
|
||||
orphanRemoval: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/mongodb-adapter",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"description": "mongoDB adapter for next-auth.",
|
||||
"homepage": "https://next-auth.js.org",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
@@ -44,4 +44,4 @@
|
||||
"jest": {
|
||||
"preset": "@next-auth/adapter-test/jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,12 @@ import { ObjectId } from "mongodb"
|
||||
|
||||
import type {
|
||||
Adapter,
|
||||
AdapterSession,
|
||||
AdapterUser,
|
||||
AdapterAccount,
|
||||
AdapterSession,
|
||||
VerificationToken,
|
||||
} from "next-auth/adapters"
|
||||
import type { MongoClient } from "mongodb"
|
||||
import type { Account } from "next-auth"
|
||||
|
||||
export interface MongoDBAdapterOptions {
|
||||
collections?: {
|
||||
@@ -56,7 +56,7 @@ export const format = {
|
||||
else if (key === "id") continue
|
||||
else newObject[key] = value
|
||||
}
|
||||
return newObject as T
|
||||
return newObject as T & { _id: ObjectId }
|
||||
},
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ export function MongoDBAdapter(
|
||||
const c = { ...defaultCollections, ...collections }
|
||||
return {
|
||||
U: _db.collection<AdapterUser>(c.Users),
|
||||
A: _db.collection<Account>(c.Accounts),
|
||||
A: _db.collection<AdapterAccount>(c.Accounts),
|
||||
S: _db.collection<AdapterSession>(c.Sessions),
|
||||
V: _db.collection<VerificationToken>(c?.VerificationTokens),
|
||||
}
|
||||
@@ -128,7 +128,7 @@ export function MongoDBAdapter(
|
||||
])
|
||||
},
|
||||
linkAccount: async (data) => {
|
||||
const account = to<Account>(data)
|
||||
const account = to<AdapterAccount>(data)
|
||||
await (await db).A.insertOne(account)
|
||||
return account
|
||||
},
|
||||
@@ -136,7 +136,7 @@ export function MongoDBAdapter(
|
||||
const { value: account } = await (
|
||||
await db
|
||||
).A.findOneAndDelete(provider_providerAccountId)
|
||||
return from<Account>(account!)
|
||||
return from<AdapterAccount>(account!)
|
||||
},
|
||||
async getSessionAndUser(sessionToken) {
|
||||
const session = await (await db).S.findOne({ sessionToken })
|
||||
@@ -156,7 +156,6 @@ export function MongoDBAdapter(
|
||||
return from<AdapterSession>(session)
|
||||
},
|
||||
async updateSession(data) {
|
||||
// @ts-expect-error
|
||||
const { _id, ...session } = to<AdapterSession>(data)
|
||||
|
||||
const result = await (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/neo4j-adapter",
|
||||
"version": "1.0.4",
|
||||
"version": "1.0.5",
|
||||
"description": "neo4j adapter for next-auth.",
|
||||
"homepage": "https://next-auth.js.org",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
@@ -50,4 +50,4 @@
|
||||
"jest": {
|
||||
"preset": "@next-auth/adapter-test/jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,8 +87,6 @@ export function Neo4jAdapter(session: Session): Adapter {
|
||||
)
|
||||
},
|
||||
|
||||
// @ts-expect-error Property 'id' is missing in type
|
||||
// We never use `session.id` anywhere in the core, so this is fine.
|
||||
async createSession(data) {
|
||||
const { userId, ...s } = format.to(data)
|
||||
await write(
|
||||
|
||||
@@ -54,7 +54,10 @@ export function client(session: Session) {
|
||||
* Reads/writes values from/to the database.
|
||||
* Properties are available under `$data`
|
||||
*/
|
||||
async write<T>(statement: string, values: T): Promise<any> {
|
||||
async write<T extends Record<string, any>>(
|
||||
statement: string,
|
||||
values: T
|
||||
): Promise<any> {
|
||||
const result = await session.writeTransaction((tx) =>
|
||||
tx.run(statement, { data: format.to(values) })
|
||||
)
|
||||
|
||||
@@ -38,7 +38,7 @@ runBasicTests({
|
||||
return format.from(result?.records[0]?.get("u")?.properties)
|
||||
},
|
||||
|
||||
async session(sessionToken: any) {
|
||||
async session(sessionToken: string) {
|
||||
const result = await neo4jSession.readTransaction((tx) =>
|
||||
tx.run(
|
||||
`MATCH (u:User)-[:HAS_SESSION]->(s:Session)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
NEO4J_USER=neo4j
|
||||
NEO4J_PASS=password
|
||||
CONTAINER_NAME=next-auth-neo4j-test-e
|
||||
@@ -29,7 +28,7 @@ neo4j:4.2.0
|
||||
# -e NEO4J_ACCEPT_LICENSE_AGREEMENT=yes \
|
||||
# neo4j:4.2.0-enterprise
|
||||
|
||||
echo "Waiting 5 sec for db to start..." && sleep 5
|
||||
echo "Waiting 10 sec for db to start..." && sleep 10
|
||||
|
||||
if $JEST_WATCH; then
|
||||
# Run jest in watch mode
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/prisma-adapter",
|
||||
"version": "1.0.4",
|
||||
"version": "1.0.5",
|
||||
"description": "Prisma adapter for next-auth.",
|
||||
"homepage": "https://next-auth.js.org",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
@@ -52,4 +52,4 @@
|
||||
"jest": {
|
||||
"preset": "@next-auth/adapter-test/jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ model User {
|
||||
}
|
||||
|
||||
model Account {
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
type String
|
||||
provider String
|
||||
@@ -35,11 +34,10 @@ model Account {
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([provider, providerAccountId])
|
||||
@@id([provider, providerAccountId])
|
||||
}
|
||||
|
||||
model Session {
|
||||
id String @id @default(cuid())
|
||||
sessionToken String @unique
|
||||
userId String
|
||||
expires DateTime
|
||||
@@ -51,5 +49,5 @@ model VerificationToken {
|
||||
token String @unique
|
||||
expires DateTime
|
||||
|
||||
@@unique([identifier, token])
|
||||
@@id([identifier, token])
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@ datasource db {
|
||||
}
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
previewFeatures = ["mongoDb"]
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
model Account {
|
||||
|
||||
@@ -10,7 +10,7 @@ generator client {
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
name String?
|
||||
email String? @unique
|
||||
email String @unique
|
||||
emailVerified DateTime?
|
||||
image String?
|
||||
accounts Account[]
|
||||
@@ -18,7 +18,6 @@ model User {
|
||||
}
|
||||
|
||||
model Account {
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
type String
|
||||
provider String
|
||||
@@ -33,11 +32,10 @@ model Account {
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([provider, providerAccountId])
|
||||
@@id([provider, providerAccountId])
|
||||
}
|
||||
|
||||
model Session {
|
||||
id String @id @default(cuid())
|
||||
sessionToken String @unique
|
||||
userId String
|
||||
expires DateTime
|
||||
@@ -49,5 +47,5 @@ model VerificationToken {
|
||||
token String @unique
|
||||
expires DateTime
|
||||
|
||||
@@unique([identifier, token])
|
||||
@@id([identifier, token])
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { PrismaClient, Prisma } from "@prisma/client"
|
||||
import type { Adapter } from "next-auth/adapters"
|
||||
import type { Adapter, AdapterAccount } from "next-auth/adapters"
|
||||
|
||||
export function PrismaAdapter(p: PrismaClient): Adapter {
|
||||
return {
|
||||
@@ -15,9 +15,12 @@ export function PrismaAdapter(p: PrismaClient): Adapter {
|
||||
},
|
||||
updateUser: ({ id, ...data }) => p.user.update({ where: { id }, data }),
|
||||
deleteUser: (id) => p.user.delete({ where: { id } }),
|
||||
linkAccount: (data) => p.account.create({ data }) as any,
|
||||
linkAccount: (data) =>
|
||||
p.account.create({ data }) as unknown as AdapterAccount,
|
||||
unlinkAccount: (provider_providerAccountId) =>
|
||||
p.account.delete({ where: { provider_providerAccountId } }) as any,
|
||||
p.account.delete({
|
||||
where: { provider_providerAccountId },
|
||||
}) as unknown as AdapterAccount,
|
||||
async getSessionAndUser(sessionToken) {
|
||||
const userAndSession = await p.session.findUnique({
|
||||
where: { sessionToken },
|
||||
@@ -33,17 +36,18 @@ export function PrismaAdapter(p: PrismaClient): Adapter {
|
||||
deleteSession: (sessionToken) =>
|
||||
p.session.delete({ where: { sessionToken } }),
|
||||
async createVerificationToken(data) {
|
||||
// @ts-ignore
|
||||
const { id: _, ...verificationToken } = await p.verificationToken.create({
|
||||
data,
|
||||
})
|
||||
const verificationToken = await p.verificationToken.create({ data })
|
||||
// @ts-expect-errors // MongoDB needs an ID, but we don't
|
||||
if (verificationToken.id) delete verificationToken.id
|
||||
return verificationToken
|
||||
},
|
||||
async useVerificationToken(identifier_token) {
|
||||
try {
|
||||
// @ts-ignore
|
||||
const { id: _, ...verificationToken } =
|
||||
await p.verificationToken.delete({ where: { identifier_token } })
|
||||
const verificationToken = await p.verificationToken.delete({
|
||||
where: { identifier_token },
|
||||
})
|
||||
// @ts-expect-errors // MongoDB needs an ID, but we don't
|
||||
if (verificationToken.id) delete verificationToken.id
|
||||
return verificationToken
|
||||
} catch (error) {
|
||||
// If token already used/deleted, just return null
|
||||
|
||||
@@ -40,9 +40,9 @@ runBasicTests({
|
||||
where: { identifier_token },
|
||||
})
|
||||
if (!result) return null
|
||||
// @ts-ignore
|
||||
const { id: _, ...verificationToken } = result
|
||||
return verificationToken
|
||||
// @ts-ignore // MongoDB needs an ID, but we don't
|
||||
delete result.id
|
||||
return result
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/sequelize-adapter",
|
||||
"version": "1.0.5",
|
||||
"version": "1.0.6",
|
||||
"description": "Sequelize adapter for next-auth.",
|
||||
"homepage": "https://next-auth.js.org",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Account as AdapterAccount } from "next-auth"
|
||||
import type {
|
||||
Adapter,
|
||||
AdapterUser,
|
||||
AdapterAccount,
|
||||
AdapterSession,
|
||||
VerificationToken,
|
||||
} from "next-auth/adapters"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/typeorm-legacy-adapter",
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.1",
|
||||
"description": "TypeORM (legacy) adapter for next-auth.",
|
||||
"homepage": "https://next-auth.js.org",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { Adapter, AdapterSession, AdapterUser } from "next-auth/adapters"
|
||||
import type {
|
||||
Adapter,
|
||||
AdapterUser,
|
||||
AdapterAccount,
|
||||
AdapterSession,
|
||||
} from "next-auth/adapters"
|
||||
import { DataSourceOptions, DataSource, EntityManager } from "typeorm"
|
||||
import type { Account } from "next-auth"
|
||||
import * as defaultEntities from "./entities"
|
||||
import { parseDataSourceConfig, updateConnectionEntities } from "./utils"
|
||||
|
||||
@@ -87,7 +91,7 @@ export function TypeORMLegacyAdapter(
|
||||
},
|
||||
async getUserByAccount(provider_providerAccountId) {
|
||||
const m = await getManager(c)
|
||||
const account = await m.findOne<Account & { user: AdapterUser }>(
|
||||
const account = await m.findOne<AdapterAccount & { user: AdapterUser }>(
|
||||
"AccountEntity",
|
||||
{ where: provider_providerAccountId, relations: ["user"] }
|
||||
)
|
||||
@@ -115,9 +119,8 @@ export function TypeORMLegacyAdapter(
|
||||
},
|
||||
async unlinkAccount(providerAccountId) {
|
||||
const m = await getManager(c)
|
||||
await m.delete<Account>("AccountEntity", providerAccountId)
|
||||
await m.delete<AdapterAccount>("AccountEntity", providerAccountId)
|
||||
},
|
||||
// @ts-expect-error
|
||||
async createSession(data) {
|
||||
const m = await getManager(c)
|
||||
const session = await m.save("SessionEntity", data)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/upstash-redis-adapter",
|
||||
"version": "3.0.1",
|
||||
"version": "3.0.3",
|
||||
"description": "Upstash adapter for next-auth. It uses Upstash's connectionless (HTTP based) Redis client.",
|
||||
"homepage": "https://next-auth.js.org",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
@@ -49,4 +49,4 @@
|
||||
"jest": {
|
||||
"preset": "@next-auth/adapter-test/jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Account as AdapterAccount } from "next-auth"
|
||||
import type {
|
||||
Adapter,
|
||||
AdapterUser,
|
||||
AdapterAccount,
|
||||
AdapterSession,
|
||||
VerificationToken,
|
||||
} from "next-auth/adapters"
|
||||
@@ -117,7 +117,6 @@ export function UpstashRedisAdapter(
|
||||
const id = uuid()
|
||||
// TypeScript thinks the emailVerified field is missing
|
||||
// but all fields are copied directly from user, so it's there
|
||||
// @ts-expect-error
|
||||
return await setUser(id, { ...user, id })
|
||||
},
|
||||
getUser,
|
||||
@@ -144,10 +143,7 @@ export function UpstashRedisAdapter(
|
||||
const id = `${account.provider}:${account.providerAccountId}`
|
||||
return await setAccount(id, { ...account, id })
|
||||
},
|
||||
async createSession(session) {
|
||||
const id = session.sessionToken
|
||||
return await setSession(id, { ...session, id })
|
||||
},
|
||||
createSession: (session) => setSession(session.sessionToken, session),
|
||||
async getSessionAndUser(sessionToken) {
|
||||
const session = await getSession(sessionToken)
|
||||
if (!session) return null
|
||||
@@ -165,13 +161,20 @@ export function UpstashRedisAdapter(
|
||||
},
|
||||
async createVerificationToken(verificationToken) {
|
||||
await setObjectAsJson(
|
||||
verificationTokenKeyPrefix + verificationToken.identifier,
|
||||
verificationTokenKeyPrefix +
|
||||
verificationToken.identifier +
|
||||
":" +
|
||||
verificationToken.token,
|
||||
verificationToken
|
||||
)
|
||||
return verificationToken
|
||||
},
|
||||
async useVerificationToken(verificationToken) {
|
||||
const tokenKey = verificationTokenKeyPrefix + verificationToken.identifier
|
||||
const tokenKey =
|
||||
verificationTokenKeyPrefix +
|
||||
verificationToken.identifier +
|
||||
":" +
|
||||
verificationToken.token
|
||||
|
||||
const token = await client.get<VerificationToken>(tokenKey)
|
||||
if (!token) return null
|
||||
|
||||
@@ -11,6 +11,14 @@ if (!process.env.UPSTASH_REDIS_URL || !process.env.UPSTASH_REDIS_KEY) {
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
if (process.env.CI) {
|
||||
// TODO: Fix this
|
||||
test('Skipping UpstashRedisAdapter tests in CI because of "Request failed" errors. Should revisit', () => {
|
||||
expect(true).toBe(true)
|
||||
})
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const client = new Redis({
|
||||
url: process.env.UPSTASH_REDIS_URL,
|
||||
token: process.env.UPSTASH_REDIS_KEY,
|
||||
|
||||
@@ -1,23 +1,32 @@
|
||||
const swcConfig = require("./swc.config")
|
||||
|
||||
/** @type {import('jest').Config} */
|
||||
module.exports = {
|
||||
projects: [
|
||||
{
|
||||
displayName: "core",
|
||||
testMatch: ["**/*.test.ts"],
|
||||
testMatch: ["<rootDir>/tests/**/*.test.ts"],
|
||||
rootDir: ".",
|
||||
setupFilesAfterEnv: ["./config/jest-setup.js"],
|
||||
transform: {
|
||||
"\\.(js|jsx|ts|tsx)$": ["@swc/jest", require("./swc.config")],
|
||||
"\\.(js|jsx|ts|tsx)$": ["@swc/jest", swcConfig],
|
||||
},
|
||||
coveragePathIgnorePatterns: ["tests"],
|
||||
testEnvironment: "@edge-runtime/jest-environment",
|
||||
transformIgnorePatterns: ["node_modules/(?!uuid)/"],
|
||||
/** @type {import("@edge-runtime/vm").EdgeVMOptions} */
|
||||
testEnvironmentOptions: {
|
||||
codeGeneration: {
|
||||
strings: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: "client",
|
||||
testMatch: ["**/*.test.js"],
|
||||
testMatch: ["<rootDir>/src/client/**/*.test.js"],
|
||||
setupFilesAfterEnv: ["./config/jest-setup.js"],
|
||||
rootDir: ".",
|
||||
transform: {
|
||||
"\\.(js|jsx|ts|tsx)$": ["@swc/jest", require("./swc.config")],
|
||||
"\\.(js|jsx|ts|tsx)$": ["@swc/jest", swcConfig],
|
||||
},
|
||||
testEnvironment: "jsdom",
|
||||
coveragePathIgnorePatterns: ["__tests__"],
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/** @type {import("@swc/core").Config} */
|
||||
module.exports = {
|
||||
jsc: {
|
||||
parser: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "next-auth",
|
||||
"version": "4.11.0",
|
||||
"version": "4.14.0",
|
||||
"description": "Authentication for Next.js",
|
||||
"homepage": "https://next-auth.js.org",
|
||||
"repository": "https://github.com/nextauthjs/next-auth.git",
|
||||
@@ -70,7 +70,7 @@
|
||||
"@babel/runtime": "^7.16.3",
|
||||
"@panva/hkdf": "^1.0.1",
|
||||
"cookie": "^0.5.0",
|
||||
"jose": "^4.3.7",
|
||||
"jose": "^4.9.3",
|
||||
"oauth": "^0.9.15",
|
||||
"openid-client": "^5.1.0",
|
||||
"preact": "^10.6.3",
|
||||
@@ -78,7 +78,7 @@
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"next": "12.2.5",
|
||||
"next": "^12.2.5",
|
||||
"nodemailer": "^6.6.5",
|
||||
"react": "^17.0.2 || ^18",
|
||||
"react-dom": "^17.0.2 || ^18"
|
||||
@@ -96,6 +96,7 @@
|
||||
"@babel/preset-env": "^7.18.2",
|
||||
"@babel/preset-react": "^7.17.12",
|
||||
"@babel/preset-typescript": "^7.17.12",
|
||||
"@edge-runtime/jest-environment": "1.1.0-beta.35",
|
||||
"@next-auth/tsconfig": "workspace:*",
|
||||
"@swc/core": "^1.2.198",
|
||||
"@swc/jest": "^0.2.21",
|
||||
@@ -119,7 +120,7 @@
|
||||
"jest-environment-jsdom": "^28.1.1",
|
||||
"jest-watch-typeahead": "^1.1.0",
|
||||
"msw": "^0.42.3",
|
||||
"next": "12.2.5",
|
||||
"next": "12.3.1",
|
||||
"postcss": "^8.4.14",
|
||||
"postcss-cli": "^9.1.0",
|
||||
"postcss-nested": "^5.0.6",
|
||||
@@ -130,4 +131,4 @@
|
||||
"engines": {
|
||||
"node": "^12.19.0 || ^14.15.0 || ^16.13.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,15 @@ import { Account, User, Awaitable } from "."
|
||||
|
||||
export interface AdapterUser extends User {
|
||||
id: string
|
||||
email: string
|
||||
emailVerified: Date | null
|
||||
}
|
||||
|
||||
export interface AdapterAccount extends Account {
|
||||
userId: string
|
||||
}
|
||||
|
||||
export interface AdapterSession {
|
||||
id: string
|
||||
/** A randomly generated value that is used to get hold of the session. */
|
||||
sessionToken: string
|
||||
/** Used to connect the session to a particular user */
|
||||
@@ -55,13 +59,30 @@ export interface VerificationToken {
|
||||
* [Adapters Overview](https://next-auth.js.org/adapters/overview) |
|
||||
* [Create a custom adapter](https://next-auth.js.org/tutorials/creating-a-database-adapter)
|
||||
*/
|
||||
export interface Adapter {
|
||||
export type Adapter<WithVerificationToken = boolean> = DefaultAdapter &
|
||||
(WithVerificationToken extends true
|
||||
? {
|
||||
createVerificationToken: (
|
||||
verificationToken: VerificationToken
|
||||
) => Awaitable<VerificationToken | null | undefined>
|
||||
/**
|
||||
* Return verification token from the database
|
||||
* and delete it so it cannot be used again.
|
||||
*/
|
||||
useVerificationToken: (params: {
|
||||
identifier: string
|
||||
token: string
|
||||
}) => Awaitable<VerificationToken | null>
|
||||
}
|
||||
: {})
|
||||
|
||||
export interface DefaultAdapter {
|
||||
createUser: (user: Omit<AdapterUser, "id">) => Awaitable<AdapterUser>
|
||||
getUser: (id: string) => Awaitable<AdapterUser | null>
|
||||
getUserByEmail: (email: string) => Awaitable<AdapterUser | null>
|
||||
/** Using the provider id and the id of the user for a specific account, get the user. */
|
||||
getUserByAccount: (
|
||||
providerAccountId: Pick<Account, "provider" | "providerAccountId">
|
||||
providerAccountId: Pick<AdapterAccount, "provider" | "providerAccountId">
|
||||
) => Awaitable<AdapterUser | null>
|
||||
updateUser: (user: Partial<AdapterUser>) => Awaitable<AdapterUser>
|
||||
/** @todo Implement */
|
||||
@@ -69,12 +90,12 @@ export interface Adapter {
|
||||
userId: string
|
||||
) => Promise<void> | Awaitable<AdapterUser | null | undefined>
|
||||
linkAccount: (
|
||||
account: Account
|
||||
) => Promise<void> | Awaitable<Account | null | undefined>
|
||||
account: AdapterAccount
|
||||
) => Promise<void> | Awaitable<AdapterAccount | null | undefined>
|
||||
/** @todo Implement */
|
||||
unlinkAccount?: (
|
||||
providerAccountId: Pick<Account, "provider" | "providerAccountId">
|
||||
) => Promise<void> | Awaitable<Account | undefined>
|
||||
providerAccountId: Pick<AdapterAccount, "provider" | "providerAccountId">
|
||||
) => Promise<void> | Awaitable<AdapterAccount | undefined>
|
||||
/** Creates a session for the user and returns it. */
|
||||
createSession: (session: {
|
||||
sessionToken: string
|
||||
|
||||
@@ -94,10 +94,18 @@ export function BroadcastChannel(name = "nextauth.message") {
|
||||
/** Notify other tabs/windows. */
|
||||
post(message: Record<string, unknown>) {
|
||||
if (typeof window === "undefined") return
|
||||
localStorage.setItem(
|
||||
name,
|
||||
JSON.stringify({ ...message, timestamp: now() })
|
||||
)
|
||||
try {
|
||||
localStorage.setItem(
|
||||
name,
|
||||
JSON.stringify({ ...message, timestamp: now() })
|
||||
)
|
||||
} catch {
|
||||
/**
|
||||
* The localStorage API isn't always available.
|
||||
* It won't work in private mode prior to Safari 11 for example.
|
||||
* Notifications are simply dropped if an error is encountered.
|
||||
*/
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { EventCallbacks, LoggerInstance } from ".."
|
||||
import type { Adapter } from "../adapters"
|
||||
|
||||
/**
|
||||
* Same as the default `Error`, but it is JSON serializable.
|
||||
@@ -58,6 +57,11 @@ export class MissingAdapter extends UnknownError {
|
||||
code = "EMAIL_REQUIRES_ADAPTER_ERROR"
|
||||
}
|
||||
|
||||
export class MissingAdapterMethods extends UnknownError {
|
||||
name = "MissingAdapterMethodsError"
|
||||
code = "MISSING_ADAPTER_METHODS_ERROR"
|
||||
}
|
||||
|
||||
export class UnsupportedStrategy extends UnknownError {
|
||||
name = "UnsupportedStrategyError"
|
||||
code = "CALLBACK_CREDENTIALS_JWT_ERROR"
|
||||
@@ -99,10 +103,10 @@ export function eventsErrorHandler(
|
||||
}
|
||||
|
||||
/** Handles adapter induced errors. */
|
||||
export function adapterErrorHandler(
|
||||
adapter: Adapter | undefined,
|
||||
export function adapterErrorHandler<TAdapter>(
|
||||
adapter: TAdapter | undefined,
|
||||
logger: LoggerInstance
|
||||
): Adapter | undefined {
|
||||
): TAdapter | undefined {
|
||||
if (!adapter) return
|
||||
|
||||
return Object.keys(adapter).reduce<any>((acc, name) => {
|
||||
|
||||
@@ -94,13 +94,21 @@ export async function NextAuthHandler<
|
||||
assertionResult.forEach(logger.warn)
|
||||
} else if (assertionResult instanceof Error) {
|
||||
// Bail out early if there's an error in the user config
|
||||
const { pages, theme } = userOptions
|
||||
logger.error(assertionResult.code, assertionResult)
|
||||
|
||||
const htmlPages = ["signin", "signout", "error", "verify-request"]
|
||||
if (!htmlPages.includes(req.action) || req.method !== "GET") {
|
||||
const message = `There is a problem with the server configuration. Check the server logs for more information.`
|
||||
return {
|
||||
status: 500,
|
||||
headers: [{ key: "Content-Type", value: "application/json" }],
|
||||
body: { message } as any,
|
||||
}
|
||||
}
|
||||
const { pages, theme } = userOptions
|
||||
|
||||
const authOnErrorPage =
|
||||
pages?.error &&
|
||||
req.action === "signin" &&
|
||||
req.query?.callbackUrl.startsWith(pages.error)
|
||||
pages?.error && req.query?.callbackUrl?.startsWith(pages.error)
|
||||
|
||||
if (!pages?.error || authOnErrorPage) {
|
||||
if (authOnErrorPage) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { randomBytes, randomUUID } from "crypto"
|
||||
import { NextAuthOptions } from ".."
|
||||
import logger from "../utils/logger"
|
||||
import parseUrl from "../utils/parse-url"
|
||||
@@ -70,6 +71,7 @@ export async function init({
|
||||
// and are request-specific.
|
||||
url,
|
||||
action,
|
||||
// @ts-expect-errors
|
||||
provider,
|
||||
cookies: {
|
||||
...cookie.defaultCookies(
|
||||
@@ -86,6 +88,10 @@ export async function init({
|
||||
strategy: userOptions.adapter ? "database" : "jwt",
|
||||
maxAge,
|
||||
updateAge: 24 * 60 * 60,
|
||||
generateSessionToken: () => {
|
||||
// Use `randomUUID` if available. (Node 15.6+)
|
||||
return randomUUID?.() ?? randomBytes(32).toString("hex")
|
||||
},
|
||||
...userOptions.session,
|
||||
},
|
||||
// JWT options
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
MissingSecret,
|
||||
UnsupportedStrategy,
|
||||
InvalidCallbackUrl,
|
||||
MissingAdapterMethods,
|
||||
} from "../errors"
|
||||
import parseUrl from "../../utils/parse-url"
|
||||
import { defaultCookies } from "./cookie"
|
||||
@@ -120,8 +121,23 @@ export function assertConfig(params: {
|
||||
}
|
||||
}
|
||||
|
||||
if (hasEmail && !options.adapter) {
|
||||
return new MissingAdapter("E-mail login requires an adapter.")
|
||||
if (hasEmail) {
|
||||
const { adapter } = options
|
||||
if (!adapter) {
|
||||
return new MissingAdapter("E-mail login requires an adapter.")
|
||||
}
|
||||
|
||||
const missingMethods = [
|
||||
"createVerificationToken",
|
||||
"useVerificationToken",
|
||||
"getUserByEmail",
|
||||
].filter((method) => !adapter[method])
|
||||
|
||||
if (missingMethods.length) {
|
||||
return new MissingAdapterMethods(
|
||||
`Required adapter methods were missing: ${missingMethods.join(", ")}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (!warned) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { randomBytes, randomUUID } from "crypto"
|
||||
import { AccountNotLinkedError } from "../errors"
|
||||
import { fromDate } from "./utils"
|
||||
|
||||
@@ -22,11 +21,11 @@ import type { SessionToken } from "./cookie"
|
||||
*/
|
||||
export default async function callbackHandler(params: {
|
||||
sessionToken?: SessionToken
|
||||
profile: User
|
||||
account: Account
|
||||
profile: User | AdapterUser | { email: string }
|
||||
account: Account | null
|
||||
options: InternalOptions
|
||||
}) {
|
||||
const { sessionToken, profile, account, options } = params
|
||||
const { sessionToken, profile: _profile, account, options } = params
|
||||
// Input validation
|
||||
if (!account?.providerAccountId || !account.type)
|
||||
throw new Error("Missing or invalid provider account")
|
||||
@@ -37,15 +36,17 @@ export default async function callbackHandler(params: {
|
||||
adapter,
|
||||
jwt,
|
||||
events,
|
||||
session: { strategy: sessionStrategy },
|
||||
session: { strategy: sessionStrategy, generateSessionToken },
|
||||
} = options
|
||||
|
||||
// If no adapter is configured then we don't have a database and cannot
|
||||
// persist data; in this mode we just return a dummy session object.
|
||||
if (!adapter) {
|
||||
return { user: profile, account, session: {} }
|
||||
return { user: _profile as User, account }
|
||||
}
|
||||
|
||||
const profile = _profile as AdapterUser
|
||||
|
||||
const {
|
||||
createUser,
|
||||
updateUser,
|
||||
@@ -85,9 +86,7 @@ export default async function callbackHandler(params: {
|
||||
|
||||
if (account.type === "email") {
|
||||
// If signing in with an email, check if an account with the same email address exists already
|
||||
const userByEmail = profile.email
|
||||
? await getUserByEmail(profile.email)
|
||||
: null
|
||||
const userByEmail = await getUserByEmail(profile.email)
|
||||
if (userByEmail) {
|
||||
// If they are not already signed in as the same user, this flow will
|
||||
// sign them out of the current session and sign them in as the new user
|
||||
@@ -102,8 +101,7 @@ export default async function callbackHandler(params: {
|
||||
user = await updateUser({ id: userByEmail.id, emailVerified: new Date() })
|
||||
await events.updateUser?.({ user })
|
||||
} else {
|
||||
const newUser = { ...profile, emailVerified: new Date() }
|
||||
delete (newUser as Omit<AdapterUser, "id">).id
|
||||
const { id: _, ...newUser } = { ...profile, emailVerified: new Date() }
|
||||
// Create user account if there isn't one for the email address already
|
||||
user = await createUser(newUser)
|
||||
await events.createUser?.({ user })
|
||||
@@ -199,8 +197,7 @@ export default async function callbackHandler(params: {
|
||||
// If no account matching the same [provider].id or .email exists, we can
|
||||
// create a new account for the user, link it to the OAuth acccount and
|
||||
// create a new session for them so they are signed in with it.
|
||||
const newUser = { ...profile, emailVerified: null }
|
||||
delete (newUser as Omit<AdapterUser, "id">).id
|
||||
const { id: _, ...newUser } = { ...profile, emailVerified: null }
|
||||
user = await createUser(newUser)
|
||||
await events.createUser?.({ user })
|
||||
|
||||
@@ -218,9 +215,6 @@ export default async function callbackHandler(params: {
|
||||
return { session, user, isNewUser: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function generateSessionToken() {
|
||||
// Use `randomUUID` if available. (Node 15.6++)
|
||||
return randomUUID?.() ?? randomBytes(32).toString("hex")
|
||||
throw new Error("Unsupported account type")
|
||||
}
|
||||
|
||||
20
packages/next-auth/src/core/lib/email/getUserFromEmail.ts
Normal file
20
packages/next-auth/src/core/lib/email/getUserFromEmail.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { AdapterUser } from "../../../adapters"
|
||||
import type { InternalOptions } from "../../types"
|
||||
|
||||
/**
|
||||
* Query the database for a user by email address.
|
||||
* If is an existing user return a user object (otherwise use placeholder).
|
||||
*/
|
||||
export default async function getAdapterUserFromEmail({
|
||||
email,
|
||||
adapter,
|
||||
}: {
|
||||
email: string
|
||||
adapter: InternalOptions<"email">["adapter"]
|
||||
}): Promise<AdapterUser> {
|
||||
const { getUserByEmail } = adapter
|
||||
const adapterUser = email ? await getUserByEmail(email) : null
|
||||
if (adapterUser) return adapterUser
|
||||
|
||||
return { id: email, email, emailVerified: null }
|
||||
}
|
||||
@@ -36,7 +36,6 @@ export default async function email(
|
||||
theme,
|
||||
}),
|
||||
// Save in database
|
||||
// @ts-expect-error // verified in `assertConfig`
|
||||
adapter.createVerificationToken({
|
||||
identifier,
|
||||
token: hashToken(token, options),
|
||||
|
||||
@@ -39,10 +39,7 @@ export default async function getAuthorizationUrl({
|
||||
if (provider.version?.startsWith("1.")) {
|
||||
const client = oAuth1Client(options)
|
||||
const tokens = (await client.getOAuthRequestToken(params)) as any
|
||||
const url = `${
|
||||
// @ts-expect-error
|
||||
provider.authorization?.url ?? provider.authorization
|
||||
}?${new URLSearchParams({
|
||||
const url = `${provider.authorization?.url}?${new URLSearchParams({
|
||||
oauth_token: tokens.oauth_token,
|
||||
oauth_token_secret: tokens.oauth_token_secret,
|
||||
...tokens.params,
|
||||
@@ -68,7 +65,7 @@ export default async function getAuthorizationUrl({
|
||||
authorizationParams.nonce = nonce.value
|
||||
cookies.push(nonce.cookie)
|
||||
}
|
||||
|
||||
|
||||
const pkce = await createPKCE(options)
|
||||
if (pkce) {
|
||||
authorizationParams.code_challenge = pkce.code_challenge
|
||||
|
||||
@@ -7,10 +7,10 @@ import { useNonce } from "./nonce-handler"
|
||||
import { OAuthCallbackError } from "../../errors"
|
||||
|
||||
import type { CallbackParamsType, OpenIDCallbackChecks } from "openid-client"
|
||||
import type { Account, LoggerInstance, Profile } from "../../.."
|
||||
import type { LoggerInstance, Profile } from "../../.."
|
||||
import type { OAuthChecks, OAuthConfig } from "../../../providers"
|
||||
import type { InternalOptions } from "../../types"
|
||||
import type { RequestInternal, OutgoingResponse } from "../.."
|
||||
import type { RequestInternal } from "../.."
|
||||
import type { Cookie } from "../cookie"
|
||||
|
||||
export default async function oAuthCallback(params: {
|
||||
@@ -19,7 +19,7 @@ export default async function oAuthCallback(params: {
|
||||
body: RequestInternal["body"]
|
||||
method: Required<RequestInternal>["method"]
|
||||
cookies: RequestInternal["cookies"]
|
||||
}): Promise<GetProfileResult & { cookies?: OutgoingResponse["cookies"] }> {
|
||||
}) {
|
||||
const { options, query, body, method, cookies } = params
|
||||
const { logger, provider } = options
|
||||
|
||||
@@ -34,23 +34,19 @@ export default async function oAuthCallback(params: {
|
||||
logger.debug("OAUTH_CALLBACK_HANDLER_ERROR", { body })
|
||||
throw error
|
||||
}
|
||||
|
||||
|
||||
if (provider.version?.startsWith("1.")) {
|
||||
try {
|
||||
const client = await oAuth1Client(options)
|
||||
// Handle OAuth v1.x
|
||||
const { oauth_token, oauth_verifier } = query ?? {}
|
||||
// @ts-expect-error
|
||||
const tokens: TokenSet = await client.getOAuthAccessToken(
|
||||
oauth_token as string,
|
||||
// @ts-expect-error
|
||||
const tokens = (await (client as any).getOAuthAccessToken(
|
||||
oauth_token,
|
||||
null,
|
||||
oauth_verifier
|
||||
)
|
||||
// @ts-expect-error
|
||||
let profile: Profile = await client.get(
|
||||
(provider as any).profileUrl,
|
||||
)) as TokenSet
|
||||
let profile: Profile = await (client as any).get(
|
||||
provider.profileUrl,
|
||||
tokens.oauth_token,
|
||||
tokens.oauth_token_secret
|
||||
)
|
||||
@@ -59,7 +55,8 @@ export default async function oAuthCallback(params: {
|
||||
profile = JSON.parse(profile)
|
||||
}
|
||||
|
||||
return await getProfile({ profile, tokens, provider, logger })
|
||||
const newProfile = await getProfile({ profile, tokens, provider, logger })
|
||||
return { ...newProfile, cookies: [] }
|
||||
} catch (error) {
|
||||
logger.error("OAUTH_V1_GET_ACCESS_TOKEN_ERROR", error as Error)
|
||||
throw error
|
||||
@@ -82,7 +79,7 @@ export default async function oAuthCallback(params: {
|
||||
|
||||
const nonce = await useNonce(cookies?.[options.cookies.nonce.name], options)
|
||||
if (nonce && provider.idToken) {
|
||||
(checks as OpenIDCallbackChecks).nonce = nonce.value
|
||||
;(checks as OpenIDCallbackChecks).nonce = nonce.value
|
||||
resCookies.push(nonce.cookie)
|
||||
}
|
||||
|
||||
@@ -102,13 +99,10 @@ export default async function oAuthCallback(params: {
|
||||
body,
|
||||
method,
|
||||
}),
|
||||
// @ts-expect-error
|
||||
...provider.token?.params,
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
if (provider.token?.request) {
|
||||
// @ts-expect-error
|
||||
const response = await provider.token.request({
|
||||
provider,
|
||||
params,
|
||||
@@ -128,9 +122,7 @@ export default async function oAuthCallback(params: {
|
||||
}
|
||||
|
||||
let profile: Profile
|
||||
// @ts-expect-error
|
||||
if (provider.userinfo?.request) {
|
||||
// @ts-expect-error
|
||||
profile = await provider.userinfo.request({
|
||||
provider,
|
||||
tokens,
|
||||
@@ -140,7 +132,6 @@ export default async function oAuthCallback(params: {
|
||||
profile = tokens.claims()
|
||||
} else {
|
||||
profile = await client.userinfo(tokens, {
|
||||
// @ts-expect-error
|
||||
params: provider.userinfo?.params,
|
||||
})
|
||||
}
|
||||
@@ -164,25 +155,22 @@ export interface GetProfileParams {
|
||||
logger: LoggerInstance
|
||||
}
|
||||
|
||||
export interface GetProfileResult {
|
||||
// @ts-expect-error
|
||||
profile: ReturnType<OAuthConfig["profile"]> | null
|
||||
account: Omit<Account, "userId"> | null
|
||||
OAuthProfile: Profile
|
||||
}
|
||||
|
||||
/** Returns profile, raw profile and auth provider details */
|
||||
async function getProfile({
|
||||
profile: OAuthProfile,
|
||||
tokens,
|
||||
provider,
|
||||
logger,
|
||||
}: GetProfileParams): Promise<GetProfileResult> {
|
||||
}: GetProfileParams) {
|
||||
try {
|
||||
logger.debug("PROFILE_DATA", { OAuthProfile })
|
||||
// @ts-expect-error
|
||||
const profile = await provider.profile(OAuthProfile, tokens)
|
||||
profile.email = profile.email?.toLowerCase()
|
||||
if (!profile.id)
|
||||
throw new TypeError(
|
||||
`Profile id is missing in ${provider.name} OAuth profile response`
|
||||
)
|
||||
|
||||
// Return profile, raw profile and auth provider details
|
||||
return {
|
||||
profile,
|
||||
@@ -202,11 +190,9 @@ async function getProfile({
|
||||
// all providers, so we return an empty object; the user should then be
|
||||
// redirected back to the sign up page. We log the error to help developers
|
||||
// who might be trying to debug this when configuring a new provider.
|
||||
logger.error("OAUTH_PARSE_PROFILE_ERROR", error as Error)
|
||||
return {
|
||||
profile: null,
|
||||
account: null,
|
||||
logger.error("OAUTH_PARSE_PROFILE_ERROR", {
|
||||
error: error as Error,
|
||||
OAuthProfile,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,13 +22,9 @@ export async function openidClient(
|
||||
} else {
|
||||
issuer = new Issuer({
|
||||
issuer: provider.issuer as string,
|
||||
authorization_endpoint:
|
||||
// @ts-expect-error
|
||||
provider.authorization?.url ?? provider.authorization,
|
||||
// @ts-expect-error
|
||||
token_endpoint: provider.token?.url ?? provider.token,
|
||||
// @ts-expect-error
|
||||
userinfo_endpoint: provider.userinfo?.url ?? provider.userinfo,
|
||||
authorization_endpoint: provider.authorization?.url,
|
||||
token_endpoint: provider.token?.url,
|
||||
userinfo_endpoint: provider.userinfo?.url,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { merge } from "../../utils/merge"
|
||||
|
||||
import type { InternalProvider } from "../types"
|
||||
import type { Provider } from "../../providers"
|
||||
import type {
|
||||
InternalOAuthConfig,
|
||||
OAuthConfig,
|
||||
Provider,
|
||||
} from "../../providers"
|
||||
import type { InternalUrl } from "../../utils/parse-url"
|
||||
|
||||
/**
|
||||
@@ -18,52 +22,72 @@ export default function parseProviders(params: {
|
||||
} {
|
||||
const { url, providerId } = params
|
||||
|
||||
const providers = params.providers.map(({ options, ...rest }) => {
|
||||
const defaultOptions = normalizeProvider(rest as Provider)
|
||||
const userOptions = normalizeProvider(options as Provider)
|
||||
const providers = params.providers.map<InternalProvider>(
|
||||
({ options: userOptions, ...rest }) => {
|
||||
if (rest.type === "oauth") {
|
||||
const normalizedOptions = normalizeOAuthOptions(rest)
|
||||
const normalizedUserOptions = normalizeOAuthOptions(userOptions, true)
|
||||
const id = normalizedUserOptions?.id ?? rest.id
|
||||
return merge(normalizedOptions, {
|
||||
...normalizedUserOptions,
|
||||
signinUrl: `${url}/signin/${id}`,
|
||||
callbackUrl: `${url}/callback/${id}`,
|
||||
})
|
||||
}
|
||||
const id = (userOptions?.id as string) ?? rest.id
|
||||
return merge(rest, {
|
||||
...userOptions,
|
||||
signinUrl: `${url}/signin/${id}`,
|
||||
callbackUrl: `${url}/callback/${id}`,
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
return merge(defaultOptions, {
|
||||
...userOptions,
|
||||
signinUrl: `${url}/signin/${userOptions?.id ?? rest.id}`,
|
||||
callbackUrl: `${url}/callback/${userOptions?.id ?? rest.id}`,
|
||||
})
|
||||
})
|
||||
|
||||
const provider = providers.find(({ id }) => id === providerId)
|
||||
|
||||
return { providers, provider }
|
||||
return {
|
||||
providers,
|
||||
provider: providers.find(({ id }) => id === providerId),
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeProvider(provider?: Provider) {
|
||||
if (!provider) return
|
||||
/**
|
||||
* Transform OAuth options `authorization`, `token` and `profile` strings to `{ url: string; params: Record<string, string> }`
|
||||
*/
|
||||
function normalizeOAuthOptions(
|
||||
oauthOptions?: Partial<OAuthConfig<any>> | Record<string, unknown>,
|
||||
isUserOptions = false
|
||||
) {
|
||||
if (!oauthOptions) return
|
||||
|
||||
const normalized: InternalProvider = Object.entries(
|
||||
provider
|
||||
).reduce<InternalProvider>((acc, [key, value]) => {
|
||||
if (
|
||||
["authorization", "token", "userinfo"].includes(key) &&
|
||||
typeof value === "string"
|
||||
) {
|
||||
const url = new URL(value)
|
||||
acc[key] = {
|
||||
url: `${url.origin}${url.pathname}`,
|
||||
params: Object.fromEntries(url.searchParams ?? []),
|
||||
const normalized = Object.entries(oauthOptions).reduce<
|
||||
InternalOAuthConfig<Record<string, unknown>>
|
||||
>(
|
||||
(acc, [key, value]) => {
|
||||
if (
|
||||
["authorization", "token", "userinfo"].includes(key) &&
|
||||
typeof value === "string"
|
||||
) {
|
||||
const url = new URL(value)
|
||||
acc[key] = {
|
||||
url: `${url.origin}${url.pathname}`,
|
||||
params: Object.fromEntries(url.searchParams ?? []),
|
||||
}
|
||||
} else {
|
||||
acc[key] = value
|
||||
}
|
||||
} else {
|
||||
acc[key] = value
|
||||
}
|
||||
|
||||
return acc
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-reduce-type-parameter, @typescript-eslint/consistent-type-assertions
|
||||
}, {} as any)
|
||||
return acc
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-reduce-type-parameter
|
||||
{} as any
|
||||
)
|
||||
|
||||
if (normalized.type === "oauth" && !normalized.version?.startsWith("1.")) {
|
||||
if (!isUserOptions && !normalized.version?.startsWith("1.")) {
|
||||
// If provider has as an "openid-configuration" well-known endpoint
|
||||
// or an "openid" scope request, it will also likely be able to receive an `id_token`
|
||||
// Only do this if this function is not called with user options to avoid overriding in later stage.
|
||||
normalized.idToken = Boolean(
|
||||
normalized.idToken ??
|
||||
normalized.wellKnown?.includes("openid-configuration") ??
|
||||
// @ts-expect-error
|
||||
normalized.authorization?.params?.scope?.includes("openid")
|
||||
)
|
||||
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import oAuthCallback from "../lib/oauth/callback"
|
||||
import callbackHandler from "../lib/callback-handler"
|
||||
import { hashToken } from "../lib/utils"
|
||||
import getAdapterUserFromEmail from "../lib/email/getUserFromEmail"
|
||||
|
||||
import type { InternalOptions } from "../types"
|
||||
import type { RequestInternal, OutgoingResponse } from ".."
|
||||
import type { Cookie, SessionStore } from "../lib/cookie"
|
||||
import type { User } from "../.."
|
||||
import type { AdapterSession } from "../../adapters"
|
||||
|
||||
/** Handle callbacks from login services */
|
||||
export default async function callback(params: {
|
||||
options: InternalOptions<"oauth" | "credentials" | "email">
|
||||
options: InternalOptions
|
||||
query: RequestInternal["query"]
|
||||
method: Required<RequestInternal>["method"]
|
||||
body: RequestInternal["body"]
|
||||
@@ -50,7 +52,7 @@ export default async function callback(params: {
|
||||
cookies: params.cookies,
|
||||
})
|
||||
|
||||
if (oauthCookies) cookies.push(...oauthCookies)
|
||||
if (oauthCookies.length) cookies.push(...oauthCookies)
|
||||
|
||||
try {
|
||||
// Make it easier to debug when adding a new provider
|
||||
@@ -68,7 +70,7 @@ export default async function callback(params: {
|
||||
// Note: In oAuthCallback an error is logged with debug info, so it
|
||||
// should at least be visible to developers what happened if it is an
|
||||
// error with the provider.
|
||||
if (!profile) {
|
||||
if (!profile || !account || !OAuthProfile) {
|
||||
return { redirect: `${url}/signin`, cookies }
|
||||
}
|
||||
|
||||
@@ -80,7 +82,6 @@ export default async function callback(params: {
|
||||
if (adapter) {
|
||||
const { getUserByAccount } = adapter
|
||||
const userByAccount = await getUserByAccount({
|
||||
// @ts-expect-error
|
||||
providerAccountId: account.providerAccountId,
|
||||
provider: provider.id,
|
||||
})
|
||||
@@ -91,7 +92,6 @@ export default async function callback(params: {
|
||||
try {
|
||||
const isAllowed = await callbacks.signIn({
|
||||
user: userOrProfile,
|
||||
// @ts-expect-error
|
||||
account,
|
||||
profile: OAuthProfile,
|
||||
})
|
||||
@@ -110,11 +110,9 @@ export default async function callback(params: {
|
||||
}
|
||||
|
||||
// Sign user in
|
||||
// @ts-expect-error
|
||||
const { user, session, isNewUser } = await callbackHandler({
|
||||
sessionToken: sessionStore.value,
|
||||
profile,
|
||||
// @ts-expect-error
|
||||
account,
|
||||
options,
|
||||
})
|
||||
@@ -129,7 +127,6 @@ export default async function callback(params: {
|
||||
const token = await callbacks.jwt({
|
||||
token: defaultToken,
|
||||
user,
|
||||
// @ts-expect-error
|
||||
account,
|
||||
profile: OAuthProfile,
|
||||
isNewUser,
|
||||
@@ -150,10 +147,10 @@ export default async function callback(params: {
|
||||
// Save Session Token in cookie
|
||||
cookies.push({
|
||||
name: options.cookies.sessionToken.name,
|
||||
value: session.sessionToken,
|
||||
value: (session as AdapterSession).sessionToken,
|
||||
options: {
|
||||
...options.cookies.sessionToken.options,
|
||||
expires: session.expires,
|
||||
expires: (session as AdapterSession).expires,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -201,14 +198,16 @@ export default async function callback(params: {
|
||||
}
|
||||
} else if (provider.type === "email") {
|
||||
try {
|
||||
// Verified in `assertConfig`
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const { useVerificationToken, getUserByEmail } = adapter!
|
||||
const token = query?.token as string | undefined
|
||||
const identifier = query?.email as string | undefined
|
||||
|
||||
const token = query?.token
|
||||
const identifier = query?.email
|
||||
// If these are missing, the sign-in URL was manually opened without these params or the `sendVerificationRequest` method did not send the link correctly in the email.
|
||||
if (!token || !identifier) {
|
||||
return { redirect: `${url}/error?error=configuration`, cookies }
|
||||
}
|
||||
|
||||
const invite = await useVerificationToken?.({
|
||||
// @ts-expect-error -- Verified in `assertConfig`. adapter: Adapter<true>
|
||||
const invite = await adapter.useVerificationToken({
|
||||
identifier,
|
||||
token: hashToken(token, options),
|
||||
})
|
||||
@@ -218,29 +217,23 @@ export default async function callback(params: {
|
||||
return { redirect: `${url}/error?error=Verification`, cookies }
|
||||
}
|
||||
|
||||
// If it is an existing user, use that, otherwise use a placeholder
|
||||
const profile = (identifier
|
||||
? await getUserByEmail(identifier)
|
||||
: null) ?? {
|
||||
const profile = await getAdapterUserFromEmail({
|
||||
email: identifier,
|
||||
}
|
||||
// @ts-expect-error -- Verified in `assertConfig`. adapter: Adapter<true>
|
||||
adapter,
|
||||
})
|
||||
|
||||
/** @type {import("src").Account} */
|
||||
const account = {
|
||||
providerAccountId: profile.email,
|
||||
type: "email",
|
||||
type: "email" as const,
|
||||
provider: provider.id,
|
||||
}
|
||||
|
||||
// Check if user is allowed to sign in
|
||||
try {
|
||||
const signInCallbackResponse = await callbacks.signIn({
|
||||
// @ts-expect-error
|
||||
user: profile,
|
||||
// @ts-expect-error
|
||||
account,
|
||||
// @ts-expect-error
|
||||
email: { email: identifier },
|
||||
})
|
||||
if (!signInCallbackResponse) {
|
||||
return { redirect: `${url}/error?error=AccessDenied`, cookies }
|
||||
@@ -257,12 +250,9 @@ export default async function callback(params: {
|
||||
}
|
||||
|
||||
// Sign user in
|
||||
// @ts-expect-error
|
||||
const { user, session, isNewUser } = await callbackHandler({
|
||||
sessionToken: sessionStore.value,
|
||||
// @ts-expect-error
|
||||
profile,
|
||||
// @ts-expect-error
|
||||
account,
|
||||
options,
|
||||
})
|
||||
@@ -277,7 +267,6 @@ export default async function callback(params: {
|
||||
const token = await callbacks.jwt({
|
||||
token: defaultToken,
|
||||
user,
|
||||
// @ts-expect-error
|
||||
account,
|
||||
isNewUser,
|
||||
})
|
||||
@@ -297,15 +286,14 @@ export default async function callback(params: {
|
||||
// Save Session Token in cookie
|
||||
cookies.push({
|
||||
name: options.cookies.sessionToken.name,
|
||||
value: session.sessionToken,
|
||||
value: (session as AdapterSession).sessionToken,
|
||||
options: {
|
||||
...options.cookies.sessionToken.options,
|
||||
expires: session.expires,
|
||||
expires: (session as AdapterSession).expires,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
await events.signIn?.({ user, account, isNewUser })
|
||||
|
||||
// Handle first logins on new accounts
|
||||
@@ -332,14 +320,14 @@ export default async function callback(params: {
|
||||
} else if (provider.type === "credentials" && method === "POST") {
|
||||
const credentials = body
|
||||
|
||||
let user: User
|
||||
let user: User | null
|
||||
try {
|
||||
user = (await provider.authorize(credentials, {
|
||||
user = await provider.authorize(credentials, {
|
||||
query,
|
||||
body,
|
||||
headers,
|
||||
method,
|
||||
})) as User
|
||||
})
|
||||
if (!user) {
|
||||
return {
|
||||
status: 401,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import getAuthorizationUrl from "../lib/oauth/authorization-url"
|
||||
import emailSignin from "../lib/email/signin"
|
||||
import getAdapterUserFromEmail from "../lib/email/getUserFromEmail"
|
||||
import type { RequestInternal, OutgoingResponse } from ".."
|
||||
import type { InternalOptions } from "../types"
|
||||
import type { Account, User } from "../.."
|
||||
import type { Account } from "../.."
|
||||
|
||||
/** Handle requests to /api/auth/signin */
|
||||
export default async function signin(params: {
|
||||
@@ -11,7 +12,7 @@ export default async function signin(params: {
|
||||
body: RequestInternal["body"]
|
||||
}): Promise<OutgoingResponse> {
|
||||
const { options, query, body } = params
|
||||
const { url, adapter, callbacks, logger, provider } = options
|
||||
const { url, callbacks, logger, provider } = options
|
||||
|
||||
if (!provider.type) {
|
||||
return {
|
||||
@@ -54,14 +55,11 @@ export default async function signin(params: {
|
||||
return { redirect: `${url}/error?error=EmailSignin` }
|
||||
}
|
||||
|
||||
// Verified in `assertConfig`
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const { getUserByEmail } = adapter!
|
||||
// If is an existing user return a user object (otherwise use placeholder)
|
||||
const user: User = (email ? await getUserByEmail(email) : null) ?? {
|
||||
const user = await getAdapterUserFromEmail({
|
||||
email,
|
||||
id: email,
|
||||
}
|
||||
// @ts-expect-error -- Verified in `assertConfig`. adapter: Adapter<true>
|
||||
adapter: options.adapter,
|
||||
})
|
||||
|
||||
const account: Account = {
|
||||
providerAccountId: email,
|
||||
@@ -72,7 +70,6 @@ export default async function signin(params: {
|
||||
|
||||
// Check if user is allowed to sign in
|
||||
try {
|
||||
// @ts-expect-error
|
||||
const signInCallbackResponse = await callbacks.signIn({
|
||||
user,
|
||||
account,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { Adapter } from "../adapters"
|
||||
import type { Adapter, AdapterUser } from "../adapters"
|
||||
import type {
|
||||
Provider,
|
||||
CredentialInput,
|
||||
ProviderType,
|
||||
OAuthConfig,
|
||||
EmailConfig,
|
||||
CredentialsConfig,
|
||||
InternalOAuthConfig,
|
||||
} from "../providers"
|
||||
import type { TokenSetParameters } from "openid-client"
|
||||
import type { JWT, JWTOptions } from "../jwt"
|
||||
@@ -231,7 +231,7 @@ export type TokenSet = TokenSetParameters
|
||||
* Usually contains information about the provider being used
|
||||
* and also extends `TokenSet`, which is different tokens returned by OAuth Providers.
|
||||
*/
|
||||
export interface DefaultAccount extends Partial<TokenSet> {
|
||||
export interface Account extends Partial<TokenSet> {
|
||||
/**
|
||||
* This value depends on the type of the provider being used to create the account.
|
||||
* - oauth: The OAuth account's id, returned from the `profile()` callback.
|
||||
@@ -240,30 +240,23 @@ export interface DefaultAccount extends Partial<TokenSet> {
|
||||
*/
|
||||
providerAccountId: string
|
||||
/** id of the user this account belongs to. */
|
||||
userId: string
|
||||
userId?: string
|
||||
/** id of the provider used for this account */
|
||||
provider: string
|
||||
/** Provider's type for this account */
|
||||
type: ProviderType
|
||||
}
|
||||
|
||||
export interface Account extends Record<string, unknown>, DefaultAccount {}
|
||||
|
||||
export interface DefaultProfile {
|
||||
/** The OAuth profile returned from your provider */
|
||||
export interface Profile {
|
||||
sub?: string
|
||||
name?: string
|
||||
email?: string
|
||||
image?: string
|
||||
}
|
||||
|
||||
/** The OAuth profile returned from your provider */
|
||||
export interface Profile extends Record<string, unknown>, DefaultProfile {}
|
||||
|
||||
/** [Documentation](https://next-auth.js.org/configuration/callbacks) */
|
||||
export interface CallbacksOptions<
|
||||
P extends Record<string, unknown> = Profile,
|
||||
A extends Record<string, unknown> = Account
|
||||
> {
|
||||
export interface CallbacksOptions<P = Profile, A = Account> {
|
||||
/**
|
||||
* Use this callback to control if a user is allowed to sign in.
|
||||
* Returning true will continue the sign-in flow.
|
||||
@@ -272,13 +265,13 @@ export interface CallbacksOptions<
|
||||
* [Documentation](https://next-auth.js.org/configuration/callbacks#sign-in-callback)
|
||||
*/
|
||||
signIn: (params: {
|
||||
user: User
|
||||
account: A
|
||||
user: User | AdapterUser
|
||||
account: A | null
|
||||
/**
|
||||
* If OAuth provider is used, it contains the full
|
||||
* OAuth profile returned by your provider.
|
||||
*/
|
||||
profile: P & Record<string, unknown>
|
||||
profile?: P
|
||||
/**
|
||||
* If Email provider is used, on the first call, it contains a
|
||||
* `verificationRequest: true` property to indicate it is being triggered in the verification request flow.
|
||||
@@ -287,7 +280,7 @@ export interface CallbacksOptions<
|
||||
* to avoid sending emails to addresses or domains on a blocklist or to only explicitly generate them
|
||||
* for email address in an allow list.
|
||||
*/
|
||||
email: {
|
||||
email?: {
|
||||
verificationRequest?: boolean
|
||||
}
|
||||
/** If Credentials provider is used, it contains the user credentials */
|
||||
@@ -324,7 +317,7 @@ export interface CallbacksOptions<
|
||||
*/
|
||||
session: (params: {
|
||||
session: Session
|
||||
user: User
|
||||
user: User | AdapterUser
|
||||
token: JWT
|
||||
}) => Awaitable<Session>
|
||||
/**
|
||||
@@ -341,8 +334,8 @@ export interface CallbacksOptions<
|
||||
*/
|
||||
jwt: (params: {
|
||||
token: JWT
|
||||
user?: User
|
||||
account?: A
|
||||
user?: User | AdapterUser
|
||||
account?: A | null
|
||||
profile?: P
|
||||
isNewUser?: boolean
|
||||
}) => Awaitable<JWT>
|
||||
@@ -378,7 +371,7 @@ export interface EventCallbacks {
|
||||
*/
|
||||
signIn: (message: {
|
||||
user: User
|
||||
account: Account
|
||||
account: Account | null
|
||||
profile?: Profile
|
||||
isNewUser?: boolean
|
||||
}) => Awaitable<void>
|
||||
@@ -392,9 +385,9 @@ export interface EventCallbacks {
|
||||
createUser: (message: { user: User }) => Awaitable<void>
|
||||
updateUser: (message: { user: User }) => Awaitable<void>
|
||||
linkAccount: (message: {
|
||||
user: User
|
||||
user: User | AdapterUser
|
||||
account: Account
|
||||
profile: User
|
||||
profile: User | AdapterUser
|
||||
}) => Awaitable<void>
|
||||
/**
|
||||
* The message object will contain one of these depending on
|
||||
@@ -420,7 +413,7 @@ export interface PagesOptions {
|
||||
|
||||
export type ISODateString = string
|
||||
|
||||
export interface DefaultSession extends Record<string, unknown> {
|
||||
export interface DefaultSession {
|
||||
user?: {
|
||||
name?: string | null
|
||||
email?: string | null
|
||||
@@ -438,7 +431,7 @@ export interface DefaultSession extends Record<string, unknown> {
|
||||
* [`SessionProvider`](https://next-auth.js.org/getting-started/client#sessionprovider) |
|
||||
* [`session` callback](https://next-auth.js.org/configuration/callbacks#jwt-callback)
|
||||
*/
|
||||
export interface Session extends Record<string, unknown>, DefaultSession {}
|
||||
export interface Session extends DefaultSession {}
|
||||
|
||||
export type SessionStrategy = "jwt" | "database"
|
||||
|
||||
@@ -468,6 +461,13 @@ export interface SessionOptions {
|
||||
* @default 86400 // 1 day
|
||||
*/
|
||||
updateAge: number
|
||||
/**
|
||||
* Generate a custom session token for database-based sessions.
|
||||
* By default, a random UUID or string is generated depending on the Node.js version.
|
||||
* However, you can specify your own custom string (such as CUID) to be used.
|
||||
* @default `randomUUID` or `randomBytes.toHex` depending on the Node.js version
|
||||
*/
|
||||
generateSessionToken: () => string
|
||||
}
|
||||
|
||||
export interface DefaultUser {
|
||||
@@ -487,13 +487,13 @@ export interface DefaultUser {
|
||||
* [`jwt` callback](https://next-auth.js.org/configuration/callbacks#jwt-callback) |
|
||||
* [`profile` OAuth provider callback](https://next-auth.js.org/configuration/providers#using-a-custom-provider)
|
||||
*/
|
||||
export interface User extends Record<string, unknown>, DefaultUser {}
|
||||
export interface User extends DefaultUser {}
|
||||
|
||||
// Below are types that are only supposed be used by next-auth internally
|
||||
|
||||
/** @internal */
|
||||
export type InternalProvider<T extends ProviderType = any> = (T extends "oauth"
|
||||
? OAuthConfig<any>
|
||||
export type InternalProvider<T = ProviderType> = (T extends "oauth"
|
||||
? InternalOAuthConfig<any>
|
||||
: T extends "email"
|
||||
? EmailConfig
|
||||
: T extends "credentials"
|
||||
@@ -515,7 +515,10 @@ export type NextAuthAction =
|
||||
| "_log"
|
||||
|
||||
/** @internal */
|
||||
export interface InternalOptions<T extends ProviderType = any> {
|
||||
export interface InternalOptions<
|
||||
TProviderType = ProviderType,
|
||||
WithVerificationToken = TProviderType extends "email" ? true : false
|
||||
> {
|
||||
providers: InternalProvider[]
|
||||
/**
|
||||
* Parsed from `NEXTAUTH_URL` or `x-forwarded-host` on Vercel.
|
||||
@@ -523,9 +526,7 @@ export interface InternalOptions<T extends ProviderType = any> {
|
||||
*/
|
||||
url: InternalUrl
|
||||
action: NextAuthAction
|
||||
provider: T extends string
|
||||
? InternalProvider<T>
|
||||
: InternalProvider<T> | undefined
|
||||
provider: InternalProvider<TProviderType>
|
||||
csrfToken?: string
|
||||
csrfTokenVerified?: boolean
|
||||
secret: string
|
||||
@@ -536,7 +537,9 @@ export interface InternalOptions<T extends ProviderType = any> {
|
||||
pages: Partial<PagesOptions>
|
||||
jwt: JWTOptions
|
||||
events: Partial<EventCallbacks>
|
||||
adapter?: Adapter
|
||||
adapter: WithVerificationToken extends true
|
||||
? Adapter<WithVerificationToken>
|
||||
: Adapter<WithVerificationToken> | undefined
|
||||
callbacks: CallbacksOptions
|
||||
cookies: CookiesOptions
|
||||
callbackUrl: string
|
||||
|
||||
@@ -118,12 +118,14 @@ export async function unstable_getServerSession(
|
||||
},
|
||||
})
|
||||
|
||||
const { body, cookies } = session
|
||||
const { body, cookies, status = 200 } = session
|
||||
|
||||
cookies?.forEach((cookie) => setCookie(res, cookie))
|
||||
|
||||
if (body && typeof body !== "string" && Object.keys(body).length)
|
||||
return body as Session
|
||||
if (body && typeof body !== "string" && Object.keys(body).length) {
|
||||
if (status === 200) return body as Session
|
||||
throw new Error((body as any).message)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ export interface NextAuthMiddlewareOptions {
|
||||
* ```
|
||||
*
|
||||
* ---
|
||||
* [Documentation](https://next-auth.js.org/getting-started/nextjs/middleware#api) | [`signIn` callback](configuration/callbacks#sign-in-callback)
|
||||
* [Documentation](https://next-auth.js.org/configuration/nextjs#middleware) | [`signIn` callback](configuration/callbacks#sign-in-callback)
|
||||
*/
|
||||
authorized?: AuthorizedCallback
|
||||
}
|
||||
@@ -101,17 +101,17 @@ async function handleMiddleware(
|
||||
options: NextAuthMiddlewareOptions | undefined,
|
||||
onSuccess?: (token: JWT | null) => Promise<NextMiddlewareResult>
|
||||
) {
|
||||
const { pathname, search, origin } = req.nextUrl
|
||||
const { pathname, search, origin, basePath } = req.nextUrl
|
||||
|
||||
const signInPage = options?.pages?.signIn ?? "/api/auth/signin"
|
||||
const errorPage = options?.pages?.error ?? "/api/auth/error"
|
||||
const basePath = parseUrl(process.env.NEXTAUTH_URL).path
|
||||
const authPath = parseUrl(process.env.NEXTAUTH_URL).path
|
||||
const publicPaths = ["/_next", "/favicon.ico"]
|
||||
|
||||
// Avoid infinite redirects/invalid response
|
||||
// on paths that never require authentication
|
||||
if (
|
||||
pathname.startsWith(basePath) ||
|
||||
`${basePath}${pathname}`.startsWith(authPath) ||
|
||||
[signInPage, errorPage].includes(pathname) ||
|
||||
publicPaths.some((p) => pathname.startsWith(p))
|
||||
) {
|
||||
@@ -125,7 +125,7 @@ async function handleMiddleware(
|
||||
`\nhttps://next-auth.js.org/errors#no_secret`
|
||||
)
|
||||
|
||||
const errorUrl = new URL(errorPage, origin)
|
||||
const errorUrl = new URL(`${basePath}${errorPage}`, origin)
|
||||
errorUrl.searchParams.append("error", "Configuration")
|
||||
|
||||
return NextResponse.redirect(errorUrl)
|
||||
@@ -145,8 +145,8 @@ async function handleMiddleware(
|
||||
if (isAuthorized) return await onSuccess?.(token)
|
||||
|
||||
// the user is not logged in, redirect to the sign-in page
|
||||
const signInUrl = new URL(signInPage, origin)
|
||||
signInUrl.searchParams.append("callbackUrl", `${pathname}${search}`)
|
||||
const signInUrl = new URL(`${basePath}${signInPage}`, origin)
|
||||
signInUrl.searchParams.append("callbackUrl", `${basePath}${pathname}${search}`)
|
||||
return NextResponse.redirect(signInUrl)
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ export interface CredentialsConfig<
|
||||
authorize: (
|
||||
credentials: Record<keyof C, string> | undefined,
|
||||
req: Pick<RequestInternal, "body" | "query" | "headers" | "method">
|
||||
) => Awaitable<(Omit<User, "id"> | { id?: string }) | null>
|
||||
) => Awaitable<User | null>
|
||||
}
|
||||
|
||||
export type CredentialsProvider = <C extends Record<string, CredentialInput>>(
|
||||
|
||||
@@ -17,14 +17,9 @@ export default function EVEOnline<P extends EVEOnlineProfile>(
|
||||
id: "eveonline",
|
||||
name: "EVE Online",
|
||||
type: "oauth",
|
||||
wellKnown:
|
||||
"https://login.eveonline.com/.well-known/oauth-authorization-server",
|
||||
authorization: {
|
||||
params: {
|
||||
scope: "publicData",
|
||||
},
|
||||
},
|
||||
idToken: true,
|
||||
authorization: "https://login.eveonline.com/v2/oauth/authorize?scope=publicData",
|
||||
token: "https://login.eveonline.com/v2/oauth/token",
|
||||
userinfo: "https://login.eveonline.com/oauth/verify",
|
||||
profile(profile) {
|
||||
return {
|
||||
id: String(profile.CharacterID),
|
||||
|
||||
@@ -1,28 +1,25 @@
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
interface HubSpotProfile extends Record<string, any> {
|
||||
|
||||
// TODO: figure out additional fields, for now using
|
||||
// TODO: figure out additional fields, for now using
|
||||
// https://legacydocs.hubspot.com/docs/methods/oauth2/get-access-token-information
|
||||
|
||||
user: string,
|
||||
user_id: string,
|
||||
user: string
|
||||
user_id: string
|
||||
|
||||
hub_domain: string,
|
||||
hub_id: string,
|
||||
hub_domain: string
|
||||
hub_id: string
|
||||
}
|
||||
|
||||
|
||||
const HubSpotConfig = {
|
||||
authorizationUrl: "https://app.hubspot.com/oauth/authorize",
|
||||
tokenUrl: "https://api.hubapi.com/oauth/v1/token",
|
||||
profileUrl: "https://api.hubapi.com/oauth/v1/access-tokens"
|
||||
profileUrl: "https://api.hubapi.com/oauth/v1/access-tokens",
|
||||
}
|
||||
|
||||
export default function HubSpot<P extends HubSpotProfile>(
|
||||
options: OAuthUserConfig<P>
|
||||
): OAuthConfig<P> {
|
||||
|
||||
return {
|
||||
id: "hubspot",
|
||||
name: "HubSpot",
|
||||
@@ -36,7 +33,6 @@ export default function HubSpot<P extends HubSpotProfile>(
|
||||
scope: "oauth",
|
||||
client_id: options.clientId,
|
||||
},
|
||||
|
||||
},
|
||||
client: {
|
||||
token_endpoint_auth_method: "client_secret_post",
|
||||
@@ -45,33 +41,27 @@ export default function HubSpot<P extends HubSpotProfile>(
|
||||
userinfo: {
|
||||
url: HubSpotConfig.profileUrl,
|
||||
async request(context) {
|
||||
|
||||
const url = `${HubSpotConfig.profileUrl}/${context.tokens.access_token}`;
|
||||
const url = `${HubSpotConfig.profileUrl}/${context.tokens.access_token}`
|
||||
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
method: "GET",
|
||||
});
|
||||
})
|
||||
|
||||
const userInfo = await response.json();
|
||||
|
||||
return { userInfo }
|
||||
}
|
||||
return await response.json()
|
||||
},
|
||||
},
|
||||
profile(profile) {
|
||||
|
||||
const { userInfo } = profile
|
||||
|
||||
return {
|
||||
id: userInfo.user_id,
|
||||
name: userInfo.user,
|
||||
email: userInfo.user,
|
||||
id: profile.user_id,
|
||||
name: profile.user,
|
||||
email: profile.user,
|
||||
|
||||
// TODO: get image from profile once it's available
|
||||
// TODO: get image from profile once it's available
|
||||
// Details available https://community.hubspot.com/t5/APIs-Integrations/Profile-photo-is-not-retrieved-with-User-API/m-p/325521
|
||||
image: null
|
||||
image: null,
|
||||
}
|
||||
},
|
||||
options,
|
||||
|
||||
@@ -110,7 +110,7 @@ export interface OAuthConfig<P> extends CommonProviderOptions, PartialIssuer {
|
||||
userinfo?: string | UserinfoEndpointHandler
|
||||
type: "oauth"
|
||||
version?: string
|
||||
profile?: (profile: P, tokens: TokenSet) => Awaitable<User & { id: string }>
|
||||
profile: (profile: P, tokens: TokenSet) => Awaitable<User>
|
||||
checks?: ChecksType | ChecksType[]
|
||||
client?: Partial<ClientMetadata>
|
||||
jwks?: { keys: JWK[] }
|
||||
@@ -147,6 +147,14 @@ export interface OAuthConfig<P> extends CommonProviderOptions, PartialIssuer {
|
||||
encoding?: string
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export interface InternalOAuthConfig<P>
|
||||
extends Omit<OAuthConfig<P>, "authorization" | "token" | "userinfo"> {
|
||||
authorization?: AuthorizationEndpointHandler
|
||||
token?: TokenEndpointHandler
|
||||
userinfo?: UserinfoEndpointHandler
|
||||
}
|
||||
|
||||
export type OAuthUserConfig<P> = Omit<
|
||||
Partial<OAuthConfig<P>>,
|
||||
"options" | "type"
|
||||
|
||||
34
packages/next-auth/src/providers/pinterest.ts
Normal file
34
packages/next-auth/src/providers/pinterest.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
export interface PinterestProfile extends Record<string, any> {
|
||||
account_type: "BUSINESS" | "PINNER"
|
||||
profile_image: string
|
||||
website_url: string
|
||||
username: string
|
||||
}
|
||||
|
||||
export default function PinterestProvider<P extends PinterestProfile>(
|
||||
options: OAuthUserConfig<P>
|
||||
): OAuthConfig<P> {
|
||||
return {
|
||||
id: "pinterest",
|
||||
name: "Pinterest",
|
||||
type: "oauth",
|
||||
authorization: {
|
||||
url: "https://www.pinterest.com/oauth",
|
||||
params: { scope: "user_accounts:read" },
|
||||
},
|
||||
checks: ["state"],
|
||||
token: "https://api.pinterest.com/v5/oauth/token",
|
||||
userinfo: "https://api.pinterest.com/v5/user_account",
|
||||
profile({ username, profile_image }) {
|
||||
return {
|
||||
id: username,
|
||||
name: username,
|
||||
image: profile_image,
|
||||
email: null,
|
||||
}
|
||||
},
|
||||
options,
|
||||
}
|
||||
}
|
||||
51
packages/next-auth/src/providers/zitadel.ts
Normal file
51
packages/next-auth/src/providers/zitadel.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import type { OAuthConfig, OAuthUserConfig } from "."
|
||||
|
||||
export interface ZitadelProfile extends Record<string, any> {
|
||||
amr: string // Authentication Method References as defined in RFC8176
|
||||
aud: string // The audience of the token, by default all client id's and the project id are included
|
||||
auth_time: number // Unix time of the authentication
|
||||
azp: string // Client id of the client who requested the token
|
||||
email: string // Email Address of the subject
|
||||
email_verified: boolean // if the email was verified by ZITADEL
|
||||
exp: number // Time the token expires (as unix time)
|
||||
family_name: string // The subjects family name
|
||||
given_name: string // Given name of the subject
|
||||
gender: string // Gender of the subject
|
||||
iat: number // Time of the token was issued at (as unix time)
|
||||
iss: string // Issuing domain of a token
|
||||
jti: string // Unique id of the token
|
||||
locale: string // Language from the subject
|
||||
name: string // The subjects full name
|
||||
nbf: number // Time the token must not be used before (as unix time)
|
||||
picture: string // The subjects profile picture
|
||||
phone: string // Phone number provided by the user
|
||||
phone_verified: boolean // if the phonenumber was verified by ZITADEL
|
||||
preferred_username: string // ZITADEL's login name of the user. Consist of username@primarydomain
|
||||
sub: string // Subject ID of the user
|
||||
}
|
||||
|
||||
export default function Zitadel<P extends ZitadelProfile>(
|
||||
options: OAuthUserConfig<P>
|
||||
): OAuthConfig<P> {
|
||||
const { issuer } = options
|
||||
|
||||
return {
|
||||
id: "zitadel",
|
||||
name: "ZITADEL",
|
||||
type: "oauth",
|
||||
version: "2",
|
||||
wellKnown: `${issuer}/.well-known/openid-configuration`,
|
||||
authorization: { params: { scope: "openid email profile" } },
|
||||
idToken: true,
|
||||
checks: ["pkce", "state"],
|
||||
async profile(profile) {
|
||||
return {
|
||||
id: profile.sub,
|
||||
name: profile.name,
|
||||
email: profile.email,
|
||||
image: profile.picture,
|
||||
}
|
||||
},
|
||||
options,
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,7 @@ export type SessionContextValue<R extends boolean = false> = R extends true
|
||||
| { data: Session; status: "authenticated" }
|
||||
| { data: null; status: "unauthenticated" | "loading" }
|
||||
|
||||
const SessionContext = React.createContext<SessionContextValue | undefined>(
|
||||
export const SessionContext = React.createContext<SessionContextValue | undefined>(
|
||||
undefined
|
||||
)
|
||||
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { InvalidCallbackUrl, MissingSecret } from "../src/core/errors"
|
||||
import {
|
||||
InvalidCallbackUrl,
|
||||
MissingAdapter,
|
||||
MissingAdapterMethods,
|
||||
MissingSecret,
|
||||
} from "../src/core/errors"
|
||||
import { handler } from "./lib"
|
||||
import EmailProvider from "../src/providers/email"
|
||||
|
||||
it("Show error page if secret is not defined", async () => {
|
||||
const { res, log } = await handler(
|
||||
@@ -14,6 +20,48 @@ it("Show error page if secret is not defined", async () => {
|
||||
expect(log.error).toBeCalledWith("NO_SECRET", expect.any(MissingSecret))
|
||||
})
|
||||
|
||||
it("Show error page if adapter is missing functions when using with email", async () => {
|
||||
const sendVerificationRequest = jest.fn()
|
||||
const missingFunctionAdapter: any = {}
|
||||
const { res, log } = await handler(
|
||||
{
|
||||
adapter: missingFunctionAdapter,
|
||||
providers: [EmailProvider({ sendVerificationRequest })],
|
||||
secret: "secret",
|
||||
},
|
||||
{ prod: true }
|
||||
)
|
||||
|
||||
expect(res.status).toBe(500)
|
||||
expect(res.html).toMatch(/there is a problem with the server configuration./i)
|
||||
expect(res.html).toMatch(/check the server logs for more information./i)
|
||||
|
||||
expect(log.error).toBeCalledWith(
|
||||
"MISSING_ADAPTER_METHODS_ERROR",
|
||||
expect.any(MissingAdapterMethods)
|
||||
)
|
||||
})
|
||||
|
||||
it("Show error page if adapter is not configured when using with email", async () => {
|
||||
const sendVerificationRequest = jest.fn()
|
||||
const { res, log } = await handler(
|
||||
{
|
||||
providers: [EmailProvider({ sendVerificationRequest })],
|
||||
secret: "secret",
|
||||
},
|
||||
{ prod: true }
|
||||
)
|
||||
|
||||
expect(res.status).toBe(500)
|
||||
expect(res.html).toMatch(/there is a problem with the server configuration./i)
|
||||
expect(res.html).toMatch(/check the server logs for more information./i)
|
||||
|
||||
expect(log.error).toBeCalledWith(
|
||||
"EMAIL_REQUIRES_ADAPTER_ERROR",
|
||||
expect.any(MissingAdapter)
|
||||
)
|
||||
})
|
||||
|
||||
it("Should show configuration error page on invalid `callbackUrl`", async () => {
|
||||
const { res, log } = await handler(
|
||||
{ providers: [] },
|
||||
|
||||
@@ -156,6 +156,7 @@ it("Redirect to error page if multiple addresses aren't allowed", async () => {
|
||||
expect(signIn).toBeCalledTimes(0)
|
||||
expect(sendVerificationRequest).toBeCalledTimes(0)
|
||||
|
||||
// @ts-expect-error
|
||||
expect(log.error.mock.calls[0]).toEqual([
|
||||
"SIGNIN_EMAIL_ERROR",
|
||||
{ error, providerId: "email" },
|
||||
|
||||
@@ -47,17 +47,19 @@ describe("Treat secret correctly", () => {
|
||||
})
|
||||
|
||||
it("Error if missing NEXTAUTH_SECRET and secret", async () => {
|
||||
const session = await unstable_getServerSession(req, res, {
|
||||
providers: [],
|
||||
logger,
|
||||
})
|
||||
const configError = new Error(
|
||||
"There is a problem with the server configuration. Check the server logs for more information."
|
||||
)
|
||||
await expect(
|
||||
unstable_getServerSession(req, res, { providers: [], logger })
|
||||
).rejects.toThrowError(configError)
|
||||
|
||||
expect(session).toEqual(null)
|
||||
expect(logger.error).toBeCalledTimes(1)
|
||||
expect(logger.error).toBeCalledWith("NO_SECRET", expect.any(MissingSecret))
|
||||
})
|
||||
|
||||
it("Only logs warning once and in development", async () => {
|
||||
process.env.NEXTAUTH_SECRET = "secret"
|
||||
// Expect console.warn to NOT be called due to NODE_ENV=production
|
||||
await unstable_getServerSession(req, res, { providers: [], logger })
|
||||
expect(console.warn).toBeCalledTimes(0)
|
||||
@@ -71,6 +73,7 @@ describe("Treat secret correctly", () => {
|
||||
// Expect console.warn to be still only be called ONCE
|
||||
await unstable_getServerSession(req, res, { providers: [], logger })
|
||||
expect(console.warn).toBeCalledTimes(1)
|
||||
delete process.env.NEXTAUTH_SECRET
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user