Compare commits

...

11 Commits

Author SHA1 Message Date
Balázs Orbán
471471b303 Merge branch 'main' into feat/auth-request-response 2023-01-02 11:19:08 +01:00
Balázs Orbán
fd80f10625 docs: don't hide classes header 2023-01-02 11:16:11 +01:00
Balázs Orbán
cb2da4dd9c chore(dev): update session example endpoint 2023-01-02 11:15:58 +01:00
Balázs Orbán
5064f20e61 feat(core): introduce AuthRequest and AuthResponse web extensions 2023-01-02 11:15:42 +01:00
Thang Vu
5a6f76bf2c fix: docs build (#6253)
* fix: docs build

* chore: move next-auth output to dist

* chore: add next-auth as deps for doc

* Revert "chore: move next-auth output to dist"

This reverts commit 9596a9134e6de4f4bd8dcfaa6d3002e98863d8f8.

* remove dist prefix
2023-01-02 16:57:14 +07:00
Thang Vu
15bed6260c chore: change Thang's email 2023-01-01 22:45:36 +07:00
Balázs Orbán
1423733d61 fix(adapters): define correct peer dependencies for DynamoDB (#6249)
* Add @aws-sdk/client-dynamodb as peer dependency

* Add missing DynamoDBClientConfig interface import

* Add missing installation requirements

Co-authored-by: Didi Keke <nyedidikeke@users.noreply.github.com>
2023-01-01 15:16:01 +00:00
Thang Vu
77d8f47f51 chore: restore turborepo next-auth#build.outputs 2023-01-01 22:06:50 +07:00
Balázs Orbán
3120d28299 chore: update lockfile 2023-01-01 16:03:09 +01:00
Nico Domino
e6a320bb0f chore(docs): fix homepage logo, build, and lighthouse improvements (#6238)
* chore(docs): fix homepage logo size

* chore(docs): fix sidebars.js solid-start doc path name

* chore(docs): image file and size optimizations

* chore(docs): fix semantic misordered headings

* chore(docs): make banner link more descriptive

* chore(docs): add solid-start redirect
2022-12-31 21:28:59 +01:00
Birk Skyum
7d4d436efe chore(examples): fix broken docs link in solidstart example (#6241) 2022-12-31 21:28:17 +01:00
27 changed files with 241 additions and 80 deletions

View File

@@ -9,6 +9,7 @@ module.exports = {
files: [ files: [
"apps/dev/pages/api/auth/[...nextauth].ts", "apps/dev/pages/api/auth/[...nextauth].ts",
"docs/{sidebars,docusaurus.config}.js", "docs/{sidebars,docusaurus.config}.js",
"packages/core/src/index.ts",
], ],
options: { printWidth: 150 }, options: { printWidth: 150 },
}, },

View File

@@ -1,8 +0,0 @@
// This is an example of how to access a session from an API route
import { unstable_getServerSession } from "next-auth/next"
import { authOptions } from "../auth/[...nextauth]"
export default async (req, res) => {
const session = await unstable_getServerSession(req, res, authOptions)
res.json(session)
}

View File

@@ -0,0 +1,21 @@
import { Auth, SessionRequest } from "@auth/core"
import { authConfig } from "../auth/[...nextauth]"
export default async function handle(req: Request) {
authConfig.secret = process.env.AUTH_SECRET
const response = await Auth(new SessionRequest(req), authConfig)
const session = await response.session()
if (!session) {
return new Response("Not authenticated", { status: 401 })
}
console.log(session.user) // Do something with the session
// Pass the original headers to set cookies (eg.: updating the session expiry)
response.headers.set("content-type", "text/plain")
return new Response("Authenticated", { headers: response.headers })
}
export const config = {
runtime: "experimental-edge",
}

View File

@@ -29,7 +29,7 @@ const Home: ParentComponent = () => {
</A>{" "} </A>{" "}
with{" "} with{" "}
<A <A
href="https://authjs.dev/reference/solid-start/modules/main" href="https://authjs.dev/reference/solidstart"
class="text-blue-500 underline font-bold" class="text-blue-500 underline font-bold"
> >
SolidStart Auth SolidStart Auth

View File

@@ -3,7 +3,7 @@ id: dynamodb
title: DynamoDB title: DynamoDB
--- ---
This is the AWS DynamoDB Adapter for next-auth. This package can only be used in conjunction with the primary next-auth package. It is not a standalone package. This is the AWS DynamoDB Adapter for `next-auth`. This package can only be used in conjunction with the primary `next-auth` package. It is not a standalone package.
By default, the adapter expects a table with a partition key `pk` and a sort key `sk`, as well as a global secondary index named `GSI1` with `GSI1PK` as partition key and `GSI1SK` as sorting key. To automatically delete sessions and verification requests after they expire using [dynamodb TTL](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html) you should [enable the TTL](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/time-to-live-ttl-how-to.html) with attribute name 'expires'. You can set whatever you want as the table name and the billing method. By default, the adapter expects a table with a partition key `pk` and a sort key `sk`, as well as a global secondary index named `GSI1` with `GSI1PK` as partition key and `GSI1SK` as sorting key. To automatically delete sessions and verification requests after they expire using [dynamodb TTL](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html) you should [enable the TTL](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/time-to-live-ttl-how-to.html) with attribute name 'expires'. You can set whatever you want as the table name and the billing method.
@@ -11,10 +11,10 @@ You can find the full schema in the table structure section below.
## Getting Started ## Getting Started
1. Install `next-auth` and `@next-auth/dynamodb-adapter` 1. Install `next-auth`, `@next-auth/dynamodb-adapter`, `@aws-sdk/client-dynamodb` and `@aws-sdk/lib-dynamodb`
```bash npm2yarn ```bash npm2yarn2pnpm
npm install next-auth @next-auth/dynamodb-adapter npm install next-auth @next-auth/dynamodb-adapter @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb
``` ```
2. Add this adapter to your `pages/api/auth/[...nextauth].js` next-auth configuration object. 2. Add this adapter to your `pages/api/auth/[...nextauth].js` next-auth configuration object.
@@ -23,7 +23,7 @@ You need to pass `DynamoDBDocument` client from the modular [`aws-sdk`](https://
The default table name is `next-auth`, but you can customise that by passing `{ tableName: 'your-table-name' }` as the second parameter in the adapter. The default table name is `next-auth`, but you can customise that by passing `{ tableName: 'your-table-name' }` as the second parameter in the adapter.
```javascript title="pages/api/auth/[...nextauth].js" ```javascript title="pages/api/auth/[...nextauth].js"
import { DynamoDB } from "@aws-sdk/client-dynamodb" import { DynamoDB, DynamoDBClientConfig } from "@aws-sdk/client-dynamodb"
import { DynamoDBDocument } from "@aws-sdk/lib-dynamodb" import { DynamoDBDocument } from "@aws-sdk/lib-dynamodb"
import NextAuth from "next-auth"; import NextAuth from "next-auth";
import Providers from "next-auth/providers"; import Providers from "next-auth/providers";
@@ -73,7 +73,7 @@ The table respects the single table design pattern. This has many advantages:
- Only one table to manage, monitor and provision. - Only one table to manage, monitor and provision.
- Querying relations is faster than with multi-table schemas (for eg. retrieving all sessions for a user). - Querying relations is faster than with multi-table schemas (for eg. retrieving all sessions for a user).
- Only one table needs to be replicated, if you want to go multi-region. - Only one table needs to be replicated if you want to go multi-region.
> This schema is adapted for use in DynamoDB and based upon our main [schema](/reference/adapters/models) > This schema is adapted for use in DynamoDB and based upon our main [schema](/reference/adapters/models)
@@ -94,7 +94,7 @@ new dynamodb.Table(this, `NextAuthTable`, {
}) })
``` ```
Alternatively you can use this cloudformation template: Alternatively, you can use this cloudformation template:
```yaml title=cloudformation.yaml ```yaml title=cloudformation.yaml
NextAuthTable: NextAuthTable:

View File

@@ -46,7 +46,7 @@ const docusaurusConfig = {
title: "Auth.js", title: "Auth.js",
logo: { logo: {
alt: "Auth.js Logo", alt: "Auth.js Logo",
src: "img/logo/logo-xs.png", src: "img/logo/logo-xs.webp",
}, },
items: [ items: [
{ {
@@ -101,7 +101,7 @@ const docusaurusConfig = {
announcementBar: { announcementBar: {
id: "new-major-announcement", id: "new-major-announcement",
content: content:
"<a target='_blank' rel='noopener noreferrer' href='https://next-auth.js.org'>NextAuth.js</a> is becoming Auth.js! 🎉 We're creating Authentication for the Web. Everyone included. Starting with SvelteKit, check out the docs <a href='/reference/sveltekit'>here</a>.", "<a target='_blank' rel='noopener noreferrer' href='https://next-auth.js.org'>NextAuth.js</a> is becoming Auth.js! 🎉 We're creating Authentication for the Web. Everyone included. Starting with SvelteKit, check out <a href='/reference/sveltekit'>the docs</a>.",
backgroundColor: "#000", backgroundColor: "#000",
textColor: "#fff", textColor: "#fff",
}, },
@@ -121,6 +121,7 @@ const docusaurusConfig = {
alt="Powered by Vercel" alt="Powered by Vercel"
style="margin-top: 8px" style="margin-top: 8px"
height="32" height="32"
width="167"
src="https://raw.githubusercontent.com/nextauthjs/next-auth/main/docs/static/img/powered-by-vercel.svg" src="https://raw.githubusercontent.com/nextauthjs/next-auth/main/docs/static/img/powered-by-vercel.svg"
/> />
</a>`, </a>`,
@@ -181,7 +182,10 @@ const docusaurusConfig = {
lastVersion: "current", lastVersion: "current",
showLastUpdateAuthor: true, showLastUpdateAuthor: true,
showLastUpdateTime: true, showLastUpdateTime: true,
remarkPlugins: [require("@sapphire/docusaurus-plugin-npm2yarn2pnpm").npm2yarn2pnpm, require("remark-github")], remarkPlugins: [
require("@sapphire/docusaurus-plugin-npm2yarn2pnpm").npm2yarn2pnpm,
require("remark-github"),
],
versions: { versions: {
current: { current: {
label: "experimental", label: "experimental",
@@ -201,7 +205,15 @@ const docusaurusConfig = {
...typedocConfig, ...typedocConfig,
id: "core", id: "core",
plugin: ["./tyepdoc"], plugin: ["./tyepdoc"],
entryPoints: ["index.ts", "adapters.ts", "errors.ts", "jwt.ts", "types.ts"].map((e) => `${coreSrc}/${e}`).concat(providers), entryPoints: [
"index.ts",
"adapters.ts",
"errors.ts",
"jwt.ts",
"types.ts",
]
.map((e) => `${coreSrc}/${e}`)
.concat(providers),
tsconfig: "../packages/core/tsconfig.json", tsconfig: "../packages/core/tsconfig.json",
out: "reference/03-core", out: "reference/03-core",
watch: process.env.TYPEDOC_WATCH, watch: process.env.TYPEDOC_WATCH,
@@ -214,7 +226,9 @@ const docusaurusConfig = {
...typedocConfig, ...typedocConfig,
id: "sveltekit", id: "sveltekit",
plugin: ["./tyepdoc"], plugin: ["./tyepdoc"],
entryPoints: ["index.ts", "client.ts"].map((e) => `../packages/frameworks-sveltekit/src/lib/${e}`), entryPoints: ["index.ts", "client.ts"].map(
(e) => `../packages/frameworks-sveltekit/src/lib/${e}`
),
tsconfig: "../packages/frameworks-sveltekit/tsconfig.json", tsconfig: "../packages/frameworks-sveltekit/tsconfig.json",
out: "reference/04-sveltekit", out: "reference/04-sveltekit",
watch: process.env.TYPEDOC_WATCH, watch: process.env.TYPEDOC_WATCH,

View File

@@ -22,6 +22,7 @@
"classnames": "^2.3.2", "classnames": "^2.3.2",
"mdx-mermaid": "1.2.2", "mdx-mermaid": "1.2.2",
"mermaid": "9.0.1", "mermaid": "9.0.1",
"next-auth": "workspace:*",
"prism-react-renderer": "1.3.5", "prism-react-renderer": "1.3.5",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",

View File

@@ -58,9 +58,9 @@ module.exports = {
label: "@auth/solid-start", label: "@auth/solid-start",
link: { link: {
type: "doc", type: "doc",
id: "reference/solid-start/index", id: "reference/solidstart/index",
}, },
items: ["reference/solid-start/client", "reference/solid-start/protected"], items: ["reference/solidstart/client", "reference/solidstart/protected"],
}, },
{ {
type: "category", type: "category",

View File

@@ -140,19 +140,19 @@ html[data-theme="dark"] hr {
border-radius: 10rem; border-radius: 10rem;
overflow: visible; overflow: visible;
box-shadow: 0 0 2rem rgba(0, 0, 0, 0.1); box-shadow: 0 0 2rem rgba(0, 0, 0, 0.1);
background-image: url("/img/mesh-1.jpg"); background-image: url("/img/mesh-1.webp");
background-size: cover; background-size: cover;
background-origin: center; background-origin: center;
} }
.home-main .section-features .row .col:nth-child(2) .feature-image-wrapper { .home-main .section-features .row .col:nth-child(2) .feature-image-wrapper {
background-image: url("/img/mesh-2.jpg"); background-image: url("/img/mesh-2.webp");
background-size: cover; background-size: cover;
background-origin: center; background-origin: center;
} }
.home-main .section-features .row .col:nth-child(3) .feature-image-wrapper { .home-main .section-features .row .col:nth-child(3) .feature-image-wrapper {
background-image: url("/img/mesh-3.jpg"); background-image: url("/img/mesh-3.webp");
background-size: cover; background-size: cover;
background-origin: center; background-origin: center;
} }
@@ -293,6 +293,6 @@ html[data-theme="dark"] #carbonads .carbon-poweredby {
See: https://github.com/TypeStrong/typedoc/issues/2006 See: https://github.com/TypeStrong/typedoc/issues/2006
*/ */
/* h3.anchor + p:has(code, strong), */ /** hack did not work as it hides property types elsewhere */ /* h3.anchor + p:has(code, strong), */ /** hack did not work as it hides property types elsewhere */
#classes { /* #classes {
display: none; display: none;
} } */

View File

@@ -6,6 +6,11 @@
margin-right: 1rem !important; margin-right: 1rem !important;
} }
.navbar__logo {
width: 29px;
height: 32px;
}
.navbar__title { .navbar__title {
font-size: 1.2rem; font-size: 1.2rem;
margin-left: 0.2rem; margin-left: 0.2rem;

View File

@@ -117,9 +117,11 @@ export default function Home() {
<div className="container"> <div className="container">
<div className="hero-inner"> <div className="hero-inner">
<img <img
src="/img/logo/logo-sm.png" src="/img/logo/logo-sm.webp"
alt="Shield with key icon" alt="Shield with key icon"
className={styles.heroLogo} className={styles.heroLogo}
height="142"
width="128"
/> />
<div className={styles.heroText}> <div className={styles.heroText}>
<h1 className="hero__title">{siteConfig.title}</h1> <h1 className="hero__title">{siteConfig.title}</h1>
@@ -214,9 +216,9 @@ export default function Home() {
<div className="row"> <div className="row">
<div className="col col--6"> <div className="col col--6">
<div className="code"> <div className="code">
<h4 className="code-heading"> <div className="code-heading">
Next.js <span>/pages/api/auth/[...nextauth].ts</span> Next.js <span>/pages/api/auth/[...nextauth].ts</span>
</h4> </div>
<CodeBlock className="prism-code language-js"> <CodeBlock className="prism-code language-js">
{nextJsCode} {nextJsCode}
</CodeBlock> </CodeBlock>
@@ -224,9 +226,9 @@ export default function Home() {
</div> </div>
<div className="col col--6"> <div className="col col--6">
<div className="code"> <div className="code">
<h4 className="code-heading"> <div className="code-heading">
SvelteKit <span>/hooks.server.ts</span> SvelteKit <span>/hooks.server.ts</span>
</h4> </div>
<CodeBlock className="prism-code language-js"> <CodeBlock className="prism-code language-js">
{svelteKitCode} {svelteKitCode}
</CodeBlock> </CodeBlock>
@@ -234,9 +236,9 @@ export default function Home() {
</div> </div>
<div className="col col--6"> <div className="col col--6">
<div className="code"> <div className="code">
<h4 className="code-heading"> <div className="code-heading">
SolidStart <span>/routes/api/auth/[...solidauth].ts</span> SolidStart <span>/routes/api/auth/[...solidauth].ts</span>
</h4> </div>
<CodeBlock className="prism-code language-js"> <CodeBlock className="prism-code language-js">
{solidStartCode} {solidStartCode}
</CodeBlock> </CodeBlock>

BIN
docs/static/img/logo/logo-sm.webp vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
docs/static/img/logo/logo-xs.webp vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
docs/static/img/logo/logo.webp vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

BIN
docs/static/img/mesh-1.webp vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
docs/static/img/mesh-2.webp vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
docs/static/img/mesh-3.webp vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -60,6 +60,11 @@
"destination": "https://github.com/nextauthjs/next-auth/discussions/categories/questions", "destination": "https://github.com/nextauthjs/next-auth/discussions/categories/questions",
"permanent": true "permanent": true
}, },
{
"source": "/reference/solid-start/:path*",
"destination": "/reference/solidstart/:path*",
"permanent": true
},
{ {
"source": "/", "source": "/",
"has": [ "has": [

View File

@@ -5,6 +5,7 @@
"repository": "https://github.com/nextauthjs/next-auth.git", "repository": "https://github.com/nextauthjs/next-auth.git",
"scripts": { "scripts": {
"build:app": "turbo run build --filter=next-auth-app", "build:app": "turbo run build --filter=next-auth-app",
"build:docs": "turbo run build --filter=docs",
"build": "turbo run build --filter=next-auth --filter=@next-auth/* --filter=@auth/* --no-deps", "build": "turbo run build --filter=next-auth --filter=@next-auth/* --filter=@auth/* --no-deps",
"test": "turbo run test --concurrency=1 --filter=[HEAD^1] --filter=./packages/* --filter=!*pouchdb-* --filter=!@*upstash* --filter=!*dynamodb-*", "test": "turbo run test --concurrency=1 --filter=[HEAD^1] --filter=./packages/* --filter=!*pouchdb-* --filter=!@*upstash* --filter=!*dynamodb-*",
"clean": "turbo run clean --no-cache", "clean": "turbo run clean --no-cache",

View File

@@ -31,6 +31,7 @@
"author": "Pol Marnette", "author": "Pol Marnette",
"license": "ISC", "license": "ISC",
"peerDependencies": { "peerDependencies": {
"@aws-sdk/client-dynamodb": "^3.36.1",
"@aws-sdk/lib-dynamodb": "^3.36.1", "@aws-sdk/lib-dynamodb": "^3.36.1",
"next-auth": "^4" "next-auth": "^4"
}, },

View File

@@ -20,7 +20,7 @@
"Balázs Orbán <info@balazsorban.com>", "Balázs Orbán <info@balazsorban.com>",
"Nico Domino <yo@ndo.dev>", "Nico Domino <yo@ndo.dev>",
"Lluis Agusti <hi@llu.lu>", "Lluis Agusti <hi@llu.lu>",
"Thang Huu Vu <thvu@hey.com>", "Thang Huu Vu <hi@thvu.dev>",
"Iain Collins <me@iaincollins.com" "Iain Collins <me@iaincollins.com"
], ],
"type": "module", "type": "module",

View File

@@ -40,17 +40,17 @@ import { logger, setLogger, type LoggerInstance } from "./lib/utils/logger.js"
import { toInternalRequest, toResponse } from "./lib/web.js" import { toInternalRequest, toResponse } from "./lib/web.js"
import type { Adapter } from "./adapters.js" import type { Adapter } from "./adapters.js"
import type { import type { CallbacksOptions, CookiesOptions, EventCallbacks, PagesOptions, SessionOptions, Theme } from "./types.js"
CallbacksOptions,
CookiesOptions,
EventCallbacks,
PagesOptions,
SessionOptions,
Theme,
} from "./types.js"
import type { Provider } from "./providers/index.js" import type { Provider } from "./providers/index.js"
import { JWTOptions } from "./jwt.js" import { JWTOptions } from "./jwt.js"
import { ProvidersRequest, ProvidersResponse, SessionRequest, SessionResponse } from "./lib/web-extension.js"
export * from "./lib/web-extension.js"
/** Returns a special {@link SessionResponse} instance to read the session from the request. */
export function Auth(request: SessionRequest, config: AuthConfig): Promise<SessionResponse>
/** Returns a special {@link ProvidersResponse} instance to read the list of providers in a client-safe way. */
export function Auth(request: ProvidersRequest, config: AuthConfig): Promise<ProvidersResponse>
/** /**
* Core functionality provided by Auth.js. * Core functionality provided by Auth.js.
* *
@@ -69,19 +69,18 @@ import { JWTOptions } from "./jwt.js"
*``` *```
* @see [Documentation](https://authjs.dev) * @see [Documentation](https://authjs.dev)
*/ */
export async function Auth( export function Auth(request: Request, config: AuthConfig): Promise<Response>
request: Request, export async function Auth(request: Request, config: AuthConfig): Promise<Response> {
config: AuthConfig
): Promise<Response> {
setLogger(config.logger, config.debug) setLogger(config.logger, config.debug)
const isAuthRequest = request instanceof SessionRequest || request instanceof ProvidersRequest
if (isAuthRequest) config.trustHost = true
const internalRequest = await toInternalRequest(request) const internalRequest = await toInternalRequest(request)
if (internalRequest instanceof Error) { if (internalRequest instanceof Error) {
logger.error(internalRequest) logger.error(internalRequest)
return new Response( return new Response(`Error: This action with HTTP ${request.method} is not supported.`, { status: 400 })
`Error: This action with HTTP ${request.method} is not supported.`,
{ status: 400 }
)
} }
const assertionResult = assertConfig(internalRequest, config) const assertionResult = assertConfig(internalRequest, config)
@@ -92,14 +91,10 @@ export async function Auth(
// Bail out early if there's an error in the user config // Bail out early if there's an error in the user config
logger.error(assertionResult) logger.error(assertionResult)
const htmlPages = ["signin", "signout", "error", "verify-request"] const htmlPages = ["signin", "signout", "error", "verify-request"]
if ( if (!htmlPages.includes(internalRequest.action) || internalRequest.method !== "GET") {
!htmlPages.includes(internalRequest.action) ||
internalRequest.method !== "GET"
) {
return new Response( return new Response(
JSON.stringify({ JSON.stringify({
message: message: "There was a problem with the server configuration. Check the server logs for more information.",
"There was a problem with the server configuration. Check the server logs for more information.",
code: assertionResult.name, code: assertionResult.name,
}), }),
{ status: 500, headers: { "Content-Type": "application/json" } } { status: 500, headers: { "Content-Type": "application/json" } }
@@ -108,19 +103,11 @@ export async function Auth(
const { pages, theme } = config const { pages, theme } = config
const authOnErrorPage = const authOnErrorPage = pages?.error && internalRequest.url.searchParams.get("callbackUrl")?.startsWith(pages.error)
pages?.error &&
internalRequest.url.searchParams
.get("callbackUrl")
?.startsWith(pages.error)
if (!pages?.error || authOnErrorPage) { if (!pages?.error || authOnErrorPage) {
if (authOnErrorPage) { if (authOnErrorPage) {
logger.error( logger.error(new ErrorPageLoop(`The error page ${pages?.error} should not require authentication`))
new ErrorPageLoop(
`The error page ${pages?.error} should not require authentication`
)
)
} }
const render = renderPage({ theme }) const render = renderPage({ theme })
const page = render.error({ error: "Configuration" }) const page = render.error({ error: "Configuration" })
@@ -144,6 +131,18 @@ export async function Auth(
headers: response.headers, headers: response.headers,
}) })
} }
if (isAuthRequest) {
switch (request.action) {
case "session":
return new SessionResponse(response.body, response)
case "providers":
return new ProvidersResponse(response.body, response)
default:
return response
}
}
return response return response
} }

View File

@@ -0,0 +1,95 @@
import type { AuthAction, Session } from "../types.js"
import { PublicProvider } from "./routes/providers.js"
/** @internal */
export abstract class AuthRequest extends Request {
abstract action: AuthAction
}
/**
* Extends the standard {@link Request} to add a `session()` method on the response
* for retrieving the {@link Session} object.
*/
export class SessionRequest extends AuthRequest {
action = "session" as const
constructor(req: Request) {
super(req.url, req)
}
}
export class SessionResponse extends Response {
action = "session" as const
/**
* Returns the {@link Session} object from the response, or `null`
* if the session is unavailable (config error, not authenticated, etc.).
*
* @example
* ```ts
* export default async function handle(req: Request) {
* const response = await Auth(new SessionRequest(req), authConfig)
* const session = await response.session()
*
* if (!session) {
* return new Response("Not authenticated", { status: 401 })
* }
*
* console.log(session.user) // Do something with the session
* return response // or return whatever you want.
* }
* ```
*/
async session(): Promise<Session | null> {
try {
const data = await this.clone().json()
if (!this.ok || !data || !Object.keys(data).length) {
return null
}
return data
} catch {
return null
}
}
}
/**
* Extends the standard {@link Request} to add a `providers()` method on the response
* for retrieving a list of client-safe provider configuration. Useful for
* rendering a list of sign-in options.
*/
export class ProvidersRequest extends AuthRequest {
action = "providers" as const
constructor(req: Request) {
super(req.url, req)
}
}
export class ProvidersResponse extends Response {
action = "providers" as const
/**
* Returns the list of providers from the response, or `null`
* if the providers are unavailable (config error, etc.).
* @example
* ```ts
* export default async function handle(req: Request) {
* const response = await Auth(new ProvidersRequest(req), authConfig)
* const providers = await response.providers()
* if (!providers) {
* return new Response("Providers unavailable", { status: 500 })
*
*
* console.log(providers) // Do something with the providers
* return response // or return whatever you want.
* }
* ```
*/
async providers(): Promise<PublicProvider[]> {
try {
if (!this.ok) return []
return Object.values(await this.clone().json())
} catch {
return []
}
}
}

View File

@@ -2,6 +2,7 @@ import { parse as parseCookie, serialize } from "cookie"
import { AuthError, UnknownAction } from "../errors.js" import { AuthError, UnknownAction } from "../errors.js"
import type { AuthAction, RequestInternal, ResponseInternal } from "../types.js" import type { AuthAction, RequestInternal, ResponseInternal } from "../types.js"
import { ProvidersRequest, SessionRequest } from "./web-extension.js"
async function getBody(req: Request): Promise<Record<string, any> | undefined> { async function getBody(req: Request): Promise<Record<string, any> | undefined> {
if (!("body" in req) || !req.body || req.method !== "POST") return if (!("body" in req) || !req.body || req.method !== "POST") return
@@ -34,8 +35,11 @@ export async function toInternalRequest(
// see init.ts // see init.ts
const url = new URL(req.url.replace(/\/$/, "")) const url = new URL(req.url.replace(/\/$/, ""))
const { pathname } = url const { pathname } = url
let action: AuthAction | undefined
if (req instanceof SessionRequest || req instanceof ProvidersRequest) {
action = req.action
} else action = actions.find((a) => pathname.includes(a))
const action = actions.find((a) => pathname.includes(a))
if (!action) { if (!action) {
throw new UnknownAction("Cannot detect action.") throw new UnknownAction("Cannot detect action.")
} }

View File

@@ -9,7 +9,7 @@
"Balázs Orbán <info@balazsorban.com>", "Balázs Orbán <info@balazsorban.com>",
"Nico Domino <yo@ndo.dev>", "Nico Domino <yo@ndo.dev>",
"Lluis Agusti <hi@llu.lu>", "Lluis Agusti <hi@llu.lu>",
"Thang Huu Vu <thvu@hey.com>" "Thang Huu Vu <hi@thvu.dev>"
], ],
"main": "index.js", "main": "index.js",
"module": "index.js", "module": "index.js",

16
pnpm-lock.yaml generated
View File

@@ -258,6 +258,7 @@ importers:
docusaurus-plugin-typedoc: ^0.18.0 docusaurus-plugin-typedoc: ^0.18.0
mdx-mermaid: 1.2.2 mdx-mermaid: 1.2.2
mermaid: 9.0.1 mermaid: 9.0.1
next-auth: workspace:*
prism-react-renderer: 1.3.5 prism-react-renderer: 1.3.5
react: ^18.2.0 react: ^18.2.0
react-dom: ^18.2.0 react-dom: ^18.2.0
@@ -272,6 +273,7 @@ importers:
classnames: 2.3.2 classnames: 2.3.2
mdx-mermaid: 1.2.2_mermaid@9.0.1+react@18.2.0 mdx-mermaid: 1.2.2_mermaid@9.0.1+react@18.2.0
mermaid: 9.0.1 mermaid: 9.0.1
next-auth: link:../packages/next-auth
prism-react-renderer: 1.3.5_react@18.2.0 prism-react-renderer: 1.3.5_react@18.2.0
react: 18.2.0 react: 18.2.0
react-dom: 18.2.0_react@18.2.0 react-dom: 18.2.0_react@18.2.0
@@ -13604,7 +13606,7 @@ packages:
/axios/0.21.4_debug@3.2.7: /axios/0.21.4_debug@3.2.7:
resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==} resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==}
dependencies: dependencies:
follow-redirects: 1.15.1 follow-redirects: 1.15.1_debug@3.2.7
transitivePeerDependencies: transitivePeerDependencies:
- debug - debug
dev: false dev: false
@@ -19295,6 +19297,18 @@ packages:
debug: debug:
optional: true optional: 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: /follow-redirects/1.15.1_debug@4.3.4:
resolution: {integrity: sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==} resolution: {integrity: sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==}
engines: {node: '>=4.0'} engines: {node: '>=4.0'}

View File

@@ -1,19 +1,25 @@
{ {
"$schema": "https://turborepo.org/schema.json", "$schema": "https://turborepo.org/schema.json",
"pipeline": { "pipeline": {
"docs#build": {
"dependsOn": [
"^build",
"next-auth#build",
"@auth/core#build",
"@auth/sveltekit#build"
]
},
"build": { "build": {
"dependsOn": ["^build"] "dependsOn": ["^build"]
}, },
"next-auth#build": { "next-auth#build": {
"dependsOn": ["^build"] "dependsOn": ["^build"],
"outputs": [
"client/**",
"core/**",
"css/**",
"jwt/**",
"next/**",
"providers/**",
"react/**",
"index.d.ts",
"index.js",
"adapters.d.ts",
"middleware.d.ts",
"middleware.js"
]
}, },
"clean": { "clean": {
"cache": false "cache": false