mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
Compare commits
24 Commits
feat/skip-
...
@auth/core
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b9a84350b5 | ||
|
|
44c38247da | ||
|
|
9b9af4d5e5 | ||
|
|
fd2179bdca | ||
|
|
7bb037bb9d | ||
|
|
52f70e9f4f | ||
|
|
505f69b519 | ||
|
|
b21709db40 | ||
|
|
aff7b37ef9 | ||
|
|
daa85be1ad | ||
|
|
c31718ca10 | ||
|
|
fbcfedf0e8 | ||
|
|
bd032335eb | ||
|
|
128e0f3a10 | ||
|
|
557fb9d741 | ||
|
|
b4d6ed5f5f | ||
|
|
035836da98 | ||
|
|
294039a497 | ||
|
|
b2450ef625 | ||
|
|
a81bb3e51e | ||
|
|
bb506f7eb9 | ||
|
|
87d9cc4244 | ||
|
|
d2e3b76031 | ||
|
|
c36834b3b0 |
1
.github/ISSUE_TEMPLATE/2_bug_provider.yml
vendored
1
.github/ISSUE_TEMPLATE/2_bug_provider.yml
vendored
@@ -25,6 +25,7 @@ body:
|
||||
- "Custom provider"
|
||||
- "42 School"
|
||||
- "Apple"
|
||||
- "Asgardeo"
|
||||
- "Atlassian"
|
||||
- "Auth0"
|
||||
- "Authentik"
|
||||
|
||||
16
.github/workflows/release.yml
vendored
16
.github/workflows/release.yml
vendored
@@ -35,6 +35,22 @@ jobs:
|
||||
UPSTASH_REDIS_KEY: ${{ secrets.UPSTASH_REDIS_KEY }}
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||
# - name: Run E2E tests
|
||||
# if: github.repository == 'nextauthjs/next-auth'
|
||||
# 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
|
||||
# if: github.repository == 'nextauthjs/next-auth'
|
||||
# uses: actions/upload-artifact@v3
|
||||
# with:
|
||||
# name: playwright-report
|
||||
# path: apps/dev/nextjs/playwright-report/
|
||||
# retention-days: 30
|
||||
# - name: Coverage
|
||||
# uses: codecov/codecov-action@v1
|
||||
# with:
|
||||
|
||||
@@ -9,6 +9,10 @@ NEXTAUTH_URL=http://localhost:3000
|
||||
# and/or verification tokens.
|
||||
NEXTAUTH_SECRET=secret
|
||||
|
||||
ASGARDEO_CLIENT_ID=
|
||||
ASGARDEO_CLIENT_SECRET=
|
||||
ASGARDEO_ISSUER=
|
||||
|
||||
AUTH0_ID=
|
||||
AUTH0_SECRET=
|
||||
AUTH0_ISSUER=
|
||||
|
||||
4
apps/dev/nextjs/.gitignore
vendored
Normal file
4
apps/dev/nextjs/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
node_modules/
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/playwright/.cache/
|
||||
@@ -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",
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Auth, type AuthConfig } from "@auth/core"
|
||||
|
||||
// Providers
|
||||
import Apple from "@auth/core/providers/apple"
|
||||
import Asgardeo from "@auth/core/providers/asgardeo"
|
||||
import Auth0 from "@auth/core/providers/auth0"
|
||||
import AzureAD from "@auth/core/providers/azure-ad"
|
||||
import AzureB2C from "@auth/core/providers/azure-ad-b2c"
|
||||
@@ -82,6 +83,7 @@ export const authConfig: AuthConfig = {
|
||||
},
|
||||
}),
|
||||
Apple({ clientId: process.env.APPLE_ID, clientSecret: process.env.APPLE_SECRET }),
|
||||
Asgardeo({ clientId: process.env.ASGARDEO_CLIENT_ID, clientSecret: process.env.ASGARDEO_CLIENT_SECRET, issuer: process.env.ASGARDEO_ISSUER }),
|
||||
Auth0({ clientId: process.env.AUTH0_ID, clientSecret: process.env.AUTH0_SECRET, issuer: process.env.AUTH0_ISSUER }),
|
||||
AzureAD({
|
||||
clientId: process.env.AZURE_AD_CLIENT_ID,
|
||||
|
||||
107
apps/dev/nextjs/playwright.config.ts
Normal file
107
apps/dev/nextjs/playwright.config.ts
Normal 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;
|
||||
39
apps/dev/nextjs/tests/signin.spec.ts
Normal file
39
apps/dev/nextjs/tests/signin.spec.ts
Normal 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()
|
||||
})
|
||||
@@ -70,7 +70,7 @@ Auth.js is extremely customizable, [our guides section](/guides/overview) will t
|
||||
|
||||
To be able to use `useSession` first you'll need to expose the session context, [`<SessionProvider />`](/reference/react/#sessionprovider), at the top level of your application:
|
||||
|
||||
```ts title="pages/_app.ts"
|
||||
```ts title="pages/_app.tsx"
|
||||
import { SessionProvider } from "next-auth/react"
|
||||
|
||||
export default function App({
|
||||
@@ -186,14 +186,14 @@ http://localhost:3000/api/auth/callback/github
|
||||
Auth.js will already magically create this API endpoint for you when we start the application later. Note that because we're using Next.js, locally it starts our server on the port `3000`, hence the origin is `http://localhost:3000`.
|
||||
:::
|
||||
|
||||
Next you'll be presented with the following screen which presents all the configuration for your new OAuth app. For now, let's we need two things from it: the **Client ID** and **Client Secret** for our new OAuth app:
|
||||
Next you'll be presented with the following screen which presents all the configuration for your new OAuth app. For now, we need two things from it: the **Client ID** and **Client Secret** for our new OAuth app:
|
||||
|
||||
<img src={gettingClientIdSecretImg} />
|
||||
|
||||
The Client ID is always there, a public identifier of your OAuth application within Github. Click on the **Generate a new client Secret** button and should be presented with a new string (which is just a randomized string).
|
||||
|
||||
:::warning
|
||||
🔥 Keep both your Client ID and Client Secret secure and never expose them to the public or shared with people outside your organization. With tem a malicious actor could hijack your application and cause you and your user serious problems!
|
||||
🔥 Keep both your Client ID and Client Secret secure and never expose them to the public or shared with people outside your organization. With them, a malicious actor could hijack your application and cause you and your user serious problems!
|
||||
:::
|
||||
|
||||
Now let's copy both the Client ID and Client Secret and paste them in an environment file in the root of your project like so:
|
||||
|
||||
@@ -5,7 +5,7 @@ title: TypeScript
|
||||
Auth.js has its own type definitions to use in your TypeScript projects safely. Even if you don't use TypeScript, IDEs like VSCode will pick this up to provide you with a better developer experience. While you are typing, you will get suggestions about what certain objects/functions look like, and sometimes links to documentation, examples, and other valuable resources.
|
||||
|
||||
Check out the example repository showcasing how to use `next-auth` on a Next.js application with TypeScript:
|
||||
https://github.com/nextauthjs/next-auth-typescript-example
|
||||
https://github.com/nextauthjs/next-auth-example
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
153
docs/docs/guides/03-basics/role-based-authentication.md
Normal file
153
docs/docs/guides/03-basics/role-based-authentication.md
Normal file
@@ -0,0 +1,153 @@
|
||||
---
|
||||
title: Role-based authentication
|
||||
---
|
||||
|
||||
There are two ways to add role-based authentication (RBAC) to your application, based on the [session strategy](/concepts/session-strategies) you choose. Let's see an example for each of these.
|
||||
|
||||
## Getting the role
|
||||
|
||||
We are going to start by adding a `profile()` callback to the providers' config to determine the user role:
|
||||
|
||||
```ts title="/pages/api/auth/[...nextauth].ts"
|
||||
import NextAuth from "next-auth"
|
||||
import Google from "next-auth/providers/google"
|
||||
|
||||
export default NextAuth({
|
||||
providers: [
|
||||
Google({
|
||||
profile(profile) {
|
||||
return { role: profile.role ?? "user", ... }
|
||||
},
|
||||
...
|
||||
})
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
:::tip
|
||||
To determine the user's role, you can either add your logic or if your provider assigns roles already, use that instead.
|
||||
:::
|
||||
|
||||
## Persisting the role
|
||||
### With JWT
|
||||
|
||||
When you don't have a database configured, the role will be persisted in a cookie, by using the `jwt()` callback. On sign-in, the `role` property is exposed from the `profile` callback on the `user` object. Persist the `user.role` value by assigning it to `token.role`. That's it!
|
||||
|
||||
If you also want to use the role on the client, you can expose it via the `session` callback.
|
||||
|
||||
```ts title="/pages/api/auth/[...nextauth].ts"
|
||||
import NextAuth from "next-auth"
|
||||
import Google from "next-auth/providers/google"
|
||||
|
||||
export default NextAuth({
|
||||
providers: [
|
||||
Google({
|
||||
profile(profile) {
|
||||
return { role: profile.role ?? "user", ... }
|
||||
},
|
||||
...
|
||||
})
|
||||
],
|
||||
// highlight-start
|
||||
callbacks: {
|
||||
jwt({ token, user }) {
|
||||
if(user) token.role = user.role
|
||||
return token
|
||||
},
|
||||
session({ session, token }) {
|
||||
session.user.role = token.role
|
||||
return session
|
||||
}
|
||||
}
|
||||
// highlight-end
|
||||
})
|
||||
```
|
||||
|
||||
:::info
|
||||
With this strategy, if you want to update the role, the user needs to be forced to sign in again.
|
||||
:::
|
||||
|
||||
### With Database
|
||||
|
||||
When you have a database, you can save the user role on the [User model](/reference/adapters/models#user). The below example is showing you how to do this with Prisma, but the idea is the same for all adapters.
|
||||
|
||||
First, add a `role` column to the User model.
|
||||
|
||||
```ts title="/prisma/schema.prisma"
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
name String?
|
||||
email String? @unique
|
||||
emailVerified DateTime?
|
||||
image String?
|
||||
role String? // New column
|
||||
accounts Account[]
|
||||
sessions Session[]
|
||||
}
|
||||
```
|
||||
|
||||
The `profile()` callback's return value is used to create users in the database. That's it! Your newly created users will now have an assigned role.
|
||||
|
||||
If you also want to use the role on the client, you can expose it via the `session` callback.
|
||||
|
||||
```ts title="/pages/api/auth/[...nextauth].ts"
|
||||
import NextAuth from "next-auth"
|
||||
import Google from "next-auth/providers/google"
|
||||
// highlight-next-line
|
||||
import prisma from "lib/prisma"
|
||||
|
||||
export default NextAuth({
|
||||
// highlight-next-line
|
||||
adapter: PrismaAdapter(prisma),
|
||||
providers: [
|
||||
Google({
|
||||
profile(profile) {
|
||||
return { role: profile.role ?? "user", ... }
|
||||
}
|
||||
...
|
||||
})
|
||||
],
|
||||
// highlight-start
|
||||
callbacks: {
|
||||
session({ session, user }) {
|
||||
session.user.role = user.role
|
||||
return session
|
||||
}
|
||||
}
|
||||
// highlight-end
|
||||
})
|
||||
```
|
||||
|
||||
:::info
|
||||
It is up to you how you want to manage to update the roles, either through direct database access or building your role update API.
|
||||
:::
|
||||
|
||||
## Using the role
|
||||
|
||||
If you want to use the role in the client, for both cases above, when using the `useSession` hook, `session.user.role` will have the required role if you exposed it via the `session` callback. You can use this to render a different UI for different users.
|
||||
|
||||
```ts title="/pages/admin.tsx"
|
||||
import { useSession } from "next-auth/react"
|
||||
|
||||
export default function Page() {
|
||||
const session = await useSession()
|
||||
|
||||
if (session?.user.role === "admin") {
|
||||
return <p>You are an admin, welcome!</p>
|
||||
}
|
||||
|
||||
return <p>You are not authorized to view this page!</p>
|
||||
}
|
||||
```
|
||||
|
||||
:::tip
|
||||
When using Next.js and JWT, you can alternatively also use [Middleware](https://next-auth.js.org/configuration/nextjs#wrap-middleware) to redirect the user based on their role, even before rendering the page.
|
||||
:::
|
||||
|
||||
## Resources
|
||||
|
||||
- [Concepts: Session strategies](/concepts/session-strategies)
|
||||
- [Next.js: Middleware](https://next-auth.js.org/configuration/nextjs#wrap-middleware)
|
||||
- [Adapters: User model](/reference/adapters/models#user)
|
||||
- [Adapters: Prisma adapter](/reference/adapters/prisma)
|
||||
- [TypeScript](/getting-started/typescript)
|
||||
@@ -1,64 +0,0 @@
|
||||
---
|
||||
title: Role based logins
|
||||
---
|
||||
|
||||
To add role based authentication to your application, you must do three things.
|
||||
|
||||
1. Update your database schema
|
||||
2. Add the `role` to the session object
|
||||
3. Check for `role` in your pages/components
|
||||
|
||||
First modify the `user` table and add a `role` column with the type of `String?`.
|
||||
|
||||
Below is an example Prisma schema file.
|
||||
|
||||
```javascript title="/prisma/schema.prisma"
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
name String?
|
||||
email String? @unique
|
||||
emailVerified DateTime?
|
||||
image String?
|
||||
role String? // New Column
|
||||
accounts Account[]
|
||||
sessions Session[]
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Next, implement a custom session callback in the `[...nextauth].js` file, as shown below.
|
||||
|
||||
```javascript title="/pages/api/auth/[...nextauth].js"
|
||||
callbacks: {
|
||||
async session({ session, token, user }) {
|
||||
session.user.role = user.role; // Add role value to user object so it is passed along with session
|
||||
return session;
|
||||
},
|
||||
```
|
||||
|
||||
Going forward, when using the `getSession` hook, check that `session.user.role` matches the required role. The example below assumes the role `'admin'` is required.
|
||||
|
||||
```javascript title="/pages/admin.js"
|
||||
import { getSession } from "next-auth/react"
|
||||
|
||||
export default function Page() {
|
||||
const session = await getSession({ req })
|
||||
|
||||
if (session && session.user.role === "admin") {
|
||||
return (
|
||||
<div>
|
||||
<h1>Admin</h1>
|
||||
<p>Welcome to the Admin Portal!</p>
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<div>
|
||||
<h1>You are not authorized to view this page!</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then it is up to you how you manage your roles, either through direct database access or building your own role update API.
|
||||
@@ -16,4 +16,4 @@ sidebar_label: Email options
|
||||
See our guides on magic links authentication for further tips on how to customize this provider:
|
||||
|
||||
- [Tutorial](/getting-started/email-tutorial)
|
||||
- [Guide deep-dive](guides/providers/email)
|
||||
- [Guide deep-dive](/guides/providers/email)
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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.
|
||||
7
docs/static/img/providers/asgardeo-dark.svg
vendored
Normal file
7
docs/static/img/providers/asgardeo-dark.svg
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 99.881 86.449">
|
||||
<g id="asgardeo-trifactor-logo-dark-16x40" transform="translate(-553.024 -388.98)">
|
||||
<path id="Path_264" data-name="Path 264" d="M743.533,388.98l9.161,15.892-10.153,17.6h20.306l9.209,15.892H714.97Z" transform="translate(-119.151 0)" fill="#ff7300"/>
|
||||
<path id="Path_265" data-name="Path 265" d="M705.95,438.364l9.209-15.892h20.306l-10.153-17.6,9.162-15.892,28.6,49.393Z" transform="translate(-152.926 0.009)" fill="#ff7300"/>
|
||||
<path id="Path_266" data-name="Path 266" d="M749.175,446.183l-10.153-17.6-10.2,17.6H710.46l28.6-49.393,28.515,49.393Z" transform="translate(-136.043 29.246)"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 711 B |
7
docs/static/img/providers/asgardeo.svg
vendored
Normal file
7
docs/static/img/providers/asgardeo.svg
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 99.881 86.449">
|
||||
<g id="asgardeo-trifactor-logo-light-16x40" transform="translate(-553.024 -388.98)">
|
||||
<path id="Path_264" data-name="Path 264" d="M743.533,388.98l9.161,15.892-10.153,17.6h20.306l9.209,15.892H714.97Z" transform="translate(-119.151)" fill="#ff7300"/>
|
||||
<path id="Path_265" data-name="Path 265" d="M705.95,438.364l9.209-15.892h20.306l-10.153-17.6,9.162-15.892,28.6,49.393Z" transform="translate(-152.926 0.009)" fill="#ff7300"/>
|
||||
<path id="Path_266" data-name="Path 266" d="M749.175,446.183l-10.153-17.6-10.2,17.6H710.46l28.6-49.393,28.515,49.393Z" transform="translate(-136.043 29.246)" fill="#fff"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 722 B |
@@ -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",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@next-auth/dynamodb-adapter",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
"version": "1.0.6",
|
||||
"version": "1.2.0",
|
||||
"description": "AWS DynamoDB adapter for next-auth.",
|
||||
"keywords": [
|
||||
"next-auth",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@next-auth/xata-adapter",
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.2",
|
||||
"description": "Xata adapter for next-auth.",
|
||||
"homepage": "https://authjs.dev",
|
||||
"repository": "https://github.com/nextauthjs/next-auth",
|
||||
@@ -43,4 +43,4 @@
|
||||
"jest": {
|
||||
"preset": "@next-auth/adapter-test/jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@auth/core",
|
||||
"version": "0.2.5",
|
||||
"version": "0.3.0",
|
||||
"description": "Authentication for the Web.",
|
||||
"keywords": [
|
||||
"authentication",
|
||||
@@ -92,4 +92,4 @@
|
||||
"postcss": "8.4.19",
|
||||
"postcss-nested": "6.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
|
||||
@@ -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}`)
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as checks from "./checks.js"
|
||||
import * as o from "oauth4webapi"
|
||||
|
||||
import type {
|
||||
CookiesOptions,
|
||||
InternalOptions,
|
||||
RequestInternal,
|
||||
ResponseInternal,
|
||||
@@ -58,10 +58,10 @@ export async function getAuthorizationUrl(
|
||||
|
||||
const cookies: Cookie[] = []
|
||||
|
||||
if (provider.checks?.includes("state")) {
|
||||
const { value, raw } = await createState(options)
|
||||
authParams.set("state", raw)
|
||||
cookies.push(value)
|
||||
const state = await checks.state.create(options)
|
||||
if (state) {
|
||||
authParams.set("state", state.value)
|
||||
cookies.push(state.cookie)
|
||||
}
|
||||
|
||||
if (provider.checks?.includes("pkce")) {
|
||||
@@ -70,17 +70,17 @@ export async function getAuthorizationUrl(
|
||||
// a random `nonce` must be used for CSRF protection.
|
||||
provider.checks = ["nonce"]
|
||||
} else {
|
||||
const { code_challenge, pkce } = await createPKCE(options)
|
||||
authParams.set("code_challenge", code_challenge)
|
||||
const { value, cookie } = await checks.pkce.create(options)
|
||||
authParams.set("code_challenge", value)
|
||||
authParams.set("code_challenge_method", "S256")
|
||||
cookies.push(pkce)
|
||||
cookies.push(cookie)
|
||||
}
|
||||
}
|
||||
|
||||
if (provider.checks?.includes("nonce")) {
|
||||
const nonce = await createNonce(options)
|
||||
const nonce = await checks.nonce.create(options)
|
||||
if (nonce) {
|
||||
authParams.set("nonce", nonce.value)
|
||||
cookies.push(nonce)
|
||||
cookies.push(nonce.cookie)
|
||||
}
|
||||
|
||||
// TODO: This does not work in normalizeOAuth because authorization endpoint can come from discovery
|
||||
@@ -92,52 +92,3 @@ export async function getAuthorizationUrl(
|
||||
logger.debug("authorization url is ready", { url, cookies, provider })
|
||||
return { redirect: url, cookies }
|
||||
}
|
||||
|
||||
/** Returns a signed cookie. */
|
||||
export async function signCookie(
|
||||
type: keyof CookiesOptions,
|
||||
value: string,
|
||||
maxAge: number,
|
||||
options: InternalOptions<"oauth">
|
||||
): Promise<Cookie> {
|
||||
const { cookies, jwt, logger } = options
|
||||
|
||||
logger.debug(`CREATE_${type.toUpperCase()}`, { value, maxAge })
|
||||
|
||||
const expires = new Date()
|
||||
expires.setTime(expires.getTime() + maxAge * 1000)
|
||||
return {
|
||||
name: cookies[type].name,
|
||||
value: await jwt.encode({ ...jwt, maxAge, token: { value } }),
|
||||
options: { ...cookies[type].options, expires },
|
||||
}
|
||||
}
|
||||
|
||||
const STATE_MAX_AGE = 60 * 15 // 15 minutes in seconds
|
||||
async function createState(options: InternalOptions<"oauth">) {
|
||||
const raw = o.generateRandomState()
|
||||
const maxAge = STATE_MAX_AGE
|
||||
const value = await signCookie("state", raw, maxAge, options)
|
||||
return { value, raw }
|
||||
}
|
||||
|
||||
const PKCE_MAX_AGE = 60 * 15 // 15 minutes in seconds
|
||||
async function createPKCE(options: InternalOptions<"oauth">) {
|
||||
const code_verifier = o.generateRandomCodeVerifier()
|
||||
const code_challenge = await o.calculatePKCECodeChallenge(code_verifier)
|
||||
const maxAge = PKCE_MAX_AGE
|
||||
const pkce = await signCookie(
|
||||
"pkceCodeVerifier",
|
||||
code_verifier,
|
||||
maxAge,
|
||||
options
|
||||
)
|
||||
return { code_challenge, pkce }
|
||||
}
|
||||
|
||||
const NONCE_MAX_AGE = 60 * 15 // 15 minutes in seconds
|
||||
async function createNonce(options: InternalOptions<"oauth">) {
|
||||
const raw = o.generateRandomNonce()
|
||||
const maxAge = NONCE_MAX_AGE
|
||||
return await signCookie("nonce", raw, maxAge, options)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import * as checks from "./checks.js"
|
||||
import * as o from "oauth4webapi"
|
||||
import { OAuthCallbackError, OAuthProfileParseError } from "../../errors.js"
|
||||
import { useNonce } from "./nonce-handler.js"
|
||||
import { usePKCECodeVerifier } from "./pkce-handler.js"
|
||||
import { useState } from "./state-handler.js"
|
||||
|
||||
import type {
|
||||
InternalOptions,
|
||||
@@ -73,7 +71,7 @@ export async function handleOAuth(
|
||||
|
||||
const resCookies: Cookie[] = []
|
||||
|
||||
const state = await useState(cookies, resCookies, options)
|
||||
const state = await checks.state.use(cookies, resCookies, options)
|
||||
|
||||
const parameters = o.validateAuthResponse(
|
||||
as,
|
||||
@@ -91,7 +89,7 @@ export async function handleOAuth(
|
||||
throw new OAuthCallbackError(parameters.error)
|
||||
}
|
||||
|
||||
const codeVerifier = await usePKCECodeVerifier(
|
||||
const codeVerifier = await checks.pkce.use(
|
||||
cookies?.[options.cookies.pkceCodeVerifier.name],
|
||||
options
|
||||
)
|
||||
@@ -99,12 +97,15 @@ export async function handleOAuth(
|
||||
if (codeVerifier) resCookies.push(codeVerifier.cookie)
|
||||
|
||||
// TODO:
|
||||
const nonce = await useNonce(cookies?.[options.cookies.nonce.name], options)
|
||||
const nonce = await checks.nonce.use(
|
||||
cookies?.[options.cookies.nonce.name],
|
||||
options
|
||||
)
|
||||
if (nonce && provider.type === "oidc") {
|
||||
resCookies.push(nonce.cookie)
|
||||
}
|
||||
|
||||
const codeGrantResponse = await o.authorizationCodeGrantRequest(
|
||||
let codeGrantResponse = await o.authorizationCodeGrantRequest(
|
||||
as,
|
||||
client,
|
||||
parameters,
|
||||
@@ -112,6 +113,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) {
|
||||
|
||||
155
packages/core/src/lib/oauth/checks.ts
Normal file
155
packages/core/src/lib/oauth/checks.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
import * as o from "oauth4webapi"
|
||||
import * as jwt from "../../jwt.js"
|
||||
|
||||
import type {
|
||||
InternalOptions,
|
||||
RequestInternal,
|
||||
CookiesOptions,
|
||||
} from "../../types.js"
|
||||
import type { Cookie } from "../cookie.js"
|
||||
|
||||
import { InvalidState } from "../../errors.js"
|
||||
|
||||
/** Returns a signed cookie. */
|
||||
export async function signCookie(
|
||||
type: keyof CookiesOptions,
|
||||
value: string,
|
||||
maxAge: number,
|
||||
options: InternalOptions<"oauth">
|
||||
): Promise<Cookie> {
|
||||
const { cookies, logger } = options
|
||||
|
||||
logger.debug(`CREATE_${type.toUpperCase()}`, { value, maxAge })
|
||||
|
||||
const expires = new Date()
|
||||
expires.setTime(expires.getTime() + maxAge * 1000)
|
||||
return {
|
||||
name: cookies[type].name,
|
||||
value: await jwt.encode({ ...options.jwt, maxAge, token: { value } }),
|
||||
options: { ...cookies[type].options, expires },
|
||||
}
|
||||
}
|
||||
|
||||
const PKCE_MAX_AGE = 60 * 15 // 15 minutes in seconds
|
||||
export const pkce = {
|
||||
async create(options: InternalOptions<"oauth">) {
|
||||
const code_verifier = o.generateRandomCodeVerifier()
|
||||
const value = await o.calculatePKCECodeChallenge(code_verifier)
|
||||
const maxAge = PKCE_MAX_AGE
|
||||
const cookie = await signCookie(
|
||||
"pkceCodeVerifier",
|
||||
code_verifier,
|
||||
maxAge,
|
||||
options
|
||||
)
|
||||
return { cookie, value }
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns code_verifier if provider uses PKCE,
|
||||
* and clears the container cookie afterwards.
|
||||
*/
|
||||
async use(
|
||||
codeVerifier: string | undefined,
|
||||
options: InternalOptions<"oauth">
|
||||
): Promise<{ codeVerifier: string; cookie: Cookie } | undefined> {
|
||||
const { cookies, provider } = options
|
||||
|
||||
if (!provider?.checks?.includes("pkce") || !codeVerifier) {
|
||||
return
|
||||
}
|
||||
|
||||
const pkce = (await jwt.decode({
|
||||
...options.jwt,
|
||||
token: codeVerifier,
|
||||
})) as any
|
||||
|
||||
return {
|
||||
codeVerifier: pkce?.value ?? undefined,
|
||||
cookie: {
|
||||
name: cookies.pkceCodeVerifier.name,
|
||||
value: "",
|
||||
options: { ...cookies.pkceCodeVerifier.options, maxAge: 0 },
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const STATE_MAX_AGE = 60 * 15 // 15 minutes in seconds
|
||||
export const state = {
|
||||
async create(options: InternalOptions<"oauth">) {
|
||||
if (!options.provider.checks.includes("state")) return
|
||||
// TODO: support customizing the state
|
||||
const value = o.generateRandomState()
|
||||
const maxAge = STATE_MAX_AGE
|
||||
const cookie = await signCookie("state", value, maxAge, options)
|
||||
return { cookie, value }
|
||||
},
|
||||
/**
|
||||
* Returns state from the saved cookie
|
||||
* if the provider supports states,
|
||||
* and clears the container cookie afterwards.
|
||||
*/
|
||||
async use(
|
||||
cookies: RequestInternal["cookies"],
|
||||
resCookies: Cookie[],
|
||||
options: InternalOptions<"oauth">
|
||||
): Promise<string | undefined> {
|
||||
const { provider, jwt } = options
|
||||
if (!provider.checks.includes("state")) return
|
||||
|
||||
const state = cookies?.[options.cookies.state.name]
|
||||
|
||||
if (!state) throw new InvalidState("State was missing from the cookies.")
|
||||
|
||||
// IDEA: Let the user do something with the returned state
|
||||
const value = (await jwt.decode({ ...options.jwt, token: state })) as any
|
||||
|
||||
if (!value?.value) throw new InvalidState("Could not parse state cookie.")
|
||||
|
||||
// Clear the state cookie after use
|
||||
resCookies.push({
|
||||
name: options.cookies.state.name,
|
||||
value: "",
|
||||
options: { ...options.cookies.state.options, maxAge: 0 },
|
||||
})
|
||||
|
||||
return value.value
|
||||
},
|
||||
}
|
||||
|
||||
const NONCE_MAX_AGE = 60 * 15 // 15 minutes in seconds
|
||||
export const nonce = {
|
||||
async create(options: InternalOptions<"oauth">) {
|
||||
if (!options.provider.checks.includes("nonce")) return
|
||||
const value = o.generateRandomNonce()
|
||||
const maxAge = NONCE_MAX_AGE
|
||||
const cookie = await signCookie("nonce", value, maxAge, options)
|
||||
return { cookie, value }
|
||||
},
|
||||
/**
|
||||
* Returns nonce from if the provider supports nonce,
|
||||
* and clears the container cookie afterwards.
|
||||
*/
|
||||
async use(
|
||||
nonce: string | undefined,
|
||||
options: InternalOptions<"oauth">
|
||||
): Promise<{ value: string; cookie: Cookie } | undefined> {
|
||||
const { cookies, provider } = options
|
||||
|
||||
if (!provider?.checks?.includes("nonce") || !nonce) {
|
||||
return
|
||||
}
|
||||
|
||||
const value = (await jwt.decode({ ...options.jwt, token: nonce })) as any
|
||||
|
||||
return {
|
||||
value: value?.value ?? undefined,
|
||||
cookie: {
|
||||
name: cookies.nonce.name,
|
||||
value: "",
|
||||
options: { ...cookies.nonce.options, maxAge: 0 },
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
import * as o from "oauth4webapi"
|
||||
import * as jwt from "../../jwt.js"
|
||||
|
||||
import type { InternalOptions } from "../../types.js"
|
||||
import type { Cookie } from "../cookie.js"
|
||||
|
||||
const NONCE_MAX_AGE = 60 * 15 // 15 minutes in seconds
|
||||
|
||||
/**
|
||||
* Returns nonce if the provider supports it
|
||||
* and saves it in a cookie
|
||||
*/
|
||||
export async function createNonce(options: InternalOptions<"oauth">): Promise<
|
||||
| undefined
|
||||
| {
|
||||
value: string
|
||||
cookie: Cookie
|
||||
}
|
||||
> {
|
||||
const { cookies, logger, provider } = options
|
||||
if (!provider.checks?.includes("nonce")) {
|
||||
// Provider does not support nonce, return nothing.
|
||||
return
|
||||
}
|
||||
|
||||
const nonce = o.generateRandomNonce()
|
||||
|
||||
const expires = new Date()
|
||||
expires.setTime(expires.getTime() + NONCE_MAX_AGE * 1000)
|
||||
|
||||
// Encrypt nonce and save it to an encrypted cookie
|
||||
const encryptedNonce = await jwt.encode({
|
||||
...options.jwt,
|
||||
maxAge: NONCE_MAX_AGE,
|
||||
token: { nonce },
|
||||
})
|
||||
|
||||
logger.debug("CREATE_ENCRYPTED_NONCE", {
|
||||
nonce,
|
||||
maxAge: NONCE_MAX_AGE,
|
||||
})
|
||||
|
||||
return {
|
||||
cookie: {
|
||||
name: cookies.nonce.name,
|
||||
value: encryptedNonce,
|
||||
options: { ...cookies.nonce.options, expires },
|
||||
},
|
||||
value: nonce,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns nonce from if the provider supports nonce,
|
||||
* and clears the container cookie afterwards.
|
||||
*/
|
||||
export async function useNonce(
|
||||
nonce: string | undefined,
|
||||
options: InternalOptions<"oauth">
|
||||
): Promise<{ value: string; cookie: Cookie } | undefined> {
|
||||
const { cookies, provider } = options
|
||||
|
||||
if (!provider?.checks?.includes("nonce") || !nonce) {
|
||||
return
|
||||
}
|
||||
|
||||
const value = (await jwt.decode({ ...options.jwt, token: nonce })) as any
|
||||
|
||||
return {
|
||||
value: value?.value ?? undefined,
|
||||
cookie: {
|
||||
name: cookies.nonce.name,
|
||||
value: "",
|
||||
options: { ...cookies.nonce.options, maxAge: 0 },
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
import * as o from "oauth4webapi"
|
||||
import * as jwt from "../../jwt.js"
|
||||
|
||||
import type { InternalOptions } from "../../types.js"
|
||||
import type { Cookie } from "../cookie.js"
|
||||
|
||||
const PKCE_CODE_CHALLENGE_METHOD = "S256"
|
||||
const PKCE_MAX_AGE = 60 * 15 // 15 minutes in seconds
|
||||
|
||||
/**
|
||||
* Returns `code_challenge` and `code_challenge_method`
|
||||
* and saves them in a cookie.
|
||||
*/
|
||||
export async function createPKCE(options: InternalOptions<"oauth">): Promise<
|
||||
| undefined
|
||||
| {
|
||||
code_challenge: string
|
||||
code_challenge_method: "S256"
|
||||
cookie: Cookie
|
||||
}
|
||||
> {
|
||||
const { cookies, logger, provider } = options
|
||||
if (!provider.checks?.includes("pkce")) {
|
||||
// Provider does not support PKCE, return nothing.
|
||||
return
|
||||
}
|
||||
const code_verifier = o.generateRandomCodeVerifier()
|
||||
const code_challenge = await o.calculatePKCECodeChallenge(code_verifier)
|
||||
|
||||
const maxAge = cookies.pkceCodeVerifier.options.maxAge ?? PKCE_MAX_AGE
|
||||
|
||||
const expires = new Date()
|
||||
expires.setTime(expires.getTime() + maxAge * 1000)
|
||||
|
||||
// Encrypt code_verifier and save it to an encrypted cookie
|
||||
const encryptedCodeVerifier = await jwt.encode({
|
||||
...options.jwt,
|
||||
maxAge,
|
||||
token: { code_verifier },
|
||||
})
|
||||
|
||||
logger.debug("CREATE_PKCE_CHALLENGE_VERIFIER", {
|
||||
code_challenge,
|
||||
code_challenge_method: PKCE_CODE_CHALLENGE_METHOD,
|
||||
code_verifier,
|
||||
maxAge,
|
||||
})
|
||||
|
||||
return {
|
||||
code_challenge,
|
||||
code_challenge_method: PKCE_CODE_CHALLENGE_METHOD,
|
||||
cookie: {
|
||||
name: cookies.pkceCodeVerifier.name,
|
||||
value: encryptedCodeVerifier,
|
||||
options: { ...cookies.pkceCodeVerifier.options, expires },
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns code_verifier if provider uses PKCE,
|
||||
* and clears the container cookie afterwards.
|
||||
*/
|
||||
export async function usePKCECodeVerifier(
|
||||
codeVerifier: string | undefined,
|
||||
options: InternalOptions<"oauth">
|
||||
): Promise<{ codeVerifier: string; cookie: Cookie } | undefined> {
|
||||
const { cookies, provider } = options
|
||||
|
||||
if (!provider?.checks?.includes("pkce") || !codeVerifier) {
|
||||
return
|
||||
}
|
||||
|
||||
const pkce = (await jwt.decode({
|
||||
...options.jwt,
|
||||
token: codeVerifier,
|
||||
})) as any
|
||||
|
||||
return {
|
||||
codeVerifier: pkce?.value ?? undefined,
|
||||
cookie: {
|
||||
name: cookies.pkceCodeVerifier.name,
|
||||
value: "",
|
||||
options: { ...cookies.pkceCodeVerifier.options, maxAge: 0 },
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
import * as o from "oauth4webapi"
|
||||
import type { InternalOptions, RequestInternal } from "../../types.js"
|
||||
import type { Cookie } from "../cookie.js"
|
||||
import { InvalidState } from "../../errors.js"
|
||||
|
||||
const STATE_MAX_AGE = 60 * 15 // 15 minutes in seconds
|
||||
|
||||
/** Returns state if the provider supports it */
|
||||
export async function createState(
|
||||
options: InternalOptions<"oauth">
|
||||
): Promise<{ cookie: Cookie; value: string } | undefined> {
|
||||
const { logger, provider, jwt, cookies } = options
|
||||
|
||||
if (!provider.checks?.includes("state")) {
|
||||
// Provider does not support state, return nothing
|
||||
return
|
||||
}
|
||||
|
||||
const state = o.generateRandomState()
|
||||
const maxAge = cookies.state.options.maxAge ?? STATE_MAX_AGE
|
||||
|
||||
const encodedState = await jwt.encode({
|
||||
...jwt,
|
||||
maxAge,
|
||||
token: { state },
|
||||
})
|
||||
|
||||
logger.debug("CREATE_STATE", { state, maxAge })
|
||||
|
||||
const expires = new Date()
|
||||
expires.setTime(expires.getTime() + maxAge * 1000)
|
||||
return {
|
||||
value: state,
|
||||
cookie: {
|
||||
name: cookies.state.name,
|
||||
value: encodedState,
|
||||
options: { ...cookies.state.options, expires },
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns state from the saved cookie
|
||||
* if the provider supports states,
|
||||
* and clears the container cookie afterwards.
|
||||
*/
|
||||
export async function useState(
|
||||
cookies: RequestInternal["cookies"],
|
||||
resCookies: Cookie[],
|
||||
options: InternalOptions<"oauth">
|
||||
): Promise<string | undefined> {
|
||||
const { provider, jwt } = options
|
||||
if (!provider.checks.includes("state")) return
|
||||
|
||||
const state = cookies?.[options.cookies.state.name]
|
||||
|
||||
if (!state) throw new InvalidState("State was missing from the cookies.")
|
||||
|
||||
// IDEA: Let the user do something with the returned state
|
||||
const value = (await jwt.decode({ ...options.jwt, token: state })) as any
|
||||
|
||||
if (!value?.value) throw new InvalidState("Could not parse state cookie.")
|
||||
|
||||
// Clear the state cookie after use
|
||||
resCookies.push({
|
||||
name: options.cookies.state.name,
|
||||
value: "",
|
||||
options: { ...options.cookies.state.options, maxAge: 0 },
|
||||
})
|
||||
|
||||
return value.value
|
||||
}
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
112
packages/core/src/providers/asgardeo.ts
Normal file
112
packages/core/src/providers/asgardeo.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* <div style={{backgroundColor: "#24292f", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
|
||||
* <span>Built-in <b>Asgardeo</b> integration.</span>
|
||||
* <a href="https://wso2.com/asgardeo/">
|
||||
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/asgardeo-dark.svg" height="48" width="48"/>
|
||||
* </a>
|
||||
* </div>
|
||||
*
|
||||
* ---
|
||||
* @module providers/asgardeo
|
||||
*/
|
||||
|
||||
import type { OIDCConfig, OIDCUserConfig } from "./index.js"
|
||||
|
||||
export interface AsgardeoProfile {
|
||||
sub: string
|
||||
given_name: string
|
||||
email: string
|
||||
picture: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Asgardeo login to your page.
|
||||
* ## Documentation
|
||||
*
|
||||
* https://wso2.com/asgardeo/docs/guides/authentication
|
||||
*
|
||||
*
|
||||
* ## Instructions
|
||||
*
|
||||
* - Log into https://console.asgardeo.io.
|
||||
* - Next, go to "Application" tab (More info: https://wso2.com/asgardeo/docs/guides/applications/register-oidc-web-app/).
|
||||
* - Register standard based - Open id connect, application.
|
||||
* - Add callback URL: http://localhost:3000/api/auth/callback/asgardeo and https://your-domain.com/api/auth/callback/asgardeo
|
||||
* - After registering the application, go to protocol tab.
|
||||
* - Check `code` grant type.
|
||||
* - Add Authorized redirect URLs & Allowed origins fields.
|
||||
* - Make Email, First Name, Photo URL user attributes mandatory from the console.
|
||||
*
|
||||
* Create a `.env` file in the project root add the following entries:
|
||||
*
|
||||
* These values can be collected from the application created.
|
||||
*
|
||||
* ```
|
||||
* ASGARDEO_CLIENT_ID=<Copy client ID from protocol tab here>
|
||||
* ASGARDEO_CLIENT_SECRET=<Copy client from protocol tab here>
|
||||
* ASGARDEO_ISSUER=<Copy the issuer url from the info tab here>
|
||||
* ```
|
||||
*
|
||||
* In `pages/api/auth/[...nextauth].js` find or add the `Asgardeo` entries:
|
||||
*
|
||||
* ```js
|
||||
* import Asgardeo from "next-auth/providers/asgardeo";
|
||||
* ...
|
||||
* providers: [
|
||||
* Asgardeo({
|
||||
* clientId: process.env.ASGARDEO_CLIENT_ID,
|
||||
* clientSecret: process.env.ASGARDEO_CLIENT_SECRET,
|
||||
* issuer: process.env.ASGARDEO_ISSUER
|
||||
* }),
|
||||
* ],
|
||||
*
|
||||
* ...
|
||||
* ```
|
||||
*
|
||||
* ## Resources
|
||||
*
|
||||
* @see [Asgardeo - Authentication Guide](https://wso2.com/asgardeo/docs/guides/authentication)
|
||||
* @see [Learn more about OAuth](https://authjs.dev/concepts/oauth)
|
||||
* @see [Source code](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/asgardeo.ts)
|
||||
*
|
||||
* ## Notes
|
||||
*
|
||||
* By default, Auth.js assumes that the Asgardeo provider is
|
||||
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
|
||||
*
|
||||
* :::tip
|
||||
*
|
||||
* The Asgardeo provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/asgardeo.ts).
|
||||
* To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options).
|
||||
*
|
||||
* :::
|
||||
*
|
||||
* :::info **Disclaimer**
|
||||
*
|
||||
* If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue).
|
||||
*
|
||||
* Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from
|
||||
* the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec,
|
||||
* we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions).
|
||||
*
|
||||
* :::
|
||||
*/
|
||||
export default function Asgardeo(
|
||||
config: OIDCUserConfig<AsgardeoProfile>
|
||||
): OIDCConfig<AsgardeoProfile> {
|
||||
return {
|
||||
id: "asgardeo",
|
||||
name: "Asgardeo",
|
||||
type: "oidc",
|
||||
wellKnown: `${config?.issuer}/oauth2/token/.well-known/openid-configuration`,
|
||||
style: {
|
||||
logo: "/asgardeo.svg",
|
||||
logoDark: "/asgardeo-dark.svg",
|
||||
bg: "#fff",
|
||||
text: "#000",
|
||||
bgDark: "#000",
|
||||
textDark: "#fff",
|
||||
},
|
||||
options: config,
|
||||
}
|
||||
}
|
||||
@@ -34,22 +34,21 @@ export default function AzureAD<P extends AzureADProfile>(
|
||||
)
|
||||
|
||||
// Confirm that profile photo was returned
|
||||
if (response.ok) {
|
||||
const pictureBuffer = await response.arrayBuffer()
|
||||
const pictureBase64 = Buffer.from(pictureBuffer).toString("base64")
|
||||
return {
|
||||
id: profile.sub,
|
||||
name: profile.name,
|
||||
email: profile.email,
|
||||
image: `data:image/jpeg;base64, ${pictureBase64}`,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
id: profile.sub,
|
||||
name: profile.name,
|
||||
email: profile.email,
|
||||
image: null,
|
||||
}
|
||||
let image
|
||||
// TODO: Do this without Buffer
|
||||
if (response.ok && typeof Buffer !== "undefined") {
|
||||
try {
|
||||
const pictureBuffer = await response.arrayBuffer()
|
||||
const pictureBase64 = Buffer.from(pictureBuffer).toString("base64")
|
||||
image = `data:image/jpeg;base64, ${pictureBase64}`
|
||||
} catch {}
|
||||
}
|
||||
|
||||
return {
|
||||
id: profile.sub,
|
||||
name: profile.name,
|
||||
email: profile.email,
|
||||
image: image ?? null,
|
||||
}
|
||||
},
|
||||
style: {
|
||||
|
||||
@@ -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">
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@auth/sveltekit",
|
||||
"version": "0.1.12",
|
||||
"version": "0.2.0",
|
||||
"description": "Authentication for SvelteKit.",
|
||||
"keywords": [
|
||||
"authentication",
|
||||
@@ -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",
|
||||
|
||||
268
pnpm-lock.yaml
generated
268
pnpm-lock.yaml
generated
@@ -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
|
||||
@@ -139,7 +143,7 @@ importers:
|
||||
vercel: ^23.1.2
|
||||
dependencies:
|
||||
dotenv: 16.0.3
|
||||
gatsby: 5.5.0-next.0_biqbaboplfbrettd7655fr4n2y
|
||||
gatsby: 5.6.0-next.0_biqbaboplfbrettd7655fr4n2y
|
||||
next-auth: link:../../../packages/next-auth
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
@@ -562,7 +566,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 +580,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
|
||||
@@ -7488,8 +7492,8 @@ packages:
|
||||
resolution: {integrity: sha512-Tbsj02wXCbqGmzdnXNk0SOF19ChhRU70BsroIi4Pm6Ehp56in6vch94mfbdQ17DozxkL3BAVjbZ4Qc1a0HFRAg==}
|
||||
dev: false
|
||||
|
||||
/@builder.io/partytown/0.5.4:
|
||||
resolution: {integrity: sha512-qnikpQgi30AS01aFlNQV6l8/qdZIcP76mp90ti+u4rucXHsn4afSKivQXApqxvrQG9+Ibv45STyvHizvxef/7A==}
|
||||
/@builder.io/partytown/0.7.5:
|
||||
resolution: {integrity: sha512-Zbr2Eo0AQ4yzmQr/36/h+6LKjmdVBB3Q5cGzO6rtlIKB/IOpbQVUZW+XAnhpJmJr9sIF97OZjgbhG9k7Sjn4yw==}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
@@ -9076,14 +9080,14 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@gatsbyjs/parcel-namer-relative-to-cwd/2.5.0-next.0_@parcel+core@2.8.2:
|
||||
resolution: {integrity: sha512-1BEOXlcXkIrOPgRdNXf404CfmqmtpcKcAgmde+gpGUS040fT5BRTT+9kxnJQaj7ZbLMhRxI83zjZ6Nkwi/8g8w==}
|
||||
/@gatsbyjs/parcel-namer-relative-to-cwd/2.6.0-next.0_@parcel+core@2.8.2:
|
||||
resolution: {integrity: sha512-YAEMsZhRG59bs9Ps3TKh/jxQ2ysnHU7+eVEEq/gssh2o+og//WTiR7KldR6xFEWQWMmmYQThAGyuJe/sN9hVNA==}
|
||||
engines: {node: '>=18.0.0', parcel: 2.x}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.20.7
|
||||
'@parcel/namer-default': 2.8.2_@parcel+core@2.8.2
|
||||
'@parcel/plugin': 2.8.2_@parcel+core@2.8.2
|
||||
gatsby-core-utils: 4.5.0-next.0
|
||||
gatsby-core-utils: 4.6.0-next.0
|
||||
transitivePeerDependencies:
|
||||
- '@parcel/core'
|
||||
dev: false
|
||||
@@ -11346,13 +11350,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:
|
||||
@@ -14545,21 +14549,12 @@ packages:
|
||||
engines: {node: '>=4'}
|
||||
dev: false
|
||||
|
||||
/axios/0.21.4_debug@3.2.7:
|
||||
resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==}
|
||||
dependencies:
|
||||
follow-redirects: 1.15.1_debug@3.2.7
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
dev: false
|
||||
|
||||
/axios/0.21.4_debug@4.3.4:
|
||||
resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==}
|
||||
dependencies:
|
||||
follow-redirects: 1.15.1_debug@4.3.4
|
||||
follow-redirects: 1.15.1
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
dev: true
|
||||
|
||||
/axios/0.25.0:
|
||||
resolution: {integrity: sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==}
|
||||
@@ -14951,8 +14946,8 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/babel-plugin-remove-graphql-queries/5.5.0-next.0_jqsf6f3mvfsscspe5xblmymkvm:
|
||||
resolution: {integrity: sha512-y7xJGPPHPljVR3nhZNllraBJ42F1j1iJsrUlUWQW5nLDNM/iPyWdpKmvKaChvU9gez5cs8pQexVcFh6NbUzcMg==}
|
||||
/babel-plugin-remove-graphql-queries/5.6.0-next.0_34zmit57noivsgqvgdpgpcttx4:
|
||||
resolution: {integrity: sha512-hrxx7U73x6TUL+x/h2/OrQT4hdeGkYjK39oiYtt6erSVz8q1iOYl4tfCYN0wuHyrvpRdXJ7UBrAhLgBFIPn8Gw==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0
|
||||
@@ -14961,8 +14956,8 @@ packages:
|
||||
'@babel/core': 7.20.12
|
||||
'@babel/runtime': 7.20.7
|
||||
'@babel/types': 7.20.7
|
||||
gatsby: 5.5.0-next.0_biqbaboplfbrettd7655fr4n2y
|
||||
gatsby-core-utils: 4.5.0-next.0
|
||||
gatsby: 5.6.0-next.0_biqbaboplfbrettd7655fr4n2y
|
||||
gatsby-core-utils: 4.6.0-next.0
|
||||
dev: false
|
||||
|
||||
/babel-plugin-styled-components/2.0.7_styled-components@5.3.6:
|
||||
@@ -15046,8 +15041,8 @@ packages:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/babel-preset-gatsby/3.5.0-next.0_pp2vm42zn6vfmnpuhar3irht7i:
|
||||
resolution: {integrity: sha512-ljJsjFAfZjfKoCwO0bw3vxC3PmbE4XTZ91kIlKv8p+au5bOtDu2cFxltLte8khR6aAg5vEwUwvr4D4PUP4Wgsg==}
|
||||
/babel-preset-gatsby/3.6.0-next.0_pp2vm42zn6vfmnpuhar3irht7i:
|
||||
resolution: {integrity: sha512-Yg3s+TqJcaiolWzERdtCc/nQMnQDGqv6VvB4CEsRFAzZy9YoDLN6BauTpJ/FWeJc595TdszCYLqYLoa0cXcfVw==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.11.6
|
||||
@@ -15068,8 +15063,8 @@ packages:
|
||||
babel-plugin-macros: 3.1.0
|
||||
babel-plugin-transform-react-remove-prop-types: 0.4.24
|
||||
core-js: 3.26.0
|
||||
gatsby-core-utils: 4.5.0-next.0
|
||||
gatsby-legacy-polyfills: 3.5.0-next.0
|
||||
gatsby-core-utils: 4.6.0-next.0
|
||||
gatsby-legacy-polyfills: 3.6.0-next.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
@@ -15304,6 +15299,7 @@ packages:
|
||||
term-size: 2.2.1
|
||||
type-fest: 0.8.1
|
||||
widest-line: 3.1.0
|
||||
dev: true
|
||||
|
||||
/boxen/5.1.2:
|
||||
resolution: {integrity: sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==}
|
||||
@@ -15733,6 +15729,7 @@ packages:
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
supports-color: 7.2.0
|
||||
dev: true
|
||||
|
||||
/chalk/4.1.1:
|
||||
resolution: {integrity: sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==}
|
||||
@@ -16500,8 +16497,8 @@ packages:
|
||||
readable-stream: 3.6.0
|
||||
dev: true
|
||||
|
||||
/create-gatsby/3.5.0-next.0:
|
||||
resolution: {integrity: sha512-KmDOCTD0AO/kY2ctCP5j+5Cxc8TGuMs32vvyaKEbLAhkctuvQoRKUQ9NR4+uWSJoWuO5zEm008e3HKI+TpM60A==}
|
||||
/create-gatsby/3.6.0-next.0:
|
||||
resolution: {integrity: sha512-eeTIlCWsSWhQkfbcRstHO8n7I6kkf/tWkyjHt5nEGoIn2HW1457Qt4QMLhMyowhNBw/nXHiU+QuUvg+1oY8z9g==}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.20.7
|
||||
@@ -20371,19 +20368,6 @@ packages:
|
||||
peerDependenciesMeta:
|
||||
debug:
|
||||
optional: true
|
||||
dev: true
|
||||
|
||||
/follow-redirects/1.15.1_debug@3.2.7:
|
||||
resolution: {integrity: sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==}
|
||||
engines: {node: '>=4.0'}
|
||||
peerDependencies:
|
||||
debug: '*'
|
||||
peerDependenciesMeta:
|
||||
debug:
|
||||
optional: true
|
||||
dependencies:
|
||||
debug: 3.2.7
|
||||
dev: false
|
||||
|
||||
/follow-redirects/1.15.1_debug@4.3.4:
|
||||
resolution: {integrity: sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==}
|
||||
@@ -20544,6 +20528,16 @@ packages:
|
||||
graceful-fs: 4.2.10
|
||||
jsonfile: 6.1.0
|
||||
universalify: 2.0.0
|
||||
dev: true
|
||||
|
||||
/fs-extra/11.1.0:
|
||||
resolution: {integrity: sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==}
|
||||
engines: {node: '>=14.14'}
|
||||
dependencies:
|
||||
graceful-fs: 4.2.10
|
||||
jsonfile: 6.1.0
|
||||
universalify: 2.0.0
|
||||
dev: false
|
||||
|
||||
/fs-extra/7.0.1:
|
||||
resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==}
|
||||
@@ -20642,8 +20636,8 @@ packages:
|
||||
/functions-have-names/1.2.3:
|
||||
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
|
||||
|
||||
/gatsby-cli/5.5.0-next.0:
|
||||
resolution: {integrity: sha512-zDxea5Umb9LKU2kXSac84J/5QL4TvvGxyBkqUtvRq9rZRGYV+MTXgQTIzhlJQens+5kBepahERMLcifB9pJAwg==}
|
||||
/gatsby-cli/5.6.0-next.0:
|
||||
resolution: {integrity: sha512-B38HOsNXQ1r8EAap/J0E6CRv2QYxuf8J+Uc+aASbbDgEmfZw8A95ayT6JM8rqYaf9mambUmzot/QF/hKQBY1uA==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
@@ -20664,13 +20658,13 @@ packages:
|
||||
clipboardy: 2.3.0
|
||||
common-tags: 1.8.2
|
||||
convert-hrtime: 3.0.0
|
||||
create-gatsby: 3.5.0-next.0
|
||||
create-gatsby: 3.6.0-next.0
|
||||
envinfo: 7.8.1
|
||||
execa: 5.1.1
|
||||
fs-exists-cached: 1.0.0
|
||||
fs-extra: 10.1.0
|
||||
gatsby-core-utils: 4.5.0-next.0
|
||||
gatsby-telemetry: 4.5.0-next.0
|
||||
fs-extra: 11.1.0
|
||||
gatsby-core-utils: 4.6.0-next.0
|
||||
gatsby-telemetry: 4.6.0-next.0
|
||||
hosted-git-info: 3.0.8
|
||||
is-valid-path: 0.1.1
|
||||
joi: 17.7.0
|
||||
@@ -20694,8 +20688,8 @@ packages:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/gatsby-core-utils/4.5.0-next.0:
|
||||
resolution: {integrity: sha512-5rnnAppOADM+aRkglo0ImObnOsENc80NOR0RT1v5Qr/BFhs1KFIDQc4lSW0dvDDCeBDi8Ngj52t1GMvCTuUaTg==}
|
||||
/gatsby-core-utils/4.6.0-next.0:
|
||||
resolution: {integrity: sha512-4cc4d7WfJmYWprWBIxQBRiwyT2uECR9/40rqPCe62yeshTHIZUVCxeqzEeU6R0qkDpLjHkP0F/itv2oNa0AspQ==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.20.7
|
||||
@@ -20703,8 +20697,9 @@ packages:
|
||||
configstore: 5.0.1
|
||||
fastq: 1.15.0
|
||||
file-type: 16.5.4
|
||||
fs-extra: 10.1.0
|
||||
fs-extra: 11.1.0
|
||||
got: 11.8.6
|
||||
hash-wasm: 4.9.0
|
||||
import-from: 4.0.0
|
||||
lmdb: 2.5.3
|
||||
lock: 1.1.0
|
||||
@@ -20715,20 +20710,20 @@ packages:
|
||||
xdg-basedir: 4.0.0
|
||||
dev: false
|
||||
|
||||
/gatsby-graphiql-explorer/3.5.0-next.0:
|
||||
resolution: {integrity: sha512-CY0paEPQwJWJyXi9U8I79ubL3FKWc7Z8QeR6B6QMacodmWdn8KHl7j+qMYG+ErHmM3ruAhxMI5HJyCDOZaHFwA==}
|
||||
/gatsby-graphiql-explorer/3.6.0-next.0:
|
||||
resolution: {integrity: sha512-pXW8ufxLdklCjVtKyUZ6Nr6ROjH/5dUqbnp6hMdUGLWFjvpj3AWc/DH3wsDOLw//bBEZqHAcEyj2DwNcFv8mHw==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
dev: false
|
||||
|
||||
/gatsby-legacy-polyfills/3.5.0-next.0:
|
||||
resolution: {integrity: sha512-UW+/a4IGFcv7fCEK5BF4K7cwIBE3FEtbuVDSJ8a89zWSHWFLs82cSfc3+bDXPqv3m5/E2rWs0KLCWwEerzHNhQ==}
|
||||
/gatsby-legacy-polyfills/3.6.0-next.0:
|
||||
resolution: {integrity: sha512-XwGPOPDF/Xo/wZWW52YmFlZ/89Qb/jZdxBaPHfs2zRKvTAFmFkHBl+KAy5MBISY4IFWTfOyBN2UjnklmxRn93w==}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.20.7
|
||||
core-js-compat: 3.9.0
|
||||
dev: false
|
||||
|
||||
/gatsby-link/5.5.0-next.0_y2kppt6lrltqk6wasg3eswwzsa:
|
||||
resolution: {integrity: sha512-+TydIhkhkIs/8DnVK7Zv6gai6VSmeloGOPlme2aN/Y1a0DaYLlfH9pkF0to7DVYCC6Fn99n3VlHmUrWQxnA1bw==}
|
||||
/gatsby-link/5.6.0-next.0_y2kppt6lrltqk6wasg3eswwzsa:
|
||||
resolution: {integrity: sha512-tKTT58UQI3PQdWTmALZ9UYZ7hN4lgxrkLNhHRlfhhjsZXAwqqVXfR51gfV8cvEltEOdqdfrUSMZ9167KxEnp5A==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
peerDependencies:
|
||||
'@gatsbyjs/reach-router': ^2.0.0
|
||||
@@ -20737,33 +20732,33 @@ packages:
|
||||
dependencies:
|
||||
'@gatsbyjs/reach-router': 2.0.0_biqbaboplfbrettd7655fr4n2y
|
||||
'@types/reach__router': 1.3.11
|
||||
gatsby-page-utils: 3.5.0-next.0
|
||||
gatsby-page-utils: 3.6.0-next.0
|
||||
prop-types: 15.8.1
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
dev: false
|
||||
|
||||
/gatsby-page-utils/3.5.0-next.0:
|
||||
resolution: {integrity: sha512-MHD6azIuWYxwSxQ7pgow5K53OdIVbsKUKRKP8/0WUIMG8xZkOBZtRCgGL0QrPB7ze73hujxhMDGd8firz8bNYw==}
|
||||
/gatsby-page-utils/3.6.0-next.0:
|
||||
resolution: {integrity: sha512-3jul3U94W/33H8DMoaEfo6z26Tlp62Lf9FtS828s1A076ao9CFBpT2xrCCAn+rF8uXKAY1oB3b337Oaxdyx70g==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.20.7
|
||||
bluebird: 3.7.2
|
||||
chokidar: 3.5.3
|
||||
fs-exists-cached: 1.0.0
|
||||
gatsby-core-utils: 4.5.0-next.0
|
||||
gatsby-core-utils: 4.6.0-next.0
|
||||
glob: 7.2.3
|
||||
lodash: 4.17.21
|
||||
micromatch: 4.0.5
|
||||
dev: false
|
||||
|
||||
/gatsby-parcel-config/1.5.0-next.0_@parcel+core@2.8.2:
|
||||
resolution: {integrity: sha512-k+MqR2JQX1/siMttbyIX1zOfv3ZI390H2Pk2SjdUFJxV4dYZzcLermRkAOSNHUQIrrQ7TaLDMtq9AOcIjMmNmA==}
|
||||
/gatsby-parcel-config/1.6.0-next.0_@parcel+core@2.8.2:
|
||||
resolution: {integrity: sha512-zqNIyHiaegO0hX8LJ6qmPAsZC0G3QmjjDvcmKVp4xvU8c21L7LUDJP++9Ek1iKm45gfuCweXeeKX01W5EWzDFA==}
|
||||
engines: {parcel: 2.x}
|
||||
peerDependencies:
|
||||
'@parcel/core': ^2.0.0
|
||||
dependencies:
|
||||
'@gatsbyjs/parcel-namer-relative-to-cwd': 2.5.0-next.0_@parcel+core@2.8.2
|
||||
'@gatsbyjs/parcel-namer-relative-to-cwd': 2.6.0-next.0_@parcel+core@2.8.2
|
||||
'@parcel/bundler-default': 2.8.2_@parcel+core@2.8.2
|
||||
'@parcel/compressor-raw': 2.8.2_@parcel+core@2.8.2
|
||||
'@parcel/core': 2.8.2
|
||||
@@ -20778,8 +20773,8 @@ packages:
|
||||
'@parcel/transformer-json': 2.8.2_@parcel+core@2.8.2
|
||||
dev: false
|
||||
|
||||
/gatsby-plugin-page-creator/5.5.0-next.0_tiqeetwuawru2xunllrkp6n53m:
|
||||
resolution: {integrity: sha512-OY0BOHl93aKqHCVwCFrABlGJFCW5T7tp5XqY5MPBmXRgCqWKVuZuZqgpHXoDTwjJSXMcn3sVzC5RhH7OgKIAiQ==}
|
||||
/gatsby-plugin-page-creator/5.6.0-next.0_4kofk2l43xwpw753oec5pxbv5e:
|
||||
resolution: {integrity: sha512-zNfm0f5wpVhlwDnvGwkfQtIcIHJeRJBeYQCkUYUPwoA1s4AJFY6+wbYSRYHcM8r2K5aDfE1/t32YEY43UNIzGA==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
peerDependencies:
|
||||
gatsby: ^5.0.0-next
|
||||
@@ -20789,12 +20784,12 @@ packages:
|
||||
'@sindresorhus/slugify': 1.1.2
|
||||
chokidar: 3.5.3
|
||||
fs-exists-cached: 1.0.0
|
||||
fs-extra: 10.1.0
|
||||
gatsby: 5.5.0-next.0_biqbaboplfbrettd7655fr4n2y
|
||||
gatsby-core-utils: 4.5.0-next.0
|
||||
gatsby-page-utils: 3.5.0-next.0
|
||||
gatsby-plugin-utils: 4.5.0-next.0_tiqeetwuawru2xunllrkp6n53m
|
||||
gatsby-telemetry: 4.5.0-next.0
|
||||
fs-extra: 11.1.0
|
||||
gatsby: 5.6.0-next.0_biqbaboplfbrettd7655fr4n2y
|
||||
gatsby-core-utils: 4.6.0-next.0
|
||||
gatsby-page-utils: 3.6.0-next.0
|
||||
gatsby-plugin-utils: 4.6.0-next.0_4kofk2l43xwpw753oec5pxbv5e
|
||||
gatsby-telemetry: 4.6.0-next.0
|
||||
globby: 11.1.0
|
||||
lodash: 4.17.21
|
||||
transitivePeerDependencies:
|
||||
@@ -20803,8 +20798,8 @@ packages:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/gatsby-plugin-typescript/5.5.0-next.0_gatsby@5.5.0-next.0:
|
||||
resolution: {integrity: sha512-Snj6bZC1hPf+tmhbSJ2UBUO6f2vd6AGvEF06M3Tk22jSpZZ3ia0wPCQzAWal03hV0QO2CTRaY/1ZVns7WSlDzg==}
|
||||
/gatsby-plugin-typescript/5.6.0-next.0_gatsby@5.6.0-next.0:
|
||||
resolution: {integrity: sha512-CUguJx8GjTQHymcfBOcwUXZtetfhLYtgUehkM8ovvNlscgavGxmbGD90gXMmo5JgrLPhdx8krtMsdIeCOOvo4w==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
peerDependencies:
|
||||
gatsby: ^5.0.0-next
|
||||
@@ -20815,14 +20810,14 @@ packages:
|
||||
'@babel/plugin-proposal-optional-chaining': 7.20.7_@babel+core@7.20.12
|
||||
'@babel/preset-typescript': 7.18.6_@babel+core@7.20.12
|
||||
'@babel/runtime': 7.20.7
|
||||
babel-plugin-remove-graphql-queries: 5.5.0-next.0_jqsf6f3mvfsscspe5xblmymkvm
|
||||
gatsby: 5.5.0-next.0_biqbaboplfbrettd7655fr4n2y
|
||||
babel-plugin-remove-graphql-queries: 5.6.0-next.0_34zmit57noivsgqvgdpgpcttx4
|
||||
gatsby: 5.6.0-next.0_biqbaboplfbrettd7655fr4n2y
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/gatsby-plugin-utils/4.5.0-next.0_tiqeetwuawru2xunllrkp6n53m:
|
||||
resolution: {integrity: sha512-PomEO3MOAEGAxhpiAVqvCd6FH4VNk5ce3J/2YDAY3mZcNl7443TzyBoLyebki27BY+cAhddYXx5dxsa23UplKw==}
|
||||
/gatsby-plugin-utils/4.6.0-next.0_4kofk2l43xwpw753oec5pxbv5e:
|
||||
resolution: {integrity: sha512-3xidC+kpOz9xtsG5yv4UGB87pA0bo3YEHBTtUf5kshNEycMeTeh6xgb6gnD56kCVQ4DFrUBAe5U95hWH8n65Aw==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
peerDependencies:
|
||||
gatsby: ^5.0.0-next
|
||||
@@ -20830,10 +20825,10 @@ packages:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.20.7
|
||||
fastq: 1.15.0
|
||||
fs-extra: 10.1.0
|
||||
gatsby: 5.5.0-next.0_biqbaboplfbrettd7655fr4n2y
|
||||
gatsby-core-utils: 4.5.0-next.0
|
||||
gatsby-sharp: 1.5.0-next.0
|
||||
fs-extra: 11.1.0
|
||||
gatsby: 5.6.0-next.0_biqbaboplfbrettd7655fr4n2y
|
||||
gatsby-core-utils: 4.6.0-next.0
|
||||
gatsby-sharp: 1.6.0-next.0
|
||||
graphql: 16.6.0
|
||||
graphql-compose: 9.0.10_graphql@16.6.0
|
||||
import-from: 4.0.0
|
||||
@@ -20841,8 +20836,8 @@ packages:
|
||||
mime: 3.0.0
|
||||
dev: false
|
||||
|
||||
/gatsby-react-router-scroll/6.5.0-next.0_y2kppt6lrltqk6wasg3eswwzsa:
|
||||
resolution: {integrity: sha512-x8prKBH32JDvDVjo04j0XHZ1kXKcshqkTCUDQu/5yQ/l3dy485Ut/kXlTylbp591fw8uuEqjgvF5ElsBrmMlWg==}
|
||||
/gatsby-react-router-scroll/6.6.0-next.0_y2kppt6lrltqk6wasg3eswwzsa:
|
||||
resolution: {integrity: sha512-zSVEibH40eR64HROVbQ2OTV4sSDVXCgAEIG9yykhGJv5oz2yEFwWcjmPYWEtaiu2qcjPnLSkjsPOw5tVqrFr7A==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
peerDependencies:
|
||||
'@gatsbyjs/reach-router': ^2.0.0
|
||||
@@ -20856,8 +20851,8 @@ packages:
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
dev: false
|
||||
|
||||
/gatsby-script/2.5.0-next.0_y2kppt6lrltqk6wasg3eswwzsa:
|
||||
resolution: {integrity: sha512-eYFPQQ6QwjZxw8H4ojuJIFM62cFDos/CZFHxKAxyEvt9Qv1m2bzWCIK4PxRswESa0jAQYpbx+i/nLFA3P3zYsQ==}
|
||||
/gatsby-script/2.6.0-next.0_y2kppt6lrltqk6wasg3eswwzsa:
|
||||
resolution: {integrity: sha512-kJQ+NqtS4ue3I7t0Asgy+bAdnwQ0Wzu6BK3z/jnj57lU6mBthU5XWj9RiCu3W4GjsIW/qqjN1e/rqvukN+sWMw==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
peerDependencies:
|
||||
'@gatsbyjs/reach-router': ^2.0.0
|
||||
@@ -20869,16 +20864,16 @@ packages:
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
dev: false
|
||||
|
||||
/gatsby-sharp/1.5.0-next.0:
|
||||
resolution: {integrity: sha512-6ETKQa9CgNqpuxfrAovKxJsC8kwqbw3mhMTDBgD6ceeQr6GgKx9xG6jmNBbA6IxEiFvKpwz5cAIuJr8P5ylznw==}
|
||||
/gatsby-sharp/1.6.0-next.0:
|
||||
resolution: {integrity: sha512-QaGkqU55JaZAbjgU1QyrX0rbyRUEFluMT7LykLlfGMCdox4DzE5ERtP5BVIHCzRXc0JAZtmFCDcFmpPSKxB28w==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
dependencies:
|
||||
'@types/sharp': 0.31.1
|
||||
sharp: 0.31.3
|
||||
dev: false
|
||||
|
||||
/gatsby-telemetry/4.5.0-next.0:
|
||||
resolution: {integrity: sha512-RcD7x7hnc9wOe9K0xvufQHLBjNW8xcQcf4e6DY7ffftBVIlYAudIlBe1n9BRtTUXOHPumHIGDhp97CyDv+m4HQ==}
|
||||
/gatsby-telemetry/4.6.0-next.0:
|
||||
resolution: {integrity: sha512-LQcqw21mO0s5sKPoi5MWYqcK7iVp5qej5fTpOYgDenabGDSIxYAoRa5CGQ6tRwGUjMu3NYykmCpPdsY0DHNHyw==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
@@ -20886,10 +20881,10 @@ packages:
|
||||
'@babel/runtime': 7.20.7
|
||||
'@turist/fetch': 7.2.0_node-fetch@2.6.7
|
||||
'@turist/time': 0.0.2
|
||||
boxen: 4.2.0
|
||||
boxen: 5.1.2
|
||||
configstore: 5.0.1
|
||||
fs-extra: 10.1.0
|
||||
gatsby-core-utils: 4.5.0-next.0
|
||||
fs-extra: 11.1.0
|
||||
gatsby-core-utils: 4.6.0-next.0
|
||||
git-up: 7.0.0
|
||||
is-docker: 2.2.1
|
||||
lodash: 4.17.21
|
||||
@@ -20898,20 +20893,20 @@ packages:
|
||||
- encoding
|
||||
dev: false
|
||||
|
||||
/gatsby-worker/2.5.0-next.0:
|
||||
resolution: {integrity: sha512-u/IdMyXh1vxWNG19oq4KCD9p9tcYzQ3tFcG98Jai6phyzjIOvl4RIhV8jByTJ5YvupIWlOtfsrd4ra+cuGLAoA==}
|
||||
/gatsby-worker/2.6.0-next.0:
|
||||
resolution: {integrity: sha512-C2bBppw4MqtEo1pEUma2UE06txv22q+ix04G3idMxjqFLtWq+H3+fQmuDZB4laI601ClzyAHK99dSNKQrfGqeA==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
dependencies:
|
||||
'@babel/core': 7.20.12
|
||||
'@babel/runtime': 7.20.7
|
||||
fs-extra: 10.1.0
|
||||
fs-extra: 11.1.0
|
||||
signal-exit: 3.0.7
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/gatsby/5.5.0-next.0_biqbaboplfbrettd7655fr4n2y:
|
||||
resolution: {integrity: sha512-5wR5rmmQ96PbMF3J4vNCwN7eIHcbrp3ReNVPzL6Pt673Pzlt3ga/Jaf6qUmeUORXaHpp5faDfWvcrfF9a92zQQ==}
|
||||
/gatsby/5.6.0-next.0_biqbaboplfbrettd7655fr4n2y:
|
||||
resolution: {integrity: sha512-QwM9R3JtRo7Qk1A0v5Hgpo45SXtRYh7OzZntOaj8VsDk7HebITZ5eLrdTq/wPYNYn3Jlng2bG+bObcIEjjqcyw==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
@@ -20927,7 +20922,7 @@ packages:
|
||||
'@babel/runtime': 7.20.7
|
||||
'@babel/traverse': 7.20.12
|
||||
'@babel/types': 7.20.7
|
||||
'@builder.io/partytown': 0.5.4
|
||||
'@builder.io/partytown': 0.7.5
|
||||
'@gatsbyjs/reach-router': 2.0.0_biqbaboplfbrettd7655fr4n2y
|
||||
'@gatsbyjs/webpack-hot-middleware': 2.25.3
|
||||
'@graphql-codegen/add': 3.2.3_graphql@16.6.0
|
||||
@@ -20951,14 +20946,14 @@ packages:
|
||||
address: 1.2.2
|
||||
anser: 2.1.1
|
||||
autoprefixer: 10.4.13_postcss@8.4.20
|
||||
axios: 0.21.4_debug@3.2.7
|
||||
axios: 0.21.4_debug@4.3.4
|
||||
babel-jsx-utils: 1.1.0
|
||||
babel-loader: 8.3.0_la66t7xldg4uecmyawueag5wkm
|
||||
babel-plugin-add-module-exports: 1.0.4
|
||||
babel-plugin-dynamic-import-node: 2.3.3
|
||||
babel-plugin-lodash: 3.3.4
|
||||
babel-plugin-remove-graphql-queries: 5.5.0-next.0_jqsf6f3mvfsscspe5xblmymkvm
|
||||
babel-preset-gatsby: 3.5.0-next.0_pp2vm42zn6vfmnpuhar3irht7i
|
||||
babel-plugin-remove-graphql-queries: 5.6.0-next.0_34zmit57noivsgqvgdpgpcttx4
|
||||
babel-preset-gatsby: 3.6.0-next.0_pp2vm42zn6vfmnpuhar3irht7i
|
||||
better-opn: 2.1.1
|
||||
bluebird: 3.7.2
|
||||
browserslist: 4.21.4
|
||||
@@ -20967,14 +20962,14 @@ packages:
|
||||
chokidar: 3.5.3
|
||||
common-tags: 1.8.2
|
||||
compression: 1.7.4
|
||||
cookie: 0.4.2
|
||||
cookie: 0.5.0
|
||||
core-js: 3.26.0
|
||||
cors: 2.8.5
|
||||
css-loader: 5.2.7_webpack@5.75.0
|
||||
css-minimizer-webpack-plugin: 2.0.0_webpack@5.75.0
|
||||
css.escape: 1.5.1
|
||||
date-fns: 2.29.3
|
||||
debug: 3.2.7
|
||||
debug: 4.3.4
|
||||
deepmerge: 4.2.2
|
||||
detect-port: 1.5.1
|
||||
devcert: 1.2.2
|
||||
@@ -20998,21 +20993,21 @@ packages:
|
||||
file-loader: 6.2.0_webpack@5.75.0
|
||||
find-cache-dir: 3.3.2
|
||||
fs-exists-cached: 1.0.0
|
||||
fs-extra: 10.1.0
|
||||
gatsby-cli: 5.5.0-next.0
|
||||
gatsby-core-utils: 4.5.0-next.0
|
||||
gatsby-graphiql-explorer: 3.5.0-next.0
|
||||
gatsby-legacy-polyfills: 3.5.0-next.0
|
||||
gatsby-link: 5.5.0-next.0_y2kppt6lrltqk6wasg3eswwzsa
|
||||
gatsby-page-utils: 3.5.0-next.0
|
||||
gatsby-parcel-config: 1.5.0-next.0_@parcel+core@2.8.2
|
||||
gatsby-plugin-page-creator: 5.5.0-next.0_tiqeetwuawru2xunllrkp6n53m
|
||||
gatsby-plugin-typescript: 5.5.0-next.0_gatsby@5.5.0-next.0
|
||||
gatsby-plugin-utils: 4.5.0-next.0_tiqeetwuawru2xunllrkp6n53m
|
||||
gatsby-react-router-scroll: 6.5.0-next.0_y2kppt6lrltqk6wasg3eswwzsa
|
||||
gatsby-script: 2.5.0-next.0_y2kppt6lrltqk6wasg3eswwzsa
|
||||
gatsby-telemetry: 4.5.0-next.0
|
||||
gatsby-worker: 2.5.0-next.0
|
||||
fs-extra: 11.1.0
|
||||
gatsby-cli: 5.6.0-next.0
|
||||
gatsby-core-utils: 4.6.0-next.0
|
||||
gatsby-graphiql-explorer: 3.6.0-next.0
|
||||
gatsby-legacy-polyfills: 3.6.0-next.0
|
||||
gatsby-link: 5.6.0-next.0_y2kppt6lrltqk6wasg3eswwzsa
|
||||
gatsby-page-utils: 3.6.0-next.0
|
||||
gatsby-parcel-config: 1.6.0-next.0_@parcel+core@2.8.2
|
||||
gatsby-plugin-page-creator: 5.6.0-next.0_4kofk2l43xwpw753oec5pxbv5e
|
||||
gatsby-plugin-typescript: 5.6.0-next.0_gatsby@5.6.0-next.0
|
||||
gatsby-plugin-utils: 4.6.0-next.0_4kofk2l43xwpw753oec5pxbv5e
|
||||
gatsby-react-router-scroll: 6.6.0-next.0_y2kppt6lrltqk6wasg3eswwzsa
|
||||
gatsby-script: 2.6.0-next.0_y2kppt6lrltqk6wasg3eswwzsa
|
||||
gatsby-telemetry: 4.6.0-next.0
|
||||
gatsby-worker: 2.6.0-next.0
|
||||
glob: 7.2.3
|
||||
globby: 11.1.0
|
||||
got: 11.8.6
|
||||
@@ -21029,11 +21024,10 @@ packages:
|
||||
latest-version: 7.0.0
|
||||
lmdb: 2.5.3
|
||||
lodash: 4.17.21
|
||||
md5-file: 5.0.0
|
||||
meant: 1.0.3
|
||||
memoizee: 0.4.15
|
||||
micromatch: 4.0.5
|
||||
mime: 2.6.0
|
||||
mime: 3.0.0
|
||||
mini-css-extract-plugin: 1.6.2_webpack@5.75.0
|
||||
mitt: 1.2.0
|
||||
moment: 2.29.4
|
||||
@@ -21083,11 +21077,11 @@ packages:
|
||||
webpack-dev-middleware: 4.3.0_webpack@5.75.0
|
||||
webpack-merge: 5.8.0
|
||||
webpack-stats-plugin: 1.1.1
|
||||
webpack-virtual-modules: 0.3.2
|
||||
webpack-virtual-modules: 0.5.0
|
||||
xstate: 4.35.2
|
||||
yaml-loader: 0.8.0
|
||||
optionalDependencies:
|
||||
gatsby-sharp: 1.5.0-next.0
|
||||
gatsby-sharp: 1.6.0-next.0
|
||||
transitivePeerDependencies:
|
||||
- '@swc/core'
|
||||
- '@types/webpack'
|
||||
@@ -21854,6 +21848,10 @@ packages:
|
||||
resolution: {integrity: sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==}
|
||||
dev: true
|
||||
|
||||
/hash-wasm/4.9.0:
|
||||
resolution: {integrity: sha512-7SW7ejyfnRxuOc7ptQHSf4LDoZaWOivfzqw+5rpcQku0nHfmicPKE51ra9BiRLAmT8+gGLestr1XroUkqdjL6w==}
|
||||
dev: false
|
||||
|
||||
/hasha/5.2.2:
|
||||
resolution: {integrity: sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -25827,12 +25825,6 @@ packages:
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/md5-file/5.0.0:
|
||||
resolution: {integrity: sha512-xbEFXCYVWrSx/gEKS1VPlg84h/4L20znVIulKw6kMfmBUAZNAnF00eczz9ICMl+/hjQGo5KSXRxbL/47X3rmMw==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/mdast-squeeze-paragraphs/4.0.0:
|
||||
resolution: {integrity: sha512-zxdPn69hkQ1rm4J+2Cs2j6wDEv7O17TfXTJ33tl/+JPIoEmtV9t2ZzBM5LPHE8QlHsmVD8t3vPKCyY3oH+H8MQ==}
|
||||
dependencies:
|
||||
@@ -27799,8 +27791,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
|
||||
@@ -32139,6 +32131,7 @@ packages:
|
||||
/term-size/2.2.1:
|
||||
resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/terminal-link/2.1.1:
|
||||
resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==}
|
||||
@@ -34252,17 +34245,8 @@ packages:
|
||||
resolution: {integrity: sha512-aWwE/YuO2W7VCOyWwyDJ7BRSYRYjeXat+X31YiasMM3FS6/4X9W4Mb9Q0g+jIdVgArr1Mb08sHBJKMT5M9+gVA==}
|
||||
dev: false
|
||||
|
||||
/webpack-virtual-modules/0.3.2:
|
||||
resolution: {integrity: sha512-RXQXioY6MhzM4CNQwmBwKXYgBs6ulaiQ8bkNQEl2J6Z+V+s7lgl/wGvaI/I0dLnYKB8cKsxQc17QOAVIphPLDw==}
|
||||
dependencies:
|
||||
debug: 3.2.7
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/webpack-virtual-modules/0.5.0:
|
||||
resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==}
|
||||
dev: true
|
||||
|
||||
/webpack/5.73.0:
|
||||
resolution: {integrity: sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA==}
|
||||
|
||||
@@ -34,6 +34,9 @@
|
||||
"test": {
|
||||
"outputs": []
|
||||
},
|
||||
"e2e": {
|
||||
"outputs": ["playwright-report/**"]
|
||||
},
|
||||
"@next-auth/upstash-redis-adapter#test": {
|
||||
"env": ["UPSTASH_REDIS_KEY", "UPSTASH_REDIS_URL"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user