Compare commits

...

8 Commits

Author SHA1 Message Date
Thang Vu
0dd29bcc17 remove engines restriction 2023-01-21 16:12:22 +07:00
Thang Vu
44c66b7406 Merge branch 'main' into fix/add-node-19 2023-01-21 15:57:09 +07:00
Atila Fassina
294039a497 docs(xata-adapter): adjust json schema (#6440) 2023-01-20 11:17:00 +00:00
Balázs Orbán
b2450ef625 fix(providers): conform Twitch provider to spec with escape hatch (#6365)
* fix(core): add explicit non-conform escape hatch

* fix(core): default to first supported auth method

* fix(core): stringify `claims` authorization url param

* fix(providers): conform Twitch provider to spec with escape hatch

* configure `client_secret_post` explicitly in provider
2023-01-19 10:28:14 +00:00
Balázs Orbán
a81bb3e51e feat(core): option to opt out of CSRF checks (#6379)
* feat(core): add way to opt-out of CSRF checks

* fix logic

* add warning if CSRF endpoint used when skipped
2023-01-19 10:27:18 +00:00
Robin Panta
bb506f7eb9 docs: Fix token expiry comparision in database strategy (#6430)
* Fix token expiry comparision in database strategy

fixes the condition used for example
in database strategy

* Apply suggestions from code review

Co-authored-by: Balázs Orbán <info@balazsorban.com>
2023-01-18 15:26:23 +00:00
Thang Vu
87d9cc4244 feat: e2e tests (#6380)
* feat: e2e test init

* run e2e test on CI

* Add credentials to ci

* Update pnpm-lock.yaml

* move test to dev

* add dotenv

* remove in examples

* add e2e command

* revert

* add output cache for turbo e2e

* correct path for upload artifact

* Update release.yml
2023-01-18 19:43:50 +07:00
Richard Shin
d99f9b714a fix: add node 19 as compatible engine 2023-01-17 21:43:15 -05:00
22 changed files with 521 additions and 110 deletions

View File

@@ -35,6 +35,21 @@ jobs:
UPSTASH_REDIS_KEY: ${{ secrets.UPSTASH_REDIS_KEY }}
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
- name: Run E2E tests
run: pnpm e2e
timeout-minutes: 15
env:
AUTH0_USERNAME: ${{ secrets.AUTH0_USERNAME }}
AUTH0_PASSWORD: ${{ secrets.AUTH0_PASSWORD }}
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
- name: Upload E2E artifacts
uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: apps/dev/nextjs/playwright-report/
retention-days: 30
# - name: Coverage
# uses: codecov/codecov-action@v1
# with:

4
apps/dev/nextjs/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
node_modules/
/test-results/
/playwright-report/
/playwright/.cache/

View File

@@ -9,10 +9,12 @@
"build": "next build",
"start": "next start",
"email": "fake-smtp-server",
"start:email": "pnpm email"
"start:email": "pnpm email",
"e2e": "pnpm dlx playwright test"
},
"license": "ISC",
"dependencies": {
"@auth/core": "workspace:*",
"@next-auth/fauna-adapter": "workspace:*",
"@next-auth/prisma-adapter": "workspace:*",
"@next-auth/supabase-adapter": "workspace:*",
@@ -22,15 +24,16 @@
"faunadb": "^4",
"next": "13.1.1",
"next-auth": "workspace:*",
"@auth/core": "workspace:*",
"nodemailer": "^6",
"react": "^18",
"react-dom": "^18"
},
"devDependencies": {
"@playwright/test": "1.29.2",
"@types/jsonwebtoken": "^8.5.5",
"@types/react": "^18.0.15",
"@types/react-dom": "^18.0.6",
"dotenv": "^16.0.3",
"fake-smtp-server": "^0.8.0",
"pg": "^8.7.3",
"prisma": "^3",

View File

@@ -0,0 +1,107 @@
import type { PlaywrightTestConfig } from '@playwright/test';
import { devices } from '@playwright/test';
/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
require('dotenv').config();
/**
* See https://playwright.dev/docs/test-configuration.
*/
const config: PlaywrightTestConfig = {
testDir: './tests',
/* Maximum time one test can run for. */
timeout: 30 * 1000,
expect: {
/**
* Maximum time expect() should wait for the condition to be met.
* For example in `await expect(locator).toHaveText();`
*/
timeout: 5000
},
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'html',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
actionTimeout: 0,
/* Base URL to use in actions like `await page.goto('/')`. */
// baseURL: 'http://localhost:3000',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},
/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
},
},
{
name: 'firefox',
use: {
...devices['Desktop Firefox'],
},
},
{
name: 'webkit',
use: {
...devices['Desktop Safari'],
},
},
/* Test against mobile viewports. */
// {
// name: 'Mobile Chrome',
// use: {
// ...devices['Pixel 5'],
// },
// },
// {
// name: 'Mobile Safari',
// use: {
// ...devices['iPhone 12'],
// },
// },
/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: {
// channel: 'msedge',
// },
// },
// {
// name: 'Google Chrome',
// use: {
// channel: 'chrome',
// },
// },
],
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
// outputDir: 'test-results/',
/* Run your local dev server before starting the tests */
// webServer: {
// command: 'npm run start',
// port: 3000,
// },
};
export default config;

View File

@@ -0,0 +1,39 @@
import { test, expect } from "@playwright/test"
test("Sign in with Auth0", async ({ page }) => {
// Go to NextAuth example app
await page.goto("https://next-auth-example.vercel.app")
// Click 'Sign In'
await page.click("#__next > header > div > p > a")
// Auth0 Login Provider
await page.click('body > div > div form[action*="auth0"] > button')
// Enter Credentials (Username/Password Login) on Auth0 Widget
await page.type("#username", process.env.AUTH0_USERNAME!)
await page.type("#password", process.env.AUTH0_PASSWORD!)
// Snap a screenshot
// await page.screenshot({ path: "1-auth0-login.png", fullPage: true })
// Press submit on Auth0 form
await page.click('body > div > main > section > div button[type="submit"]')
// Wait for next-auth example page login status header to appear
await page.waitForTimeout(2000)
// Snap a screenshot
// await page.screenshot({
// path: "2-next-auth-redirect-result.png",
// fullPage: false,
// })
// Check session object after successful login
const response = await page.goto(
"https://next-auth-example.vercel.app/api/auth/session"
)
const session = await response?.json()
expect(session?.user?.email).toBe(process.env.AUTH0_USERNAME)
// TODO: Check whole object with .toEqual()
})

View File

@@ -136,7 +136,7 @@ export default Auth(new Request("https://example.com"), {
const [google] = await prisma.account.findMany({
where: { userId: user.id, provider: "google" },
})
if (google.expires_at >= Date.now()) {
if (google.expires_at < Date.now()) {
// If the access token has expired, try to refresh it
try {
// https://accounts.google.com/.well-known/openid-configuration

View File

@@ -32,7 +32,6 @@ Now that we're ready, let's create a new Xata project using our next-auth schema
```json title="schema.json"
{
"formatVersion": "",
"tables": [
{
"name": "nextauth_users",

View File

@@ -3,57 +3,13 @@ id: warnings
title: Warnings
---
This is a list of warning output from Auth.js.
A list of warnings from Auth.js that need your attention.
All warnings indicate things which you should take a look at, but do not inhibit normal operation.
---
## Debug enabled
## Client
The `debug` option was evaluated to `true`. It adds extra logs in the terminal which is useful in development, but since it can print sensitive information about users, make sure to set this to `false` in production. In Node.js environments, you can for example set `debug: process.env.NODE_ENV !== "production"`. Consult with your runtime/framework on how to set this value correctly.
#### NEXTAUTH_URL
## CSRF disabled
Environment variable `NEXTAUTH_URL` missing. Please set it in your `.env` file.
:::note
On [Vercel](https://vercel.com) deployments, we will read the `VERCEL_URL` environment variable, so you won't need to define `NEXTAUTH_URL`.
:::
---
## Server
These warnings are displayed on the terminal.
#### NO_SECRET
In development, we generate a `secret` based on your configuration for convenience. This is volatile and will throw an error in production. [Read more](https://authjs.dev/reference/configuration/auth-config/#secret)
#### TWITTER_OAUTH_2_BETA
Twitter OAuth 2.0 is currently in beta as certain changes might still be necessary. This is not covered by semver. See the docs https://authjs.dev/reference/providers/twitter#oauth-2
#### EXPERIMENTAL_API
Some APIs are still experimental; they may be changed or removed in the future. Use at your own risk.
## Adapter
### ADAPTER_TYPEORM_UPDATING_ENTITIES
This warning occurs when typeorm finds that the provided entities differ from the database entities. By default while not in `production` the typeorm adapter will always synchronize changes made to the entities codefiles.
Disable this warning by setting `synchronize: false` in your typeorm config
Example:
```js title="/pages/api/auth/[...nextauth].js"
adapter: TypeORMLegacyAdapter({
type: 'mysql',
username: process.env.DATABASE_USERNAME,
password: process.env.DATABASE_PASSWORD,
host: process.env.DATABASE_HOST,
database: process.env.DATABASE_DB,
synchronize: false
}),
```
You were trying to get a CSRF response from Auth.js (eg.: by calling a `/csrf` endpoint), but in this setup, CSRF protection via Auth.js was turned off. This is likely if you are not directly using `@auth/core` but a framework library (like `@auth/sveltekit`) that already has CSRF protection built-in. You likely won't need the CSRF response.

View File

@@ -18,7 +18,8 @@
"lint": "prettier --check .",
"format": "prettier --write .",
"release": "release",
"version:pr": "node ./config/version-pr"
"version:pr": "node ./config/version-pr",
"e2e": "turbo run e2e --filter=next-auth-app"
},
"devDependencies": {
"@actions/core": "^1.10.0",

View File

@@ -296,4 +296,16 @@ export interface AuthConfig {
cookies?: Partial<CookiesOptions>
/** @todo */
trustHost?: boolean
skipCSRFCheck?: typeof skipCSRFCheck
}
/**
* :::danger
* This option is inteded for framework authors.
* :::
*
* Auth.js comes with built-in {@link https://authjs.dev/concepts/security#csrf CSRF} protection, but
* if you are implementing a framework that is already protected against CSRF attacks, you can skip this check by
* passing this value to {@link AuthConfig.skipCSRFCheck}.
*/
export const skipCSRFCheck = Symbol("skip-csrf-check")

View File

@@ -45,7 +45,7 @@ export function assertConfig(
const { url } = request
const warnings: WarningCode[] = []
if (!warned && options.debug) warnings.push("debug_enabled")
if (!warned && options.debug) warnings.push("debug-enabled")
if (!options.trustHost) {
return new UntrustedHost(`Host must be trusted. URL was: ${request.url}`)

View File

@@ -1,14 +1,15 @@
import { SessionStore } from "./cookie.js"
import { UnknownAction } from "../errors.js"
import { skipCSRFCheck } from "../index.js"
import { SessionStore } from "./cookie.js"
import { init } from "./init.js"
import renderPage from "./pages/index.js"
import * as routes from "./routes/index.js"
import type {
RequestInternal,
ResponseInternal,
AuthConfig,
ErrorPageParam,
RequestInternal,
ResponseInternal,
} from "../types.js"
export async function AuthInternal<
@@ -19,6 +20,8 @@ export async function AuthInternal<
): Promise<ResponseInternal<Body>> {
const { action, providerId, error, method } = request
const csrfDisabled = authOptions.skipCSRFCheck === skipCSRFCheck
const { options, cookies } = await init({
authOptions,
action,
@@ -28,6 +31,7 @@ export async function AuthInternal<
csrfToken: request.body?.csrfToken,
cookies: request.cookies,
isPost: method === "POST",
csrfDisabled,
})
const sessionStore = new SessionStore(
@@ -48,12 +52,22 @@ export async function AuthInternal<
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
return { ...session, cookies } as any
}
case "csrf":
case "csrf": {
if (csrfDisabled) {
options.logger.warn("csrf-disabled")
cookies.push({
name: options.cookies.csrfToken.name,
value: "",
options: { ...options.cookies.csrfToken.options, maxAge: 0 },
})
return { status: 404, cookies }
}
return {
headers: { "Content-Type": "application/json" },
body: { csrfToken: options.csrfToken } as any,
cookies,
}
}
case "signin":
if (pages.signIn) {
let signinUrl = `${pages.signIn}${
@@ -125,8 +139,7 @@ export async function AuthInternal<
} else {
switch (action) {
case "signin":
// Verified CSRF Token required for all sign in routes
if (options.csrfTokenVerified && options.provider) {
if ((csrfDisabled || options.csrfTokenVerified) && options.provider) {
const signin = await routes.signin(
request.query,
request.body,
@@ -138,8 +151,7 @@ export async function AuthInternal<
return { redirect: `${options.url}/signin?csrf=true`, cookies }
case "signout":
// Verified CSRF Token required for signout
if (options.csrfTokenVerified) {
if (csrfDisabled || options.csrfTokenVerified) {
const signout = await routes.signout(sessionStore, options)
if (signout.cookies) cookies.push(...signout.cookies)
return { ...signout, cookies }
@@ -150,6 +162,7 @@ export async function AuthInternal<
// Verified CSRF Token required for credentials providers only
if (
options.provider.type === "credentials" &&
!csrfDisabled &&
!options.csrfTokenVerified
) {
return { redirect: `${options.url}/signin?csrf=true`, cookies }

View File

@@ -25,6 +25,7 @@ interface InitParams {
/** CSRF token value extracted from the incoming request. From body if POST, from query if GET */
csrfToken?: string
/** Is the incoming request a POST request? */
csrfDisabled: boolean
isPost: boolean
cookies: RequestInternal["cookies"]
}
@@ -38,6 +39,7 @@ export async function init({
cookies: reqCookies,
callbackUrl: reqCallbackUrl,
csrfToken: reqCsrfToken,
csrfDisabled,
isPost,
}: InitParams): Promise<{
options: InternalOptions
@@ -117,26 +119,28 @@ export async function init({
const cookies: cookie.Cookie[] = []
const {
csrfToken,
cookie: csrfCookie,
csrfTokenVerified,
} = await createCSRFToken({
options,
cookieValue: reqCookies?.[options.cookies.csrfToken.name],
isPost,
bodyValue: reqCsrfToken,
})
options.csrfToken = csrfToken
options.csrfTokenVerified = csrfTokenVerified
if (csrfCookie) {
cookies.push({
name: options.cookies.csrfToken.name,
value: csrfCookie,
options: options.cookies.csrfToken.options,
if (!csrfDisabled) {
const {
csrfToken,
cookie: csrfCookie,
csrfTokenVerified,
} = await createCSRFToken({
options,
cookieValue: reqCookies?.[options.cookies.csrfToken.name],
isPost,
bodyValue: reqCsrfToken,
})
options.csrfToken = csrfToken
options.csrfTokenVerified = csrfTokenVerified
if (csrfCookie) {
cookies.push({
name: options.cookies.csrfToken.name,
value: csrfCookie,
options: options.cookies.csrfToken.options,
})
}
}
const { callbackUrl, callbackUrlCookie } = await createCallbackUrl({

View File

@@ -104,7 +104,7 @@ export async function handleOAuth(
resCookies.push(nonce.cookie)
}
const codeGrantResponse = await o.authorizationCodeGrantRequest(
let codeGrantResponse = await o.authorizationCodeGrantRequest(
as,
client,
parameters,
@@ -112,6 +112,12 @@ export async function handleOAuth(
codeVerifier?.codeVerifier ?? "auth" // TODO: review fallback code verifier
)
if (provider.token?.conform) {
codeGrantResponse =
(await provider.token.conform(codeGrantResponse.clone())) ??
codeGrantResponse
}
let challenges: o.WWWAuthenticateChallenge[] | undefined
if ((challenges = o.parseWwwAuthenticateChallenges(codeGrantResponse))) {
for (const challenge of challenges) {

View File

@@ -96,6 +96,9 @@ function normalizeEndpoint(
// NOTE: This need to be checked when constructing the URL
// for the authorization, token and userinfo endpoints.
const url = new URL(e?.url ?? "https://authjs.dev")
for (const k in e?.params) url.searchParams.set(k, e?.params[k])
return { url, request: e?.request }
for (const k in e?.params) {
if (e?.params && k === "claims") e.params[k] = JSON.stringify(e.params[k])
url.searchParams.set(k, e?.params[k])
}
return { url, request: e?.request, conform: e?.conform }
}

View File

@@ -1,6 +1,6 @@
import { AuthError } from "../../errors.js"
export type WarningCode = "debug_enabled"
export type WarningCode = "debug-enabled" | "csrf-disabled"
/**
* Override any of the methods, and the rest will use the default logger.
@@ -38,7 +38,7 @@ export const logger: LoggerInstance = {
}
},
warn(code) {
const url = `https://errors.authjs.dev#${code}`
const url = `https://warnings.authjs.dev#${code}`
console.warn(`${yellow}[auth][warn][${code}]${reset}`, `Read more: ${url}`)
},
debug(message, metadata) {

View File

@@ -42,6 +42,8 @@ interface AdvancedEndpointHandler<P extends UrlParams, C, R> {
* You should **try to avoid using advanced options** unless you are very comfortable using them.
*/
request?: EndpointRequest<C, R, P>
/** @internal */
conform?: (response: Response) => Awaitable<Response | undefined>
}
/** Either an URL (containing all the parameters) or an object with more granular control. */
@@ -184,7 +186,11 @@ export type OAuthConfigInternal<Profile> = Omit<
OAuthEndpointType
> & {
authorization?: { url: URL }
token?: { url: URL; request?: TokenEndpointHandler["request"] }
token?: {
url: URL
request?: TokenEndpointHandler["request"]
conform?: TokenEndpointHandler["conform"]
}
userinfo?: { url: URL; request?: UserinfoEndpointHandler["request"] }
} & Pick<Required<OAuthConfig<Profile>>, "clientId" | "checks" | "profile">

View File

@@ -1,4 +1,4 @@
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
import type { OIDCConfig, OIDCUserConfig } from "./index.js"
export interface TwitchProfile extends Record<string, any> {
sub: string
@@ -7,26 +7,52 @@ export interface TwitchProfile extends Record<string, any> {
picture: string
}
export default function Twitch<P extends TwitchProfile>(
options: OAuthUserConfig<P>
): OAuthConfig<P> {
export default function Twitch(
config: OIDCUserConfig<TwitchProfile>
): OIDCConfig<TwitchProfile> {
return {
issuer: "https://id.twitch.tv/oauth2",
id: "twitch",
name: "Twitch",
type: "oidc",
client: { token_endpoint_auth_method: "client_secret_post" },
authorization: {
params: {
scope: "openid user:read:email",
claims: {
id_token: {
email: null,
picture: null,
preferred_username: null,
},
id_token: { email: null, picture: null, preferred_username: null },
},
},
},
token: {
async conform(response) {
const body = await response.json()
if (response.ok) {
if (typeof body.scope === "string") {
console.warn(
"'scope' is a string. Redundant workaround, please open an issue."
)
} else if (Array.isArray(body.scope)) {
body.scope = body.scope.join(" ")
return new Response(JSON.stringify(body), response)
} else if ("scope" in body) {
delete body.scope
return new Response(JSON.stringify(body), response)
}
} else {
const { message: error_description, error } = body
if (typeof error !== "string") {
return new Response(
JSON.stringify({ error: "invalid_request", error_description }),
response
)
}
console.warn(
"Response has 'error'. Redundant workaround, please open an issue."
)
}
},
},
style: {
logo: "/twitch.svg",
logoDark: "/twitch-dark.svg",
@@ -35,6 +61,6 @@ export default function Twitch<P extends TwitchProfile>(
bgDark: "#65459B",
textDark: "#fff",
},
options,
options: config,
}
}

View File

@@ -32,7 +32,7 @@
"test:unit": "vitest"
},
"devDependencies": {
"@playwright/test": "^1.28.1",
"@playwright/test": "1.29.2",
"@sveltejs/adapter-auto": "^1.0.0",
"@sveltejs/kit": "^1.0.0",
"@sveltejs/package": "^1.0.0",
@@ -69,4 +69,4 @@
},
"./package.json": "./package.json"
}
}
}

View File

@@ -126,8 +126,5 @@
"react": "^18",
"react-dom": "^18",
"whatwg-fetch": "^3.6.2"
},
"engines": {
"node": "^12.19.0 || ^14.15.0 || ^16.13.0 || ^18.12.0"
}
}

231
pnpm-lock.yaml generated
View File

@@ -68,11 +68,13 @@ importers:
'@next-auth/prisma-adapter': workspace:*
'@next-auth/supabase-adapter': workspace:*
'@next-auth/typeorm-legacy-adapter': workspace:*
'@playwright/test': 1.29.2
'@prisma/client': ^3
'@supabase/supabase-js': ^2.0.5
'@types/jsonwebtoken': ^8.5.5
'@types/react': ^18.0.15
'@types/react-dom': ^18.0.6
dotenv: ^16.0.3
fake-smtp-server: ^0.8.0
faunadb: ^4
next: 13.1.1
@@ -99,9 +101,11 @@ importers:
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
devDependencies:
'@playwright/test': 1.29.2
'@types/jsonwebtoken': 8.5.8
'@types/react': 18.0.26
'@types/react-dom': 18.0.6
dotenv: 16.0.3
fake-smtp-server: 0.8.0
pg: 8.7.3
prisma: 3.15.2
@@ -129,6 +133,27 @@ importers:
typescript: 4.9.4
vite: 4.0.1
apps/examples/nextjs:
specifiers:
'@types/node': ^17
'@types/react': ^18.0.15
next: latest
next-auth: latest
nodemailer: ^6
react: ^18.2.0
react-dom: ^18.2.0
typescript: ^4
dependencies:
next: 13.1.2_biqbaboplfbrettd7655fr4n2y
next-auth: 4.18.8_xpfrgizk2uibqsegg5y3s2zbwy
nodemailer: 6.8.0
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
devDependencies:
'@types/node': 17.0.45
'@types/react': 18.0.26
typescript: 4.9.4
apps/playgrounds/gatsby:
specifiers:
dotenv: ^16.0.0
@@ -562,7 +587,7 @@ importers:
packages/frameworks-sveltekit:
specifiers:
'@auth/core': workspace:*
'@playwright/test': ^1.28.1
'@playwright/test': 1.29.2
'@sveltejs/adapter-auto': ^1.0.0
'@sveltejs/kit': ^1.0.0
'@sveltejs/package': ^1.0.0
@@ -576,7 +601,7 @@ importers:
dependencies:
'@auth/core': link:../core
devDependencies:
'@playwright/test': 1.28.1
'@playwright/test': 1.29.2
'@sveltejs/adapter-auto': 1.0.0_@sveltejs+kit@1.0.1
'@sveltejs/kit': 1.0.1_svelte@3.54.0+vite@4.0.1
'@sveltejs/package': 1.0.1_gf4dcx76vtk2o62ixxeqx7chra
@@ -10610,6 +10635,10 @@ packages:
/@next/env/13.1.1:
resolution: {integrity: sha512-vFMyXtPjSAiOXOywMojxfKIqE3VWN5RCAx+tT3AS3pcKjMLFTCJFUWsKv8hC+87Z1F4W3r68qTwDFZIFmd5Xkw==}
/@next/env/13.1.2:
resolution: {integrity: sha512-PpT4UZIX66VMTqXt4HKEJ+/PwbS+tWmmhZlazaws1a+dbUA5pPdjntQ46Jvj616i3ZKN9doS9LHx3y50RLjAWg==}
dev: false
/@next/swc-android-arm-eabi/13.1.1:
resolution: {integrity: sha512-qnFCx1kT3JTWhWve4VkeWuZiyjG0b5T6J2iWuin74lORCupdrNukxkq9Pm+Z7PsatxuwVJMhjUoYz7H4cWzx2A==}
engines: {node: '>= 10'}
@@ -10618,6 +10647,15 @@ packages:
requiresBuild: true
optional: true
/@next/swc-android-arm-eabi/13.1.2:
resolution: {integrity: sha512-7mRz1owoGsbfIcdOJA3kk7KEwPZ+OvVT1z9DkR/yru4QdVLF69h/1SHy0vlUNQMxDRllabhxCfkoZCB34GOGAg==}
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'}
@@ -10626,6 +10664,15 @@ packages:
requiresBuild: true
optional: true
/@next/swc-android-arm64/13.1.2:
resolution: {integrity: sha512-mgjZ2eJSayovQm1LcE54BLSI4jjnnnLtq5GY5g+DdPuUiCT644gKtjZ/w2BQvuIecCqqBO+Ph9yzo/wUTq7NLg==}
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'}
@@ -10634,6 +10681,15 @@ packages:
requiresBuild: true
optional: true
/@next/swc-darwin-arm64/13.1.2:
resolution: {integrity: sha512-RikoQqy109r2222UJlyGs4dZw2BibkfPqpeFdW5JEGv+L2PStlHID8DwyVYbmHfQ0VIBGvbf/NAUtFakAWlhwg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: false
optional: true
/@next/swc-darwin-x64/13.1.1:
resolution: {integrity: sha512-qWr9qEn5nrnlhB0rtjSdR00RRZEtxg4EGvicIipqZWEyayPxhUu6NwKiG8wZiYZCLfJ5KWr66PGSNeDMGlNaiA==}
engines: {node: '>= 10'}
@@ -10642,6 +10698,15 @@ packages:
requiresBuild: true
optional: true
/@next/swc-darwin-x64/13.1.2:
resolution: {integrity: sha512-JbDZjaTvL8gyPC5TAH6OnD4jmXPkyUxRYPvu08ZmhT/XAFBb/Cso0BdXyDax/BPCG70mimP9d3hXNKNq+A0VtQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: false
optional: true
/@next/swc-freebsd-x64/13.1.1:
resolution: {integrity: sha512-UwP4w/NcQ7V/VJEj3tGVszgb4pyUCt3lzJfUhjDMUmQbzG9LDvgiZgAGMYH6L21MoyAATJQPDGiAMWAPKsmumA==}
engines: {node: '>= 10'}
@@ -10650,6 +10715,15 @@ packages:
requiresBuild: true
optional: true
/@next/swc-freebsd-x64/13.1.2:
resolution: {integrity: sha512-ax4j8VrdFQ/xc3W7Om0u1vnDxVApQHKsChBbAMynCrnycZmpbqK4MZu4ZkycT+mx2eccCiqZROpbzDbEdPosEw==}
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'}
@@ -10658,6 +10732,15 @@ packages:
requiresBuild: true
optional: true
/@next/swc-linux-arm-gnueabihf/13.1.2:
resolution: {integrity: sha512-NcRHTesnCxnUvSJa637PQJffBBkmqi5XS/xVWGY7dI6nyJ+pC96Oj7kd+mcjnFUQI5lHKbg39qBWKtOzbezc4w==}
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'}
@@ -10666,6 +10749,15 @@ packages:
requiresBuild: true
optional: true
/@next/swc-linux-arm64-gnu/13.1.2:
resolution: {integrity: sha512-AxJdjocLtPrsBY4P2COSBIc3crT5bpjgGenNuINoensOlXhBkYM0aRDYZdydwXOhG+kN2ngUvfgitop9pa204w==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-linux-arm64-musl/13.1.1:
resolution: {integrity: sha512-GA67ZbDq2AW0CY07zzGt07M5b5Yaq5qUpFIoW3UFfjOPgb0Sqf3DAW7GtFMK1sF4ROHsRDMGQ9rnT0VM2dVfKA==}
engines: {node: '>= 10'}
@@ -10674,6 +10766,15 @@ packages:
requiresBuild: true
optional: true
/@next/swc-linux-arm64-musl/13.1.2:
resolution: {integrity: sha512-JmNimDkcCRq7P5zpkdqeaSZ69qKDntEPtyIaMNWqy5M0WUJxGim0Fs6Qzxayiyvuuh9Guxks4woQ/j/ZvX/c8Q==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-linux-x64-gnu/13.1.1:
resolution: {integrity: sha512-nnjuBrbzvqaOJaV+XgT8/+lmXrSCOt1YYZn/irbDb2fR2QprL6Q7WJNgwsZNxiLSfLdv+2RJGGegBx9sLBEzGA==}
engines: {node: '>= 10'}
@@ -10682,6 +10783,15 @@ packages:
requiresBuild: true
optional: true
/@next/swc-linux-x64-gnu/13.1.2:
resolution: {integrity: sha512-TsLsjZwUlgmvI42neTuIoD6K9RlXCUzqPtvIClgXxVO0um0DiZwK+M+0zX/uVXhMVphfPY2c5YeR1zFSIONY4A==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-linux-x64-musl/13.1.1:
resolution: {integrity: sha512-CM9xnAQNIZ8zf/igbIT/i3xWbQZYaF397H+JroF5VMOCUleElaMdQLL5riJml8wUfPoN3dtfn2s4peSr3azz/g==}
engines: {node: '>= 10'}
@@ -10690,6 +10800,15 @@ packages:
requiresBuild: true
optional: true
/@next/swc-linux-x64-musl/13.1.2:
resolution: {integrity: sha512-eSkyXgCXydEFPTkcncQOGepafedPte6JT/OofB9uvruucrrMVBagCASOuPxodWEMrlfEKSXVnExMKIlfmQMD7A==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-win32-arm64-msvc/13.1.1:
resolution: {integrity: sha512-pzUHOGrbgfGgPlOMx9xk3QdPJoRPU+om84hqVoe6u+E0RdwOG0Ho/2UxCgDqmvpUrMab1Deltlt6RqcXFpnigQ==}
engines: {node: '>= 10'}
@@ -10698,6 +10817,15 @@ packages:
requiresBuild: true
optional: true
/@next/swc-win32-arm64-msvc/13.1.2:
resolution: {integrity: sha512-DmXFaRTgt2KrV9dmRLifDJE+cYiutHVFIw5/C9BtnwXH39uf3YbPxeD98vNrtqqqZVVLXY/1ySaSIwzYnqeY9g==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@next/swc-win32-ia32-msvc/13.1.1:
resolution: {integrity: sha512-WeX8kVS46aobM9a7Xr/kEPcrTyiwJqQv/tbw6nhJ4fH9xNZ+cEcyPoQkwPo570dCOLz3Zo9S2q0E6lJ/EAUOBg==}
engines: {node: '>= 10'}
@@ -10706,6 +10834,15 @@ packages:
requiresBuild: true
optional: true
/@next/swc-win32-ia32-msvc/13.1.2:
resolution: {integrity: sha512-3+nBkuFs/wT+lmRVQNH5SyDT7I4vUlNPntosEaEP63FuYQdPLaxz0GvcR66MdFSFh2fsvazpe4wciOwVS4FItQ==}
engines: {node: '>= 10'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@next/swc-win32-x64-msvc/13.1.1:
resolution: {integrity: sha512-mVF0/3/5QAc5EGVnb8ll31nNvf3BWpPY4pBb84tk+BfQglWLqc5AC9q1Ht/YMWiEgs8ALNKEQ3GQnbY0bJF2Gg==}
engines: {node: '>= 10'}
@@ -10714,6 +10851,15 @@ packages:
requiresBuild: true
optional: true
/@next/swc-win32-x64-msvc/13.1.2:
resolution: {integrity: sha512-avsyveEvcvH42PvKjR4Pb8JlLttuGURr2H3ZhS2b85pHOiZ7yjH3rMUoGnNzuLMApyxYaCvd4MedPrLhnNhkog==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@nicolo-ribaudo/chokidar-2/2.1.8-no-fsevents.3:
resolution: {integrity: sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==}
requiresBuild: true
@@ -11346,13 +11492,13 @@ packages:
nullthrows: 1.1.1
dev: false
/@playwright/test/1.28.1:
resolution: {integrity: sha512-xN6spdqrNlwSn9KabIhqfZR7IWjPpFK1835tFNgjrlysaSezuX8PYUwaz38V/yI8TJLG9PkAMEXoHRXYXlpTPQ==}
/@playwright/test/1.29.2:
resolution: {integrity: sha512-+3/GPwOgcoF0xLz/opTnahel1/y42PdcgZ4hs+BZGIUjtmEFSXGg+nFoaH3NSmuc7a6GSFwXDJ5L7VXpqzigNg==}
engines: {node: '>=14'}
hasBin: true
dependencies:
'@types/node': 18.11.10
playwright-core: 1.28.1
playwright-core: 1.29.2
dev: true
/@pmmmwh/react-refresh-webpack-plugin/0.5.10_xsftmjzvfioxqs4ify53ibh7ay:
@@ -26547,6 +26693,33 @@ packages:
engines: {node: '>= 0.4.0'}
dev: true
/next-auth/4.18.8_xpfrgizk2uibqsegg5y3s2zbwy:
resolution: {integrity: sha512-USP8ihmvB7iCGtkS0+toe2QPrzdbZfkydQZX56JOI9Ft5n/BardOXh3D4wQ2An+vpq/jDKojGlgfv21wVElW7A==}
engines: {node: ^12.19.0 || ^14.15.0 || ^16.13.0 || ^18.12.0}
peerDependencies:
next: ^12.2.5 || ^13
nodemailer: ^6.6.5
react: ^17.0.2 || ^18
react-dom: ^17.0.2 || ^18
peerDependenciesMeta:
nodemailer:
optional: true
dependencies:
'@babel/runtime': 7.20.7
'@panva/hkdf': 1.0.2
cookie: 0.5.0
jose: 4.11.1
next: 13.1.2_biqbaboplfbrettd7655fr4n2y
nodemailer: 6.8.0
oauth: 0.9.15
openid-client: 5.1.6
preact: 10.11.3
preact-render-to-string: 5.2.3_preact@10.11.3
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
uuid: 8.3.2
dev: false
/next-tick/1.1.0:
resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==}
@@ -26638,6 +26811,50 @@ packages:
- babel-plugin-macros
dev: false
/next/13.1.2_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-Rdnnb2YH///w78FEOR/IQ6TXga+qpth4OqFSem48ng1PYYKr6XBsIk1XVaRcIGM3o6iiHnun0nJvkJHDf+ICyQ==}
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.2
'@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.2
'@next/swc-android-arm64': 13.1.2
'@next/swc-darwin-arm64': 13.1.2
'@next/swc-darwin-x64': 13.1.2
'@next/swc-freebsd-x64': 13.1.2
'@next/swc-linux-arm-gnueabihf': 13.1.2
'@next/swc-linux-arm64-gnu': 13.1.2
'@next/swc-linux-arm64-musl': 13.1.2
'@next/swc-linux-x64-gnu': 13.1.2
'@next/swc-linux-x64-musl': 13.1.2
'@next/swc-win32-arm64-msvc': 13.1.2
'@next/swc-win32-ia32-msvc': 13.1.2
'@next/swc-win32-x64-msvc': 13.1.2
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
dev: false
/nice-try/1.0.5:
resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==}
@@ -27799,8 +28016,8 @@ packages:
resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==}
dev: false
/playwright-core/1.28.1:
resolution: {integrity: sha512-3PixLnGPno0E8rSBJjtwqTwJe3Yw72QwBBBxNoukIj3lEeBNXwbNiKrNuB1oyQgTBw5QHUhNO3SteEtHaMK6ag==}
/playwright-core/1.29.2:
resolution: {integrity: sha512-94QXm4PMgFoHAhlCuoWyaBYKb92yOcGVHdQLoxQ7Wjlc7Flg4aC/jbFW7xMR52OfXMVkWicue4WXE7QEegbIRA==}
engines: {node: '>=14'}
hasBin: true
dev: true

View File

@@ -34,6 +34,9 @@
"test": {
"outputs": []
},
"e2e": {
"outputs": ["playwright-report/**"]
},
"@next-auth/upstash-redis-adapter#test": {
"env": ["UPSTASH_REDIS_KEY", "UPSTASH_REDIS_URL"]
}