mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
Compare commits
36 Commits
fix/confor
...
fix/issue-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
190c5044be | ||
|
|
b204710b5b | ||
|
|
533320eb94 | ||
|
|
dfe6509472 | ||
|
|
1bde7cc8df | ||
|
|
cef05d5e2d | ||
|
|
c0dea283ba | ||
|
|
0204766e0f | ||
|
|
a336ba762c | ||
|
|
681d53c2f8 | ||
|
|
06e891c0ea | ||
|
|
b9a84350b5 | ||
|
|
44c38247da | ||
|
|
9b9af4d5e5 | ||
|
|
fd2179bdca | ||
|
|
7bb037bb9d | ||
|
|
52f70e9f4f | ||
|
|
505f69b519 | ||
|
|
b21709db40 | ||
|
|
aff7b37ef9 | ||
|
|
daa85be1ad | ||
|
|
c31718ca10 | ||
|
|
fbcfedf0e8 | ||
|
|
bd032335eb | ||
|
|
128e0f3a10 | ||
|
|
557fb9d741 | ||
|
|
b4d6ed5f5f | ||
|
|
035836da98 | ||
|
|
294039a497 | ||
|
|
b2450ef625 | ||
|
|
a81bb3e51e | ||
|
|
bb506f7eb9 | ||
|
|
87d9cc4244 | ||
|
|
d2e3b76031 | ||
|
|
c36834b3b0 | ||
|
|
8f7145801a |
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:
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -84,6 +84,7 @@ docs/providers.json
|
||||
packages/core/*.js
|
||||
packages/core/*.d.ts
|
||||
packages/core/*.d.ts.map
|
||||
packages/core/src/providers/oauth-types.ts
|
||||
packages/core/lib
|
||||
packages/core/providers
|
||||
packages/core/src/lib/pages/styles.ts
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
@@ -19,8 +19,8 @@
|
||||
"vite": "4.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@auth/core": "0.2.5",
|
||||
"@auth/sveltekit": "0.1.12"
|
||||
"@auth/core": "workspace:*",
|
||||
"@auth/sveltekit": "workspace:*"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -16,11 +16,25 @@ import { format, generateUpdateExpression } from "./utils"
|
||||
|
||||
export { format, generateUpdateExpression }
|
||||
|
||||
export interface DynamoDBAdapterOptions {
|
||||
tableName?: string,
|
||||
partitionKey?: string,
|
||||
sortKey?: string,
|
||||
indexName?: string,
|
||||
indexPartitionKey?: string,
|
||||
indexSortKey?: string
|
||||
}
|
||||
|
||||
export function DynamoDBAdapter(
|
||||
client: DynamoDBDocument,
|
||||
options?: { tableName: string }
|
||||
options?: DynamoDBAdapterOptions
|
||||
): Adapter {
|
||||
const TableName = options?.tableName ?? "next-auth"
|
||||
const pk = options?.partitionKey ?? 'pk'
|
||||
const sk = options?.sortKey ?? 'sk'
|
||||
const IndexName = options?.indexName ?? 'GSI1'
|
||||
const GSI1PK = options?.indexPartitionKey ?? 'GSI1PK'
|
||||
const GSI1SK = options?.indexSortKey ?? 'GSI1SK'
|
||||
|
||||
return {
|
||||
async createUser(data) {
|
||||
@@ -33,11 +47,11 @@ export function DynamoDBAdapter(
|
||||
TableName,
|
||||
Item: format.to({
|
||||
...user,
|
||||
pk: `USER#${user.id}`,
|
||||
sk: `USER#${user.id}`,
|
||||
[pk]: `USER#${user.id}`,
|
||||
[sk]: `USER#${user.id}`,
|
||||
type: "USER",
|
||||
GSI1PK: `USER#${user.email as string}`,
|
||||
GSI1SK: `USER#${user.email as string}`,
|
||||
[GSI1PK]: `USER#${user.email as string}`,
|
||||
[GSI1SK]: `USER#${user.email as string}`,
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -47,8 +61,8 @@ export function DynamoDBAdapter(
|
||||
const data = await client.get({
|
||||
TableName,
|
||||
Key: {
|
||||
pk: `USER#${userId}`,
|
||||
sk: `USER#${userId}`,
|
||||
[pk]: `USER#${userId}`,
|
||||
[sk]: `USER#${userId}`,
|
||||
},
|
||||
})
|
||||
return format.from<AdapterUser>(data.Item)
|
||||
@@ -56,11 +70,11 @@ export function DynamoDBAdapter(
|
||||
async getUserByEmail(email) {
|
||||
const data = await client.query({
|
||||
TableName,
|
||||
IndexName: "GSI1",
|
||||
IndexName,
|
||||
KeyConditionExpression: "#gsi1pk = :gsi1pk AND #gsi1sk = :gsi1sk",
|
||||
ExpressionAttributeNames: {
|
||||
"#gsi1pk": "GSI1PK",
|
||||
"#gsi1sk": "GSI1SK",
|
||||
"#gsi1pk": GSI1PK,
|
||||
"#gsi1sk": GSI1SK,
|
||||
},
|
||||
ExpressionAttributeValues: {
|
||||
":gsi1pk": `USER#${email}`,
|
||||
@@ -73,11 +87,11 @@ export function DynamoDBAdapter(
|
||||
async getUserByAccount({ provider, providerAccountId }) {
|
||||
const data = await client.query({
|
||||
TableName,
|
||||
IndexName: "GSI1",
|
||||
IndexName,
|
||||
KeyConditionExpression: "#gsi1pk = :gsi1pk AND #gsi1sk = :gsi1sk",
|
||||
ExpressionAttributeNames: {
|
||||
"#gsi1pk": "GSI1PK",
|
||||
"#gsi1sk": "GSI1SK",
|
||||
"#gsi1pk": GSI1PK,
|
||||
"#gsi1sk": GSI1SK,
|
||||
},
|
||||
ExpressionAttributeValues: {
|
||||
":gsi1pk": `ACCOUNT#${provider}`,
|
||||
@@ -90,8 +104,8 @@ export function DynamoDBAdapter(
|
||||
const res = await client.get({
|
||||
TableName,
|
||||
Key: {
|
||||
pk: `USER#${accounts.userId}`,
|
||||
sk: `USER#${accounts.userId}`,
|
||||
[pk]: `USER#${accounts.userId}`,
|
||||
[sk]: `USER#${accounts.userId}`,
|
||||
},
|
||||
})
|
||||
return format.from<AdapterUser>(res.Item)
|
||||
@@ -106,8 +120,8 @@ export function DynamoDBAdapter(
|
||||
TableName,
|
||||
Key: {
|
||||
// next-auth type is incorrect it should be Partial<AdapterUser> & {id: string} instead of just Partial<AdapterUser>
|
||||
pk: `USER#${user.id as string}`,
|
||||
sk: `USER#${user.id as string}`,
|
||||
[pk]: `USER#${user.id as string}`,
|
||||
[sk]: `USER#${user.id as string}`,
|
||||
},
|
||||
UpdateExpression,
|
||||
ExpressionAttributeNames,
|
||||
@@ -123,7 +137,7 @@ export function DynamoDBAdapter(
|
||||
const res = await client.query({
|
||||
TableName,
|
||||
KeyConditionExpression: "#pk = :pk",
|
||||
ExpressionAttributeNames: { "#pk": "pk" },
|
||||
ExpressionAttributeNames: { "#pk": pk },
|
||||
ExpressionAttributeValues: { ":pk": `USER#${userId}` },
|
||||
})
|
||||
if (!res.Items) return null
|
||||
@@ -134,8 +148,8 @@ export function DynamoDBAdapter(
|
||||
return {
|
||||
DeleteRequest: {
|
||||
Key: {
|
||||
sk: item.sk,
|
||||
pk: item.pk,
|
||||
[sk]: item.sk,
|
||||
[pk]: item.pk,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -152,10 +166,10 @@ export function DynamoDBAdapter(
|
||||
const item = {
|
||||
...data,
|
||||
id: randomBytes(16).toString("hex"),
|
||||
pk: `USER#${data.userId}`,
|
||||
sk: `ACCOUNT#${data.provider}#${data.providerAccountId}`,
|
||||
GSI1PK: `ACCOUNT#${data.provider}`,
|
||||
GSI1SK: `ACCOUNT#${data.providerAccountId}`,
|
||||
[pk]: `USER#${data.userId}`,
|
||||
[sk]: `ACCOUNT#${data.provider}#${data.providerAccountId}`,
|
||||
[GSI1PK]: `ACCOUNT#${data.provider}`,
|
||||
[GSI1SK]: `ACCOUNT#${data.providerAccountId}`,
|
||||
}
|
||||
await client.put({ TableName, Item: format.to(item) })
|
||||
return data
|
||||
@@ -163,11 +177,11 @@ export function DynamoDBAdapter(
|
||||
async unlinkAccount({ provider, providerAccountId }) {
|
||||
const data = await client.query({
|
||||
TableName,
|
||||
IndexName: "GSI1",
|
||||
IndexName,
|
||||
KeyConditionExpression: "#gsi1pk = :gsi1pk AND #gsi1sk = :gsi1sk",
|
||||
ExpressionAttributeNames: {
|
||||
"#gsi1pk": "GSI1PK",
|
||||
"#gsi1sk": "GSI1SK",
|
||||
"#gsi1pk": GSI1PK,
|
||||
"#gsi1sk": GSI1SK,
|
||||
},
|
||||
ExpressionAttributeValues: {
|
||||
":gsi1pk": `ACCOUNT#${provider}`,
|
||||
@@ -179,8 +193,8 @@ export function DynamoDBAdapter(
|
||||
await client.delete({
|
||||
TableName,
|
||||
Key: {
|
||||
pk: `USER#${account.userId}`,
|
||||
sk: `ACCOUNT#${provider}#${providerAccountId}`,
|
||||
[pk]: `USER#${account.userId}`,
|
||||
[sk]: `ACCOUNT#${provider}#${providerAccountId}`,
|
||||
},
|
||||
ReturnValues: "ALL_OLD",
|
||||
})
|
||||
@@ -189,11 +203,11 @@ export function DynamoDBAdapter(
|
||||
async getSessionAndUser(sessionToken) {
|
||||
const data = await client.query({
|
||||
TableName,
|
||||
IndexName: "GSI1",
|
||||
IndexName,
|
||||
KeyConditionExpression: "#gsi1pk = :gsi1pk AND #gsi1sk = :gsi1sk",
|
||||
ExpressionAttributeNames: {
|
||||
"#gsi1pk": "GSI1PK",
|
||||
"#gsi1sk": "GSI1SK",
|
||||
"#gsi1pk": GSI1PK,
|
||||
"#gsi1sk": GSI1SK,
|
||||
},
|
||||
ExpressionAttributeValues: {
|
||||
":gsi1pk": `SESSION#${sessionToken}`,
|
||||
@@ -205,8 +219,8 @@ export function DynamoDBAdapter(
|
||||
const res = await client.get({
|
||||
TableName,
|
||||
Key: {
|
||||
pk: `USER#${session.userId}`,
|
||||
sk: `USER#${session.userId}`,
|
||||
[pk]: `USER#${session.userId}`,
|
||||
[sk]: `USER#${session.userId}`,
|
||||
},
|
||||
})
|
||||
const user = format.from<AdapterUser>(res.Item)
|
||||
@@ -221,10 +235,10 @@ export function DynamoDBAdapter(
|
||||
await client.put({
|
||||
TableName,
|
||||
Item: format.to({
|
||||
pk: `USER#${data.userId}`,
|
||||
sk: `SESSION#${data.sessionToken}`,
|
||||
GSI1SK: `SESSION#${data.sessionToken}`,
|
||||
GSI1PK: `SESSION#${data.sessionToken}`,
|
||||
[pk]: `USER#${data.userId}`,
|
||||
[sk]: `SESSION#${data.sessionToken}`,
|
||||
[GSI1SK]: `SESSION#${data.sessionToken}`,
|
||||
[GSI1PK]: `SESSION#${data.sessionToken}`,
|
||||
type: "SESSION",
|
||||
...data,
|
||||
}),
|
||||
@@ -235,11 +249,11 @@ export function DynamoDBAdapter(
|
||||
const { sessionToken } = session
|
||||
const data = await client.query({
|
||||
TableName,
|
||||
IndexName: "GSI1",
|
||||
IndexName,
|
||||
KeyConditionExpression: "#gsi1pk = :gsi1pk AND #gsi1sk = :gsi1sk",
|
||||
ExpressionAttributeNames: {
|
||||
"#gsi1pk": "GSI1PK",
|
||||
"#gsi1sk": "GSI1SK",
|
||||
"#gsi1pk": GSI1PK,
|
||||
"#gsi1sk": GSI1SK,
|
||||
},
|
||||
ExpressionAttributeValues: {
|
||||
":gsi1pk": `SESSION#${sessionToken}`,
|
||||
@@ -266,11 +280,11 @@ export function DynamoDBAdapter(
|
||||
async deleteSession(sessionToken) {
|
||||
const data = await client.query({
|
||||
TableName,
|
||||
IndexName: "GSI1",
|
||||
IndexName,
|
||||
KeyConditionExpression: "#gsi1pk = :gsi1pk AND #gsi1sk = :gsi1sk",
|
||||
ExpressionAttributeNames: {
|
||||
"#gsi1pk": "GSI1PK",
|
||||
"#gsi1sk": "GSI1SK",
|
||||
"#gsi1pk": GSI1PK,
|
||||
"#gsi1sk": GSI1SK,
|
||||
},
|
||||
ExpressionAttributeValues: {
|
||||
":gsi1pk": `SESSION#${sessionToken}`,
|
||||
@@ -292,8 +306,8 @@ export function DynamoDBAdapter(
|
||||
await client.put({
|
||||
TableName,
|
||||
Item: format.to({
|
||||
pk: `VT#${data.identifier}`,
|
||||
sk: `VT#${data.token}`,
|
||||
[pk]: `VT#${data.identifier}`,
|
||||
[sk]: `VT#${data.token}`,
|
||||
type: "VT",
|
||||
...data,
|
||||
}),
|
||||
@@ -304,8 +318,8 @@ export function DynamoDBAdapter(
|
||||
const data = await client.delete({
|
||||
TableName,
|
||||
Key: {
|
||||
pk: `VT#${identifier}`,
|
||||
sk: `VT#${token}`,
|
||||
[pk]: `VT#${identifier}`,
|
||||
[sk]: `VT#${token}`,
|
||||
},
|
||||
ReturnValues: "ALL_OLD",
|
||||
})
|
||||
|
||||
11
packages/adapter-firebase/src/getFirebase.ts
Normal file
11
packages/adapter-firebase/src/getFirebase.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { initializeApp, getApps, FirebaseOptions } from "firebase/app"
|
||||
|
||||
export default function getFirebase(firebaseOptions: FirebaseOptions) {
|
||||
const apps = getApps()
|
||||
const app = apps.find((app) => app.name === firebaseOptions.projectId)
|
||||
if (app) {
|
||||
return app
|
||||
} else {
|
||||
return initializeApp(firebaseOptions)
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import type {
|
||||
} from "next-auth/adapters"
|
||||
|
||||
import { getConverter } from "./converter"
|
||||
import getFirebase from "./getFirebase"
|
||||
|
||||
export type IndexableObject = Record<string, unknown>
|
||||
|
||||
@@ -39,7 +40,7 @@ export function FirestoreAdapter({
|
||||
emulator,
|
||||
...firebaseOptions
|
||||
}: FirebaseOptions & FirestoreAdapterOptions): Adapter {
|
||||
const firebaseApp = initializeApp(firebaseOptions)
|
||||
const firebaseApp = getFirebase(firebaseOptions)
|
||||
const db = getFirestore(firebaseApp)
|
||||
|
||||
if (emulator) {
|
||||
|
||||
@@ -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",
|
||||
@@ -69,7 +69,7 @@
|
||||
"preact-render-to-string": "5.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"nodemailer": "6.8.0"
|
||||
"nodemailer": "^6.8.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"nodemailer": {
|
||||
@@ -77,10 +77,11 @@
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "pnpm css && tsc",
|
||||
"build": "pnpm css && pnpm providers && tsc",
|
||||
"clean": "rm -rf *.js *.d.ts* lib providers",
|
||||
"css": "node scripts/generate-css",
|
||||
"dev": "pnpm css && tsc -w"
|
||||
"dev": "pnpm css && pnpm providers && tsc -w",
|
||||
"providers": "node scripts/generate-providers"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next-auth/tsconfig": "workspace:*",
|
||||
|
||||
18
packages/core/scripts/generate-providers.js
Normal file
18
packages/core/scripts/generate-providers.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import { join } from "path"
|
||||
import { readdirSync, writeFileSync } from "fs"
|
||||
|
||||
const providersPath = join(process.cwd(), "src/providers")
|
||||
|
||||
const files = readdirSync(providersPath, "utf8")
|
||||
|
||||
const providers = files.map((file) => {
|
||||
const strippedProviderName = file.substring(0, file.indexOf("."))
|
||||
return `"${strippedProviderName}"`
|
||||
}).filter((provider) => provider !== '"oauth-types"' && provider !== '"index"')
|
||||
|
||||
const result = `
|
||||
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
|
||||
export type OAuthProviderType =
|
||||
| ${providers.join("\n | ")}`
|
||||
|
||||
writeFileSync(join(providersPath, "oauth-types.ts"), result)
|
||||
@@ -27,14 +27,14 @@
|
||||
* ## Resources
|
||||
*
|
||||
* - [Getting started](https://authjs.dev/getting-started/introduction)
|
||||
* - [Most common use case guides](https://authjs.dev/guides/overview)
|
||||
* - [Most common use case guides](https://authjs.dev/guides)
|
||||
*
|
||||
* @module main
|
||||
*/
|
||||
|
||||
import { assertConfig } from "./lib/assert.js"
|
||||
import { ErrorPageLoop } from "./errors.js"
|
||||
import { AuthInternal } from "./lib/index.js"
|
||||
import { AuthInternal, skipCSRFCheck } from "./lib/index.js"
|
||||
import renderPage from "./lib/pages/index.js"
|
||||
import { logger, setLogger, type LoggerInstance } from "./lib/utils/logger.js"
|
||||
import { toInternalRequest, toResponse } from "./lib/web.js"
|
||||
@@ -51,6 +51,8 @@ import type {
|
||||
import type { Provider } from "./providers/index.js"
|
||||
import { JWTOptions } from "./jwt.js"
|
||||
|
||||
export { skipCSRFCheck }
|
||||
|
||||
/**
|
||||
* Core functionality provided by Auth.js.
|
||||
*
|
||||
@@ -296,4 +298,5 @@ export interface AuthConfig {
|
||||
cookies?: Partial<CookiesOptions>
|
||||
/** @todo */
|
||||
trustHost?: boolean
|
||||
skipCSRFCheck?: typeof skipCSRFCheck
|
||||
}
|
||||
|
||||
@@ -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,14 @@
|
||||
import { SessionStore } from "./cookie.js"
|
||||
import { UnknownAction } from "../errors.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 +19,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 +30,7 @@ export async function AuthInternal<
|
||||
csrfToken: request.body?.csrfToken,
|
||||
cookies: request.cookies,
|
||||
isPost: method === "POST",
|
||||
csrfDisabled,
|
||||
})
|
||||
|
||||
const sessionStore = new SessionStore(
|
||||
@@ -48,19 +51,29 @@ 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}${
|
||||
pages.signIn.includes("?") ? "&" : "?"
|
||||
}callbackUrl=${encodeURIComponent(options.callbackUrl)}`
|
||||
}${new URLSearchParams({ callbackUrl: options.callbackUrl })}`
|
||||
if (error)
|
||||
signinUrl = `${signinUrl}&error=${encodeURIComponent(error)}`
|
||||
signinUrl = `${signinUrl}&${new URLSearchParams({ error })}`
|
||||
return { redirect: signinUrl, cookies }
|
||||
}
|
||||
|
||||
@@ -125,8 +138,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 +150,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 +161,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 }
|
||||
@@ -173,3 +185,14 @@ export async function AuthInternal<
|
||||
}
|
||||
throw new UnknownAction(`Cannot handle action: ${action}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* :::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")
|
||||
|
||||
@@ -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
|
||||
@@ -90,54 +90,5 @@ 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)
|
||||
return { redirect: url.toString(), cookies }
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
@@ -196,7 +203,7 @@ async function getProfile(
|
||||
// If we didn't get a response either there was a problem with the provider
|
||||
// response *or* the user cancelled the action with the provider.
|
||||
//
|
||||
// Unfortuately, we can't tell which - at least not in a way that works for
|
||||
// Unfortunately, we can't tell which - at least not in a way that works for
|
||||
// all providers, so we return an empty object; the user should then be
|
||||
// redirected back to the sign up page. We log the error to help developers
|
||||
// who might be trying to debug this when configuring a new provider.
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ export async function callback(params: {
|
||||
return {
|
||||
redirect: `${pages.newUser}${
|
||||
pages.newUser.includes("?") ? "&" : "?"
|
||||
}callbackUrl=${encodeURIComponent(callbackUrl)}`,
|
||||
}${new URLSearchParams({ callbackUrl })}`,
|
||||
cookies,
|
||||
}
|
||||
}
|
||||
@@ -256,7 +256,7 @@ export async function callback(params: {
|
||||
return {
|
||||
redirect: `${pages.newUser}${
|
||||
pages.newUser.includes("?") ? "&" : "?"
|
||||
}callbackUrl=${encodeURIComponent(callbackUrl)}`,
|
||||
}${new URLSearchParams({ callbackUrl })}`,
|
||||
cookies,
|
||||
}
|
||||
}
|
||||
@@ -350,6 +350,6 @@ export async function callback(params: {
|
||||
logger.error(error)
|
||||
url.searchParams.set("error", CallbackRouteError.name)
|
||||
url.pathname += "/error"
|
||||
return { redirect: url, cookies }
|
||||
return { redirect: url.toString(), cookies }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,13 +13,13 @@ export async function handleAuthorized(
|
||||
if (!authorized) {
|
||||
logger.debug("User not authorized", params)
|
||||
url.searchParams.set("error", "AccessDenied")
|
||||
return { status: 403 as const, redirect: url }
|
||||
return { status: 403 as const, redirect: url.toString() }
|
||||
}
|
||||
} catch (e) {
|
||||
const error = new AuthorizedCallbackError(e as Error)
|
||||
logger.error(error)
|
||||
url.searchParams.set("error", "Configuration")
|
||||
return { status: 500 as const, redirect: url }
|
||||
return { status: 500 as const, redirect: url.toString() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ export async function signin(
|
||||
logger.error(error)
|
||||
url.searchParams.set("error", error.name)
|
||||
url.pathname += "/error"
|
||||
return { redirect: url }
|
||||
return { redirect: url.toString() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { SessionStore } from "../cookie.js"
|
||||
* If the session strategy is database,
|
||||
* The session is also deleted from the database.
|
||||
* In any case, the session cookie is cleared and
|
||||
* an `events.signOut` is emitted.
|
||||
* {@link EventCallbacks.signOut} is emitted.
|
||||
*/
|
||||
export async function signout(
|
||||
sessionStore: SessionStore,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -76,27 +76,22 @@ export function toResponse(res: ResponseInternal): Response {
|
||||
res.cookies?.forEach((cookie) => {
|
||||
const { name, value, options } = cookie
|
||||
const cookieHeader = serialize(name, value, options)
|
||||
if (headers.has("Set-Cookie")) {
|
||||
headers.append("Set-Cookie", cookieHeader)
|
||||
} else {
|
||||
headers.set("Set-Cookie", cookieHeader)
|
||||
}
|
||||
if (headers.has("Set-Cookie")) headers.append("Set-Cookie", cookieHeader)
|
||||
else headers.set("Set-Cookie", cookieHeader)
|
||||
// headers.set("Set-Cookie", cookieHeader) // TODO: Remove. Seems to be a bug with Headers in the runtime
|
||||
})
|
||||
|
||||
const body =
|
||||
headers.get("content-type") === "application/json"
|
||||
? JSON.stringify(res.body)
|
||||
: res.body
|
||||
let body = res.body
|
||||
|
||||
const response = new Response(body, {
|
||||
headers,
|
||||
status: res.redirect ? 302 : res.status ?? 200,
|
||||
})
|
||||
if (headers.get("content-type") === "application/json")
|
||||
body = JSON.stringify(res.body)
|
||||
else if (headers.get("content-type") === "application/x-www-form-urlencoded")
|
||||
body = new URLSearchParams(res.body).toString()
|
||||
|
||||
if (res.redirect) {
|
||||
response.headers.set("Location", res.redirect.toString())
|
||||
}
|
||||
const status = res.redirect ? 302 : res.status ?? 200
|
||||
const response = new Response(body, { headers, status })
|
||||
|
||||
if (res.redirect) response.headers.set("Location", res.redirect)
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
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: {
|
||||
|
||||
@@ -49,10 +49,6 @@ export interface CredentialsConfig<
|
||||
|
||||
export type CredentialsProviderType = "Credentials"
|
||||
|
||||
export type CredentialsConfigInternal<
|
||||
C extends Record<string, CredentialInput> = Record<string, CredentialInput>
|
||||
> = CredentialsConfig<C> & { options: CredentialsConfig<C> }
|
||||
|
||||
/**
|
||||
* The Credentials provider allows you to handle signing in with arbitrary credentials,
|
||||
* such as a username and password, domain, or two factor authentication or hardware device (e.g. YubiKey U2F / FIDO).
|
||||
|
||||
@@ -1,21 +1,77 @@
|
||||
import type { OAuthConfig, OAuthUserConfig } from "./index.js"
|
||||
|
||||
/**
|
||||
* Corresponds to the user structure documented here:
|
||||
* https://discord.com/developers/docs/resources/user#user-object-user-structure
|
||||
*/
|
||||
export interface DiscordProfile extends Record<string, any> {
|
||||
accent_color: number
|
||||
avatar: string
|
||||
banner: string
|
||||
banner_color: string
|
||||
discriminator: string
|
||||
email: string
|
||||
flags: number
|
||||
/** the user's id (i.e. the numerical snowflake) */
|
||||
id: string
|
||||
image_url: string
|
||||
locale: string
|
||||
mfa_enabled: boolean
|
||||
premium_type: number
|
||||
public_flags: number
|
||||
/** the user's username, not unique across the platform */
|
||||
username: string
|
||||
/** the user's 4-digit discord-tag */
|
||||
discriminator: string
|
||||
/**
|
||||
* the user's avatar hash:
|
||||
* https://discord.com/developers/docs/reference#image-formatting
|
||||
*/
|
||||
avatar: string | null
|
||||
/** whether the user belongs to an OAuth2 application */
|
||||
bot?: boolean
|
||||
/**
|
||||
* whether the user is an Official Discord System user (part of the urgent
|
||||
* message system)
|
||||
*/
|
||||
system?: boolean
|
||||
/** whether the user has two factor enabled on their account */
|
||||
mfa_enabled: boolean
|
||||
/**
|
||||
* the user's banner hash:
|
||||
* https://discord.com/developers/docs/reference#image-formatting
|
||||
*/
|
||||
banner: string | null
|
||||
|
||||
/** the user's banner color encoded as an integer representation of hexadecimal color code */
|
||||
accent_color: number | null
|
||||
|
||||
/**
|
||||
* the user's chosen language option:
|
||||
* https://discord.com/developers/docs/reference#locales
|
||||
*/
|
||||
locale: string
|
||||
/** whether the email on this account has been verified */
|
||||
verified: boolean
|
||||
/** the user's email */
|
||||
email: string | null
|
||||
/**
|
||||
* the flags on a user's account:
|
||||
* https://discord.com/developers/docs/resources/user#user-object-user-flags
|
||||
*/
|
||||
flags: number
|
||||
/**
|
||||
* the type of Nitro subscription on a user's account:
|
||||
* https://discord.com/developers/docs/resources/user#user-object-premium-types
|
||||
*/
|
||||
premium_type: number
|
||||
/**
|
||||
* the public flags on a user's account:
|
||||
* https://discord.com/developers/docs/resources/user#user-object-user-flags
|
||||
*/
|
||||
public_flags: number
|
||||
/** undocumented field; corresponds to the user's custom nickname */
|
||||
display_name: string | null
|
||||
/**
|
||||
* undocumented field; corresponds to the Discord feature where you can e.g.
|
||||
* put your avatar inside of an ice cube
|
||||
*/
|
||||
avatar_decoration: string | null
|
||||
/**
|
||||
* undocumented field; corresponds to the premium feature where you can
|
||||
* select a custom banner color
|
||||
*/
|
||||
banner_color: string | null
|
||||
/** undocumented field; the CDN URL of their profile picture */
|
||||
image_url: string
|
||||
}
|
||||
|
||||
export default function Discord<P extends DiscordProfile>(
|
||||
|
||||
@@ -65,12 +65,16 @@ export type Provider<P extends Profile = Profile> = (
|
||||
| EmailConfig
|
||||
| CredentialsConfig
|
||||
) & {
|
||||
/**
|
||||
* Used to deep merge user-provided config with the default config
|
||||
* @internal
|
||||
*/
|
||||
options: Record<string, unknown>
|
||||
}
|
||||
|
||||
export type BuiltInProviders = Record<
|
||||
OAuthProviderType,
|
||||
(options: Partial<OAuthConfig<any>>) => OAuthConfig<any>
|
||||
(config: Partial<OAuthConfig<any>>) => OAuthConfig<any>
|
||||
> &
|
||||
Record<CredentialsProviderType, typeof CredentialsProvider> &
|
||||
Record<EmailProviderType, typeof EmailProvider>
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
|
||||
export type OAuthProviderType =
|
||||
| "42-school"
|
||||
| "apple"
|
||||
| "atlassian"
|
||||
| "auth0"
|
||||
| "authentik"
|
||||
| "azure-ad-b2c"
|
||||
| "azure-ad"
|
||||
| "battlenet"
|
||||
| "box"
|
||||
| "boxyhq-saml"
|
||||
| "bungie"
|
||||
| "cognito"
|
||||
| "coinbase"
|
||||
| "credentials"
|
||||
| "discord"
|
||||
| "dropbox"
|
||||
| "duende-identity-server6"
|
||||
| "email"
|
||||
| "eveonline"
|
||||
| "facebook"
|
||||
| "faceit"
|
||||
| "foursquare"
|
||||
| "freshbooks"
|
||||
| "fusionauth"
|
||||
| "github"
|
||||
| "gitlab"
|
||||
| "google"
|
||||
| "hubspot"
|
||||
| "identity-server4"
|
||||
| "index"
|
||||
| "instagram"
|
||||
| "kakao"
|
||||
| "keycloak"
|
||||
| "line"
|
||||
| "linkedin"
|
||||
| "mailchimp"
|
||||
| "mailru"
|
||||
| "medium"
|
||||
| "naver"
|
||||
| "netlify"
|
||||
| "oauth-types.js"
|
||||
| "oauth"
|
||||
| "okta"
|
||||
| "onelogin"
|
||||
| "osso"
|
||||
| "osu"
|
||||
| "patreon"
|
||||
| "pinterest"
|
||||
| "pipedrive"
|
||||
| "reddit"
|
||||
| "salesforce"
|
||||
| "slack"
|
||||
| "spotify"
|
||||
| "strava"
|
||||
| "todoist"
|
||||
| "trakt"
|
||||
| "twitch"
|
||||
| "twitter"
|
||||
| "united-effects"
|
||||
| "vk"
|
||||
| "wikimedia"
|
||||
| "wordpress"
|
||||
| "workos"
|
||||
| "yandex"
|
||||
| "zitadel"
|
||||
| "zoho"
|
||||
| "zoom"
|
||||
@@ -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. */
|
||||
@@ -64,7 +66,7 @@ export type TokenEndpointHandler = EndpointHandler<
|
||||
params: CallbackParamsType
|
||||
/**
|
||||
* When using this custom flow, make sure to do all the necessary security checks.
|
||||
* Thist object contains parameters you have to match against the request to make sure it is valid.
|
||||
* This object contains parameters you have to match against the request to make sure it is valid.
|
||||
*/
|
||||
checks: OAuthChecks
|
||||
},
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ export interface CallbacksOptions<P = Profile, A = Account> {
|
||||
* Its content is forwarded to the `session` callback,
|
||||
* where you can control what should be returned to the client.
|
||||
* Anything else will be kept inaccessible from the client.
|
||||
*
|
||||
*
|
||||
* Returning `null` will invalidate the JWT session by clearing
|
||||
* the user's cookies. You'll still have to monitor and invalidate
|
||||
* unexpired tokens from future requests yourself to prevent
|
||||
@@ -220,7 +220,7 @@ export interface CallbacksOptions<P = Profile, A = Account> {
|
||||
account?: A | null
|
||||
profile?: P
|
||||
isNewUser?: boolean
|
||||
}) => Awaitable<JWT|null>
|
||||
}) => Awaitable<JWT | null>
|
||||
}
|
||||
|
||||
/** [Documentation](https://authjs.dev/reference/configuration/auth-config#cookies) */
|
||||
@@ -452,7 +452,7 @@ export interface ResponseInternal<
|
||||
status?: number
|
||||
headers?: Headers | HeadersInit
|
||||
body?: Body
|
||||
redirect?: URL | string
|
||||
redirect?: string
|
||||
cookies?: Cookie[]
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
// After build, copy the files in ./package to the root directory, excluding the package.json file.
|
||||
|
||||
import fs from "fs/promises"
|
||||
import path from "path"
|
||||
|
||||
const __dirname = path.dirname(new URL(import.meta.url).pathname)
|
||||
let __dirname = path.dirname(new URL(import.meta.url).pathname)
|
||||
|
||||
// The above hack to polyfill "__dirname" for ESM does not work on Windows computers,
|
||||
// so we might have to manually perform more steps.
|
||||
__dirname = __dirname.split(path.sep).join(path.posix.sep)
|
||||
if (__dirname.match(/^\/\w:\//)) {
|
||||
__dirname = __dirname.slice(3) // Remove the drive prefix.
|
||||
}
|
||||
|
||||
const root = path.join(__dirname, "..")
|
||||
const pkgDir = path.join(root, "package")
|
||||
|
||||
323
pnpm-lock.yaml
generated
323
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
|
||||
@@ -110,8 +114,8 @@ importers:
|
||||
|
||||
apps/dev/sveltekit:
|
||||
specifiers:
|
||||
'@auth/core': 0.2.5
|
||||
'@auth/sveltekit': 0.1.12
|
||||
'@auth/core': workspace:*
|
||||
'@auth/sveltekit': workspace:*
|
||||
'@sveltejs/adapter-auto': next
|
||||
'@sveltejs/kit': next
|
||||
svelte: 3.55.0
|
||||
@@ -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.2_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
|
||||
@@ -2873,7 +2877,7 @@ packages:
|
||||
'@babel/core': 7.20.12
|
||||
'@babel/helper-plugin-utils': 7.20.2
|
||||
'@babel/helper-skip-transparent-expression-wrappers': 7.20.0
|
||||
'@babel/plugin-proposal-optional-chaining': 7.18.9_@babel+core@7.20.12
|
||||
'@babel/plugin-proposal-optional-chaining': 7.20.7_@babel+core@7.20.12
|
||||
|
||||
/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/7.18.9_@babel+core@7.20.2:
|
||||
resolution: {integrity: sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==}
|
||||
@@ -2884,7 +2888,7 @@ packages:
|
||||
'@babel/core': 7.20.2
|
||||
'@babel/helper-plugin-utils': 7.20.2
|
||||
'@babel/helper-skip-transparent-expression-wrappers': 7.20.0
|
||||
'@babel/plugin-proposal-optional-chaining': 7.18.9_@babel+core@7.20.2
|
||||
'@babel/plugin-proposal-optional-chaining': 7.20.7_@babel+core@7.20.2
|
||||
dev: true
|
||||
|
||||
/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/7.18.9_@babel+core@7.20.5:
|
||||
@@ -2896,7 +2900,7 @@ packages:
|
||||
'@babel/core': 7.20.5
|
||||
'@babel/helper-plugin-utils': 7.20.2
|
||||
'@babel/helper-skip-transparent-expression-wrappers': 7.20.0
|
||||
'@babel/plugin-proposal-optional-chaining': 7.18.9_@babel+core@7.20.5
|
||||
'@babel/plugin-proposal-optional-chaining': 7.20.7_@babel+core@7.20.5
|
||||
dev: true
|
||||
|
||||
/@babel/plugin-proposal-async-generator-functions/7.17.12:
|
||||
@@ -3619,7 +3623,30 @@ packages:
|
||||
'@babel/helper-plugin-utils': 7.20.2
|
||||
'@babel/helper-skip-transparent-expression-wrappers': 7.20.0
|
||||
'@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.20.12
|
||||
dev: false
|
||||
|
||||
/@babel/plugin-proposal-optional-chaining/7.20.7_@babel+core@7.20.2:
|
||||
resolution: {integrity: sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0-0
|
||||
dependencies:
|
||||
'@babel/core': 7.20.2
|
||||
'@babel/helper-plugin-utils': 7.20.2
|
||||
'@babel/helper-skip-transparent-expression-wrappers': 7.20.0
|
||||
'@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.20.2
|
||||
dev: true
|
||||
|
||||
/@babel/plugin-proposal-optional-chaining/7.20.7_@babel+core@7.20.5:
|
||||
resolution: {integrity: sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0-0
|
||||
dependencies:
|
||||
'@babel/core': 7.20.5
|
||||
'@babel/helper-plugin-utils': 7.20.2
|
||||
'@babel/helper-skip-transparent-expression-wrappers': 7.20.0
|
||||
'@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.20.5
|
||||
dev: true
|
||||
|
||||
/@babel/plugin-proposal-private-methods/7.17.12:
|
||||
resolution: {integrity: sha512-SllXoxo19HmxhDWm3luPz+cPhtoTSKLJE9PXshsfrOzBqs60QP0r8OaJItrPhAj0d7mZMnNF0Y1UUggCDgMz1A==}
|
||||
@@ -7488,8 +7515,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 +9103,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 +11373,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:
|
||||
@@ -14040,8 +14067,10 @@ packages:
|
||||
indent-string: 4.0.0
|
||||
dev: true
|
||||
|
||||
/ajv-formats/2.1.1:
|
||||
/ajv-formats/2.1.1_ajv@8.11.0:
|
||||
resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==}
|
||||
peerDependencies:
|
||||
ajv: ^8.0.0
|
||||
peerDependenciesMeta:
|
||||
ajv:
|
||||
optional: true
|
||||
@@ -14545,26 +14574,17 @@ 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
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
dev: true
|
||||
|
||||
/axios/0.25.0:
|
||||
resolution: {integrity: sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==}
|
||||
dependencies:
|
||||
follow-redirects: 1.15.1
|
||||
follow-redirects: 1.15.1_debug@4.3.4
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
dev: true
|
||||
@@ -14951,8 +14971,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_x3fnz3zp5bgltr62wwyqvgm7l4:
|
||||
resolution: {integrity: sha512-hrxx7U73x6TUL+x/h2/OrQT4hdeGkYjK39oiYtt6erSVz8q1iOYl4tfCYN0wuHyrvpRdXJ7UBrAhLgBFIPn8Gw==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0
|
||||
@@ -14961,8 +14981,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.2_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 +15066,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 +15088,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 +15324,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 +15754,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 +16522,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.1:
|
||||
resolution: {integrity: sha512-7PSTSNLa1w0LZ81WXZ09IBVcE+SCuOk8QOPJC3tK2D50iTeKo1rBp2mZEX0ao5sC5q/N869HuLBPkzdvP5Lvig==}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.20.7
|
||||
@@ -19713,7 +19735,7 @@ packages:
|
||||
dependencies:
|
||||
'@apidevtools/json-schema-ref-parser': 9.0.9
|
||||
ajv: 8.11.0
|
||||
ajv-formats: 2.1.1
|
||||
ajv-formats: 2.1.1_ajv@8.11.0
|
||||
body-parser: 1.20.0
|
||||
content-type: 1.0.4
|
||||
deep-freeze: 0.0.1
|
||||
@@ -20363,28 +20385,6 @@ packages:
|
||||
resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==}
|
||||
dev: true
|
||||
|
||||
/follow-redirects/1.15.1:
|
||||
resolution: {integrity: sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==}
|
||||
engines: {node: '>=4.0'}
|
||||
peerDependencies:
|
||||
debug: '*'
|
||||
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==}
|
||||
engines: {node: '>=4.0'}
|
||||
@@ -20395,7 +20395,6 @@ packages:
|
||||
optional: true
|
||||
dependencies:
|
||||
debug: 4.3.4
|
||||
dev: true
|
||||
|
||||
/for-each/0.3.3:
|
||||
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
|
||||
@@ -20544,6 +20543,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 +20651,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.1:
|
||||
resolution: {integrity: sha512-tFrTZlEezIxFKMkCXXKu3tKBrjqpZt+PykNABkSgOdMGO1pT/xysSClCbKOMockWosrpgaNup+1rP8sjVRdIsw==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
@@ -20664,13 +20673,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.1
|
||||
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 +20703,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 +20712,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 +20725,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 +20747,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 +20788,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.1_wxxlwg4kb5uwyfjvsdo3jviy2e:
|
||||
resolution: {integrity: sha512-9yE2C4Xbc+llNRzzC9MbpgSM1GQCi4lUqlNy8Hq/C25S0wTTRW+FQZxuWTiR+AUTUL8MK9wJsNF5f0bZc8hfJw==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
peerDependencies:
|
||||
gatsby: ^5.0.0-next
|
||||
@@ -20789,12 +20799,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.2_biqbaboplfbrettd7655fr4n2y
|
||||
gatsby-core-utils: 4.6.0-next.0
|
||||
gatsby-page-utils: 3.6.0-next.0
|
||||
gatsby-plugin-utils: 4.6.0-next.1_wxxlwg4kb5uwyfjvsdo3jviy2e
|
||||
gatsby-telemetry: 4.6.0-next.0
|
||||
globby: 11.1.0
|
||||
lodash: 4.17.21
|
||||
transitivePeerDependencies:
|
||||
@@ -20803,8 +20813,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.2:
|
||||
resolution: {integrity: sha512-CUguJx8GjTQHymcfBOcwUXZtetfhLYtgUehkM8ovvNlscgavGxmbGD90gXMmo5JgrLPhdx8krtMsdIeCOOvo4w==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
peerDependencies:
|
||||
gatsby: ^5.0.0-next
|
||||
@@ -20815,14 +20825,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_x3fnz3zp5bgltr62wwyqvgm7l4
|
||||
gatsby: 5.6.0-next.2_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.1_wxxlwg4kb5uwyfjvsdo3jviy2e:
|
||||
resolution: {integrity: sha512-RhM2cQ6RYmdGQkXKs4d+KfG5ovGjvbF4fps/H4Sk81nJ1AnlPGbUlMaCdgFhhTJL1ID8l4rYdpQKXM87THJosQ==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
peerDependencies:
|
||||
gatsby: ^5.0.0-next
|
||||
@@ -20830,10 +20840,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.2_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 +20851,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 +20866,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 +20879,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 +20896,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 +20908,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.2_biqbaboplfbrettd7655fr4n2y:
|
||||
resolution: {integrity: sha512-cXyyDjlA2V3NhnfJtLhcrShrYU6k/FB67el5MZIqzrHjb7sg77lJztMpoy4PvXs4MlNvhRupSclIO+o9+G0pyg==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
@@ -20927,7 +20937,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 +20961,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_x3fnz3zp5bgltr62wwyqvgm7l4
|
||||
babel-preset-gatsby: 3.6.0-next.0_pp2vm42zn6vfmnpuhar3irht7i
|
||||
better-opn: 2.1.1
|
||||
bluebird: 3.7.2
|
||||
browserslist: 4.21.4
|
||||
@@ -20967,14 +20977,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 +21008,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.1
|
||||
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.1_wxxlwg4kb5uwyfjvsdo3jviy2e
|
||||
gatsby-plugin-typescript: 5.6.0-next.0_gatsby@5.6.0-next.2
|
||||
gatsby-plugin-utils: 4.6.0-next.1_wxxlwg4kb5uwyfjvsdo3jviy2e
|
||||
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 +21039,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 +21092,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 +21863,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'}
|
||||
@@ -22180,7 +22193,7 @@ packages:
|
||||
engines: {node: '>=8.0.0'}
|
||||
dependencies:
|
||||
eventemitter3: 4.0.7
|
||||
follow-redirects: 1.15.1
|
||||
follow-redirects: 1.15.1_debug@4.3.4
|
||||
requires-port: 1.0.0
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
@@ -25827,12 +25840,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 +27806,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
|
||||
@@ -30471,7 +30478,7 @@ packages:
|
||||
dependencies:
|
||||
'@types/json-schema': 7.0.11
|
||||
ajv: 8.11.0
|
||||
ajv-formats: 2.1.1
|
||||
ajv-formats: 2.1.1_ajv@8.11.0
|
||||
ajv-keywords: 5.1.0_ajv@8.11.0
|
||||
dev: true
|
||||
|
||||
@@ -32139,6 +32146,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 +34260,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==}
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": ["lib/**", "providers/**", "*.js", "*.d.ts", "*.d.ts.map"]
|
||||
},
|
||||
"@auth/sveltekit#build": {
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": ["client.*", "index.*"]
|
||||
},
|
||||
"clean": {
|
||||
"cache": false
|
||||
},
|
||||
@@ -34,6 +38,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