mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
Compare commits
10 Commits
@auth/core
...
@auth/core
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ddd47cc0a | ||
|
|
0100888d9b | ||
|
|
9eeea02fe2 | ||
|
|
0a57fea430 | ||
|
|
51750e1a06 | ||
|
|
039a14d992 | ||
|
|
da821d2789 | ||
|
|
be5c42e350 | ||
|
|
b68f461f8b | ||
|
|
95c5ba0b5d |
1
apps/dev/nextjs/next-env.d.ts
vendored
1
apps/dev/nextjs/next-env.d.ts
vendored
@@ -1,5 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
/// <reference types="next/navigation-types/compat/navigation" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"@prisma/client": "^3",
|
||||
"@supabase/supabase-js": "^2.0.5",
|
||||
"faunadb": "^4",
|
||||
"next": "13.1.1",
|
||||
"next": "13.3.0",
|
||||
"next-auth": "workspace:*",
|
||||
"nodemailer": "^6",
|
||||
"react": "^18",
|
||||
|
||||
@@ -102,7 +102,7 @@ export const authConfig: AuthConfig = {
|
||||
Facebook({ clientId: process.env.FACEBOOK_ID, clientSecret: process.env.FACEBOOK_SECRET }),
|
||||
Foursquare({ clientId: process.env.FOURSQUARE_ID, clientSecret: process.env.FOURSQUARE_SECRET }),
|
||||
Freshbooks({ clientId: process.env.FRESHBOOKS_ID, clientSecret: process.env.FRESHBOOKS_SECRET }),
|
||||
GitHub({ clientId: process.env.GITHUB_ID, clientSecret: process.env.GITHUB_SECRET }),
|
||||
GitHub({ clientId: process.env.GITHUB_ID, clientSecret: process.env.GITHUB_SECRET, redirectProxy: process.env.AUTH_REDIRECT_PROXY_URL }),
|
||||
Gitlab({ clientId: process.env.GITLAB_ID, clientSecret: process.env.GITLAB_SECRET }),
|
||||
Google({ clientId: process.env.GOOGLE_ID, clientSecret: process.env.GOOGLE_SECRET }),
|
||||
// IDS4({ clientId: process.env.IDS4_ID, clientSecret: process.env.IDS4_SECRET, issuer: process.env.IDS4_ISSUER }),
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
@@ -19,8 +23,17 @@
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
]
|
||||
],
|
||||
"strictNullChecks": true
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules", "jest.config.js"]
|
||||
}
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"jest.config.js"
|
||||
]
|
||||
}
|
||||
@@ -18,77 +18,55 @@ See below for more detailed provider settings.
|
||||
|
||||
## Vercel
|
||||
|
||||
1. Make sure to expose the Vercel [System Environment Variables](https://vercel.com/docs/concepts/projects/environment-variables#system-environment-variables) in your project settings.
|
||||
2. Create a `NEXTAUTH_SECRET` environment variable for all environments.
|
||||
1. Make sure to expose the Vercel [System Environment Variables](https://vercel.com/docs/concepts/projects/environment-variables#system-environment-variables) in your project settings. This way, we can detect the environment. (Setting `NEXTAUTH_URL` environment variable on Vercel is **unnecessary**).
|
||||
2. Create a `NEXTAUTH_SECRET` environment variable for both Production and Preview environments.
|
||||
a. You can use `openssl rand -base64 32` or https://generate-secret.vercel.app/32 to generate a random value.
|
||||
b. You **do not** need the `NEXTAUTH_URL` environment variable in Vercel.
|
||||
3. Add your provider's client ID and client secret to environment variables. _(Skip this step if not using an [OAuth Provider](/reference/providers/index))_
|
||||
4. Deploy!
|
||||
|
||||
Example repository: https://github.com/nextauthjs/next-auth-example
|
||||
|
||||
A few notes about deploying to Vercel. The environment variables are read server-side, so you do not need to prefix them with `NEXT_PUBLIC_`. When deploying here, you do not need to explicitly set the `NEXTAUTH_URL` environment variable. With other providers **you will** need to also set this environment variable.
|
||||
A few notes about deploying to Vercel. The environment variables are read server-side, so you **should not** prefix them with `NEXT_PUBLIC_` to avoid accidentally bundling a secret in the client-side JavaScript code.
|
||||
|
||||
### Securing a preview deployment
|
||||
|
||||
Securing a preview deployment (with an OAuth provider) comes with some critical obstacles. Most OAuth providers only allow a single redirect/callback URL, or at least a set of full static URLs. Meaning you cannot set the value before publishing the site and you cannot use wildcard subdomains in the callback URL settings of your OAuth provider. Here are a few ways you can still use Auth.js to secure your Preview Deployments.
|
||||
Most OAuth providers cannot be configured with multiple callback URLs or using a wildcard.
|
||||
|
||||
#### Using the Credentials Provider
|
||||
However, Auth.js **supports Preview deployments**, even **with OAuth providers**:
|
||||
|
||||
You could check in your `/pages/api/auth/[...nextauth].js` API route / configuration file to see if you're currently in a Vercel preview environment, and if so, enable a simple "credential provider", meaning username/password. Vercel offers a few built-in [system environment variables](https://vercel.com/docs/concepts/projects/environment-variables#system-environment-variables) which you could check against, like `VERCEL_ENV`. This would allow you to use this basic, for testing only, authentication strategy in your preview deployments.
|
||||
1. Determine a stable deployment URL. Eg.: A deployment whose URL does not change between builds, for example. `auth.yourdomain.com`),
|
||||
2. Set `AUTH_REDIRECT_PROXY_URL` to that URL, adding the path up until your `[...nextauth]` route. Eg.: (`https://auth.yourdomain.com/api/auth`)
|
||||
3. For your OAuth provider, set the callback URL using the stable deployment URL. Eg.: For GitHub `https://auth.yourdomain.com/api/auth/callback/github`)
|
||||
|
||||
Some things to be aware of here, include:
|
||||
:::info
|
||||
To support preview deployments, the `AUTH_SECRET` value needs to be the same for the stable deployment and deployments that will need OAuth support.
|
||||
:::
|
||||
|
||||
- Do not let this potential testing-only user have access to any critical data
|
||||
- If possible, maybe do not even connect this preview deployment to your production database
|
||||
|
||||
##### Example
|
||||
<details>
|
||||
<summary>
|
||||
<b>How does this work?</b>
|
||||
</summary>
|
||||
To support preview deployments, Auth.js uses the stable deployment URL as a redirect proxy server.
|
||||
|
||||
```js title="/pages/api/auth/[...nextauth].js"
|
||||
import NextAuth from "next-auth"
|
||||
import GoogleProvider from "next-auth/providers/google"
|
||||
import CredentialsProvider from "next-auth/providers/credentials"
|
||||
It will redirect the OAuth callback request to the preview deployment URL, but only when the `AUTH_REDIRECT_PROXY_URL` environment variable is set. The stable deployment can still act as a regular app.
|
||||
|
||||
export default NextAuth({
|
||||
providers: [
|
||||
process.env.VERCEL_ENV === "preview"
|
||||
? CredentialsProvider({
|
||||
name: "Credentials",
|
||||
credentials: {
|
||||
username: {
|
||||
label: "Username",
|
||||
type: "text",
|
||||
placeholder: "jsmith",
|
||||
},
|
||||
password: { label: "Password", type: "password" },
|
||||
},
|
||||
async authorize() {
|
||||
return {
|
||||
id: 1,
|
||||
name: "J Smith",
|
||||
email: "jsmith@example.com",
|
||||
image: "https://i.pravatar.cc/150?u=jsmith@example.com",
|
||||
}
|
||||
},
|
||||
})
|
||||
: GoogleProvider({
|
||||
clientId: process.env.GOOGLE_ID,
|
||||
clientSecret: process.env.GOOGLE_SECRET,
|
||||
}),
|
||||
],
|
||||
})
|
||||
```
|
||||
When a user initiates an OAuth sign-in flow on a preview deployment, we save its URL in the `state` query parameter but set the `redirect_uri` to the stable deployment.
|
||||
|
||||
#### Using the branch based preview URL
|
||||
Then, the OAuth provider will redirect the user to the stable deployment, which then will verify the `state` parameter and redirect the user to the preview deployment URL if the `state` is valid. This is secured by relying on the same server-side `AUTH_SECRET` for the stable deployment and the preview deployment.
|
||||
|
||||
Preview deployments at Vercel are often available via multiple URLs. For example, PR's merged to `master` or `main`, will be available the commit and PR specific preview URLs, but also the branch specific preview URLs. This branch specific URL will obviously not change as long as you work with that same branch. Therefore, you could add to your OAuth provider your `{project}-git-main-{user}.vercel.app` preview URL. As this will stay constant for that branch, you can reuse that preview deployment / URL for testing any authentication related deployments.
|
||||
See also:
|
||||
<ul>
|
||||
<li><a href="https://www.ietf.org/rfc/rfc6749.html#section-4.1.1">OAuth 2.0 specification: `state` query parameter</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
## Netlify
|
||||
|
||||
Netlify is very similar to Vercel in that you can deploy a Next.js project without almost any extra work.
|
||||
|
||||
In order to setup Auth.js correctly here, you will want to make sure you add your `NEXTAUTH_SECRET` environment variable in the project settings. If you are using the [Essential Next.js Build Plugin](https://github.com/netlify/netlify-plugin-nextjs) within your project, you **do not** need to set the `NEXTAUTH_URL` environment variable as it is set automatically as part of the build process.
|
||||
To set up Auth.js correctly here, you will want to make sure you add your `NEXTAUTH_SECRET` environment variable in the project settings. If you are using the [Essential Next.js Build Plugin](https://github.com/netlify/netlify-plugin-nextjs) within your project, you **do not** need to set the `NEXTAUTH_URL` environment variable as it is set automatically as part of the build process.
|
||||
|
||||
Netlify also exposes some [system environment variables](https://docs.netlify.com/configure-builds/environment-variables/) from which you can check which `NODE_ENV` you are currently in and much more.
|
||||
|
||||
After this, just make sure you either have your OAuth provider setup correctly with `clientId` / `clientSecret`'s and callback URLs.
|
||||
After this, make sure you either have your OAuth provider set up correctly with `clientId` / `clientSecret`'s and callback URLs.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/mongodb-adapter",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"description": "mongoDB adapter for next-auth.",
|
||||
"homepage": "https://authjs.dev",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
@@ -31,7 +31,7 @@
|
||||
"dist"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"mongodb": "^5 | ^4",
|
||||
"mongodb": "^5 || ^4",
|
||||
"next-auth": "^4"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@auth/core",
|
||||
"version": "0.6.0",
|
||||
"version": "0.7.0",
|
||||
"description": "Authentication for the Web.",
|
||||
"keywords": [
|
||||
"authentication",
|
||||
|
||||
@@ -337,4 +337,36 @@ export interface AuthConfig {
|
||||
/** @todo */
|
||||
trustHost?: boolean
|
||||
skipCSRFCheck?: typeof skipCSRFCheck
|
||||
/**
|
||||
* When set, during an OAuth sign-in flow,
|
||||
* the `redirect_uri` of the authorization request
|
||||
* will be set based on this value.
|
||||
*
|
||||
* This is useful if your OAuth Provider only supports a single `redirect_uri`
|
||||
* or you want to use OAuth on preview URLs (like Vercel), where you don't know the final deployment URL beforehand.
|
||||
*
|
||||
* The url needs to include the full path up to where Auth.js is initialized.
|
||||
*
|
||||
* @note This will auto-enable the `state` {@link OAuth2Config.checks} on the provider.
|
||||
*
|
||||
* @example
|
||||
* ```
|
||||
* "https://authjs.example.com/api/auth"
|
||||
* ```
|
||||
*
|
||||
* You can also override this individually for each provider.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* GitHub({
|
||||
* ...
|
||||
* redirectProxyUrl: "https://github.example.com/api/auth"
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* @default `AUTH_REDIRECT_PROXY_URL` environment variable
|
||||
*
|
||||
* See also: [Guide: Securing a Preview Deployment](https://authjs.dev/guides/basics/deployment#securing-a-preview-deployment)
|
||||
*/
|
||||
redirectProxyUrl?: string
|
||||
}
|
||||
|
||||
@@ -56,10 +56,26 @@ export async function init({
|
||||
providers: authOptions.providers,
|
||||
url,
|
||||
providerId,
|
||||
options: authOptions,
|
||||
})
|
||||
|
||||
const maxAge = 30 * 24 * 60 * 60 // Sessions expire after 30 days of being idle by default
|
||||
|
||||
let isOnRedirectProxy = false
|
||||
if (
|
||||
(provider?.type === "oauth" || provider?.type === "oidc") &&
|
||||
provider.redirectProxyUrl
|
||||
) {
|
||||
try {
|
||||
isOnRedirectProxy =
|
||||
new URL(provider.redirectProxyUrl).origin === url.origin
|
||||
} catch {
|
||||
throw new TypeError(
|
||||
`redirectProxyUrl must be a valid URL. Received: ${provider.redirectProxyUrl}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// User provided options are overridden by other options,
|
||||
// except for the options with special handling above
|
||||
const options: InternalOptions = {
|
||||
@@ -113,6 +129,7 @@ export async function init({
|
||||
callbacks: { ...defaultCallbacks, ...authOptions.callbacks },
|
||||
logger,
|
||||
callbackUrl: url.origin,
|
||||
isOnRedirectProxy,
|
||||
}
|
||||
|
||||
// Init cookies
|
||||
|
||||
@@ -15,7 +15,7 @@ import type { Cookie } from "../cookie.js"
|
||||
*/
|
||||
export async function getAuthorizationUrl(
|
||||
query: RequestInternal["query"],
|
||||
options: InternalOptions<"oauth">
|
||||
options: InternalOptions<"oauth" | "oidc">
|
||||
): Promise<ResponseInternal> {
|
||||
const { logger, provider } = options
|
||||
|
||||
@@ -41,12 +41,21 @@ export async function getAuthorizationUrl(
|
||||
}
|
||||
|
||||
const authParams = url.searchParams
|
||||
|
||||
let redirect_uri: string = provider.callbackUrl
|
||||
let data: object | undefined
|
||||
if (!options.isOnRedirectProxy && provider.redirectProxyUrl) {
|
||||
redirect_uri = provider.redirectProxyUrl
|
||||
data = { origin: provider.callbackUrl }
|
||||
logger.debug("using redirect proxy", { redirect_uri, data })
|
||||
}
|
||||
|
||||
const params = Object.assign(
|
||||
{
|
||||
response_type: "code",
|
||||
// clientId can technically be undefined, should we check this in assert.ts or rely on the Authorization Server to do it?
|
||||
client_id: provider.clientId,
|
||||
redirect_uri: provider.callbackUrl,
|
||||
redirect_uri,
|
||||
// @ts-expect-error TODO:
|
||||
...provider.authorization?.params,
|
||||
},
|
||||
@@ -58,7 +67,7 @@ export async function getAuthorizationUrl(
|
||||
|
||||
const cookies: Cookie[] = []
|
||||
|
||||
const state = await checks.state.create(options)
|
||||
const state = await checks.state.create(options, data)
|
||||
if (state) {
|
||||
authParams.set("state", state.value)
|
||||
cookies.push(state.cookie)
|
||||
@@ -68,7 +77,7 @@ export async function getAuthorizationUrl(
|
||||
if (as && !as.code_challenge_methods_supported?.includes("S256")) {
|
||||
// We assume S256 PKCE support, if the server does not advertise that,
|
||||
// a random `nonce` must be used for CSRF protection.
|
||||
provider.checks = ["nonce"]
|
||||
if (provider.type === "oidc") provider.checks = ["nonce"] as any
|
||||
} else {
|
||||
const { value, cookie } = await checks.pkce.create(options)
|
||||
authParams.set("code_challenge", value)
|
||||
|
||||
@@ -24,7 +24,8 @@ import type { Cookie } from "../cookie.js"
|
||||
export async function handleOAuth(
|
||||
query: RequestInternal["query"],
|
||||
cookies: RequestInternal["cookies"],
|
||||
options: InternalOptions<"oauth">
|
||||
options: InternalOptions<"oauth" | "oidc">,
|
||||
randomState?: string
|
||||
) {
|
||||
const { logger, provider } = options
|
||||
let as: o.AuthorizationServer
|
||||
@@ -71,7 +72,12 @@ export async function handleOAuth(
|
||||
|
||||
const resCookies: Cookie[] = []
|
||||
|
||||
const state = await checks.state.use(cookies, resCookies, options)
|
||||
const state = await checks.state.use(
|
||||
cookies,
|
||||
resCookies,
|
||||
options,
|
||||
randomState
|
||||
)
|
||||
|
||||
const codeGrantParams = o.validateAuthResponse(
|
||||
as,
|
||||
@@ -91,11 +97,15 @@ export async function handleOAuth(
|
||||
|
||||
const codeVerifier = await checks.pkce.use(cookies, resCookies, options)
|
||||
|
||||
let redirect_uri = provider.callbackUrl
|
||||
if (!options.isOnRedirectProxy && provider.redirectProxyUrl) {
|
||||
redirect_uri = provider.redirectProxyUrl
|
||||
}
|
||||
let codeGrantResponse = await o.authorizationCodeGrantRequest(
|
||||
as,
|
||||
client,
|
||||
codeGrantParams,
|
||||
provider.callbackUrl,
|
||||
redirect_uri,
|
||||
codeVerifier ?? "auth" // TODO: review fallback code verifier
|
||||
)
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as jose from "jose"
|
||||
import * as o from "oauth4webapi"
|
||||
import { InvalidCheck } from "../../errors.js"
|
||||
import { encode, decode } from "../../jwt.js"
|
||||
import { decode, encode } from "../../jwt.js"
|
||||
|
||||
import type {
|
||||
CookiesOptions,
|
||||
@@ -18,7 +19,8 @@ export async function signCookie(
|
||||
type: keyof CookiesOptions,
|
||||
value: string,
|
||||
maxAge: number,
|
||||
options: InternalOptions<"oauth">
|
||||
options: InternalOptions<"oauth" | "oidc">,
|
||||
data?: any
|
||||
): Promise<Cookie> {
|
||||
const { cookies, logger } = options
|
||||
|
||||
@@ -26,13 +28,11 @@ export async function signCookie(
|
||||
|
||||
const expires = new Date()
|
||||
expires.setTime(expires.getTime() + maxAge * 1000)
|
||||
const token: any = { value }
|
||||
if (type === "state" && data) token.data = data
|
||||
return {
|
||||
name: cookies[type].name,
|
||||
value: await encode<CheckPayload>({
|
||||
...options.jwt,
|
||||
maxAge,
|
||||
token: { value },
|
||||
}),
|
||||
value: await encode({ ...options.jwt, maxAge, token }),
|
||||
options: { ...cookies[type].options, expires },
|
||||
}
|
||||
}
|
||||
@@ -92,14 +92,45 @@ export const pkce = {
|
||||
}
|
||||
|
||||
const STATE_MAX_AGE = 60 * 15 // 15 minutes in seconds
|
||||
export function decodeState(value: string):
|
||||
| {
|
||||
/** If defined, a redirect proxy is being used to support multiple OAuth apps with a single callback URL */
|
||||
origin?: string
|
||||
/** Random value for CSRF protection */
|
||||
random: string
|
||||
}
|
||||
| undefined {
|
||||
try {
|
||||
const decoder = new TextDecoder()
|
||||
return JSON.parse(decoder.decode(jose.base64url.decode(value)))
|
||||
} catch {}
|
||||
}
|
||||
|
||||
export const state = {
|
||||
async create(options: InternalOptions<"oauth">) {
|
||||
if (!options.provider.checks.includes("state")) return
|
||||
// TODO: support customizing the state
|
||||
const value = o.generateRandomState()
|
||||
async create(options: InternalOptions<"oauth">, data?: object) {
|
||||
const { provider } = options
|
||||
if (!provider.checks.includes("state")) {
|
||||
if (data) {
|
||||
throw new InvalidCheck(
|
||||
"State data was provided but the provider is not configured to use state."
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const encodedState = jose.base64url.encode(
|
||||
JSON.stringify({ ...data, random: o.generateRandomState() })
|
||||
)
|
||||
|
||||
const maxAge = STATE_MAX_AGE
|
||||
const cookie = await signCookie("state", value, maxAge, options)
|
||||
return { cookie, value }
|
||||
const cookie = await signCookie(
|
||||
"state",
|
||||
encodedState,
|
||||
maxAge,
|
||||
options,
|
||||
data
|
||||
)
|
||||
return { cookie, value: encodedState }
|
||||
},
|
||||
/**
|
||||
* Returns state if the provider is configured to use state,
|
||||
@@ -111,7 +142,8 @@ export const state = {
|
||||
async use(
|
||||
cookies: RequestInternal["cookies"],
|
||||
resCookies: Cookie[],
|
||||
options: InternalOptions<"oauth">
|
||||
options: InternalOptions<"oauth">,
|
||||
paramRandom?: string
|
||||
): Promise<string | undefined> {
|
||||
const { provider } = options
|
||||
if (!provider.checks.includes("state")) return
|
||||
@@ -121,10 +153,23 @@ export const state = {
|
||||
if (!state) throw new InvalidCheck("State cookie was missing.")
|
||||
|
||||
// IDEA: Let the user do something with the returned state
|
||||
const value = await decode<CheckPayload>({ ...options.jwt, token: state })
|
||||
const encodedState = await decode<CheckPayload>({
|
||||
...options.jwt,
|
||||
token: state,
|
||||
})
|
||||
|
||||
if (!value?.value)
|
||||
throw new InvalidCheck("State value could not be parsed.")
|
||||
if (!encodedState?.value)
|
||||
throw new InvalidCheck("State (cookie) value could not be parsed.")
|
||||
|
||||
const decodedState = decodeState(encodedState.value)
|
||||
|
||||
if (!decodedState)
|
||||
throw new InvalidCheck("State (encoded) value could not be parsed.")
|
||||
|
||||
if (decodedState.random !== paramRandom)
|
||||
throw new InvalidCheck(
|
||||
`Random state values did not match. Expected: ${decodedState.random}. Got: ${paramRandom}`
|
||||
)
|
||||
|
||||
// Clear the state cookie after use
|
||||
resCookies.push({
|
||||
@@ -133,13 +178,13 @@ export const state = {
|
||||
options: { ...options.cookies.state.options, maxAge: 0 },
|
||||
})
|
||||
|
||||
return value.value
|
||||
return encodedState.value
|
||||
},
|
||||
}
|
||||
|
||||
const NONCE_MAX_AGE = 60 * 15 // 15 minutes in seconds
|
||||
export const nonce = {
|
||||
async create(options: InternalOptions<"oauth">) {
|
||||
async create(options: InternalOptions<"oidc">) {
|
||||
if (!options.provider.checks.includes("nonce")) return
|
||||
const value = o.generateRandomNonce()
|
||||
const maxAge = NONCE_MAX_AGE
|
||||
@@ -156,7 +201,7 @@ export const nonce = {
|
||||
async use(
|
||||
cookies: RequestInternal["cookies"],
|
||||
resCookies: Cookie[],
|
||||
options: InternalOptions<"oauth">
|
||||
options: InternalOptions<"oidc">
|
||||
): Promise<string | undefined> {
|
||||
const { provider } = options
|
||||
|
||||
|
||||
34
packages/core/src/lib/oauth/handle-state.ts
Normal file
34
packages/core/src/lib/oauth/handle-state.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { InvalidCheck } from "../../errors.js"
|
||||
import { decodeState } from "./checks.js"
|
||||
|
||||
import type { OAuthConfigInternal } from "../../providers/oauth.js"
|
||||
import type { InternalOptions, RequestInternal } from "../../types.js"
|
||||
|
||||
/**
|
||||
* When the authorization flow contains a state, we check if it's a redirect proxy
|
||||
* and if so, we return the random state and the original redirect URL.
|
||||
*/
|
||||
export function handleState(
|
||||
query: RequestInternal["query"],
|
||||
provider: OAuthConfigInternal<any>,
|
||||
isOnRedirectProxy: InternalOptions["isOnRedirectProxy"]
|
||||
) {
|
||||
let randomState: string | undefined
|
||||
let proxyRedirect: string | undefined
|
||||
|
||||
if (provider.redirectProxyUrl && !query?.state) {
|
||||
throw new InvalidCheck(
|
||||
"Missing state in query, but required for redirect proxy"
|
||||
)
|
||||
}
|
||||
|
||||
const state = decodeState(query?.state)
|
||||
randomState = state?.random
|
||||
|
||||
if (isOnRedirectProxy) {
|
||||
if (!state?.origin) return { randomState }
|
||||
proxyRedirect = `${state.origin}?${new URLSearchParams(query)}`
|
||||
}
|
||||
|
||||
return { randomState, proxyRedirect }
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import { merge } from "./utils/merge.js"
|
||||
|
||||
import type { InternalProvider } from "../types.js"
|
||||
import type {
|
||||
OAuthConfig,
|
||||
OAuthConfigInternal,
|
||||
@@ -8,6 +7,7 @@ import type {
|
||||
OAuthUserConfig,
|
||||
Provider,
|
||||
} from "../providers/index.js"
|
||||
import type { AuthConfig, InternalProvider } from "../types.js"
|
||||
|
||||
/**
|
||||
* Adds `signinUrl` and `callbackUrl` to each provider
|
||||
@@ -17,11 +17,12 @@ export default function parseProviders(params: {
|
||||
providers: Provider[]
|
||||
url: URL
|
||||
providerId?: string
|
||||
options: AuthConfig
|
||||
}): {
|
||||
providers: InternalProvider[]
|
||||
provider?: InternalProvider
|
||||
} {
|
||||
const { url, providerId } = params
|
||||
const { url, providerId, options } = params
|
||||
|
||||
const providers = params.providers.map((p) => {
|
||||
const provider = typeof p === "function" ? p() : p
|
||||
@@ -34,6 +35,7 @@ export default function parseProviders(params: {
|
||||
})
|
||||
|
||||
if (provider.type === "oauth" || provider.type === "oidc") {
|
||||
merged.redirectProxyUrl ??= options.redirectProxyUrl
|
||||
return normalizeOAuth(merged)
|
||||
}
|
||||
|
||||
@@ -62,11 +64,17 @@ function normalizeOAuth(
|
||||
|
||||
const userinfo = normalizeEndpoint(c.userinfo, c.issuer)
|
||||
|
||||
const checks = c.checks ?? ["pkce"]
|
||||
if (c.redirectProxyUrl) {
|
||||
if (!checks.includes("state")) checks.push("state")
|
||||
c.redirectProxyUrl = `${c.redirectProxyUrl}/callback/${c.id}`
|
||||
}
|
||||
|
||||
return {
|
||||
...c,
|
||||
authorization,
|
||||
token,
|
||||
checks: c.checks ?? ["pkce"],
|
||||
checks,
|
||||
userinfo,
|
||||
profile: c.profile ?? defaultProfile,
|
||||
}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { handleLogin } from "../callback-handler.js"
|
||||
import { CallbackRouteError, Verification } from "../../errors.js"
|
||||
import { handleLogin } from "../callback-handler.js"
|
||||
import { handleOAuth } from "../oauth/callback.js"
|
||||
import { handleState } from "../oauth/handle-state.js"
|
||||
import { createHash } from "../web.js"
|
||||
import { handleAuthorized } from "./shared.js"
|
||||
|
||||
import type { AdapterSession } from "../../adapters.js"
|
||||
import type {
|
||||
Account,
|
||||
InternalOptions,
|
||||
RequestInternal,
|
||||
ResponseInternal,
|
||||
InternalOptions,
|
||||
Account,
|
||||
} from "../../types.js"
|
||||
import type { Cookie, SessionStore } from "../cookie.js"
|
||||
|
||||
@@ -43,10 +44,22 @@ export async function callback(params: {
|
||||
|
||||
try {
|
||||
if (provider.type === "oauth" || provider.type === "oidc") {
|
||||
const { proxyRedirect, randomState } = handleState(
|
||||
query,
|
||||
provider,
|
||||
options.isOnRedirectProxy
|
||||
)
|
||||
|
||||
if (proxyRedirect) {
|
||||
logger.debug("proxy redirect", { proxyRedirect, randomState })
|
||||
return { redirect: proxyRedirect }
|
||||
}
|
||||
|
||||
const authorizationResult = await handleOAuth(
|
||||
query,
|
||||
params.cookies,
|
||||
options
|
||||
options,
|
||||
randomState
|
||||
)
|
||||
|
||||
if (authorizationResult.cookies.length) {
|
||||
|
||||
@@ -18,7 +18,7 @@ import type {
|
||||
export async function signin(
|
||||
query: RequestInternal["query"],
|
||||
body: RequestInternal["body"],
|
||||
options: InternalOptions<"oauth" | "email">
|
||||
options: InternalOptions<"oauth" | "oidc" | "email">
|
||||
): Promise<ResponseInternal> {
|
||||
const { url, logger, provider } = options
|
||||
try {
|
||||
|
||||
@@ -37,7 +37,7 @@ export async function toInternalRequest(
|
||||
|
||||
const action = actions.find((a) => pathname.includes(a))
|
||||
if (!action) {
|
||||
throw new UnknownAction("Cannot detect action.")
|
||||
throw new UnknownAction(`Cannot detect action in pathname (${pathname}).`)
|
||||
}
|
||||
|
||||
if (req.method !== "GET" && req.method !== "POST") {
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import type { Client } from "oauth4webapi"
|
||||
import type { Awaitable, Profile, TokenSet, User } from "../types.js"
|
||||
import type { CommonProviderOptions } from "../providers/index.js"
|
||||
import type {
|
||||
AuthConfig,
|
||||
Awaitable,
|
||||
Profile,
|
||||
TokenSet,
|
||||
User,
|
||||
} from "../types.js"
|
||||
|
||||
// TODO:
|
||||
// TODO: fix types
|
||||
type AuthorizationParameters = any
|
||||
type CallbackParamsType = any
|
||||
type IssuerMetadata = any
|
||||
@@ -95,7 +101,7 @@ export interface OAuthProviderButtonStyles {
|
||||
textDark: string
|
||||
}
|
||||
|
||||
/** TODO: */
|
||||
/** TODO: Document */
|
||||
export interface OAuth2Config<Profile>
|
||||
extends CommonProviderOptions,
|
||||
PartialIssuer {
|
||||
@@ -143,11 +149,14 @@ export interface OAuth2Config<Profile>
|
||||
* The CSRF protection performed on the callback endpoint.
|
||||
* @default ["pkce"]
|
||||
*
|
||||
* @note When `redirectProxyUrl` or {@link AuthConfig.redirectProxyUrl} is set,
|
||||
* `"state"` will be added to checks automatically.
|
||||
*
|
||||
* [RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients (PKCE)](https://www.rfc-editor.org/rfc/rfc7636.html#section-4) |
|
||||
* [RFC 6749 - The OAuth 2.0 Authorization Framework](https://www.rfc-editor.org/rfc/rfc6749.html#section-4.1.1) |
|
||||
* [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html#IDToken) |
|
||||
*/
|
||||
checks?: Array<"pkce" | "state" | "none" | "nonce">
|
||||
checks?: Array<"pkce" | "state" | "none">
|
||||
clientId?: string
|
||||
clientSecret?: string
|
||||
/**
|
||||
@@ -157,9 +166,20 @@ export interface OAuth2Config<Profile>
|
||||
client?: Partial<Client>
|
||||
style?: OAuthProviderButtonStyles
|
||||
/**
|
||||
* [Documentation](https://authjs.dev/reference/providers/oauth#allowdangerousemailaccountlinking-option)
|
||||
* Normally, when you sign in with an OAuth provider and another account
|
||||
* with the same email address already exists,
|
||||
* the accounts are not linked automatically.
|
||||
*
|
||||
* Automatic account linking on sign in is not secure
|
||||
* between arbitrary providers and is disabled by default.
|
||||
* Learn more in our [Security FAQ](https://authjs.dev/reference/faq#security).
|
||||
*
|
||||
* However, it may be desirable to allow automatic account linking if you trust that the provider involved has securely verified the email address
|
||||
* associated with the account. Set `allowDangerousEmailAccountLinking: true`
|
||||
* to enable automatic account linking.
|
||||
*/
|
||||
allowDangerousEmailAccountLinking?: boolean
|
||||
redirectProxyUrl?: AuthConfig["redirectProxyUrl"]
|
||||
/**
|
||||
* The options provided by the user.
|
||||
* We will perform a deep-merge of these values
|
||||
@@ -170,10 +190,11 @@ export interface OAuth2Config<Profile>
|
||||
options?: OAuthUserConfig<Profile>
|
||||
}
|
||||
|
||||
/** TODO: */
|
||||
/** TODO: Document */
|
||||
export interface OIDCConfig<Profile>
|
||||
extends Omit<OAuth2Config<Profile>, "type"> {
|
||||
extends Omit<OAuth2Config<Profile>, "type" | "checks"> {
|
||||
type: "oidc"
|
||||
checks?: OAuth2Config<Profile>["checks"] & Array<"nonce">
|
||||
}
|
||||
|
||||
export type OAuthConfig<Profile> = OIDCConfig<Profile> | OAuth2Config<Profile>
|
||||
@@ -186,7 +207,7 @@ export type OAuthEndpointType = "authorization" | "token" | "userinfo"
|
||||
*/
|
||||
export type OAuthConfigInternal<Profile> = Omit<
|
||||
OAuthConfig<Profile>,
|
||||
OAuthEndpointType
|
||||
OAuthEndpointType | "redirectProxyUrl"
|
||||
> & {
|
||||
authorization?: { url: URL }
|
||||
token?: {
|
||||
@@ -196,8 +217,24 @@ export type OAuthConfigInternal<Profile> = Omit<
|
||||
conform?: TokenEndpointHandler["conform"]
|
||||
}
|
||||
userinfo?: { url: URL; request?: UserinfoEndpointHandler["request"] }
|
||||
/**
|
||||
* Reconstructed from {@link OAuth2Config.redirectProxyUrl},
|
||||
* adding the callback action and provider id onto the URL.
|
||||
*
|
||||
* If defined, it is favoured over {@link OAuthConfigInternal.callbackUrl} in the authorization request.
|
||||
*
|
||||
* When {@link InternalOptions.isOnRedirectProxy} is set, the actual value is saved in the decoded `state.origin` parameter.
|
||||
*
|
||||
* @example `"https://auth.example.com/api/auth/callback/:provider"`
|
||||
*
|
||||
*/
|
||||
redirectProxyUrl?: OAuth2Config<Profile>["redirectProxyUrl"]
|
||||
} & Pick<Required<OAuthConfig<Profile>>, "clientId" | "checks" | "profile">
|
||||
|
||||
export type OIDCConfigInternal<Profile> = OAuthConfigInternal<Profile> & {
|
||||
checks: OIDCConfig<Profile>["checks"]
|
||||
}
|
||||
|
||||
export type OAuthUserConfig<Profile> = Omit<
|
||||
Partial<OAuthConfig<Profile>>,
|
||||
"options" | "type"
|
||||
|
||||
@@ -4,7 +4,6 @@ export default function OneLogin(options) {
|
||||
id: "onelogin",
|
||||
name: "OneLogin",
|
||||
type: "oidc",
|
||||
// TODO: Verify if issuer has "oidc/2" and remove if it does
|
||||
wellKnown: `${options.issuer}/oidc/2/.well-known/openid-configuration`,
|
||||
options,
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ export default function Trakt<P extends TraktUser>(
|
||||
id: "trakt",
|
||||
name: "Trakt",
|
||||
type: "oauth",
|
||||
// when default, trakt returns auth error. TODO: Does it?
|
||||
authorization: "https://trakt.tv/oauth/authorize?scope=",
|
||||
token: "https://api.trakt.tv/oauth/token",
|
||||
userinfo: {
|
||||
|
||||
@@ -169,7 +169,6 @@ export default function Wikimedia<P extends WikimediaProfile>(
|
||||
type: "oauth",
|
||||
token: "https://meta.wikimedia.org/w/rest.php/oauth2/access_token",
|
||||
userinfo: "https://meta.wikimedia.org/w/rest.php/oauth2/resource/profile",
|
||||
// TODO: is empty scope necessary?
|
||||
authorization:
|
||||
"https://meta.wikimedia.org/w/rest.php/oauth2/authorize?scope=",
|
||||
profile(profile) {
|
||||
|
||||
@@ -61,21 +61,22 @@ import type {
|
||||
OpenIDTokenEndpointResponse,
|
||||
} from "oauth4webapi"
|
||||
import type { Adapter, AdapterUser } from "./adapters.js"
|
||||
import { AuthConfig } from "./index.js"
|
||||
import type { JWT, JWTOptions } from "./jwt.js"
|
||||
import type { Cookie } from "./lib/cookie.js"
|
||||
import type { LoggerInstance } from "./lib/utils/logger.js"
|
||||
import type {
|
||||
CredentialInput,
|
||||
CredentialsConfig,
|
||||
EmailConfig,
|
||||
OAuthConfigInternal,
|
||||
OIDCConfigInternal,
|
||||
ProviderType,
|
||||
} from "./providers/index.js"
|
||||
import type { JWT, JWTOptions } from "./jwt.js"
|
||||
import type { Cookie } from "./lib/cookie.js"
|
||||
import type { LoggerInstance } from "./lib/utils/logger.js"
|
||||
import { AuthConfig } from "./index.js"
|
||||
|
||||
export type { AuthConfig } from "./index.js"
|
||||
export type Awaitable<T> = T | PromiseLike<T>
|
||||
export type { LoggerInstance }
|
||||
export type Awaitable<T> = T | PromiseLike<T>
|
||||
|
||||
/**
|
||||
* Change the theme of the built-in pages.
|
||||
@@ -372,12 +373,15 @@ export interface User {
|
||||
/** @internal */
|
||||
export type InternalProvider<T = ProviderType> = (T extends "oauth"
|
||||
? OAuthConfigInternal<any>
|
||||
: T extends "oidc"
|
||||
? OIDCConfigInternal<any>
|
||||
: T extends "email"
|
||||
? EmailConfig
|
||||
: T extends "credentials"
|
||||
? CredentialsConfig
|
||||
: never) & {
|
||||
signinUrl: string
|
||||
/** @example `"https://example.com/api/auth/callback/id"` */
|
||||
callbackUrl: string
|
||||
}
|
||||
|
||||
@@ -415,7 +419,6 @@ export interface ResponseInternal<
|
||||
cookies?: Cookie[]
|
||||
}
|
||||
|
||||
// TODO: rename to AuthConfigInternal
|
||||
/** @internal */
|
||||
export interface InternalOptions<TProviderType = ProviderType> {
|
||||
providers: InternalProvider[]
|
||||
@@ -436,4 +439,9 @@ export interface InternalOptions<TProviderType = ProviderType> {
|
||||
callbacks: CallbacksOptions
|
||||
cookies: CookiesOptions
|
||||
callbackUrl: string
|
||||
/**
|
||||
* If true, the OAuth callback is being proxied by the server to the original URL.
|
||||
* See also {@link OAuthConfigInternal.redirectProxyUrl}.
|
||||
*/
|
||||
isOnRedirectProxy: boolean
|
||||
}
|
||||
|
||||
@@ -204,8 +204,8 @@ We're happy to announce we've recently created an [OpenCollective](https://openc
|
||||
<sub>🥉 Bronze Financial Sponsor</sub>
|
||||
</td>
|
||||
<td align="center" valign="top">
|
||||
<a href="https://clerk.dev" target="_blank">
|
||||
<img width="128px" src="https://avatars.githubusercontent.com/u/49538330?s=200&v=4" alt="Prisma Logo" />
|
||||
<a href="https://clerk.com" target="_blank">
|
||||
<img width="128px" src="https://avatars.githubusercontent.com/u/49538330?s=200&v=4" alt="Clerk Logo" />
|
||||
</a><br />
|
||||
<div>Clerk</div><br />
|
||||
<sub>🥉 Bronze Financial Sponsor</sub>
|
||||
|
||||
169
pnpm-lock.yaml
generated
169
pnpm-lock.yaml
generated
@@ -67,7 +67,7 @@ importers:
|
||||
dotenv: ^16.0.3
|
||||
fake-smtp-server: ^0.8.0
|
||||
faunadb: ^4
|
||||
next: 13.1.1
|
||||
next: 13.3.0
|
||||
next-auth: workspace:*
|
||||
nodemailer: ^6
|
||||
pg: ^8.7.3
|
||||
@@ -85,7 +85,7 @@ importers:
|
||||
'@prisma/client': 3.15.2_prisma@3.15.2
|
||||
'@supabase/supabase-js': 2.0.5
|
||||
faunadb: 4.6.0
|
||||
next: 13.1.1_biqbaboplfbrettd7655fr4n2y
|
||||
next: 13.3.0_biqbaboplfbrettd7655fr4n2y
|
||||
next-auth: link:../../../packages/next-auth
|
||||
nodemailer: 6.8.0
|
||||
react: 18.2.0
|
||||
@@ -8332,40 +8332,9 @@ packages:
|
||||
is-promise: 4.0.0
|
||||
dev: true
|
||||
|
||||
/@next/env/13.1.1:
|
||||
resolution: {integrity: sha512-vFMyXtPjSAiOXOywMojxfKIqE3VWN5RCAx+tT3AS3pcKjMLFTCJFUWsKv8hC+87Z1F4W3r68qTwDFZIFmd5Xkw==}
|
||||
dev: false
|
||||
|
||||
/@next/env/13.3.0:
|
||||
resolution: {integrity: sha512-AjppRV4uG3No7L1plinoTQETH+j2F10TEnrMfzbTUYwze5sBUPveeeBAPZPm8OkJZ1epq9OyYKhZrvbD6/9HCQ==}
|
||||
|
||||
/@next/swc-android-arm-eabi/13.1.1:
|
||||
resolution: {integrity: sha512-qnFCx1kT3JTWhWve4VkeWuZiyjG0b5T6J2iWuin74lORCupdrNukxkq9Pm+Z7PsatxuwVJMhjUoYz7H4cWzx2A==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-android-arm64/13.1.1:
|
||||
resolution: {integrity: sha512-eCiZhTzjySubNqUnNkQCjU3Fh+ep3C6b5DCM5FKzsTH/3Gr/4Y7EiaPZKILbvnXmhWtKPIdcY6Zjx51t4VeTfA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-darwin-arm64/13.1.1:
|
||||
resolution: {integrity: sha512-9zRJSSIwER5tu9ADDkPw5rIZ+Np44HTXpYMr0rkM656IvssowPxmhK0rTreC1gpUCYwFsRbxarUJnJsTWiutPg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-darwin-arm64/13.3.0:
|
||||
resolution: {integrity: sha512-DmIQCNq6JtccLPPBzf0dgh2vzMWt5wjxbP71pCi5EWpWYE3MsP6FcRXi4MlAmFNDQOfcFXR2r7kBeG1LpZUh1w==}
|
||||
engines: {node: '>= 10'}
|
||||
@@ -8374,15 +8343,6 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@next/swc-darwin-x64/13.1.1:
|
||||
resolution: {integrity: sha512-qWr9qEn5nrnlhB0rtjSdR00RRZEtxg4EGvicIipqZWEyayPxhUu6NwKiG8wZiYZCLfJ5KWr66PGSNeDMGlNaiA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-darwin-x64/13.3.0:
|
||||
resolution: {integrity: sha512-oQoqFa88OGgwnYlnAGHVct618FRI/749se0N3S8t9Bzdv5CRbscnO0RcX901+YnNK4Q6yeiizfgO3b7kogtsZg==}
|
||||
engines: {node: '>= 10'}
|
||||
@@ -8391,33 +8351,6 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@next/swc-freebsd-x64/13.1.1:
|
||||
resolution: {integrity: sha512-UwP4w/NcQ7V/VJEj3tGVszgb4pyUCt3lzJfUhjDMUmQbzG9LDvgiZgAGMYH6L21MoyAATJQPDGiAMWAPKsmumA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-linux-arm-gnueabihf/13.1.1:
|
||||
resolution: {integrity: sha512-CnsxmKHco9sosBs1XcvCXP845Db+Wx1G0qouV5+Gr+HT/ZlDYEWKoHVDgnJXLVEQzq4FmHddBNGbXvgqM1Gfkg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-linux-arm64-gnu/13.1.1:
|
||||
resolution: {integrity: sha512-JfDq1eri5Dif+VDpTkONRd083780nsMCOKoFG87wA0sa4xL8LGcXIBAkUGIC1uVy9SMsr2scA9CySLD/i+Oqiw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-linux-arm64-gnu/13.3.0:
|
||||
resolution: {integrity: sha512-Wzz2p/WqAJUqTVoLo6H18WMeAXo3i+9DkPDae4oQG8LMloJ3if4NEZTnOnTUlro6cq+S/W4pTGa97nWTrOjbGw==}
|
||||
engines: {node: '>= 10'}
|
||||
@@ -8426,15 +8359,6 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@next/swc-linux-arm64-musl/13.1.1:
|
||||
resolution: {integrity: sha512-GA67ZbDq2AW0CY07zzGt07M5b5Yaq5qUpFIoW3UFfjOPgb0Sqf3DAW7GtFMK1sF4ROHsRDMGQ9rnT0VM2dVfKA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-linux-arm64-musl/13.3.0:
|
||||
resolution: {integrity: sha512-xPVrIQOQo9WXJYgmoTlMnAD/HlR/1e1ZIWGbwIzEirXBVBqMARUulBEIKdC19zuvoJ477qZJgBDCKtKEykCpyQ==}
|
||||
engines: {node: '>= 10'}
|
||||
@@ -8443,15 +8367,6 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@next/swc-linux-x64-gnu/13.1.1:
|
||||
resolution: {integrity: sha512-nnjuBrbzvqaOJaV+XgT8/+lmXrSCOt1YYZn/irbDb2fR2QprL6Q7WJNgwsZNxiLSfLdv+2RJGGegBx9sLBEzGA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-linux-x64-gnu/13.3.0:
|
||||
resolution: {integrity: sha512-jOFlpGuPD7W2tuXVJP4wt9a3cpNxWAPcloq5EfMJRiXsBBOjLVFZA7boXYxEBzSVgUiVVr1V9T0HFM7pULJ1qA==}
|
||||
engines: {node: '>= 10'}
|
||||
@@ -8460,15 +8375,6 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@next/swc-linux-x64-musl/13.1.1:
|
||||
resolution: {integrity: sha512-CM9xnAQNIZ8zf/igbIT/i3xWbQZYaF397H+JroF5VMOCUleElaMdQLL5riJml8wUfPoN3dtfn2s4peSr3azz/g==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-linux-x64-musl/13.3.0:
|
||||
resolution: {integrity: sha512-2OwKlzaBgmuet9XYHc3KwsEilzb04F540rlRXkAcjMHL7eCxB7uZIGtsVvKOnQLvC/elrUegwSw1+5f7WmfyOw==}
|
||||
engines: {node: '>= 10'}
|
||||
@@ -8477,15 +8383,6 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@next/swc-win32-arm64-msvc/13.1.1:
|
||||
resolution: {integrity: sha512-pzUHOGrbgfGgPlOMx9xk3QdPJoRPU+om84hqVoe6u+E0RdwOG0Ho/2UxCgDqmvpUrMab1Deltlt6RqcXFpnigQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-win32-arm64-msvc/13.3.0:
|
||||
resolution: {integrity: sha512-OeHiA6YEvndxT46g+rzFK/MQTfftKxJmzslERMu9LDdC6Kez0bdrgEYed5eXFK2Z1viKZJCGRlhd06rBusyztA==}
|
||||
engines: {node: '>= 10'}
|
||||
@@ -8494,15 +8391,6 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@next/swc-win32-ia32-msvc/13.1.1:
|
||||
resolution: {integrity: sha512-WeX8kVS46aobM9a7Xr/kEPcrTyiwJqQv/tbw6nhJ4fH9xNZ+cEcyPoQkwPo570dCOLz3Zo9S2q0E6lJ/EAUOBg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-win32-ia32-msvc/13.3.0:
|
||||
resolution: {integrity: sha512-4aB7K9mcVK1lYEzpOpqWrXHEZympU3oK65fnNcY1Qc4HLJFLJj8AViuqQd4jjjPNuV4sl8jAwTz3gN5VNGWB7w==}
|
||||
engines: {node: '>= 10'}
|
||||
@@ -8511,15 +8399,6 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@next/swc-win32-x64-msvc/13.1.1:
|
||||
resolution: {integrity: sha512-mVF0/3/5QAc5EGVnb8ll31nNvf3BWpPY4pBb84tk+BfQglWLqc5AC9q1Ht/YMWiEgs8ALNKEQ3GQnbY0bJF2Gg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-win32-x64-msvc/13.3.0:
|
||||
resolution: {integrity: sha512-Reer6rkLLcoOvB0dd66+Y7WrWVFH7sEEkF/4bJCIfsSKnTStTYaHtwIJAwbqnt9I392Tqvku0KkoqZOryWV9LQ==}
|
||||
engines: {node: '>= 10'}
|
||||
@@ -24732,50 +24611,6 @@ packages:
|
||||
/next-tick/1.1.0:
|
||||
resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==}
|
||||
|
||||
/next/13.1.1_biqbaboplfbrettd7655fr4n2y:
|
||||
resolution: {integrity: sha512-R5eBAaIa3X7LJeYvv1bMdGnAVF4fVToEjim7MkflceFPuANY3YyvFxXee/A+acrSYwYPvOvf7f6v/BM/48ea5w==}
|
||||
engines: {node: '>=14.6.0'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
fibers: '>= 3.1.0'
|
||||
node-sass: ^6.0.0 || ^7.0.0
|
||||
react: ^18.2.0
|
||||
react-dom: ^18.2.0
|
||||
sass: ^1.3.0
|
||||
peerDependenciesMeta:
|
||||
fibers:
|
||||
optional: true
|
||||
node-sass:
|
||||
optional: true
|
||||
sass:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@next/env': 13.1.1
|
||||
'@swc/helpers': 0.4.14
|
||||
caniuse-lite: 1.0.30001431
|
||||
postcss: 8.4.14
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
styled-jsx: 5.1.1_react@18.2.0
|
||||
optionalDependencies:
|
||||
'@next/swc-android-arm-eabi': 13.1.1
|
||||
'@next/swc-android-arm64': 13.1.1
|
||||
'@next/swc-darwin-arm64': 13.1.1
|
||||
'@next/swc-darwin-x64': 13.1.1
|
||||
'@next/swc-freebsd-x64': 13.1.1
|
||||
'@next/swc-linux-arm-gnueabihf': 13.1.1
|
||||
'@next/swc-linux-arm64-gnu': 13.1.1
|
||||
'@next/swc-linux-arm64-musl': 13.1.1
|
||||
'@next/swc-linux-x64-gnu': 13.1.1
|
||||
'@next/swc-linux-x64-musl': 13.1.1
|
||||
'@next/swc-win32-arm64-msvc': 13.1.1
|
||||
'@next/swc-win32-ia32-msvc': 13.1.1
|
||||
'@next/swc-win32-x64-msvc': 13.1.1
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
- babel-plugin-macros
|
||||
dev: false
|
||||
|
||||
/next/13.3.0_4cc5zw5azim2bix77d63le72su:
|
||||
resolution: {integrity: sha512-OVTw8MpIPa12+DCUkPqRGPS3thlJPcwae2ZL4xti3iBff27goH024xy4q2lhlsdoYiKOi8Kz6uJoLW/GXwgfOA==}
|
||||
engines: {node: '>=14.6.0'}
|
||||
|
||||
Reference in New Issue
Block a user