Socket
Socket
Sign inDemoInstall

@auth/core

Package Overview
Dependencies
Maintainers
2
Versions
92
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@auth/core - npm Package Compare versions

Comparing version 0.1.4 to 0.2.0

errors.d.ts

175

adapters.d.ts

@@ -1,2 +0,70 @@

import type { Account, Awaitable, User } from "./index.js";
/**
* This module contains functions and types that a database adapter
* can use to be compatible with Auth.js.
*
* A database adapter provides a common interface for Auth.js so that it can work with
* _any_ database/ORM adapter without concerning itself with the implementation details of the database/ORM.
*
* Auth.js supports 2 session strtategies to persist the login state of a user.
* The default is to use a cookie + {@link https://authjs.dev/concepts/session-strategies#jwt JWT}
* based session store (`strategy: "jwt"`),
* but you can also use a database adapter to store the session in a database.
*
* :::info Note
* Auth.js _currently_ does **not** implement {@link https://authjs.dev/concepts/session-strategies#federated-logout federated logout}.
* So even if the session is deleted from the database, the user will still be logged in to the provider (but will be logged out of the app).
* See [this discussion](https://github.com/nextauthjs/next-auth/discussions/3938) for more information.
* :::
*
* ## Installation
*
* ```bash npm2yarn2pnpm
* npm install @auth/core
* ```
*
* You can then import this submodule from `@auth/core/adapters`.
*
* ## Usage
*
* {@link https://authjs.dev/reference/adapters/overview Built-in adapters} already implement this interfac, so you likely won't need to
* implement it yourself. If you do, you can use the following example as a
* starting point.
*
* ```ts title=your-adapter.ts
* import { type Adapter } from "@auth/core/adapters"
*
* export function MyAdapter(config: {}): Adapter {
* // implement the adapter methods
* }
* ```
*
* ```ts title=index.ts
* import { MyAdapter } from "./your-adapter"
*
* const response = Auth({
* adapter: MyAdapter({ /* ...adapter config *\/ }),
* // ... auth config
* })
* ```
*
* :::caution Note
* Although `@auth/core` is framework/runtime agnostic, an adapter might rely on a client/ORM package,
* that is not yet compatible with your runtime
* (E.g. it might rely on [Node.js-specific APIs](https://nodejs.org/docs/latest/api)) when you are trying to use it elsewhere.
* Related issues should be reported to the corresponding package maintainers.
* :::
*
* ### Testing
* :::tip
* If you are writing your own adapter, there is a test suite [available](https://github.com/nextauthjs/next-auth/tree/main/packages/adapter-test)
* to ensure that your adapter is compatible with Auth.js.
* :::
*
* ## Resources
*
* - [What is a database session strategy?](https://authjs.dev/concepts/session-strategies#database)
*
* @module adapters
*/
import type { Account, Awaitable, User } from "./types.js";
export interface AdapterUser extends User {

@@ -10,7 +78,22 @@ id: string;

}
/**
* The session object implementing this interface
* is used to look up the user in the database.
*/
export interface AdapterSession {
/** A randomly generated value that is used to get hold of the session. */
sessionToken: string;
/** Used to connect the session to a particular user */
/** Connects the active session to a user in the database */
userId: string;
/**
* The absolute date when the session expires.
*
* If a session is accessed prior to its expiry date,
* it will be extended based on the `maxAge` option as defined in by {@linkcode SessionOptions.maxAge}.
* It is never extended more than once in a period defined by {@linkcode SessionOptions.updateAge}.
*
* If a session is accessed past its expiry date,
* it will be removed from the database to clean up inactive sessions.
*
*/
expires: Date;

@@ -30,80 +113,46 @@ }

*
* **Required methods**
* ## Resources
*
* _(These methods are required for all sign in flows)_
* - `createUser`
* - `getUser`
* - `getUserByEmail`
* - `getUserByAccount`
* - `linkAccount`
* - `createSession`
* - `getSessionAndUser`
* - `updateSession`
* - `deleteSession`
* - `updateUser`
*
* _(Required to support email / passwordless sign in)_
*
* - `createVerificationToken`
* - `useVerificationToken`
*
* **Unimplemented methods**
*
* _(These methods will be required in a future release, but are not yet invoked)_
* - `deleteUser`
* - `unlinkAccount`
*
* [Adapters Overview](https://next-auth.js.org/adapters/overview) |
* [Create a custom adapter](https://next-auth.js.org/tutorials/creating-a-database-adapter)
* - [Session strategies](https://authjs.dev/concepts/session-strategies#database)
* - [Using a database adapter](https://authjs.dev/guides/adapters/using-a-database-adapter)
* - [Creating a database adapter](https://authjs.dev/guides/adapters/creating-a-database-adapter)
*/
export declare type Adapter<WithVerificationToken = boolean> = DefaultAdapter & (WithVerificationToken extends true ? {
createVerificationToken: (verificationToken: VerificationToken) => Awaitable<VerificationToken | null | undefined>;
/**
* Return verification token from the database
* and delete it so it cannot be used again.
*/
useVerificationToken: (params: {
identifier: string;
token: string;
}) => Awaitable<VerificationToken | null>;
} : {});
export interface DefaultAdapter {
createUser: (user: Omit<AdapterUser, "id">) => Awaitable<AdapterUser>;
getUser: (id: string) => Awaitable<AdapterUser | null>;
getUserByEmail: (email: string) => Awaitable<AdapterUser | null>;
export interface Adapter {
createUser(user: Omit<AdapterUser, "id">): Awaitable<AdapterUser>;
getUser(id: string): Awaitable<AdapterUser | null>;
getUserByEmail(email: string): Awaitable<AdapterUser | null>;
/** Using the provider id and the id of the user for a specific account, get the user. */
getUserByAccount: (providerAccountId: Pick<AdapterAccount, "provider" | "providerAccountId">) => Awaitable<AdapterUser | null>;
updateUser: (user: Partial<AdapterUser>) => Awaitable<AdapterUser>;
/** @todo Implement */
deleteUser?: (userId: string) => Promise<void> | Awaitable<AdapterUser | null | undefined>;
linkAccount: (account: AdapterAccount) => Promise<void> | Awaitable<AdapterAccount | null | undefined>;
/** @todo Implement */
unlinkAccount?: (providerAccountId: Pick<AdapterAccount, "provider" | "providerAccountId">) => Promise<void> | Awaitable<AdapterAccount | undefined>;
getUserByAccount(providerAccountId: Pick<AdapterAccount, "provider" | "providerAccountId">): Awaitable<AdapterUser | null>;
updateUser(user: Partial<AdapterUser>): Awaitable<AdapterUser>;
/** @todo This method is currently not implemented. Defining it will have no effect */
deleteUser?(userId: string): Promise<void> | Awaitable<AdapterUser | null | undefined>;
linkAccount(account: AdapterAccount): Promise<void> | Awaitable<AdapterAccount | null | undefined>;
/** @todo This method is currently not implemented. Defining it will have no effect */
unlinkAccount?(providerAccountId: Pick<AdapterAccount, "provider" | "providerAccountId">): Promise<void> | Awaitable<AdapterAccount | undefined>;
/** Creates a session for the user and returns it. */
createSession: (session: {
createSession(session: {
sessionToken: string;
userId: string;
expires: Date;
}) => Awaitable<AdapterSession>;
getSessionAndUser: (sessionToken: string) => Awaitable<{
}): Awaitable<AdapterSession>;
getSessionAndUser(sessionToken: string): Awaitable<{
session: AdapterSession;
user: AdapterUser;
} | null>;
updateSession: (session: Partial<AdapterSession> & Pick<AdapterSession, "sessionToken">) => Awaitable<AdapterSession | null | undefined>;
updateSession(session: Partial<AdapterSession> & Pick<AdapterSession, "sessionToken">): Awaitable<AdapterSession | null | undefined>;
/**
* Deletes a session from the database.
* It is preferred that this method also returns the session
* that is being deleted for logging purposes.
* Deletes a session from the database. It is preferred that this method also
* returns the session that is being deleted for logging purposes.
*/
deleteSession: (sessionToken: string) => Promise<void> | Awaitable<AdapterSession | null | undefined>;
createVerificationToken?: (verificationToken: VerificationToken) => Awaitable<VerificationToken | null | undefined>;
deleteSession(sessionToken: string): Promise<void> | Awaitable<AdapterSession | null | undefined>;
createVerificationToken?(verificationToken: VerificationToken): Awaitable<VerificationToken | null | undefined>;
/**
* Return verification token from the database
* and delete it so it cannot be used again.
* Return verification token from the database and delete it so it cannot be
* used again.
*/
useVerificationToken?: (params: {
useVerificationToken?(params: {
identifier: string;
token: string;
}) => Awaitable<VerificationToken | null>;
}): Awaitable<VerificationToken | null>;
}
//# sourceMappingURL=adapters.d.ts.map

@@ -0,1 +1,69 @@

/**
* This module contains functions and types that a database adapter
* can use to be compatible with Auth.js.
*
* A database adapter provides a common interface for Auth.js so that it can work with
* _any_ database/ORM adapter without concerning itself with the implementation details of the database/ORM.
*
* Auth.js supports 2 session strtategies to persist the login state of a user.
* The default is to use a cookie + {@link https://authjs.dev/concepts/session-strategies#jwt JWT}
* based session store (`strategy: "jwt"`),
* but you can also use a database adapter to store the session in a database.
*
* :::info Note
* Auth.js _currently_ does **not** implement {@link https://authjs.dev/concepts/session-strategies#federated-logout federated logout}.
* So even if the session is deleted from the database, the user will still be logged in to the provider (but will be logged out of the app).
* See [this discussion](https://github.com/nextauthjs/next-auth/discussions/3938) for more information.
* :::
*
* ## Installation
*
* ```bash npm2yarn2pnpm
* npm install @auth/core
* ```
*
* You can then import this submodule from `@auth/core/adapters`.
*
* ## Usage
*
* {@link https://authjs.dev/reference/adapters/overview Built-in adapters} already implement this interfac, so you likely won't need to
* implement it yourself. If you do, you can use the following example as a
* starting point.
*
* ```ts title=your-adapter.ts
* import { type Adapter } from "@auth/core/adapters"
*
* export function MyAdapter(config: {}): Adapter {
* // implement the adapter methods
* }
* ```
*
* ```ts title=index.ts
* import { MyAdapter } from "./your-adapter"
*
* const response = Auth({
* adapter: MyAdapter({ /* ...adapter config *\/ }),
* // ... auth config
* })
* ```
*
* :::caution Note
* Although `@auth/core` is framework/runtime agnostic, an adapter might rely on a client/ORM package,
* that is not yet compatible with your runtime
* (E.g. it might rely on [Node.js-specific APIs](https://nodejs.org/docs/latest/api)) when you are trying to use it elsewhere.
* Related issues should be reported to the corresponding package maintainers.
* :::
*
* ### Testing
* :::tip
* If you are writing your own adapter, there is a test suite [available](https://github.com/nextauthjs/next-auth/tree/main/packages/adapter-test)
* to ensure that your adapter is compatible with Auth.js.
* :::
*
* ## Resources
*
* - [What is a database session strategy?](https://authjs.dev/concepts/session-strategies#database)
*
* @module adapters
*/
export {};

@@ -1,9 +0,266 @@

import type { AuthOptions } from "./lib/types.js";
export * from "./lib/types.js";
/**
* The core functionality of Auth.js.
* It receives a standard [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
* and returns a standard [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response).
*
* This is the main entry point to the Auth.js library.
*
* Based on the {@link https://developer.mozilla.org/en-US/docs/Web/API/Request Request}
* and {@link https://developer.mozilla.org/en-US/docs/Web/API/Response Response} Web standard APIs.
* Primarily used to implement [framework](https://authjs.dev/concepts/frameworks)-specific packages,
* but it can also be used directly.
*
* ## Installation
*
* ```bash npm2yarn2pnpm
* npm install @auth/core
* ```
*
* ## Usage
*
* ```ts
* import { Auth } from "@auth/core"
*
* const request = new Request("https://example.com"
* const response = await Auth(request, {...})
*
* console.log(response instanceof Response) // true
* ```
*
* ## Resources
*
* - [Gettint started](https://authjs.dev/getting-started/introduction)
* - [Most common use case guides](https://authjs.dev/guides/overview)
*
* @module main
*/
export declare function AuthHandler(request: Request, options: AuthOptions): Promise<Response>;
import { type LoggerInstance } from "./lib/utils/logger.js";
import type { Adapter } from "./adapters.js";
import type { CallbacksOptions, CookiesOptions, EventCallbacks, PagesOptions, SessionOptions, Theme } from "./types.js";
import type { Provider } from "./providers/index.js";
import { JWTOptions } from "./jwt.js";
/**
* Core functionality provided by Auth.js.
*
* Receives a standard {@link Request} and returns a {@link Response}.
*
* @example
* ```ts
* import Auth from "@auth/core"
*
* const request = new Request("https://example.com")
* const resposne = await AuthHandler(request, {
* providers: [...],
* secret: "...",
* trustHost: true,
* })
*```
* @see [Documentation](https://authjs.dev)
*/
export declare function Auth(request: Request, config: AuthConfig): Promise<Response>;
/**
* Configure the {@link Auth} method.
*
* @example
* ```ts
* import Auth, { type AuthConfig } from "@auth/core"
*
* export const authConfig: AuthConfig = {...}
*
* const request = new Request("https://example.com")
* const resposne = await AuthHandler(request, authConfig)
*
* ```
*
* @see [Initiailzation](https://authjs.dev/reference/configuration/auth-options)
*/
export interface AuthConfig {
/**
* List of authentication providers for signing in
* (e.g. Google, Facebook, Twitter, GitHub, Email, etc) in any order.
* This can be one of the built-in providers or an object with a custom provider.
* * **Default value**: `[]`
* * **Required**: *Yes*
*
* [Documentation](https://next-auth.js.org/configuration/options#providers) | [Providers documentation](https://next-auth.js.org/configuration/providers)
*/
providers: Provider[];
/**
* A random string used to hash tokens, sign cookies and generate cryptographic keys.
* If not specified, it falls back to `AUTH_SECRET` or `NEXTAUTH_SECRET` from environment variables.
* To generate a random string, you can use the following command:
*
* On Unix systems: `openssl rand -hex 32`
* Or go to https://generate-secret.vercel.app/32
*
* @default process.env.AUTH_SECRET ?? process.env.NEXTAUTH_SECRET
*
* [Documentation](https://next-auth.js.org/configuration/options#secret)
*/
secret?: string;
/**
* Configure your session like if you want to use JWT or a database,
* how long until an idle session expires, or to throttle write operations in case you are using a database.
* * **Default value**: See the documentation page
* * **Required**: No
*
* [Documentation](https://next-auth.js.org/configuration/options#session)
*/
session?: Partial<SessionOptions>;
/**
* JSON Web Tokens are enabled by default if you have not specified an adapter.
* JSON Web Tokens are encrypted (JWE) by default. We recommend you keep this behaviour.
* * **Default value**: See the documentation page
* * **Required**: *No*
*
* [Documentation](https://next-auth.js.org/configuration/options#jwt)
*/
jwt?: Partial<JWTOptions>;
/**
* Specify URLs to be used if you want to create custom sign in, sign out and error pages.
* Pages specified will override the corresponding built-in page.
* * **Default value**: `{}`
* * **Required**: *No*
*
* @example
*
* ```ts
* pages: {
* signIn: '/auth/signin',
* signOut: '/auth/signout',
* error: '/auth/error',
* verifyRequest: '/auth/verify-request',
* newUser: '/auth/new-user'
* }
* ```
*
* [Documentation](https://next-auth.js.org/configuration/options#pages) | [Pages documentation](https://next-auth.js.org/configuration/pages)
*/
pages?: Partial<PagesOptions>;
/**
* Callbacks are asynchronous functions you can use to control what happens when an action is performed.
* Callbacks are *extremely powerful*, especially in scenarios involving JSON Web Tokens
* as they **allow you to implement access controls without a database** and to **integrate with external databases or APIs**.
* * **Default value**: See the Callbacks documentation
* * **Required**: *No*
*
* [Documentation](https://next-auth.js.org/configuration/options#callbacks) | [Callbacks documentation](https://next-auth.js.org/configuration/callbacks)
*/
callbacks?: Partial<CallbacksOptions>;
/**
* Events are asynchronous functions that do not return a response, they are useful for audit logging.
* You can specify a handler for any of these events below - e.g. for debugging or to create an audit log.
* The content of the message object varies depending on the flow
* (e.g. OAuth or Email authentication flow, JWT or database sessions, etc),
* but typically contains a user object and/or contents of the JSON Web Token
* and other information relevant to the event.
* * **Default value**: `{}`
* * **Required**: *No*
*
* [Documentation](https://next-auth.js.org/configuration/options#events) | [Events documentation](https://next-auth.js.org/configuration/events)
*/
events?: Partial<EventCallbacks>;
/**
* You can use the adapter option to pass in your database adapter.
*
* * **Required**: *No*
*
* [Documentation](https://next-auth.js.org/configuration/options#adapter) |
* [Adapters Overview](https://next-auth.js.org/adapters/overview)
*/
adapter?: Adapter;
/**
* Set debug to true to enable debug messages for authentication and database operations.
* * **Default value**: `false`
* * **Required**: *No*
*
* - ⚠ If you added a custom `logger`, this setting is ignored.
*
* [Documentation](https://next-auth.js.org/configuration/options#debug) | [Logger documentation](https://next-auth.js.org/configuration/options#logger)
*/
debug?: boolean;
/**
* Override any of the logger levels (`undefined` levels will use the built-in logger),
* and intercept logs in NextAuth. You can use this option to send NextAuth logs to a third-party logging service.
* * **Default value**: `console`
* * **Required**: *No*
*
* @example
*
* ```ts
* // /pages/api/auth/[...nextauth].js
* import log from "logging-service"
* export default NextAuth({
* logger: {
* error(code, ...message) {
* log.error(code, message)
* },
* warn(code, ...message) {
* log.warn(code, message)
* },
* debug(code, ...message) {
* log.debug(code, message)
* }
* }
* })
* ```
*
* - ⚠ When set, the `debug` option is ignored
*
* [Documentation](https://next-auth.js.org/configuration/options#logger) |
* [Debug documentation](https://next-auth.js.org/configuration/options#debug)
*/
logger?: Partial<LoggerInstance>;
/**
* Changes the theme of pages.
* Set to `"light"` if you want to force pages to always be light.
* Set to `"dark"` if you want to force pages to always be dark.
* Set to `"auto"`, (or leave this option out)if you want the pages to follow the preferred system theme.
* * **Default value**: `"auto"`
* * **Required**: *No*
*
* [Documentation](https://next-auth.js.org/configuration/options#theme) | [Pages documentation]("https://next-auth.js.org/configuration/pages")
*/
theme?: Theme;
/**
* When set to `true` then all cookies set by NextAuth.js will only be accessible from HTTPS URLs.
* This option defaults to `false` on URLs that start with `http://` (e.g. http://localhost:3000) for developer convenience.
* You can manually set this option to `false` to disable this security feature and allow cookies
* to be accessible from non-secured URLs (this is not recommended).
* * **Default value**: `true` for HTTPS and `false` for HTTP sites
* * **Required**: No
*
* [Documentation](https://next-auth.js.org/configuration/options#usesecurecookies)
*
* - ⚠ **This is an advanced option.** Advanced options are passed the same way as basic options,
* but **may have complex implications** or side effects.
* You should **try to avoid using advanced options** unless you are very comfortable using them.
*/
useSecureCookies?: boolean;
/**
* You can override the default cookie names and options for any of the cookies used by NextAuth.js.
* You can specify one or more cookies with custom properties,
* but if you specify custom options for a cookie you must provide all the options for that cookie.
* If you use this feature, you will likely want to create conditional behavior
* to support setting different cookies policies in development and production builds,
* as you will be opting out of the built-in dynamic policy.
* * **Default value**: `{}`
* * **Required**: No
*
* - ⚠ **This is an advanced option.** Advanced options are passed the same way as basic options,
* but **may have complex implications** or side effects.
* You should **try to avoid using advanced options** unless you are very comfortable using them.
*
* [Documentation](https://next-auth.js.org/configuration/options#cookies) | [Usage example](https://next-auth.js.org/configuration/options#example)
*/
cookies?: Partial<CookiesOptions>;
/**
* If set to `true`, NextAuth.js will use either the `x-forwarded-host` or `host` headers,
* instead of `NEXTAUTH_URL`
* Make sure that reading `x-forwarded-host` on your hosting platform can be trusted.
* - ⚠ **This is an advanced option.** Advanced options are passed the same way as basic options,
* but **may have complex implications** or side effects.
* You should **try to avoid using advanced options** unless you are very comfortable using them.
*
* @default Boolean(process.env.NEXTAUTH_URL ?? process.env.AUTH_TRUST_HOST ?? process.env.VERCEL)
*/
trustHost?: boolean;
}
//# sourceMappingURL=index.d.ts.map

@@ -1,14 +0,66 @@

import { init } from "./lib/init.js";
/**
*
* This is the main entry point to the Auth.js library.
*
* Based on the {@link https://developer.mozilla.org/en-US/docs/Web/API/Request Request}
* and {@link https://developer.mozilla.org/en-US/docs/Web/API/Response Response} Web standard APIs.
* Primarily used to implement [framework](https://authjs.dev/concepts/frameworks)-specific packages,
* but it can also be used directly.
*
* ## Installation
*
* ```bash npm2yarn2pnpm
* npm install @auth/core
* ```
*
* ## Usage
*
* ```ts
* import { Auth } from "@auth/core"
*
* const request = new Request("https://example.com"
* const response = await Auth(request, {...})
*
* console.log(response instanceof Response) // true
* ```
*
* ## Resources
*
* - [Gettint started](https://authjs.dev/getting-started/introduction)
* - [Most common use case guides](https://authjs.dev/guides/overview)
*
* @module main
*/
import { assertConfig } from "./lib/assert.js";
import { SessionStore } from "./lib/cookie.js";
import { ErrorPageLoop } from "./errors.js";
import { AuthInternal } from "./lib/index.js";
import renderPage from "./lib/pages/index.js";
import { logger, setLogger } from "./lib/utils/logger.js";
import { toInternalRequest, toResponse } from "./lib/web.js";
import renderPage from "./lib/pages/index.js";
import * as routes from "./lib/routes/index.js";
import logger, { setLogger } from "./lib/utils/logger.js";
import { UntrustedHost } from "./lib/errors.js";
export * from "./lib/types.js";
const configErrorMessage = "There is a problem with the server configuration. Check the server logs for more information.";
async function AuthHandlerInternal(params) {
const { options: authOptions, req } = params;
const assertionResult = assertConfig({ options: authOptions, req });
/**
* Core functionality provided by Auth.js.
*
* Receives a standard {@link Request} and returns a {@link Response}.
*
* @example
* ```ts
* import Auth from "@auth/core"
*
* const request = new Request("https://example.com")
* const resposne = await AuthHandler(request, {
* providers: [...],
* secret: "...",
* trustHost: true,
* })
*```
* @see [Documentation](https://authjs.dev)
*/
export async function Auth(request, config) {
setLogger(config.logger, config.debug);
const internalRequest = await toInternalRequest(request);
if (internalRequest instanceof Error) {
logger.error(internalRequest);
return new Response(`Error: This action with HTTP ${request.method} is not supported.`, { status: 400 });
}
const assertionResult = assertConfig(internalRequest, config);
if (Array.isArray(assertionResult)) {

@@ -19,200 +71,27 @@ assertionResult.forEach(logger.warn);

// Bail out early if there's an error in the user config
logger.error(assertionResult.code, assertionResult);
logger.error(assertionResult);
const htmlPages = ["signin", "signout", "error", "verify-request"];
if (!htmlPages.includes(req.action) || req.method !== "GET") {
return {
status: 500,
headers: { "Content-Type": "application/json" },
body: { message: configErrorMessage },
};
if (!htmlPages.includes(internalRequest.action) ||
internalRequest.method !== "GET") {
return new Response(JSON.stringify({
message: "There was a problem with the server configuration. Check the server logs for more information.",
code: assertionResult.name,
}), { status: 500, headers: { "Content-Type": "application/json" } });
}
const { pages, theme } = authOptions;
const authOnErrorPage = pages?.error && req.query?.callbackUrl?.startsWith(pages.error);
const { pages, theme } = config;
const authOnErrorPage = pages?.error &&
internalRequest.url.searchParams
.get("callbackUrl")
?.startsWith(pages.error);
if (!pages?.error || authOnErrorPage) {
if (authOnErrorPage) {
logger.error("AUTH_ON_ERROR_PAGE_ERROR", new Error(`The error page ${pages?.error} should not require authentication`));
logger.error(new ErrorPageLoop(`The error page ${pages?.error} should not require authentication`));
}
const render = renderPage({ theme });
return render.error({ error: "configuration" });
const page = render.error({ error: "Configuration" });
return toResponse(page);
}
return {
redirect: `${pages.error}?error=Configuration`,
};
return Response.redirect(`${pages.error}?error=Configuration`);
}
const { action, providerId, error, method } = req;
const { options, cookies } = await init({
authOptions,
action,
providerId,
url: req.url,
callbackUrl: req.body?.callbackUrl ?? req.query?.callbackUrl,
csrfToken: req.body?.csrfToken,
cookies: req.cookies,
isPost: method === "POST",
});
const sessionStore = new SessionStore(options.cookies.sessionToken, req, options.logger);
if (method === "GET") {
const render = renderPage({ ...options, query: req.query, cookies });
const { pages } = options;
switch (action) {
case "providers":
return (await routes.providers(options.providers));
case "session": {
const session = await routes.session({ options, sessionStore });
if (session.cookies)
cookies.push(...session.cookies);
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
return { ...session, cookies };
}
case "csrf":
return {
headers: { "Content-Type": "application/json" },
body: { csrfToken: options.csrfToken },
cookies,
};
case "signin":
if (pages.signIn) {
let signinUrl = `${pages.signIn}${pages.signIn.includes("?") ? "&" : "?"}callbackUrl=${encodeURIComponent(options.callbackUrl)}`;
if (error)
signinUrl = `${signinUrl}&error=${encodeURIComponent(error)}`;
return { redirect: signinUrl, cookies };
}
return render.signin();
case "signout":
if (pages.signOut)
return { redirect: pages.signOut, cookies };
return render.signout();
case "callback":
if (options.provider) {
const callback = await routes.callback({
body: req.body,
query: req.query,
headers: req.headers,
cookies: req.cookies,
method,
options,
sessionStore,
});
if (callback.cookies)
cookies.push(...callback.cookies);
return { ...callback, cookies };
}
break;
case "verify-request":
if (pages.verifyRequest) {
return { redirect: pages.verifyRequest, cookies };
}
return render.verifyRequest();
case "error":
// These error messages are displayed in line on the sign in page
if ([
"Signin",
"OAuthSignin",
"OAuthCallback",
"OAuthCreateAccount",
"EmailCreateAccount",
"Callback",
"OAuthAccountNotLinked",
"EmailSignin",
"CredentialsSignin",
"SessionRequired",
].includes(error)) {
return { redirect: `${options.url}/signin?error=${error}`, cookies };
}
if (pages.error) {
return {
redirect: `${pages.error}${pages.error.includes("?") ? "&" : "?"}error=${error}`,
cookies,
};
}
return render.error({ error: error });
default:
}
}
else if (method === "POST") {
switch (action) {
case "signin":
// Verified CSRF Token required for all sign in routes
if (options.csrfTokenVerified && options.provider) {
const signin = await routes.signin({
query: req.query,
body: req.body,
options,
});
if (signin.cookies)
cookies.push(...signin.cookies);
return { ...signin, cookies };
}
return { redirect: `${options.url}/signin?csrf=true`, cookies };
case "signout":
// Verified CSRF Token required for signout
if (options.csrfTokenVerified) {
const signout = await routes.signout({ options, sessionStore });
if (signout.cookies)
cookies.push(...signout.cookies);
return { ...signout, cookies };
}
return { redirect: `${options.url}/signout?csrf=true`, cookies };
case "callback":
if (options.provider) {
// Verified CSRF Token required for credentials providers only
if (options.provider.type === "credentials" &&
!options.csrfTokenVerified) {
return { redirect: `${options.url}/signin?csrf=true`, cookies };
}
const callback = await routes.callback({
body: req.body,
query: req.query,
headers: req.headers,
cookies: req.cookies,
method,
options,
sessionStore,
});
if (callback.cookies)
cookies.push(...callback.cookies);
return { ...callback, cookies };
}
break;
case "_log":
if (authOptions.logger) {
try {
const { code, level, ...metadata } = req.body ?? {};
logger[level](code, metadata);
}
catch (error) {
// If logging itself failed...
logger.error("LOGGER_ERROR", error);
}
}
return {};
default:
}
}
return {
status: 400,
body: `Error: This action with HTTP ${method} is not supported by NextAuth.js`,
};
}
/**
* The core functionality of Auth.js.
* It receives a standard [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
* and returns a standard [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response).
*/
export async function AuthHandler(request, options) {
setLogger(options.logger, options.debug);
if (!options.trustHost) {
const error = new UntrustedHost(`Host must be trusted. URL was: ${request.url}`);
logger.error(error.code, error);
return new Response(JSON.stringify({ message: configErrorMessage }), {
status: 500,
headers: { "Content-Type": "application/json" },
});
}
const req = await toInternalRequest(request);
if (req instanceof Error) {
logger.error(req.code, req);
return new Response(`Error: This action with HTTP ${request.method} is not supported.`, { status: 400 });
}
const internalResponse = await AuthHandlerInternal({ req, options });
const internalResponse = await AuthInternal(internalRequest, config);
const response = await toResponse(internalResponse);

@@ -219,0 +98,0 @@ // If the request expects a return URL, send it as JSON

@@ -1,16 +0,11 @@

import { InvalidCallbackUrl, InvalidEndpoints, MissingAdapter, MissingAdapterMethods, MissingAPIRoute, MissingAuthorize, MissingSecret, UnsupportedStrategy } from "./errors.js";
import type { AuthOptions, RequestInternal } from "../index.js";
import { InvalidCallbackUrl, InvalidEndpoints, MissingAdapter, MissingAdapterMethods, MissingAuthorize, MissingSecret, UnsupportedStrategy } from "../errors.js";
import type { AuthConfig, RequestInternal } from "../types.js";
import type { WarningCode } from "./utils/logger.js";
declare type ConfigError = MissingAdapter | MissingAdapterMethods | MissingAPIRoute | MissingAuthorize | MissingSecret | InvalidCallbackUrl | UnsupportedStrategy | InvalidEndpoints | UnsupportedStrategy;
declare type ConfigError = InvalidCallbackUrl | InvalidEndpoints | MissingAdapter | MissingAdapterMethods | MissingAuthorize | MissingSecret | UnsupportedStrategy;
/**
* Verify that the user configured Auth.js correctly.
* Good place to mention deprecations as well.
*
* REVIEW: Make some of these and corresponding docs less Next.js specific?
*/
export declare function assertConfig(params: {
options: AuthOptions;
req: RequestInternal;
}): ConfigError | WarningCode[];
export declare function assertConfig(request: RequestInternal, options: AuthConfig): ConfigError | WarningCode[];
export {};
//# sourceMappingURL=assert.d.ts.map

@@ -1,3 +0,3 @@

import { InvalidCallbackUrl, InvalidEndpoints, MissingAdapter, MissingAdapterMethods, MissingAPIRoute, MissingAuthorize, MissingSecret, UnsupportedStrategy, } from "./errors.js";
import { defaultCookies } from "./cookie.js";
import { InvalidCallbackUrl, InvalidEndpoints, MissingAdapter, MissingAdapterMethods, MissingAuthorize, MissingSecret, UnsupportedStrategy, UntrustedHost, } from "../errors.js";
let warned = false;

@@ -15,14 +15,10 @@ function isValidHttpUrl(url, baseUrl) {

* Good place to mention deprecations as well.
*
* REVIEW: Make some of these and corresponding docs less Next.js specific?
*/
export function assertConfig(params) {
const { options, req } = params;
const { url } = req;
export function assertConfig(request, options) {
const { url } = request;
const warnings = [];
if (!warned) {
if (!url.origin)
warnings.push("NEXTAUTH_URL");
if (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}`);
}

@@ -32,7 +28,3 @@ if (!options.secret) {

}
// req.query isn't defined when asserting `unstable_getServerSession` for example
if (!req.query?.nextauth && !req.action) {
return new MissingAPIRoute("Cannot find [...nextauth].{js,ts} in `/pages/api/auth`. Make sure the filename is written correctly.");
}
const callbackUrlParam = req.query?.callbackUrl;
const callbackUrlParam = request.query?.callbackUrl;
if (callbackUrlParam && !isValidHttpUrl(callbackUrlParam, url.origin)) {

@@ -42,3 +34,3 @@ return new InvalidCallbackUrl(`Invalid callback URL. Received: ${callbackUrlParam}`);

const { callbackUrl: defaultCallbackUrl } = defaultCookies(options.useSecureCookies ?? url.protocol === "https://");
const callbackUrlCookie = req.cookies?.[options.cookies?.callbackUrl?.name ?? defaultCallbackUrl.name];
const callbackUrlCookie = request.cookies?.[options.cookies?.callbackUrl?.name ?? defaultCallbackUrl.name];
if (callbackUrlCookie && !isValidHttpUrl(callbackUrlCookie, url.origin)) {

@@ -45,0 +37,0 @@ return new InvalidCallbackUrl(`Invalid callback URL. Received: ${callbackUrlCookie}`);

@@ -1,4 +0,4 @@

import type { Account, InternalOptions, User } from "../index.js";
import type { AdapterSession, AdapterUser } from "../adapters.js";
import type { JWT } from "../jwt/index.js";
import type { Account, InternalOptions, User } from "../types.js";
import type { JWT } from "../jwt.js";
import type { SessionToken } from "./cookie.js";

@@ -17,10 +17,5 @@ /**

*/
export default function callbackHandler(params: {
sessionToken?: SessionToken;
profile: User | AdapterUser | {
email: string;
};
account: Account | null;
options: InternalOptions;
}): Promise<{
export declare function handleLogin(sessionToken: SessionToken, _profile: User | AdapterUser | {
email: string;
}, account: Account | null, options: InternalOptions): Promise<{
user: User;

@@ -27,0 +22,0 @@ account: Account;

@@ -1,2 +0,2 @@

import { AccountNotLinkedError } from "./errors.js";
import { AccountNotLinked } from "../errors.js";
import { fromDate } from "./utils/date.js";

@@ -15,4 +15,3 @@ /**

*/
export default async function callbackHandler(params) {
const { sessionToken, profile: _profile, account, options } = params;
export async function handleLogin(sessionToken, _profile, account, options) {
// Input validation

@@ -88,3 +87,3 @@ if (!account?.providerAccountId || !account.type)

}
else if (account.type === "oauth") {
else if (account.type === "oauth" || account.type === "oidc") {
// If signing in with OAuth account, check to see if the account exists already

@@ -104,3 +103,3 @@ const userByAccount = await getUserByAccount({

// and need to return an error.
throw new AccountNotLinkedError("The account is already associated with another user");
throw new AccountNotLinked("The account is already associated with another user");
}

@@ -162,3 +161,3 @@ // If there is no active session, but the account being signed in with is already

// to sign in via email to verify their identity and then link the accounts.
throw new AccountNotLinkedError("Another account already exists with the same e-mail address");
throw new AccountNotLinked("Another account already exists with the same e-mail address");
}

@@ -165,0 +164,0 @@ }

@@ -1,2 +0,2 @@

import type { InternalOptions } from "../index.js";
import type { InternalOptions } from "../types.js";
interface CreateCallbackUrlParams {

@@ -3,0 +3,0 @@ options: InternalOptions;

@@ -1,2 +0,2 @@

import type { CookieOption, CookiesOptions, LoggerInstance, SessionStrategy } from "../index.js";
import type { CookieOption, CookiesOptions, LoggerInstance, SessionStrategy } from "../types.js";
/** Stringified form of `JWT`. Extract the content with `jwt.decode` */

@@ -33,2 +33,6 @@ export declare type JWTString = string;

}>, logger: LoggerInstance | Console);
/**
* The JWT Session or database Session ID
* constructed from the cookie chunks.
*/
get value(): string;

@@ -35,0 +39,0 @@ /**

@@ -138,2 +138,6 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {

}
/**
* The JWT Session or database Session ID
* constructed from the cookie chunks.
*/
get value() {

@@ -140,0 +144,0 @@ return Object.values(__classPrivateFieldGet(this, _SessionStore_chunks, "f"))?.join("");

@@ -1,2 +0,2 @@

import type { InternalOptions } from "./types.js";
import type { InternalOptions } from "../types.js";
interface CreateCSRFTokenParams {

@@ -3,0 +3,0 @@ options: InternalOptions;

@@ -1,3 +0,3 @@

import type { CallbacksOptions } from "../index.js";
import type { CallbacksOptions } from "../types.js";
export declare const defaultCallbacks: CallbacksOptions;
//# sourceMappingURL=default-callbacks.d.ts.map

@@ -1,2 +0,2 @@

import type { InternalOptions } from "../../index.js";
import type { InternalOptions } from "../../types.js";
/**

@@ -3,0 +3,0 @@ * Starts an e-mail login flow, by generating a token,

@@ -1,2 +0,2 @@

import { randomString, createHash } from "../web.js";
import { createHash, randomString } from "../web.js";
/**

@@ -8,3 +8,2 @@ * Starts an e-mail login flow, by generating a token,

const { url, adapter, provider, callbackUrl, theme } = options;
// Generate token
const token = (await provider.generateVerificationToken?.()) ?? randomString(32);

@@ -18,3 +17,2 @@ const ONE_DAY_IN_SECONDS = 86400;

await Promise.all([
// Send to user
provider.sendVerificationRequest({

@@ -28,4 +26,4 @@ identifier,

}),
// Save in database
adapter.createVerificationToken({
// @ts-expect-error -- Verified in `assertConfig`.
adapter.createVerificationToken?.({
identifier,

@@ -32,0 +30,0 @@ token: await createHash(`${token}${secret}`),

import * as cookie from "./cookie.js";
import type { AuthOptions, InternalOptions, RequestInternal } from "../index.js";
import type { AuthConfig, InternalOptions, RequestInternal } from "../types.js";
interface InitParams {
url: URL;
authOptions: AuthOptions;
authOptions: AuthConfig;
providerId?: string;

@@ -7,0 +7,0 @@ action: InternalOptions["action"];

@@ -1,3 +0,2 @@

import { adapterErrorHandler, eventsErrorHandler } from "./errors.js";
import * as jwt from "../jwt/index.js";
import * as jwt from "../jwt.js";
import { createCallbackUrl } from "./callback-url.js";

@@ -7,4 +6,5 @@ import * as cookie from "./cookie.js";

import { defaultCallbacks } from "./default-callbacks.js";
import { AdapterError, EventError } from "../errors.js";
import parseProviders from "./providers.js";
import logger from "./utils/logger.js";
import { logger } from "./utils/logger.js";
import parseUrl from "./utils/parse-url.js";

@@ -107,1 +107,36 @@ /** Initialize all internal options and cookies. */

}
/** Wraps an object of methods and adds error handling. */
function eventsErrorHandler(methods, logger) {
return Object.keys(methods).reduce((acc, name) => {
acc[name] = async (...args) => {
try {
const method = methods[name];
return await method(...args);
}
catch (e) {
logger.error(new EventError(e));
}
};
return acc;
}, {});
}
/** Handles adapter induced errors. */
function adapterErrorHandler(adapter, logger) {
if (!adapter)
return;
return Object.keys(adapter).reduce((acc, name) => {
acc[name] = async (...args) => {
try {
logger.debug(`adapter_${name}`, { args });
const method = adapter[name];
return await method(...args);
}
catch (e) {
const error = new AdapterError(e);
logger.error(error);
throw error;
}
};
return acc;
}, {});
}

@@ -1,2 +0,2 @@

import type { CookiesOptions, InternalOptions, RequestInternal, ResponseInternal } from "../../index.js";
import type { CookiesOptions, InternalOptions, RequestInternal, ResponseInternal } from "../../types.js";
import type { Cookie } from "../cookie.js";

@@ -8,8 +8,5 @@ /**

*/
export declare function getAuthorizationUrl({ options, query, }: {
options: InternalOptions<"oauth">;
query: RequestInternal["query"];
}): Promise<ResponseInternal>;
export declare function getAuthorizationUrl(query: RequestInternal["query"], options: InternalOptions<"oauth">): Promise<ResponseInternal>;
/** Returns a signed cookie. */
export declare function signCookie(type: keyof CookiesOptions, value: string, maxAge: number, options: InternalOptions<"oauth">): Promise<Cookie>;
//# sourceMappingURL=authorization-url.d.ts.map

@@ -7,3 +7,3 @@ import * as o from "oauth4webapi";

*/
export async function getAuthorizationUrl({ options, query, }) {
export async function getAuthorizationUrl(query, options) {
const { logger, provider } = options;

@@ -62,3 +62,2 @@ let url = provider.authorization?.url;

}
url.searchParams.delete("nextauth");
// TODO: This does not work in normalizeOAuth because authorization endpoint can come from discovery

@@ -69,3 +68,3 @@ // Need to make normalizeOAuth async

}
logger.debug("GET_AUTHORIZATION_URL", { url, cookies, provider });
logger.debug("authorization url is ready", { url, cookies, provider });
return { redirect: url, cookies };

@@ -72,0 +71,0 @@ }

@@ -1,11 +0,15 @@

import type { InternalOptions, Profile, RequestInternal } from "../../index.js";
import type { InternalOptions, Profile, RequestInternal } from "../../types.js";
import type { Cookie } from "../cookie.js";
export declare function handleOAuthCallback(params: {
options: InternalOptions<"oauth">;
query: RequestInternal["query"];
body: RequestInternal["body"];
cookies: RequestInternal["cookies"];
}): Promise<{
/**
* Handles the following OAuth steps.
* https://www.rfc-editor.org/rfc/rfc6749#section-4.1.1
* https://www.rfc-editor.org/rfc/rfc6749#section-4.1.3
* https://openid.net/specs/openid-connect-core-1_0.html#UserInfoRequest
*
* @note Although requesting userinfo is not required by the OAuth2.0 spec,
* we fetch it anyway. This is because we always want a user profile.
*/
export declare function handleOAuth(query: RequestInternal["query"], cookies: RequestInternal["cookies"], options: InternalOptions<"oauth">): Promise<{
cookies: Cookie[];
profile?: import("../types.js").User | undefined;
profile?: import("../../types.js").User | undefined;
account?: {

@@ -19,3 +23,3 @@ access_token?: string | undefined;

provider: string;
type: "oauth" | "oidc";
type: "oidc" | "oauth";
providerAccountId: string;

@@ -30,3 +34,3 @@ } | {

provider: string;
type: "oauth" | "oidc";
type: "oidc" | "oauth";
providerAccountId: string;

@@ -33,0 +37,0 @@ } | undefined;

@@ -1,118 +0,106 @@

import { OAuthCallbackError } from "../errors.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 * as o from "oauth4webapi";
export async function handleOAuthCallback(params) {
const { options, query, body, cookies } = params;
/**
* Handles the following OAuth steps.
* https://www.rfc-editor.org/rfc/rfc6749#section-4.1.1
* https://www.rfc-editor.org/rfc/rfc6749#section-4.1.3
* https://openid.net/specs/openid-connect-core-1_0.html#UserInfoRequest
*
* @note Although requesting userinfo is not required by the OAuth2.0 spec,
* we fetch it anyway. This is because we always want a user profile.
*/
export async function handleOAuth(query, cookies, options) {
const { logger, provider } = options;
const errorMessage = body?.error ?? query?.error;
if (errorMessage) {
const error = new Error(errorMessage);
logger.error("OAUTH_CALLBACK_HANDLER_ERROR", {
error,
error_description: query?.error_description,
let as;
if (!provider.token?.url && !provider.userinfo?.url) {
// We assume that issuer is always defined as this has been asserted earlier
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const issuer = new URL(provider.issuer);
const discoveryResponse = await o.discoveryRequest(issuer);
const discoveredAs = await o.processDiscoveryResponse(issuer, discoveryResponse);
if (!discoveredAs.token_endpoint)
throw new TypeError("TODO: Authorization server did not provide a token endpoint.");
if (!discoveredAs.userinfo_endpoint)
throw new TypeError("TODO: Authorization server did not provide a userinfo endpoint.");
as = discoveredAs;
}
else {
as = {
issuer: provider.issuer ?? "https://a",
token_endpoint: provider.token?.url.toString(),
userinfo_endpoint: provider.userinfo?.url.toString(),
};
}
const client = {
client_id: provider.clientId,
client_secret: provider.clientSecret,
...provider.client,
};
const resCookies = [];
const state = await useState(cookies, resCookies, options);
const parameters = o.validateAuthResponse(as, client, new URLSearchParams(query), provider.checks.includes("state") ? state : o.skipStateCheck);
/** https://www.rfc-editor.org/rfc/rfc6749#section-4.1.2.1 */
if (o.isOAuth2Error(parameters)) {
logger.debug("OAuthCallbackError", {
providerId: provider.id,
...parameters,
});
logger.debug("OAUTH_CALLBACK_HANDLER_ERROR", { body });
throw error;
throw new OAuthCallbackError(parameters.error);
}
try {
let as;
if (!provider.token?.url && !provider.userinfo?.url) {
// We assume that issuer is always defined as this has been asserted earlier
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const issuer = new URL(provider.issuer);
const discoveryResponse = await o.discoveryRequest(issuer);
const discoveredAs = await o.processDiscoveryResponse(issuer, discoveryResponse);
if (!discoveredAs.token_endpoint)
throw new TypeError("TODO: Authorization server did not provide a token endpoint.");
if (!discoveredAs.userinfo_endpoint)
throw new TypeError("TODO: Authorization server did not provide a userinfo endpoint.");
as = discoveredAs;
const codeVerifier = await usePKCECodeVerifier(cookies?.[options.cookies.pkceCodeVerifier.name], options);
if (codeVerifier)
resCookies.push(codeVerifier.cookie);
// TODO:
const nonce = await useNonce(cookies?.[options.cookies.nonce.name], options);
if (nonce && provider.type === "oidc") {
resCookies.push(nonce.cookie);
}
const codeGrantResponse = await o.authorizationCodeGrantRequest(as, client, parameters, provider.callbackUrl, codeVerifier?.codeVerifier ?? "auth" // TODO: review fallback code verifier
);
let challenges;
if ((challenges = o.parseWwwAuthenticateChallenges(codeGrantResponse))) {
for (const challenge of challenges) {
console.log("challenge", challenge);
}
else {
as = {
issuer: provider.issuer ?? "https://a",
token_endpoint: provider.token?.url.toString(),
userinfo_endpoint: provider.userinfo?.url.toString(),
};
throw new Error("TODO: Handle www-authenticate challenges as needed");
}
let profile = {};
let tokens;
if (provider.type === "oidc") {
const result = await o.processAuthorizationCodeOpenIDResponse(as, client, codeGrantResponse);
if (o.isOAuth2Error(result)) {
console.log("error", result);
throw new Error("TODO: Handle OIDC response body error");
}
const client = {
client_id: provider.clientId,
client_secret: provider.clientSecret,
...provider.client,
};
const resCookies = [];
const state = await useState(cookies?.[options.cookies.state.name], options);
if (state)
resCookies.push(state.cookie);
const codeVerifier = await usePKCECodeVerifier(cookies?.[options.cookies.pkceCodeVerifier.name], options);
if (codeVerifier)
resCookies.push(codeVerifier.cookie);
// TODO:
const nonce = await useNonce(cookies?.[options.cookies.nonce.name], options);
if (nonce && provider.type === "oidc") {
resCookies.push(nonce.cookie);
profile = o.getValidatedIdTokenClaims(result);
tokens = result;
}
else {
tokens = await o.processAuthorizationCodeOAuth2Response(as, client, codeGrantResponse);
if (o.isOAuth2Error(tokens)) {
console.log("error", tokens);
throw new Error("TODO: Handle OAuth 2.0 response body error");
}
const parameters = o.validateAuthResponse(as, client, new URLSearchParams(query), provider.checks.includes("state") ? state?.value : o.skipStateCheck);
if (o.isOAuth2Error(parameters)) {
console.log("error", parameters);
throw new Error("TODO: Handle OAuth 2.0 redirect error");
if (provider.userinfo?.request) {
profile = await provider.userinfo.request({ tokens, provider });
}
const codeGrantResponse = await o.authorizationCodeGrantRequest(as, client, parameters, provider.callbackUrl, codeVerifier?.codeVerifier ?? "auth" // TODO: review fallback code verifier
);
let challenges;
if ((challenges = o.parseWwwAuthenticateChallenges(codeGrantResponse))) {
for (const challenge of challenges) {
console.log("challenge", challenge);
}
throw new Error("TODO: Handle www-authenticate challenges as needed");
else if (provider.userinfo?.url) {
const userinfoResponse = await o.userInfoRequest(as, client, tokens.access_token);
profile = await userinfoResponse.json();
}
let profile = {};
let tokens;
if (provider.type === "oidc") {
const result = await o.processAuthorizationCodeOpenIDResponse(as, client, codeGrantResponse);
if (o.isOAuth2Error(result)) {
console.log("error", result);
throw new Error("TODO: Handle OIDC response body error");
}
profile = o.getValidatedIdTokenClaims(result);
tokens = result;
}
else {
tokens = await o.processAuthorizationCodeOAuth2Response(as, client, codeGrantResponse);
if (o.isOAuth2Error(tokens)) {
console.log("error", tokens);
throw new Error("TODO: Handle OAuth 2.0 response body error");
}
if (provider.userinfo?.request) {
profile = await provider.userinfo.request({ tokens, provider });
}
else if (provider.userinfo?.url) {
const userinfoResponse = await o.userInfoRequest(as, client, tokens.access_token);
profile = await userinfoResponse.json();
}
}
const profileResult = await getProfile({
profile,
provider,
tokens,
logger,
});
return { ...profileResult, cookies: resCookies };
}
catch (error) {
throw new OAuthCallbackError(error);
}
const profileResult = await getProfile(profile, provider, tokens, logger);
return { ...profileResult, cookies: resCookies };
}
/** Returns profile, raw profile and auth provider details */
async function getProfile({ profile: OAuthProfile, tokens, provider, logger, }) {
async function getProfile(OAuthProfile, provider, tokens, logger) {
try {
logger.debug("PROFILE_DATA", { OAuthProfile });
const profile = await provider.profile(OAuthProfile, tokens);
profile.email = profile.email?.toLowerCase();
if (!profile.id)
if (!profile.id) {
throw new TypeError(`Profile id is missing in ${provider.name} OAuth profile response`);
// Return profile, raw profile and auth provider details
}
return {

@@ -137,7 +125,5 @@ profile,

// who might be trying to debug this when configuring a new provider.
logger.error("OAUTH_PARSE_PROFILE_ERROR", {
error: error,
OAuthProfile,
});
logger.debug("getProfile error details", OAuthProfile);
logger.error(new OAuthProfileParseError(error));
}
}

@@ -1,6 +0,7 @@

import type { InternalOptions } from "../../index.js";
import type { InternalOptions } from "../../types.js";
import type { Cookie } from "../cookie.js";
/**
* Returns nonce if the provider supports it
* and saves it in a cookie */
* and saves it in a cookie
*/
export declare function createNonce(options: InternalOptions<"oauth">): Promise<undefined | {

@@ -7,0 +8,0 @@ value: string;

import * as o from "oauth4webapi";
import * as jwt from "../../jwt/index.js";
import * as jwt from "../../jwt.js";
const NONCE_MAX_AGE = 60 * 15; // 15 minutes in seconds
/**
* Returns nonce if the provider supports it
* and saves it in a cookie */
* and saves it in a cookie
*/
export async function createNonce(options) {

@@ -8,0 +9,0 @@ const { cookies, logger, provider } = options;

@@ -1,2 +0,2 @@

import type { InternalOptions } from "../../index.js";
import type { InternalOptions } from "../../types.js";
import type { Cookie } from "../cookie.js";

@@ -3,0 +3,0 @@ /**

import * as o from "oauth4webapi";
import * as jwt from "../../jwt/index.js";
import * as jwt from "../../jwt.js";
const PKCE_CODE_CHALLENGE_METHOD = "S256";

@@ -4,0 +4,0 @@ const PKCE_MAX_AGE = 60 * 15; // 15 minutes in seconds

@@ -1,2 +0,2 @@

import type { InternalOptions } from "../../index.js";
import type { InternalOptions, RequestInternal } from "../../types.js";
import type { Cookie } from "../cookie.js";

@@ -9,9 +9,7 @@ /** Returns state if the provider supports it */

/**
* Returns state from if the provider supports states,
* Returns state from the saved cookie
* if the provider supports states,
* and clears the container cookie afterwards.
*/
export declare function useState(state: string | undefined, options: InternalOptions<"oauth">): Promise<{
value: string;
cookie: Cookie;
} | undefined>;
export declare function useState(cookies: RequestInternal["cookies"], resCookies: Cookie[], options: InternalOptions<"oauth">): Promise<string | undefined>;
//# sourceMappingURL=state-handler.d.ts.map
import * as o from "oauth4webapi";
import { InvalidState } from "../../errors.js";
const STATE_MAX_AGE = 60 * 15; // 15 minutes in seconds

@@ -30,18 +31,24 @@ /** Returns state if the provider supports it */

/**
* Returns state from if the provider supports states,
* Returns state from the saved cookie
* if the provider supports states,
* and clears the container cookie afterwards.
*/
export async function useState(state, options) {
const { cookies, provider, jwt } = options;
if (!provider.checks?.includes("state") || !state)
export async function useState(cookies, resCookies, options) {
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 }));
return {
value: value?.value ?? undefined,
cookie: {
name: cookies.state.name,
value: "",
options: { ...cookies.pkceCodeVerifier.options, maxAge: 0 },
},
};
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;
}

@@ -1,11 +0,11 @@

import type { Theme } from "../../index.js";
import type { ErrorPageParam, Theme } from "../../types.js";
/**
* The following errors are passed as error query parameters to the default or overridden error page.
*
* [Documentation](https://next-auth.js.org/configuration/pages#error-page) */
export declare type ErrorType = "default" | "configuration" | "accessdenied" | "verification";
* [Documentation](https://authjs.dev/guides/basics/pages#error-page)
*/
export interface ErrorProps {
url?: URL;
theme?: Theme;
error?: ErrorType;
error?: ErrorPageParam;
}

@@ -12,0 +12,0 @@ /** Renders an error page. */

@@ -1,4 +0,3 @@

import type { InternalOptions, RequestInternal, ResponseInternal } from "../../index.js";
import type { ErrorPageParam, InternalOptions, RequestInternal, ResponseInternal } from "../../types.js";
import type { Cookie } from "../cookie.js";
import type { ErrorType } from "./error.js";
declare type RenderPageParams = {

@@ -9,3 +8,3 @@ query?: RequestInternal["query"];

/**
* Unless the user defines their [own pages](https://next-auth.js.org/configuration/pages),
* Unless the user defines their [own pages](https://authjs.dev/guides/basics/pages),
* we render a set of default ones, using Preact SSR.

@@ -18,3 +17,3 @@ */

error(props?: {
error?: ErrorType;
error?: ErrorPageParam;
}): ResponseInternal<any>;

@@ -21,0 +20,0 @@ };

import { renderToString } from "preact-render-to-string";
import css from "../styles/index.js";
import ErrorPage from "./error.js";
import SigninPage from "./signin.js";
import SignoutPage from "./signout.js";
import css from "./styles.js";
import VerifyRequestPage from "./verify-request.js";
function send({ html, title, status, cookies, theme }) {
return {
cookies,
status,
headers: { "Content-Type": "text/html" },
body: `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><style>${css}</style><title>${title}</title></head><body class="__next-auth-theme-${theme?.colorScheme ?? "auto"}"><div class="page">${renderToString(html)}</div></body></html>`,
};
}
/**
* Unless the user defines their [own pages](https://next-auth.js.org/configuration/pages),
* Unless the user defines their [own pages](https://authjs.dev/guides/basics/pages),
* we render a set of default ones, using Preact SSR.

@@ -13,13 +21,7 @@ */

const { url, theme, query, cookies } = params;
function send({ html, title, status }) {
return {
cookies,
status,
headers: { "Content-Type": "text/html" },
body: `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><style>${css}</style><title>${title}</title></head><body class="__next-auth-theme-${theme?.colorScheme ?? "auto"}"><div class="page">${renderToString(html)}</div></body></html>`,
};
}
return {
signin(props) {
return send({
cookies,
theme,
html: SigninPage({

@@ -45,2 +47,4 @@ csrfToken: params.csrfToken,

return send({
cookies,
theme,
html: SignoutPage({

@@ -57,2 +61,4 @@ csrfToken: params.csrfToken,

return send({
cookies,
theme,
html: VerifyRequestPage({ url, theme, ...props }),

@@ -64,2 +70,4 @@ title: "Verify Request",

return send({
cookies,
theme,
...ErrorPage({ url, theme, ...props }),

@@ -66,0 +74,0 @@ title: "Error",

@@ -1,8 +0,3 @@

import type { InternalProvider, Theme } from "../../index.js";
/**
* The following errors are passed as error query parameters to the default or overridden sign-in page.
*
* [Documentation](https://next-auth.js.org/configuration/pages#sign-in-page) */
export declare type SignInErrorTypes = "Signin" | "OAuthSignin" | "OAuthCallback" | "OAuthCreateAccount" | "EmailCreateAccount" | "Callback" | "OAuthAccountNotLinked" | "EmailSignin" | "CredentialsSignin" | "SessionRequired" | "default";
export interface SignInServerPageParams {
import type { InternalProvider, SignInPageErrorParam, Theme } from "../../types.js";
export default function SigninPage(props: {
csrfToken: string;

@@ -12,6 +7,5 @@ providers: InternalProvider[];

email: string;
error: SignInErrorTypes;
error?: SignInPageErrorParam;
theme: Theme;
}
export default function SigninPage(props: SignInServerPageParams): import("preact").JSX.Element;
}): import("preact").JSX.Element;
//# sourceMappingURL=signin.d.ts.map
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
const signinErrors = {
default: "Unable to sign in.",
signin: "Try signing in with a different account.",
oauthsignin: "Try signing in with a different account.",
oauthcallback: "Try signing in with a different account.",
oauthcreateaccount: "Try signing in with a different account.",
emailcreateaccount: "Try signing in with a different account.",
callback: "Try signing in with a different account.",
oauthaccountnotlinked: "To confirm your identity, sign in with the same account you used originally.",
emailsignin: "The e-mail could not be sent.",
credentialssignin: "Sign in failed. Check the details you provided are correct.",
sessionrequired: "Please sign in to access this page.",
};
export default function SigninPage(props) {

@@ -7,16 +20,3 @@ const { csrfToken, providers = [], callbackUrl, theme, email, error: errorType, } = props;

}
const errors = {
Signin: "Try signing in with a different account.",
OAuthSignin: "Try signing in with a different account.",
OAuthCallback: "Try signing in with a different account.",
OAuthCreateAccount: "Try signing in with a different account.",
EmailCreateAccount: "Try signing in with a different account.",
Callback: "Try signing in with a different account.",
OAuthAccountNotLinked: "To confirm your identity, sign in with the same account you used originally.",
EmailSignin: "The e-mail could not be sent.",
CredentialsSignin: "Sign in failed. Check the details you provided are correct.",
SessionRequired: "Please sign in to access this page.",
default: "Unable to sign in.",
};
const error = errorType && (errors[errorType] ?? errors.default);
const error = errorType && (signinErrors[errorType.toLowerCase()] ?? signinErrors.default);
// TODO: move logos

@@ -23,0 +23,0 @@ const logos = "https://raw.githubusercontent.com/nextauthjs/next-auth/main/packages/next-auth/provider-logos";

@@ -1,2 +0,2 @@

import type { Theme } from "../../index.js";
import type { Theme } from "../../types.js";
export interface SignoutProps {

@@ -3,0 +3,0 @@ url: URL;

@@ -1,2 +0,2 @@

import type { Theme } from "../../index.js";
import type { Theme } from "../../types.js";
interface VerifyRequestPageProps {

@@ -3,0 +3,0 @@ url: URL;

@@ -1,2 +0,2 @@

import type { InternalProvider } from "../index.js";
import type { InternalProvider } from "../types.js";
import type { Provider } from "../providers/index.js";

@@ -3,0 +3,0 @@ /**

@@ -1,4 +0,3 @@

import type { RequestInternal, ResponseInternal } from "../../index.js";
import type { RequestInternal, ResponseInternal, InternalOptions } from "../../types.js";
import type { SessionStore } from "../cookie.js";
import type { InternalOptions } from "../types.js";
/** Handle callbacks from login services */

@@ -5,0 +4,0 @@ export declare function callback(params: {

@@ -1,5 +0,6 @@

import callbackHandler from "../callback-handler.js";
import getAdapterUserFromEmail from "../email/getUserFromEmail.js";
import { handleOAuthCallback } from "../oauth/callback.js";
import { handleLogin } from "../callback-handler.js";
import { CallbackRouteError } from "../../errors.js";
import { handleOAuth } from "../oauth/callback.js";
import { createHash } from "../web.js";
import { handleAuthorized } from "./shared.js";
/** Handle callbacks from login services */

@@ -11,148 +12,86 @@ export async function callback(params) {

const useJwtSession = sessionStrategy === "jwt";
if (provider.type === "oauth" || provider.type === "oidc") {
try {
const { profile, account, OAuthProfile, cookies: oauthCookies, } = await handleOAuthCallback({
query,
body,
options,
cookies: params.cookies,
});
if (oauthCookies.length)
cookies.push(...oauthCookies);
try {
// Make it easier to debug when adding a new provider
logger.debug("OAUTH_CALLBACK_RESPONSE", {
profile,
account,
OAuthProfile,
try {
if (provider.type === "oauth" || provider.type === "oidc") {
const authorizationResult = await handleOAuth(query, params.cookies, options);
if (authorizationResult.cookies.length) {
cookies.push(...authorizationResult.cookies);
}
logger.debug("authroization result", authorizationResult);
const { profile, account, OAuthProfile } = authorizationResult;
// If we don't have a profile object then either something went wrong
// or the user cancelled signing in. We don't know which, so we just
// direct the user to the signin page for now. We could do something
// else in future.
// TODO: Handle user cancelling signin
if (!profile || !account || !OAuthProfile) {
return { redirect: `${url}/signin`, cookies };
}
// Check if user is allowed to sign in
// Attempt to get Profile from OAuth provider details before invoking
// signIn callback - but if no user object is returned, that is fine
// (that just means it's a new user signing in for the first time).
let userOrProfile = profile;
if (adapter) {
const { getUserByAccount } = adapter;
const userByAccount = await getUserByAccount({
providerAccountId: account.providerAccountId,
provider: provider.id,
});
// If we don't have a profile object then either something went wrong
// or the user cancelled signing in. We don't know which, so we just
// direct the user to the signin page for now. We could do something
// else in future.
//
// Note: In oAuthCallback an error is logged with debug info, so it
// should at least be visible to developers what happened if it is an
// error with the provider.
if (!profile || !account || !OAuthProfile) {
return { redirect: `${url}/signin`, cookies };
}
// Check if user is allowed to sign in
// Attempt to get Profile from OAuth provider details before invoking
// signIn callback - but if no user object is returned, that is fine
// (that just means it's a new user signing in for the first time).
let userOrProfile = profile;
if (adapter) {
const { getUserByAccount } = adapter;
const userByAccount = await getUserByAccount({
providerAccountId: account.providerAccountId,
provider: provider.id,
});
if (userByAccount)
userOrProfile = userByAccount;
}
try {
const isAllowed = await callbacks.signIn({
user: userOrProfile,
account,
profile: OAuthProfile,
});
if (!isAllowed) {
return { redirect: `${url}/error?error=AccessDenied`, cookies };
}
else if (typeof isAllowed === "string") {
return { redirect: isAllowed, cookies };
}
}
catch (error) {
return {
redirect: `${url}/error?error=${encodeURIComponent(error.message)}`,
cookies,
};
}
// Sign user in
const { user, session, isNewUser } = await callbackHandler({
sessionToken: sessionStore.value,
profile,
if (userByAccount)
userOrProfile = userByAccount;
}
const unauthorizedOrError = await handleAuthorized({ user: userOrProfile, account, profile: OAuthProfile }, options);
if (unauthorizedOrError)
return { ...unauthorizedOrError, cookies };
// Sign user in
const { user, session, isNewUser } = await handleLogin(sessionStore.value, profile, account, options);
if (useJwtSession) {
const defaultToken = {
name: user.name,
email: user.email,
picture: user.image,
sub: user.id?.toString(),
};
const token = await callbacks.jwt({
token: defaultToken,
user,
account,
options,
profile: OAuthProfile,
isNewUser,
});
if (useJwtSession) {
const defaultToken = {
name: user.name,
email: user.email,
picture: user.image,
sub: user.id?.toString(),
};
const token = await callbacks.jwt({
token: defaultToken,
user,
account,
profile: OAuthProfile,
isNewUser,
});
// Encode token
const newToken = await jwt.encode({ ...jwt, token });
// Set cookie expiry date
const cookieExpires = new Date();
cookieExpires.setTime(cookieExpires.getTime() + sessionMaxAge * 1000);
const sessionCookies = sessionStore.chunk(newToken, {
expires: cookieExpires,
});
cookies.push(...sessionCookies);
}
else {
// Save Session Token in cookie
cookies.push({
name: options.cookies.sessionToken.name,
value: session.sessionToken,
options: {
...options.cookies.sessionToken.options,
expires: session.expires,
},
});
}
// @ts-expect-error
await events.signIn?.({ user, account, profile, isNewUser });
// Handle first logins on new accounts
// e.g. option to send users to a new account landing page on initial login
// Note that the callback URL is preserved, so the journey can still be resumed
if (isNewUser && pages.newUser) {
return {
redirect: `${pages.newUser}${pages.newUser.includes("?") ? "&" : "?"}callbackUrl=${encodeURIComponent(callbackUrl)}`,
cookies,
};
}
// Callback URL is already verified at this point, so safe to use if specified
return { redirect: callbackUrl, cookies };
// Encode token
const newToken = await jwt.encode({ ...jwt, token });
// Set cookie expiry date
const cookieExpires = new Date();
cookieExpires.setTime(cookieExpires.getTime() + sessionMaxAge * 1000);
const sessionCookies = sessionStore.chunk(newToken, {
expires: cookieExpires,
});
cookies.push(...sessionCookies);
}
catch (error) {
if (error.name === "AccountNotLinkedError") {
// If the email on the account is already linked, but not with this OAuth account
return {
redirect: `${url}/error?error=OAuthAccountNotLinked`,
cookies,
};
}
else if (error.name === "CreateUserError") {
return { redirect: `${url}/error?error=OAuthCreateAccount`, cookies };
}
logger.error("OAUTH_CALLBACK_HANDLER_ERROR", error);
return { redirect: `${url}/error?error=Callback`, cookies };
}
}
catch (error) {
if (error.name === "OAuthCallbackError") {
logger.error("OAUTH_CALLBACK_ERROR", {
error: error,
providerId: provider.id,
else {
// Save Session Token in cookie
cookies.push({
name: options.cookies.sessionToken.name,
value: session.sessionToken,
options: {
...options.cookies.sessionToken.options,
expires: session.expires,
},
});
return { redirect: `${url}/error?error=OAuthCallback`, cookies };
}
logger.error("OAUTH_CALLBACK_ERROR", error);
return { redirect: `${url}/error?error=Callback`, cookies };
// @ts-expect-error
await events.signIn?.({ user, account, profile, isNewUser });
// Handle first logins on new accounts
// e.g. option to send users to a new account landing page on initial login
// Note that the callback URL is preserved, so the journey can still be resumed
if (isNewUser && pages.newUser) {
return {
redirect: `${pages.newUser}${pages.newUser.includes("?") ? "&" : "?"}callbackUrl=${encodeURIComponent(callbackUrl)}`,
cookies,
};
}
return { redirect: callbackUrl, cookies };
}
}
else if (provider.type === "email") {
try {
else if (provider.type === "email") {
const token = query?.token;

@@ -165,3 +104,3 @@ const identifier = query?.email;

const secret = provider.secret ?? options.secret;
// @ts-expect-error -- Verified in `assertConfig`. adapter: Adapter<true>
// @ts-expect-error -- Verified in `assertConfig`.
const invite = await adapter.useVerificationToken({

@@ -175,7 +114,4 @@ identifier,

}
const profile = await getAdapterUserFromEmail({
email: identifier,
// @ts-expect-error -- Verified in `assertConfig`. adapter: Adapter<true>
adapter,
});
// @ts-expect-error -- Verified in `assertConfig`.
const profile = await getAdapterUserFromEmail(identifier, adapter);
const account = {

@@ -187,27 +123,7 @@ providerAccountId: profile.email,

// Check if user is allowed to sign in
try {
const signInCallbackResponse = await callbacks.signIn({
user: profile,
account,
});
if (!signInCallbackResponse) {
return { redirect: `${url}/error?error=AccessDenied`, cookies };
}
else if (typeof signInCallbackResponse === "string") {
return { redirect: signInCallbackResponse, cookies };
}
}
catch (error) {
return {
redirect: `${url}/error?error=${encodeURIComponent(error.message)}`,
cookies,
};
}
const unauthorizedOrError = await handleAuthorized({ user: profile, account }, options);
if (unauthorizedOrError)
return { ...unauthorizedOrError, cookies };
// Sign user in
const { user, session, isNewUser } = await callbackHandler({
sessionToken: sessionStore.value,
profile,
account,
options,
});
const { user, session, isNewUser } = await handleLogin(sessionStore.value, profile, account, options);
if (useJwtSession) {

@@ -260,99 +176,78 @@ const defaultToken = {

}
catch (error) {
if (error.name === "CreateUserError") {
return { redirect: `${url}/error?error=EmailCreateAccount`, cookies };
else if (provider.type === "credentials" && method === "POST") {
const credentials = body;
let user;
try {
// TODO: Forward the original request as is, instead of reconstructing it
// prettier-ignore
Object.entries(query ?? {}).forEach(([k, v]) => url.searchParams.set(k, v));
user = await provider.authorize(credentials,
// prettier-ignore
new Request(url, { headers, method, body: JSON.stringify(body) }));
if (!user) {
return {
status: 401,
redirect: `${url}/error?${new URLSearchParams({
error: "CredentialsSignin",
provider: provider.id,
})}`,
cookies,
};
}
}
logger.error("CALLBACK_EMAIL_ERROR", error);
return { redirect: `${url}/error?error=Callback`, cookies };
}
}
else if (provider.type === "credentials" && method === "POST") {
const credentials = body;
let user;
try {
user = await provider.authorize(credentials, {
query,
body,
headers,
method,
});
if (!user) {
catch (error) {
return {
status: 401,
redirect: `${url}/error?${new URLSearchParams({
error: "CredentialsSignin",
provider: provider.id,
})}`,
redirect: `${url}/error?error=${encodeURIComponent(error.message)}`,
cookies,
};
}
}
catch (error) {
return {
status: 401,
redirect: `${url}/error?error=${encodeURIComponent(error.message)}`,
cookies,
/** @type {import("src").Account} */
const account = {
providerAccountId: user.id,
type: "credentials",
provider: provider.id,
};
}
/** @type {import("src").Account} */
const account = {
providerAccountId: user.id,
type: "credentials",
provider: provider.id,
};
try {
const isAllowed = await callbacks.signIn({
const unauthorizedOrError = await handleAuthorized({ user, account, credentials }, options);
if (unauthorizedOrError)
return { ...unauthorizedOrError, cookies };
const defaultToken = {
name: user.name,
email: user.email,
picture: user.image,
sub: user.id?.toString(),
};
const token = await callbacks.jwt({
token: defaultToken,
user,
// @ts-expect-error
account,
credentials,
isNewUser: false,
});
if (!isAllowed) {
return {
status: 403,
redirect: `${url}/error?error=AccessDenied`,
cookies,
};
}
else if (typeof isAllowed === "string") {
return { redirect: isAllowed, cookies };
}
// Encode token
const newToken = await jwt.encode({ ...jwt, token });
// Set cookie expiry date
const cookieExpires = new Date();
cookieExpires.setTime(cookieExpires.getTime() + sessionMaxAge * 1000);
const sessionCookies = sessionStore.chunk(newToken, {
expires: cookieExpires,
});
cookies.push(...sessionCookies);
// @ts-expect-error
await events.signIn?.({ user, account });
return { redirect: callbackUrl, cookies };
}
catch (error) {
return {
redirect: `${url}/error?error=${encodeURIComponent(error.message)}`,
cookies,
};
}
const defaultToken = {
name: user.name,
email: user.email,
picture: user.image,
sub: user.id?.toString(),
return {
status: 500,
body: `Error: Callback for provider type ${provider.type} not supported`,
cookies,
};
const token = await callbacks.jwt({
token: defaultToken,
user,
// @ts-expect-error
account,
isNewUser: false,
});
// Encode token
const newToken = await jwt.encode({ ...jwt, token });
// Set cookie expiry date
const cookieExpires = new Date();
cookieExpires.setTime(cookieExpires.getTime() + sessionMaxAge * 1000);
const sessionCookies = sessionStore.chunk(newToken, {
expires: cookieExpires,
});
cookies.push(...sessionCookies);
// @ts-expect-error
await events.signIn?.({ user, account });
return { redirect: callbackUrl, cookies };
}
return {
status: 500,
body: `Error: Callback for provider type ${provider.type} not supported`,
cookies,
};
catch (e) {
const error = new CallbackRouteError(e, { provider: provider.id });
logger.error(error);
url.searchParams.set("error", CallbackRouteError.name);
url.pathname += "/error";
return { redirect: url, cookies };
}
}

@@ -1,2 +0,2 @@

import type { InternalProvider, ResponseInternal } from "../../index.js";
import type { InternalProvider, ResponseInternal } from "../../types.js";
export interface PublicProvider {

@@ -3,0 +3,0 @@ id: string;

@@ -1,13 +0,5 @@

import type { InternalOptions, ResponseInternal, Session } from "../../index.js";
import type { InternalOptions, ResponseInternal, Session } from "../../types.js";
import type { SessionStore } from "../cookie.js";
interface SessionParams {
options: InternalOptions;
sessionStore: SessionStore;
}
/**
* Return a session object (without any private fields)
* for Single Page App clients
*/
export declare function session(params: SessionParams): Promise<ResponseInternal<Session | {}>>;
export {};
/** Return a session object filtered via `callbacks.session` */
export declare function session(sessionStore: SessionStore, options: InternalOptions): Promise<ResponseInternal<Session | {}>>;
//# sourceMappingURL=session.d.ts.map

@@ -0,8 +1,5 @@

import { JWTSessionError, SessionTokenError } from "../../errors.js";
import { fromDate } from "../utils/date.js";
/**
* Return a session object (without any private fields)
* for Single Page App clients
*/
export async function session(params) {
const { options, sessionStore } = params;
/** Return a session object filtered via `callbacks.session` */
export async function session(sessionStore, options) {
const { adapter, jwt, events, callbacks, logger, session: { strategy: sessionStrategy, maxAge: sessionMaxAge }, } = options;

@@ -19,6 +16,3 @@ const response = {

try {
const decodedToken = await jwt.decode({
...jwt,
token: sessionToken,
});
const decodedToken = await jwt.decode({ ...jwt, token: sessionToken });
const newExpires = fromDate(sessionMaxAge);

@@ -55,72 +49,75 @@ // By default, only exposes a limited subset of information to the client

catch (error) {
// If JWT not verifiable, make sure the cookie for it is removed and return empty object
logger.error("JWT_SESSION_ERROR", error);
logger.error(new JWTSessionError(error));
// If the JWT is not verifiable remove the broken session cookie(s).
response.cookies?.push(...sessionStore.clean());
}
return response;
}
else {
try {
const { getSessionAndUser, deleteSession, updateSession } = adapter;
let userAndSession = await getSessionAndUser(sessionToken);
// If session has expired, clean up the database
if (userAndSession &&
userAndSession.session.expires.valueOf() < Date.now()) {
await deleteSession(sessionToken);
userAndSession = null;
// Retrieve session from database
try {
const { getSessionAndUser, deleteSession, updateSession } = adapter;
let userAndSession = await getSessionAndUser(sessionToken);
// If session has expired, clean up the database
if (userAndSession &&
userAndSession.session.expires.valueOf() < Date.now()) {
await deleteSession(sessionToken);
userAndSession = null;
}
if (userAndSession) {
const { user, session } = userAndSession;
const sessionUpdateAge = options.session.updateAge;
// Calculate last updated date to throttle write updates to database
// Formula: ({expiry date} - sessionMaxAge) + sessionUpdateAge
// e.g. ({expiry date} - 30 days) + 1 hour
const sessionIsDueToBeUpdatedDate = session.expires.valueOf() -
sessionMaxAge * 1000 +
sessionUpdateAge * 1000;
const newExpires = fromDate(sessionMaxAge);
// Trigger update of session expiry date and write to database, only
// if the session was last updated more than {sessionUpdateAge} ago
if (sessionIsDueToBeUpdatedDate <= Date.now()) {
await updateSession({
sessionToken: sessionToken,
expires: newExpires,
});
}
if (userAndSession) {
const { user, session } = userAndSession;
const sessionUpdateAge = options.session.updateAge;
// Calculate last updated date to throttle write updates to database
// Formula: ({expiry date} - sessionMaxAge) + sessionUpdateAge
// e.g. ({expiry date} - 30 days) + 1 hour
const sessionIsDueToBeUpdatedDate = session.expires.valueOf() -
sessionMaxAge * 1000 +
sessionUpdateAge * 1000;
const newExpires = fromDate(sessionMaxAge);
// Trigger update of session expiry date and write to database, only
// if the session was last updated more than {sessionUpdateAge} ago
if (sessionIsDueToBeUpdatedDate <= Date.now()) {
await updateSession({ sessionToken, expires: newExpires });
}
// Pass Session through to the session callback
// @ts-expect-error
const sessionPayload = await callbacks.session({
// By default, only exposes a limited subset of information to the client
// as needed for presentation purposes (e.g. "you are logged in as...").
session: {
user: {
name: user.name,
email: user.email,
image: user.image,
},
expires: session.expires.toISOString(),
// Pass Session through to the session callback
// @ts-expect-error
const sessionPayload = await callbacks.session({
// By default, only exposes a limited subset of information to the client
// as needed for presentation purposes (e.g. "you are logged in as...").
session: {
user: {
name: user.name,
email: user.email,
image: user.image,
},
user,
});
// Return session payload as response
response.body = sessionPayload;
// Set cookie again to update expiry
response.cookies?.push({
name: options.cookies.sessionToken.name,
value: sessionToken,
options: {
...options.cookies.sessionToken.options,
expires: newExpires,
},
});
// @ts-expect-error
await events.session?.({ session: sessionPayload });
}
else if (sessionToken) {
// If `sessionToken` was found set but it's not valid for a session then
// remove the sessionToken cookie from browser.
response.cookies?.push(...sessionStore.clean());
}
expires: session.expires.toISOString(),
},
user,
});
// Return session payload as response
response.body = sessionPayload;
// Set cookie again to update expiry
response.cookies?.push({
name: options.cookies.sessionToken.name,
value: sessionToken,
options: {
...options.cookies.sessionToken.options,
expires: newExpires,
},
});
// @ts-expect-error
await events.session?.({ session: sessionPayload });
}
catch (error) {
logger.error("SESSION_ERROR", error);
else if (sessionToken) {
// If `sessionToken` was found set but it's not valid for a session then
// remove the sessionToken cookie from browser.
response.cookies?.push(...sessionStore.clean());
}
}
catch (error) {
logger.error(new SessionTokenError(error));
}
return response;
}

@@ -1,8 +0,8 @@

import type { InternalOptions, RequestInternal, ResponseInternal } from "../../index.js";
/** Handle requests to /api/auth/signin */
export declare function signin(params: {
options: InternalOptions<"oauth" | "email">;
query: RequestInternal["query"];
body: RequestInternal["body"];
}): Promise<ResponseInternal>;
import type { InternalOptions, RequestInternal, ResponseInternal } from "../../types.js";
/**
* Initiates the sign in process for OAuth and Email flows .
* For OAuth, redirects to the provider's authorization URL.
* For Email, sends an email with a sign in link.
*/
export declare function signin(query: RequestInternal["query"], body: RequestInternal["body"], options: InternalOptions<"oauth" | "email">): Promise<ResponseInternal>;
//# sourceMappingURL=signin.d.ts.map

@@ -1,90 +0,53 @@

import getAdapterUserFromEmail from "../email/getUserFromEmail.js";
import emailSignin from "../email/signin.js";
import { SignInError } from "../../errors.js";
import { getAuthorizationUrl } from "../oauth/authorization-url.js";
/** Handle requests to /api/auth/signin */
export async function signin(params) {
const { options, query, body } = params;
const { url, callbacks, logger, provider } = options;
if (!provider.type) {
return {
status: 500,
// @ts-expect-error
text: `Error: Type not specified for ${provider.name}`,
};
}
if (provider.type === "oauth" || provider.type === "oidc") {
try {
return await getAuthorizationUrl({ options, query });
import { getAdapterUserFromEmail, handleAuthorized } from "./shared.js";
/**
* Initiates the sign in process for OAuth and Email flows .
* For OAuth, redirects to the provider's authorization URL.
* For Email, sends an email with a sign in link.
*/
export async function signin(query, body, options) {
const { url, logger, provider } = options;
try {
if (provider.type === "oauth" || provider.type === "oidc") {
return await getAuthorizationUrl(query, options);
}
catch (error) {
logger.error("SIGNIN_OAUTH_ERROR", {
error: error,
providerId: provider.id,
});
return { redirect: `${url}/error?error=OAuthSignin` };
}
}
else if (provider.type === "email") {
let email = body?.email;
if (!email)
return { redirect: `${url}/error?error=EmailSignin` };
const normalizer = provider.normalizeIdentifier ??
((identifier) => {
// Get the first two elements only,
// separated by `@` from user input.
let [local, domain] = identifier.toLowerCase().trim().split("@");
// The part before "@" can contain a ","
// but we remove it on the domain part
domain = domain.split(",")[0];
return `${local}@${domain}`;
});
try {
email = normalizer(body?.email);
}
catch (error) {
logger.error("SIGNIN_EMAIL_ERROR", { error, providerId: provider.id });
return { redirect: `${url}/error?error=EmailSignin` };
}
const user = await getAdapterUserFromEmail({
email,
// @ts-expect-error -- Verified in `assertConfig`. adapter: Adapter<true>
adapter: options.adapter,
});
const account = {
providerAccountId: email,
userId: email,
type: "email",
provider: provider.id,
};
// Check if user is allowed to sign in
try {
const signInCallbackResponse = await callbacks.signIn({
user,
account,
email: { verificationRequest: true },
});
if (!signInCallbackResponse) {
return { redirect: `${url}/error?error=AccessDenied` };
}
else if (typeof signInCallbackResponse === "string") {
return { redirect: signInCallbackResponse };
}
}
catch (error) {
return {
redirect: `${url}/error?${new URLSearchParams({
error: error,
})}`,
else if (provider.type === "email") {
const normalizer = provider.normalizeIdentifier ?? defaultNormalizer;
const email = normalizer(body?.email);
// @ts-expect-error -- Verified in `assertConfig`
const user = await getAdapterUserFromEmail(email, options.adapter);
const account = {
providerAccountId: email,
userId: email,
type: "email",
provider: provider.id,
};
}
try {
const unauthorizedOrError = await handleAuthorized({ user, account, email: { verificationRequest: true } }, options);
if (unauthorizedOrError)
return unauthorizedOrError;
const redirect = await emailSignin(email, options);
return { redirect };
}
catch (error) {
logger.error("SIGNIN_EMAIL_ERROR", { error, providerId: provider.id });
return { redirect: `${url}/error?error=EmailSignin` };
}
return { redirect: `${url}/signin` };
}
return { redirect: `${url}/signin` };
catch (e) {
const error = new SignInError(e, { provider: provider.id });
logger.error(error);
url.searchParams.set("error", error.name);
url.pathname += "/error";
return { redirect: url };
}
}
function defaultNormalizer(email) {
if (!email)
throw new Error("Missing email from request body.");
// Get the first two elements only,
// separated by `@` from user input.
let [local, domain] = email.toLowerCase().trim().split("@");
// The part before "@" can contain a ","
// but we remove it on the domain part
domain = domain.split(",")[0];
return `${local}@${domain}`;
}

@@ -1,8 +0,11 @@

import type { InternalOptions, ResponseInternal } from "../../index.js";
import type { InternalOptions, ResponseInternal } from "../../types.js";
import type { SessionStore } from "../cookie.js";
/** Handle requests to /api/auth/signout */
export declare function signout(params: {
options: InternalOptions;
sessionStore: SessionStore;
}): Promise<ResponseInternal>;
/**
* Destroys the session.
* 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.
*/
export declare function signout(sessionStore: SessionStore, options: InternalOptions): Promise<ResponseInternal>;
//# sourceMappingURL=signout.d.ts.map

@@ -1,36 +0,28 @@

/** Handle requests to /api/auth/signout */
export async function signout(params) {
const { options, sessionStore } = params;
const { adapter, events, jwt, callbackUrl, logger, session } = options;
const sessionToken = sessionStore?.value;
if (!sessionToken) {
return { redirect: callbackUrl };
}
if (session.strategy === "jwt") {
// Dispatch signout event
try {
const decodedJwt = await jwt.decode({ ...jwt, token: sessionToken });
// @ts-expect-error
await events.signOut?.({ token: decodedJwt });
import { SignOutError } from "../../errors.js";
/**
* Destroys the session.
* 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.
*/
export async function signout(sessionStore, options) {
const { jwt, events, callbackUrl: redirect, logger, session } = options;
const sessionToken = sessionStore.value;
if (!sessionToken)
return { redirect };
try {
if (session.strategy === "jwt") {
const token = await jwt.decode({ ...jwt, token: sessionToken });
await events.signOut?.({ token });
}
catch (error) {
// Do nothing if decoding the JWT fails
logger.error("SIGNOUT_ERROR", error);
}
}
else {
try {
const session = await adapter.deleteSession(sessionToken);
// Dispatch signout event
// @ts-expect-error
else {
const session = await options.adapter?.deleteSession(sessionToken);
await events.signOut?.({ session });
}
catch (error) {
// If error, log it but continue
logger.error("SIGNOUT_ERROR", error);
}
}
// Remove Session Token
const sessionCookies = sessionStore.clean();
return { redirect: callbackUrl, cookies: sessionCookies };
catch (error) {
logger.error(new SignOutError(error));
}
return { redirect, cookies: sessionStore.clean() };
}

@@ -1,22 +0,14 @@

export declare type WarningCode = "NEXTAUTH_URL" | "DEBUG_ENABLED";
import { AuthError } from "../../errors.js";
export declare type WarningCode = "debug_enabled";
/**
* Override any of the methods, and the rest will use the default logger.
*
* [Documentation](https://next-auth.js.org/configuration/options#logger)
* [Documentation](https://authjs.dev/reference/configuration/auth-config#logger)
*/
export interface LoggerInstance extends Record<string, Function> {
warn: (code: WarningCode) => void;
error: (code: string,
/**
* Either an instance of (JSON serializable) Error
* or an object that contains some debug information.
* (Error is still available through `metadata.error`)
*/
metadata: Error | {
error: Error;
[key: string]: unknown;
}) => void;
debug: (code: string, metadata: unknown) => void;
error: (error: AuthError) => void;
debug: (message: string, metadata?: unknown) => void;
}
declare const _logger: LoggerInstance;
export declare const logger: LoggerInstance;
/**

@@ -27,5 +19,2 @@ * Override the built-in logger with user's implementation.

export declare function setLogger(newLogger?: Partial<LoggerInstance>, debug?: boolean): void;
export default _logger;
/** Serializes client-side log messages and sends them to the server */
export declare function proxyLogger(logger?: LoggerInstance, basePath?: string): LoggerInstance;
//# sourceMappingURL=logger.d.ts.map

@@ -1,26 +0,18 @@

import { UnknownError } from "../errors.js";
/** Makes sure that error is always serializable */
function formatError(o) {
if (o instanceof Error && !(o instanceof UnknownError)) {
return { message: o.message, stack: o.stack, name: o.name };
}
if (hasErrorProperty(o)) {
o.error = formatError(o.error);
o.message = o.message ?? o.error.message;
}
return o;
}
function hasErrorProperty(x) {
return !!x?.error;
}
const _logger = {
error(code, metadata) {
metadata = formatError(metadata);
console.error(`[next-auth][error][${code}]`, `\nhttps://next-auth.js.org/errors#${code.toLowerCase()}`, metadata.message, metadata);
const red = "\x1b[31m";
const yellow = "\x1b[33m";
const grey = "\x1b[90m";
const reset = "\x1b[0m";
export const logger = {
error(error) {
const url = `https://errors.authjs.dev#${error.name.toLowerCase()}`;
console.error(error.stack);
console.error(`${red}[auth][error][${error.name}]${reset}: Read more at ${url}`);
error.metadata && console.error(JSON.stringify(error.metadata, null, 2));
},
warn(code) {
console.warn(`[next-auth][warn][${code}]`, `\nhttps://next-auth.js.org/warnings#${code.toLowerCase()}`);
const url = `https://errors.authjs.dev#${code}`;
console.warn(`${yellow}[auth][warn][${code}]${reset}`, `Read more: ${url}`);
},
debug(code, metadata) {
console.log(`[next-auth][debug][${code}]`, metadata);
debug(message, metadata) {
console.log(`${grey}[auth][debug]:${reset} ${message}`, JSON.stringify(metadata, null, 2));
},

@@ -35,39 +27,9 @@ };

if (!debug)
_logger.debug = () => { };
logger.debug = () => { };
if (newLogger.error)
_logger.error = newLogger.error;
logger.error = newLogger.error;
if (newLogger.warn)
_logger.warn = newLogger.warn;
logger.warn = newLogger.warn;
if (newLogger.debug)
_logger.debug = newLogger.debug;
logger.debug = newLogger.debug;
}
export default _logger;
/** Serializes client-side log messages and sends them to the server */
export function proxyLogger(logger = _logger, basePath) {
try {
if (typeof window === "undefined") {
return logger;
}
const clientLogger = {};
for (const level in logger) {
clientLogger[level] = (code, metadata) => {
_logger[level](code, metadata); // Logs to console
if (level === "error") {
metadata = formatError(metadata);
}
;
metadata.client = true;
const url = `${basePath}/_log`;
const body = new URLSearchParams({ level, code, ...metadata });
if (navigator.sendBeacon) {
return navigator.sendBeacon(url, body);
}
return fetch(url, { method: "POST", body, keepalive: true });
};
}
return clientLogger;
}
catch {
return _logger;
}
}

@@ -1,3 +0,4 @@

import type { RequestInternal, ResponseInternal } from "../index.js";
export declare function toInternalRequest(req: Request): Promise<RequestInternal | Error>;
import { AuthError } from "../errors.js";
import type { RequestInternal, ResponseInternal } from "../types.js";
export declare function toInternalRequest(req: Request): Promise<RequestInternal | AuthError>;
export declare function toResponse(res: ResponseInternal): Response;

@@ -4,0 +5,0 @@ /** Web compatible method to create a hash, using SHA256 */

import { parse as parseCookie, serialize } from "cookie";
import { UnknownAction } from "./errors.js";
import { UnknownAction } from "../errors.js";
async function getBody(req) {

@@ -15,4 +15,12 @@ if (!("body" in req) || !req.body || req.method !== "POST")

}
// prettier-ignore
const actions = ["providers", "session", "csrf", "signin", "signout", "callback", "verify-request", "error", "_log"];
const actions = [
"providers",
"session",
"csrf",
"signin",
"signout",
"callback",
"verify-request",
"error",
];
export async function toInternalRequest(req) {

@@ -28,2 +36,5 @@ try {

}
if (req.method !== "GET" && req.method !== "POST") {
throw new UnknownAction("Only GET and POST requests are supported.");
}
const providerIdOrAction = pathname.split("/").pop();

@@ -40,3 +51,3 @@ let providerId;

providerId,
method: req.method ?? "GET",
method: req.method,
headers: Object.fromEntries(req.headers),

@@ -43,0 +54,0 @@ body: req.body ? await getBody(req) : undefined,

{
"name": "@auth/core",
"version": "0.1.4",
"version": "0.2.0",
"description": "Authentication for the web.",

@@ -18,5 +18,4 @@ "homepage": "https://authjs.dev",

"files": [
"adapters.*",
"index.*",
"jwt",
"*.js",
"*.d.ts",
"lib",

@@ -34,9 +33,19 @@ "providers",

},
"./errors": {
"import": "./errors.js",
"types": "./errors.d.ts"
},
"./jwt": {
"types": "./jwt/index.d.ts",
"import": "./jwt/index.js"
"types": "./jwt.d.ts",
"import": "./jwt.js"
},
"./providers": {
"types": "./providers/index.d.ts"
},
"./providers/*": {
"types": "./providers/*.d.ts",
"import": "./providers/*.js"
},
"./types": {
"types": "./types.d.ts"
}

@@ -72,7 +81,9 @@ },

"scripts": {
"build": "pnpm clean && tsc && pnpm css",
"clean": "rm -rf adapters.* index.* jwt lib providers",
"build": "pnpm clean && pnpm css && tsc",
"clean": "rm -rf *.js *.d.ts lib providers",
"css": "node ./scripts/generate-css.js",
"lint": "pnpm prettier --check src && eslint src",
"format": "pnpm prettier --write . && eslint src --fix",
"dev": "pnpm css && tsc -w"
}
}

@@ -1,2 +0,2 @@

import { OAuthConfig, OAuthUserConfig } from "./index.js";
import type { OAuthConfig, OAuthUserConfig } from "./index.js";
/**

@@ -3,0 +3,0 @@ * See more at:

@@ -11,2 +11,3 @@ import type { OAuthConfig, OAuthUserConfig } from "./index.js";

* https://docs.microsoft.com/en-us/graph/api/profilephoto-get?view=graph-rest-1.0#examples
*
* @default 48

@@ -13,0 +14,0 @@ */

/** @type {import(".").OAuthProvider} */
export default function Box(options: Partial<import("./oauth").OAuthConfig<any>>): import("./oauth").OAuthConfig<any>;
export default function Box(options: any): {
id: string;
name: string;
type: string;
authorization: string;
token: string;
userinfo: string;
profile(profile: any): {
id: any;
name: any;
email: any;
image: any;
};
style: {
logo: string;
logoDark: string;
bg: string;
text: string;
bgDark: string;
textDark: string;
};
options: any;
};
//# sourceMappingURL=box.d.ts.map
/** @type {import(".").OAuthProvider} */
export default function Bungie(options: Partial<import("./oauth").OAuthConfig<any>>): import("./oauth").OAuthConfig<any>;
export default function Bungie(options: any): {
id: string;
name: string;
type: string;
authorization: string;
token: string;
userinfo: string;
profile(profile: any): {
id: any;
name: any;
email: null;
image: string;
};
options: any;
};
//# sourceMappingURL=bungie.d.ts.map
/** @type {import(".").OAuthProvider} */
export default function Coinbase(options: Partial<import("./oauth").OAuthConfig<any>>): import("./oauth").OAuthConfig<any>;
export default function Coinbase(options: any): {
id: string;
name: string;
type: string;
authorization: string;
token: string;
userinfo: string;
profile(profile: any): {
id: any;
name: any;
email: any;
image: any;
};
options: any;
};
//# sourceMappingURL=coinbase.d.ts.map
import type { CommonProviderOptions } from "./index.js";
import type { Awaitable, RequestInternal, User } from "../index.js";
export interface CredentialInput {
import type { Awaitable, User } from "../types.js";
import type { JSXInternal } from "preact/src/jsx.js";
/**
* Besieds providing type safety inside {@link CredentialsConfig.authorize}
* it also determines how the credentials input fields will be rendered
* on the default sign in page.
*/
export interface CredentialInput extends Partial<JSXInternal.IntrinsicElements["input"]> {
label?: string;
type?: string;
value?: string;
placeholder?: string;
}
export interface CredentialsConfig<C extends Record<string, CredentialInput> = Record<string, CredentialInput>> extends CommonProviderOptions {
/** The Credentials Provider needs to be configured. */
export interface CredentialsConfig<CredentialsInputs extends Record<string, CredentialInput> = Record<string, CredentialInput>> extends CommonProviderOptions {
type: "credentials";
credentials: C;
authorize: (credentials: Record<keyof C, string> | undefined, req: Pick<RequestInternal, "body" | "query" | "headers" | "method">) => Awaitable<User | null>;
credentials: CredentialsInputs;
/**
* Gives full control over how you handle the credentials received from the user.
*
* :::warning
* There is no validation on the user inputs by default, so make sure you do so
* by a popular library like [Zod](https://zod.dev)
* :::
*
* @example
* ```ts
* //...
* async authorize(, request) {
* const response = await fetch(request)
* if(!response.ok) return null
* return await response.json() ?? null
* }
* //...
*/
authorize: (
/** See {@link CredentialInput} */
credentials: Record<keyof CredentialsInputs, string> | undefined,
/** The original request is forward for convenience */
request: Request) => Awaitable<User | null>;
}
export declare type CredentialsProvider = <C extends Record<string, CredentialInput>>(options: Partial<CredentialsConfig<C>>) => CredentialsConfig<C>;
export declare type CredentialsProviderType = "Credentials";
declare type UserCredentialsConfig<C extends Record<string, CredentialInput>> = Partial<Omit<CredentialsConfig<C>, "options">> & Pick<CredentialsConfig<C>, "authorize" | "credentials">;
export default function Credentials<C extends Record<string, CredentialInput> = Record<string, CredentialInput>>(options: UserCredentialsConfig<C>): CredentialsConfig<C>;
export {};
export declare 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).
*
* It is intended to support use cases where you have an existing system you need to authenticate users against.
*
* It comes with the constraint that users authenticated in this manner are not persisted in the database,
* and consequently that the Credentials provider can only be used if JSON Web Tokens are enabled for sessions.
*
* :::warning **NOTE**
*
* The functionality provided for credentials based authentication is
* **intentionally limited** to _discourage_ use of passwords
* due to the _inherent security risks_ associated with them
* and the _additional complexity_ associated
* with supporting usernames and passwords.
*
* :::
*
* @example
* ```js
* import Auth from "@auth/core"
* import { Credentials } from "@auth/core/providers/credentials"
*
* const request = new Request("https://example.com")
* const resposne = await AuthHandler(request, {
* providers: [
* Credentials({
* credentials: {
* username: { label: "Username" },
* password: { label: "Password", type: "password" }
* },
* async authorize({ request }) {
* const response = await fetch(request)
* if(!response.ok) return null
* return await response.json() ?? null
* }
* })
* ],
* secret: "...",
* trustHost: true,
* })
* ```
* @see [Username/Password Example](https://authjs.dev/guides/providers/credentials#example---username--password)
* @see [Web3/Signin With Ethereum Example](https://authjs.dev/guides/providers/credentials#example---web3--signin-with-ethereum)
*/
export default function Credentials<CredentialsInputs extends Record<string, CredentialInput> = Record<string, CredentialInput>>(config: Partial<CredentialsConfig<CredentialsInputs>>): CredentialsConfig;
//# sourceMappingURL=credentials.d.ts.map

@@ -1,2 +0,48 @@

export default function Credentials(options) {
/**
* 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).
*
* It is intended to support use cases where you have an existing system you need to authenticate users against.
*
* It comes with the constraint that users authenticated in this manner are not persisted in the database,
* and consequently that the Credentials provider can only be used if JSON Web Tokens are enabled for sessions.
*
* :::warning **NOTE**
*
* The functionality provided for credentials based authentication is
* **intentionally limited** to _discourage_ use of passwords
* due to the _inherent security risks_ associated with them
* and the _additional complexity_ associated
* with supporting usernames and passwords.
*
* :::
*
* @example
* ```js
* import Auth from "@auth/core"
* import { Credentials } from "@auth/core/providers/credentials"
*
* const request = new Request("https://example.com")
* const resposne = await AuthHandler(request, {
* providers: [
* Credentials({
* credentials: {
* username: { label: "Username" },
* password: { label: "Password", type: "password" }
* },
* async authorize({ request }) {
* const response = await fetch(request)
* if(!response.ok) return null
* return await response.json() ?? null
* }
* })
* ],
* secret: "...",
* trustHost: true,
* })
* ```
* @see [Username/Password Example](https://authjs.dev/guides/providers/credentials#example---username--password)
* @see [Web3/Signin With Ethereum Example](https://authjs.dev/guides/providers/credentials#example---web3--signin-with-ethereum)
*/
export default function Credentials(config) {
return {

@@ -8,4 +54,5 @@ id: "credentials",

authorize: () => null,
options,
// @ts-expect-error
options: config,
};
}

@@ -26,3 +26,3 @@ /**

* *Resources:*
* - [NextAuth.js Documentation](https://next-auth.js.org/providers/dropbox)
* - [NextAuth.js Documentation](https://authjs.dev/reference/oauth-providers/dropbox)
* - [Dropbox Documentation](https://developers.dropbox.com/oauth-guide)

@@ -32,3 +32,17 @@ * - [Configuration](https://www.dropbox.com/developers/apps)

/** @type {import(".").OAuthProvider} */
export default function Dropbox(options: Partial<import("./oauth").OAuthConfig<any>>): import("./oauth").OAuthConfig<any>;
export default function Dropbox(options: any): {
id: string;
name: string;
type: string;
authorization: string;
token: string;
userinfo: string;
profile(profile: any): {
id: any;
name: any;
email: any;
image: any;
};
options: any;
};
//# sourceMappingURL=dropbox.d.ts.map

@@ -26,3 +26,3 @@ /**

* *Resources:*
* - [NextAuth.js Documentation](https://next-auth.js.org/providers/dropbox)
* - [NextAuth.js Documentation](https://authjs.dev/reference/oauth-providers/dropbox)
* - [Dropbox Documentation](https://developers.dropbox.com/oauth-guide)

@@ -29,0 +29,0 @@ * - [Configuration](https://www.dropbox.com/developers/apps)

@@ -1,2 +0,2 @@

import type { OAuthConfig, OAuthUserConfig } from "./oauth";
import type { OAuthConfig, OAuthUserConfig } from "./oauth.js";
export interface DuendeISUser extends Record<string, any> {

@@ -3,0 +3,0 @@ email: string;

import type { CommonProviderOptions } from "./index.js";
import type { Options as SMTPTransportOptions } from "nodemailer/lib/smtp-transport";
import type { Awaitable, Theme } from "../index.js";
import type { Awaitable, Theme } from "../types.js";
export interface SendVerificationRequestParams {

@@ -12,6 +12,19 @@ identifier: string;

}
/**
* The Email Provider needs to be configured with an e-mail client.
* By default, it uses `nodemailer`, which you have to install if this
* provider is present.
*
* You can use a other services as well, like:
* - [Postmark](https://postmarkapp.com)
* - [Mailgun](https://www.mailgun.com)
* - [SendGrid](https://sendgrid.com)
* - etc.
*
* @see [Custom email service with Auth.js](https://authjs.dev/guides/providers/email#custom-email-service)
*/
export interface EmailConfig extends CommonProviderOptions {
type: "email";
server: string | SMTPTransportOptions;
/** @default "NextAuth <no-reply@example.com>" */
/** @default `"Auth.js <no-reply@authjs.dev>"` */
from?: string;

@@ -21,6 +34,7 @@ /**

* in seconds. Defaults to 1 day
*
* @default 86400
*/
maxAge?: number;
/** [Documentation](https://next-auth.js.org/providers/email#customizing-emails) */
/** [Documentation](https://authjs.dev/reference/providers/email#customizing-emails) */
sendVerificationRequest: (params: SendVerificationRequestParams) => Awaitable<void>;

@@ -30,4 +44,5 @@ /**

* You can make it predictable or modify it as you like with this method.
*
* @example
* ```js
* ```ts
* Providers.Email({

@@ -39,3 +54,3 @@ * async generateVerificationToken() {

* ```
* [Documentation](https://next-auth.js.org/providers/email#customizing-the-verification-token)
* [Documentation](https://authjs.dev/reference/providers/email#customizing-the-verification-token)
*/

@@ -57,11 +72,9 @@ generateVerificationToken?: () => Awaitable<string>;

*
* [Documentation](https://next-auth.js.org/providers/email#normalizing-the-e-mail-address) | [RFC 2821](https://tools.ietf.org/html/rfc2821) | [Email syntax](https://en.wikipedia.org/wiki/Email_address#Syntax)
* [Documentation](https://authjs.dev/reference/providers/email#normalizing-the-e-mail-address) | [RFC 2821](https://tools.ietf.org/html/rfc2821) | [Email syntax](https://en.wikipedia.org/wiki/Email_address#Syntax)
*/
normalizeIdentifier?: (identifier: string) => string;
options: EmailUserConfig;
}
export declare type EmailUserConfig = Partial<Omit<EmailConfig, "options">>;
export declare type EmailProvider = (options: EmailUserConfig) => EmailConfig;
export declare type EmailProviderType = "Email";
export default function Email(options: EmailUserConfig): EmailConfig;
export declare type EmailProviderType = "email";
/** TODO: */
export declare function Email(config: EmailConfig): EmailConfig;
//# sourceMappingURL=email.d.ts.map
import { createTransport } from "nodemailer";
export default function Email(options) {
/** TODO: */
export function Email(config) {
return {

@@ -7,5 +8,4 @@ id: "email",

name: "Email",
// Server can be an SMTP connection string or a nodemailer config object
server: { host: "localhost", port: 25, auth: { user: "", pass: "" } },
from: "NextAuth <no-reply@example.com>",
from: "Auth.js <no-reply@authjs.dev>",
maxAge: 24 * 60 * 60,

@@ -28,3 +28,4 @@ async sendVerificationRequest(params) {

},
options,
// @ts-expect-error
options: config,
};

@@ -31,0 +32,0 @@ }

/** @type {import(".").OAuthProvider} */
export default function FACEIT(options: Partial<import("./oauth").OAuthConfig<any>>): import("./oauth").OAuthConfig<any>;
export default function FACEIT(options: any): {
id: string;
name: string;
type: string;
authorization: string;
headers: {
Authorization: string;
};
token: string;
userinfo: string;
profile(profile: any): {
id: any;
name: any;
email: any;
image: any;
};
options: any;
};
//# sourceMappingURL=faceit.d.ts.map
/** @type {import(".").OAuthProvider} */
export default function Foursquare(options: Partial<import("./oauth").OAuthConfig<any>>): import("./oauth").OAuthConfig<any>;
export default function Foursquare(options: any): {
id: string;
name: string;
type: string;
authorization: string;
token: string;
userinfo: {
url: string;
request({ tokens, provider }: {
tokens: any;
provider: any;
}): Promise<any>;
};
profile({ response: { profile } }: {
response: {
profile: any;
};
}): {
id: any;
name: string;
email: any;
image: string | null;
};
style: {
logo: string;
logoDark: string;
bg: string;
text: string;
bgDark: string;
textDark: string;
};
options: any;
};
//# sourceMappingURL=foursquare.d.ts.map
/** @type {import(".").OAuthProvider} */
export default function Freshbooks(options: Partial<import("./oauth").OAuthConfig<any>>): import("./oauth").OAuthConfig<any>;
export default function Freshbooks(options: any): any;
//# sourceMappingURL=freshbooks.d.ts.map

@@ -1,3 +0,4 @@

import { OAuthConfig, OAuthUserConfig } from "./oauth";
/** This is the default openid signature returned from FusionAuth
import type { OAuthConfig, OAuthUserConfig } from "./oauth.js";
/**
* This is the default openid signature returned from FusionAuth
* it can be customized using [lambda functions](https://fusionauth.io/docs/v1/tech/lambdas)

@@ -4,0 +5,0 @@ */

import type { OAuthConfig, OAuthUserConfig } from "./index.js";
/** @see https://docs.github.com/en/rest/users/users#get-the-authenticated-user */
export interface GithubEmail extends Record<string, any> {
email: string;
primary: boolean;
verified: boolean;
visibility: "public" | "private";
}
/** @see [Get the authenticated user](https://docs.github.com/en/rest/users/users#get-the-authenticated-user) */
export interface GithubProfile extends Record<string, any> {

@@ -50,9 +56,50 @@ login: string;

}
export interface GithubEmail extends Record<string, any> {
email: string;
primary: boolean;
verified: boolean;
visibility: "public" | "private";
}
export default function Github<P extends GithubProfile>(options: OAuthUserConfig<P>): OAuthConfig<P>;
/**
* Add GitHub login to your page and make requests to [GitHub APIs](https://docs.github.com/en/rest).
*
* ## Example
*
* @example
*
* ```ts
* import Auth from "@auth/core"
* import { GitHub } from "@auth/core/providers/github"
*
* const request = new Request("https://example.com")
* const resposne = await AuthHandler(request, {
* providers: [GitHub({ clientId: "", clientSecret: "" })],
* })
* ```
*
* ## Resources
*
* @see [GitHub - Creating an OAuth App](https://docs.github.com/en/developers/apps/building-oauth-apps/creating-an-oauth-app)
* @see [GitHub - Authorizing OAuth Apps](https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps)
* @see [GitHub - Configure your GitHub OAuth Apps](https://github.com/settings/developers)
* @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/github.ts)
*
* ## Notes
*
* By default, Auth.js assumes that the GitHub provider is
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
*
* :::tip
*
* The GitHub provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/github.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 GitHub<Profile extends GithubProfile>(options: OAuthUserConfig<Profile>): OAuthConfig<Profile>;
//# sourceMappingURL=github.d.ts.map

@@ -1,2 +0,49 @@

export default function Github(options) {
/**
* Add GitHub login to your page and make requests to [GitHub APIs](https://docs.github.com/en/rest).
*
* ## Example
*
* @example
*
* ```ts
* import Auth from "@auth/core"
* import { GitHub } from "@auth/core/providers/github"
*
* const request = new Request("https://example.com")
* const resposne = await AuthHandler(request, {
* providers: [GitHub({ clientId: "", clientSecret: "" })],
* })
* ```
*
* ## Resources
*
* @see [GitHub - Creating an OAuth App](https://docs.github.com/en/developers/apps/building-oauth-apps/creating-an-oauth-app)
* @see [GitHub - Authorizing OAuth Apps](https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps)
* @see [GitHub - Configure your GitHub OAuth Apps](https://github.com/settings/developers)
* @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/github.ts)
*
* ## Notes
*
* By default, Auth.js assumes that the GitHub provider is
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
*
* :::tip
*
* The GitHub provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/github.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 GitHub(options) {
return {

@@ -3,0 +50,0 @@ id: "github",

@@ -46,3 +46,48 @@ import type { OAuthConfig, OAuthUserConfig } from "./index.js";

}
/**
* Add GitLab login to your page.
*
* ## Example
*
* @example
*
* ```js
* import Auth from "@auth/core"
* import { GitLab } from "@auth/core/providers/gitlab"
*
* const request = new Request("https://example.com")
* const resposne = await AuthHandler(request, {
* providers: [
* GitLab({clientId: "", clientSecret: ""})
* ]
* })
* ```
*
* ## Resources
*
* @see [Link 1](https://example.com)
*
* ## Notes
*
* By default, Auth.js assumes that the GitLab provider is
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
*
* :::tip
*
* The GitLab provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/gitlab.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 GitLab<P extends GitLabProfile>(options: OAuthUserConfig<P>): OAuthConfig<P>;
//# sourceMappingURL=gitlab.d.ts.map

@@ -0,1 +1,46 @@

/**
* Add GitLab login to your page.
*
* ## Example
*
* @example
*
* ```js
* import Auth from "@auth/core"
* import { GitLab } from "@auth/core/providers/gitlab"
*
* const request = new Request("https://example.com")
* const resposne = await AuthHandler(request, {
* providers: [
* GitLab({clientId: "", clientSecret: ""})
* ]
* })
* ```
*
* ## Resources
*
* @see [Link 1](https://example.com)
*
* ## Notes
*
* By default, Auth.js assumes that the GitLab provider is
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
*
* :::tip
*
* The GitLab provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/gitlab.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 GitLab(options) {

@@ -2,0 +47,0 @@ return {

/** @type {import(".").OAuthProvider} */
export default function IdentityServer4(options: Partial<import("./oauth").OAuthConfig<any>>): import("./oauth").OAuthConfig<any>;
export default function IdentityServer4(options: any): {
id: string;
name: string;
type: string;
options: any;
};
//# sourceMappingURL=identity-server4.d.ts.map

@@ -1,16 +0,50 @@

import type { OAuthConfig, OAuthProvider, OAuthProviderType } from "./oauth.js";
import type { EmailConfig, EmailProvider, EmailProviderType } from "./email.js";
import type { CredentialsConfig, CredentialsProvider, CredentialsProviderType } from "./credentials.js";
import { Profile } from "../types.js";
import CredentialsProvider from "./credentials.js";
import type { CredentialsConfig, CredentialsProviderType } from "./credentials.js";
import type { Email as EmailProvider, EmailConfig, EmailProviderType } from "./email.js";
import type { OAuth2Config, OAuthConfig, OAuthProviderType, OIDCConfig } from "./oauth.js";
export * from "./credentials.js";
export * from "./email.js";
export * from "./oauth.js";
/**
* Providers passed to Auth.js must define one of these types.
*
* @see [RFC 6749 - The OAuth 2.0 Authorization Framework](https://www.rfc-editor.org/rfc/rfc6749.html#section-2.3)
* @see [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication)
* @see [Email or Passwordless Authentication](https://authjs.dev/concepts/oauth)
* @see [Credentials-based Authentication](https://authjs.dev/concepts/credentials)
*/
export declare type ProviderType = "oidc" | "oauth" | "email" | "credentials";
/** Shared across all {@link ProviderType} */
export interface CommonProviderOptions {
/**
* Uniquely identifies the provider in {@link AuthConfig.providers}
* It's also part of the URL
*/
id: string;
/**
* The provider name used on the default sign-in page's sign-in button.
* For example if it's "Google", the corresponding button will say:
* "Sign in with Google"
*/
name: string;
/** See {@link ProviderType} */
type: ProviderType;
options?: Record<string, unknown>;
}
export declare type Provider = OAuthConfig<any> | EmailConfig | CredentialsConfig;
export declare type BuiltInProviders = Record<OAuthProviderType, OAuthProvider> & Record<CredentialsProviderType, CredentialsProvider> & Record<EmailProviderType, EmailProvider>;
/**
* Must be a supported authentication provider config:
* - {@link OAuthConfig}
* - {@link EmailConfigInternal}
* - {@link CredentialsConfigInternal}
*
* For more information, see the guides:
*
* @see [OAuth/OIDC guide](https://authjs.dev/guides/providers/custom-provider)
* @see [Email (Passwordless) guide](https://authjs.dev/guides/providers/email)
* @see [Credentials guide](https://authjs.dev/guides/providers/credentials)
*/
export declare type Provider<P extends Profile = Profile> = (OIDCConfig<P> | OAuth2Config<P> | EmailConfig | CredentialsConfig) & {
options: Record<string, unknown>;
};
export declare type BuiltInProviders = Record<OAuthProviderType, (options: Partial<OAuthConfig<any>>) => OAuthConfig<any>> & Record<CredentialsProviderType, typeof CredentialsProvider> & Record<EmailProviderType, typeof EmailProvider>;
export declare type AppProviders = Array<Provider | ReturnType<BuiltInProviders[keyof BuiltInProviders]>>;

@@ -17,0 +51,0 @@ export interface AppProvider extends CommonProviderOptions {

@@ -25,6 +25,31 @@ /**

* ```
* [NextAuth.js Documentation](https://next-auth.js.org/providers/instagram) | [Instagram Documentation](https://developers.facebook.com/docs/instagram-basic-display-api/getting-started) | [Configuration](https://developers.facebook.com/apps)
* [NextAuth.js Documentation](https://authjs.dev/reference/providers/instagram) | [Instagram Documentation](https://developers.facebook.com/docs/instagram-basic-display-api/getting-started) | [Configuration](https://developers.facebook.com/apps)
*/
/** @type {import(".").OAuthProvider} */
export default function Instagram(options: Partial<import("src/providers").OAuthConfig<any>>): import("src/providers").OAuthConfig<any>;
export default function Instagram(options: any): {
id: string;
name: string;
type: string;
authorization: string;
token: string;
userinfo: string;
client: {
token_endpoint_auth_method: string;
};
profile(profile: any): Promise<{
id: any;
name: any;
email: null;
image: null;
}>;
style: {
logo: string;
logoDark: string;
bg: string;
text: string;
bgDark: string;
textDark: string;
};
options: any;
};
//# sourceMappingURL=instagram.d.ts.map

@@ -25,3 +25,3 @@ /**

* ```
* [NextAuth.js Documentation](https://next-auth.js.org/providers/instagram) | [Instagram Documentation](https://developers.facebook.com/docs/instagram-basic-display-api/getting-started) | [Configuration](https://developers.facebook.com/apps)
* [NextAuth.js Documentation](https://authjs.dev/reference/providers/instagram) | [Instagram Documentation](https://developers.facebook.com/docs/instagram-basic-display-api/getting-started) | [Configuration](https://developers.facebook.com/apps)
*/

@@ -28,0 +28,0 @@ /** @type {import(".").OAuthProvider} */

/** @type {import(".").OAuthProvider} */
export default function Mailchimp(options: Partial<import("./oauth").OAuthConfig<any>>): import("./oauth").OAuthConfig<any>;
export default function Mailchimp(options: any): {
id: string;
name: string;
type: string;
authorization: string;
token: string;
userinfo: string;
profile(profile: any): {
id: any;
name: any;
email: any;
image: null;
};
style: {
logo: string;
logoDark: string;
bg: string;
text: string;
bgDark: string;
textDark: string;
};
options: any;
};
//# sourceMappingURL=mailchimp.d.ts.map
/** @type {import(".").OAuthProvider} */
export default function MailRu(options: Partial<import("./oauth").OAuthConfig<any>>): import("./oauth").OAuthConfig<any>;
export default function MailRu(options: any): {
id: string;
name: string;
type: string;
authorization: string;
token: string;
userinfo: string;
options: any;
};
//# sourceMappingURL=mailru.d.ts.map
/** @type {import(".").OAuthProvider} */
export default function Medium(options: Partial<import("./oauth").OAuthConfig<any>>): import("./oauth").OAuthConfig<any>;
export default function Medium(options: any): {
id: string;
name: string;
type: string;
authorization: string;
token: string;
userinfo: string;
profile(profile: any): {
id: any;
name: any;
email: null;
image: any;
};
options: any;
};
//# sourceMappingURL=medium.d.ts.map
/** @type {import(".").OAuthProvider} */
export default function Netlify(options: Partial<import("./oauth").OAuthConfig<any>>): import("./oauth").OAuthConfig<any>;
export default function Netlify(options: any): {
id: string;
name: string;
type: string;
authorization: string;
token: string;
userinfo: string;
profile(profile: any): {
id: any;
name: any;
email: any;
image: any;
};
options: any;
};
//# sourceMappingURL=netlify.d.ts.map

@@ -1,2 +0,2 @@

export declare 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" | "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";
export declare 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";
//# sourceMappingURL=oauth-types.d.ts.map

@@ -0,4 +1,4 @@

import type { Client } from "oauth4webapi";
import type { Awaitable, Profile, TokenSet, User } from "../types.js";
import type { CommonProviderOptions } from "../providers/index.js";
import type { Profile, TokenSet, User, Awaitable } from "../index.js";
import type { Client } from "oauth4webapi";
declare type AuthorizationParameters = any;

@@ -66,2 +66,3 @@ declare type CallbackParamsType = any;

}
/** TODO: */
export interface OAuth2Config<P> extends CommonProviderOptions, PartialIssuer {

@@ -71,4 +72,5 @@ /**

* a specific provider.
*
* @example
* ```js
* ```ts
* signIn('github') // "github" is the provider ID

@@ -105,3 +107,3 @@ * ```

*
* [Documentation](https://next-auth.js.org/adapters/models#user)
* [Documentation](https://authjs.dev/reference/adapters/models#user)
*/

@@ -123,6 +125,7 @@ profile?: ProfileCallback<P>;

/**
* [Documentation](https://next-auth.js.org/configuration/providers/oauth#allowdangerousemailaccountlinking-option)
* [Documentation](https://authjs.dev/reference/providers/oauth#allowdangerousemailaccountlinking-option)
*/
allowDangerousEmailAccountLinking?: boolean;
}
/** TODO: */
export interface OIDCConfig<P> extends Omit<OAuth2Config<P>, "type"> {

@@ -154,3 +157,2 @@ type: "oidc";

export declare type OAuthUserConfig<P> = Omit<Partial<OAuthConfig<P>>, "options" | "type"> & Required<Pick<OAuthConfig<P>, "clientId" | "clientSecret">>;
export declare type OAuthProvider = (options: Partial<OAuthConfig<any>>) => OAuthConfig<any>;
//# sourceMappingURL=oauth.d.ts.map
/** @type {import(".").OAuthProvider} */
export default function OneLogin(options: Partial<import("./oauth").OAuthConfig<any>>): import("./oauth").OAuthConfig<any>;
export default function OneLogin(options: any): {
id: string;
name: string;
type: string;
wellKnown: string;
options: any;
};
//# sourceMappingURL=onelogin.d.ts.map
/** @type {import(".").OAuthProvider} */
export default function Osso(options: Partial<import("./oauth").OAuthConfig<any>>): import("./oauth").OAuthConfig<any>;
export default function Osso(options: any): {
id: string;
name: string;
type: string;
authorization: string;
token: string;
userinfo: string;
options: any;
};
//# sourceMappingURL=osso.d.ts.map

@@ -1,2 +0,2 @@

import { OAuthConfig, OAuthUserConfig } from "./index.js";
import type { OAuthConfig, OAuthUserConfig } from "./index.js";
export interface PinterestProfile extends Record<string, any> {

@@ -3,0 +3,0 @@ account_type: "BUSINESS" | "PINNER";

/** @type {import(".").OAuthProvider} */
export default function Reddit(options: Partial<import("./oauth").OAuthConfig<any>>): import("./oauth").OAuthConfig<any>;
export default function Reddit(options: any): {
id: string;
name: string;
type: string;
authorization: string;
token: string;
userinfo: string;
options: any;
};
//# sourceMappingURL=reddit.d.ts.map

@@ -11,3 +11,51 @@ import type { OAuthConfig, OAuthUserConfig } from "./index.js";

}
/**
* Add Spotify login to your page.
*
* ## Example
*
* @example
*
* ```ts
* import Auth from "@auth/core"
* import { Spotify } from "@auth/core/providers/spotify"
*
* const request = new Request("https://example.com")
* const resposne = await AuthHandler(request, {
* providers: [
* Spotify({clientId: "", clientSecret: ""})
* ]
* })
* ```
*
* ---
*
* ## Resources
* @see [Link 1](https://example.com)
*
* ---
*
* ## Notes
*
* By default, Auth.js assumes that the Spotify provider is
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
*
* :::tip
*
* The Spotify provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/spotify.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/github-discussions).
*
* :::
*/
export default function Spotify<P extends SpotifyProfile>(options: OAuthUserConfig<P>): OAuthConfig<P>;
//# sourceMappingURL=spotify.d.ts.map

@@ -0,1 +1,49 @@

/**
* Add Spotify login to your page.
*
* ## Example
*
* @example
*
* ```ts
* import Auth from "@auth/core"
* import { Spotify } from "@auth/core/providers/spotify"
*
* const request = new Request("https://example.com")
* const resposne = await AuthHandler(request, {
* providers: [
* Spotify({clientId: "", clientSecret: ""})
* ]
* })
* ```
*
* ---
*
* ## Resources
* @see [Link 1](https://example.com)
*
* ---
*
* ## Notes
*
* By default, Auth.js assumes that the Spotify provider is
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
*
* :::tip
*
* The Spotify provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/spotify.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/github-discussions).
*
* :::
*/
export default function Spotify(options) {

@@ -2,0 +50,0 @@ return {

/** @type {import(".").OAuthProvider} */
export default function WordPress(options: Partial<import("./oauth").OAuthConfig<any>>): import("./oauth").OAuthConfig<any>;
export default function WordPress(options: any): {
id: string;
name: string;
type: string;
authorization: string;
token: string;
userinfo: string;
profile(profile: any): {
id: any;
name: any;
email: any;
image: any;
};
options: any;
};
//# sourceMappingURL=wordpress.d.ts.map
/** @type {import(".").OAuthProvider} */
export default function Yandex(options: Partial<import("./oauth").OAuthConfig<any>>): import("./oauth").OAuthConfig<any>;
export default function Yandex(options: any): {
id: string;
name: string;
type: string;
authorization: string;
token: string;
userinfo: string;
profile(profile: any): {
id: any;
name: any;
email: any;
image: string | null;
};
options: any;
};
//# sourceMappingURL=yandex.d.ts.map
/** @type {import(".").OAuthProvider} */
export default function Zoho(options: Partial<import("./oauth").OAuthConfig<any>>): import("./oauth").OAuthConfig<any>;
export default function Zoho(options: any): {
id: string;
name: string;
type: string;
authorization: string;
token: string;
userinfo: string;
profile(profile: any): {
id: any;
name: string;
email: any;
image: null;
};
options: any;
};
//# sourceMappingURL=zoho.d.ts.map
# Auth.js
Authentication for the web.
Authentication for the web.

@@ -1,3 +0,76 @@

import type { Account, Awaitable, User } from "./index.js"
/**
* This module contains functions and types that a database adapter
* can use to be compatible with Auth.js.
*
* A database adapter provides a common interface for Auth.js so that it can work with
* _any_ database/ORM adapter without concerning itself with the implementation details of the database/ORM.
*
* Auth.js supports 2 session strtategies to persist the login state of a user.
* The default is to use a cookie + {@link https://authjs.dev/concepts/session-strategies#jwt JWT}
* based session store (`strategy: "jwt"`),
* but you can also use a database adapter to store the session in a database.
*
* :::info Note
* Auth.js _currently_ does **not** implement {@link https://authjs.dev/concepts/session-strategies#federated-logout federated logout}.
* So even if the session is deleted from the database, the user will still be logged in to the provider (but will be logged out of the app).
* See [this discussion](https://github.com/nextauthjs/next-auth/discussions/3938) for more information.
* :::
*
* ## Installation
*
* ```bash npm2yarn2pnpm
* npm install @auth/core
* ```
*
* You can then import this submodule from `@auth/core/adapters`.
*
* ## Usage
*
* {@link https://authjs.dev/reference/adapters/overview Built-in adapters} already implement this interfac, so you likely won't need to
* implement it yourself. If you do, you can use the following example as a
* starting point.
*
* ```ts title=your-adapter.ts
* import { type Adapter } from "@auth/core/adapters"
*
* export function MyAdapter(config: {}): Adapter {
* // implement the adapter methods
* }
* ```
*
* ```ts title=index.ts
* import { MyAdapter } from "./your-adapter"
*
* const response = Auth({
* adapter: MyAdapter({ /* ...adapter config *\/ }),
* // ... auth config
* })
* ```
*
* :::caution Note
* Although `@auth/core` is framework/runtime agnostic, an adapter might rely on a client/ORM package,
* that is not yet compatible with your runtime
* (E.g. it might rely on [Node.js-specific APIs](https://nodejs.org/docs/latest/api)) when you are trying to use it elsewhere.
* Related issues should be reported to the corresponding package maintainers.
* :::
*
* ### Testing
* :::tip
* If you are writing your own adapter, there is a test suite [available](https://github.com/nextauthjs/next-auth/tree/main/packages/adapter-test)
* to ensure that your adapter is compatible with Auth.js.
* :::
*
* ## Resources
*
* - [What is a database session strategy?](https://authjs.dev/concepts/session-strategies#database)
*
* @module adapters
*/
import type { Account, Awaitable, User } from "./types.js"
// TODO: Discuss if we should expose methods to serialize and deserialize
// the data? Many adapters share this logic, so it could be useful to
// have a common implementation.
export interface AdapterUser extends User {

@@ -13,7 +86,22 @@ id: string

/**
* The session object implementing this interface
* is used to look up the user in the database.
*/
export interface AdapterSession {
/** A randomly generated value that is used to get hold of the session. */
sessionToken: string
/** Used to connect the session to a particular user */
/** Connects the active session to a user in the database */
userId: string
/**
* The absolute date when the session expires.
*
* If a session is accessed prior to its expiry date,
* it will be extended based on the `maxAge` option as defined in by {@linkcode SessionOptions.maxAge}.
* It is never extended more than once in a period defined by {@linkcode SessionOptions.updateAge}.
*
* If a session is accessed past its expiry date,
* it will be removed from the database to clean up inactive sessions.
*
*/
expires: Date

@@ -35,98 +123,58 @@ }

*
* **Required methods**
* ## Resources
*
* _(These methods are required for all sign in flows)_
* - `createUser`
* - `getUser`
* - `getUserByEmail`
* - `getUserByAccount`
* - `linkAccount`
* - `createSession`
* - `getSessionAndUser`
* - `updateSession`
* - `deleteSession`
* - `updateUser`
*
* _(Required to support email / passwordless sign in)_
*
* - `createVerificationToken`
* - `useVerificationToken`
*
* **Unimplemented methods**
*
* _(These methods will be required in a future release, but are not yet invoked)_
* - `deleteUser`
* - `unlinkAccount`
*
* [Adapters Overview](https://next-auth.js.org/adapters/overview) |
* [Create a custom adapter](https://next-auth.js.org/tutorials/creating-a-database-adapter)
* - [Session strategies](https://authjs.dev/concepts/session-strategies#database)
* - [Using a database adapter](https://authjs.dev/guides/adapters/using-a-database-adapter)
* - [Creating a database adapter](https://authjs.dev/guides/adapters/creating-a-database-adapter)
*/
export type Adapter<WithVerificationToken = boolean> = DefaultAdapter &
(WithVerificationToken extends true
? {
createVerificationToken: (
verificationToken: VerificationToken
) => Awaitable<VerificationToken | null | undefined>
/**
* Return verification token from the database
* and delete it so it cannot be used again.
*/
useVerificationToken: (params: {
identifier: string
token: string
}) => Awaitable<VerificationToken | null>
}
: {})
export interface DefaultAdapter {
createUser: (user: Omit<AdapterUser, "id">) => Awaitable<AdapterUser>
getUser: (id: string) => Awaitable<AdapterUser | null>
getUserByEmail: (email: string) => Awaitable<AdapterUser | null>
export interface Adapter {
createUser(user: Omit<AdapterUser, "id">): Awaitable<AdapterUser>
getUser(id: string): Awaitable<AdapterUser | null>
getUserByEmail(email: string): Awaitable<AdapterUser | null>
/** Using the provider id and the id of the user for a specific account, get the user. */
getUserByAccount: (
getUserByAccount(
providerAccountId: Pick<AdapterAccount, "provider" | "providerAccountId">
) => Awaitable<AdapterUser | null>
updateUser: (user: Partial<AdapterUser>) => Awaitable<AdapterUser>
/** @todo Implement */
deleteUser?: (
): Awaitable<AdapterUser | null>
updateUser(user: Partial<AdapterUser>): Awaitable<AdapterUser>
/** @todo This method is currently not implemented. Defining it will have no effect */
deleteUser?(
userId: string
) => Promise<void> | Awaitable<AdapterUser | null | undefined>
linkAccount: (
): Promise<void> | Awaitable<AdapterUser | null | undefined>
linkAccount(
account: AdapterAccount
) => Promise<void> | Awaitable<AdapterAccount | null | undefined>
/** @todo Implement */
unlinkAccount?: (
): Promise<void> | Awaitable<AdapterAccount | null | undefined>
/** @todo This method is currently not implemented. Defining it will have no effect */
unlinkAccount?(
providerAccountId: Pick<AdapterAccount, "provider" | "providerAccountId">
) => Promise<void> | Awaitable<AdapterAccount | undefined>
): Promise<void> | Awaitable<AdapterAccount | undefined>
/** Creates a session for the user and returns it. */
createSession: (session: {
createSession(session: {
sessionToken: string
userId: string
expires: Date
}) => Awaitable<AdapterSession>
getSessionAndUser: (
}): Awaitable<AdapterSession>
getSessionAndUser(
sessionToken: string
) => Awaitable<{ session: AdapterSession; user: AdapterUser } | null>
updateSession: (
): Awaitable<{ session: AdapterSession; user: AdapterUser } | null>
updateSession(
session: Partial<AdapterSession> & Pick<AdapterSession, "sessionToken">
) => Awaitable<AdapterSession | null | undefined>
): Awaitable<AdapterSession | null | undefined>
/**
* Deletes a session from the database.
* It is preferred that this method also returns the session
* that is being deleted for logging purposes.
* Deletes a session from the database. It is preferred that this method also
* returns the session that is being deleted for logging purposes.
*/
deleteSession: (
deleteSession(
sessionToken: string
) => Promise<void> | Awaitable<AdapterSession | null | undefined>
createVerificationToken?: (
): Promise<void> | Awaitable<AdapterSession | null | undefined>
createVerificationToken?(
verificationToken: VerificationToken
) => Awaitable<VerificationToken | null | undefined>
): Awaitable<VerificationToken | null | undefined>
/**
* Return verification token from the database
* and delete it so it cannot be used again.
* Return verification token from the database and delete it so it cannot be
* used again.
*/
useVerificationToken?: (params: {
useVerificationToken?(params: {
identifier: string
token: string
}) => Awaitable<VerificationToken | null>
}): Awaitable<VerificationToken | null>
}

@@ -1,34 +0,89 @@

import { init } from "./lib/init.js"
/**
*
* This is the main entry point to the Auth.js library.
*
* Based on the {@link https://developer.mozilla.org/en-US/docs/Web/API/Request Request}
* and {@link https://developer.mozilla.org/en-US/docs/Web/API/Response Response} Web standard APIs.
* Primarily used to implement [framework](https://authjs.dev/concepts/frameworks)-specific packages,
* but it can also be used directly.
*
* ## Installation
*
* ```bash npm2yarn2pnpm
* npm install @auth/core
* ```
*
* ## Usage
*
* ```ts
* import { Auth } from "@auth/core"
*
* const request = new Request("https://example.com"
* const response = await Auth(request, {...})
*
* console.log(response instanceof Response) // true
* ```
*
* ## Resources
*
* - [Gettint started](https://authjs.dev/getting-started/introduction)
* - [Most common use case guides](https://authjs.dev/guides/overview)
*
* @module main
*/
import { assertConfig } from "./lib/assert.js"
import { SessionStore } from "./lib/cookie.js"
import { ErrorPageLoop } from "./errors.js"
import { AuthInternal } 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"
import renderPage from "./lib/pages/index.js"
import * as routes from "./lib/routes/index.js"
import logger, { setLogger } from "./lib/utils/logger.js"
import type { ErrorType } from "./lib/pages/error.js"
import type { Adapter } from "./adapters.js"
import type {
AuthOptions,
RequestInternal,
ResponseInternal,
} from "./lib/types.js"
import { UntrustedHost } from "./lib/errors.js"
CallbacksOptions,
CookiesOptions,
EventCallbacks,
PagesOptions,
SessionOptions,
Theme,
} from "./types.js"
import type { Provider } from "./providers/index.js"
import { JWTOptions } from "./jwt.js"
export * from "./lib/types.js"
/**
* Core functionality provided by Auth.js.
*
* Receives a standard {@link Request} and returns a {@link Response}.
*
* @example
* ```ts
* import Auth from "@auth/core"
*
* const request = new Request("https://example.com")
* const resposne = await AuthHandler(request, {
* providers: [...],
* secret: "...",
* trustHost: true,
* })
*```
* @see [Documentation](https://authjs.dev)
*/
export async function Auth(
request: Request,
config: AuthConfig
): Promise<Response> {
setLogger(config.logger, config.debug)
const configErrorMessage =
"There is a problem with the server configuration. Check the server logs for more information."
const internalRequest = await toInternalRequest(request)
if (internalRequest instanceof Error) {
logger.error(internalRequest)
return new Response(
`Error: This action with HTTP ${request.method} is not supported.`,
{ status: 400 }
)
}
async function AuthHandlerInternal<
Body extends string | Record<string, any> | any[]
>(params: {
req: RequestInternal
options: AuthOptions
/** REVIEW: Is this the best way to skip parsing the body in Node.js? */
parsedBody?: any
}): Promise<ResponseInternal<Body>> {
const { options: authOptions, req } = params
const assertionResult = assertConfig(internalRequest, config)
const assertionResult = assertConfig({ options: authOptions, req })
if (Array.isArray(assertionResult)) {

@@ -38,16 +93,25 @@ assertionResult.forEach(logger.warn)

// Bail out early if there's an error in the user config
logger.error((assertionResult as any).code, assertionResult)
logger.error(assertionResult)
const htmlPages = ["signin", "signout", "error", "verify-request"]
if (!htmlPages.includes(req.action) || req.method !== "GET") {
return {
status: 500,
headers: { "Content-Type": "application/json" },
body: { message: configErrorMessage } as any,
}
if (
!htmlPages.includes(internalRequest.action) ||
internalRequest.method !== "GET"
) {
return new Response(
JSON.stringify({
message:
"There was a problem with the server configuration. Check the server logs for more information.",
code: assertionResult.name,
}),
{ status: 500, headers: { "Content-Type": "application/json" } }
)
}
const { pages, theme } = authOptions
const { pages, theme } = config
const authOnErrorPage =
pages?.error && req.query?.callbackUrl?.startsWith(pages.error)
pages?.error &&
internalRequest.url.searchParams
.get("callbackUrl")
?.startsWith(pages.error)

@@ -57,4 +121,3 @@ if (!pages?.error || authOnErrorPage) {

logger.error(
"AUTH_ON_ERROR_PAGE_ERROR",
new Error(
new ErrorPageLoop(
`The error page ${pages?.error} should not require authentication`

@@ -65,213 +128,11 @@ )

const render = renderPage({ theme })
return render.error({ error: "configuration" })
const page = render.error({ error: "Configuration" })
return toResponse(page)
}
return {
redirect: `${pages.error}?error=Configuration`,
}
return Response.redirect(`${pages.error}?error=Configuration`)
}
const { action, providerId, error, method } = req
const internalResponse = await AuthInternal(internalRequest, config)
const { options, cookies } = await init({
authOptions,
action,
providerId,
url: req.url,
callbackUrl: req.body?.callbackUrl ?? req.query?.callbackUrl,
csrfToken: req.body?.csrfToken,
cookies: req.cookies,
isPost: method === "POST",
})
const sessionStore = new SessionStore(
options.cookies.sessionToken,
req,
options.logger
)
if (method === "GET") {
const render = renderPage({ ...options, query: req.query, cookies })
const { pages } = options
switch (action) {
case "providers":
return (await routes.providers(options.providers)) as any
case "session": {
const session = await routes.session({ options, sessionStore })
if (session.cookies) cookies.push(...session.cookies)
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
return { ...session, cookies } as any
}
case "csrf":
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)}`
if (error)
signinUrl = `${signinUrl}&error=${encodeURIComponent(error)}`
return { redirect: signinUrl, cookies }
}
return render.signin()
case "signout":
if (pages.signOut) return { redirect: pages.signOut, cookies }
return render.signout()
case "callback":
if (options.provider) {
const callback = await routes.callback({
body: req.body,
query: req.query,
headers: req.headers,
cookies: req.cookies,
method,
options,
sessionStore,
})
if (callback.cookies) cookies.push(...callback.cookies)
return { ...callback, cookies }
}
break
case "verify-request":
if (pages.verifyRequest) {
return { redirect: pages.verifyRequest, cookies }
}
return render.verifyRequest()
case "error":
// These error messages are displayed in line on the sign in page
if (
[
"Signin",
"OAuthSignin",
"OAuthCallback",
"OAuthCreateAccount",
"EmailCreateAccount",
"Callback",
"OAuthAccountNotLinked",
"EmailSignin",
"CredentialsSignin",
"SessionRequired",
].includes(error as string)
) {
return { redirect: `${options.url}/signin?error=${error}`, cookies }
}
if (pages.error) {
return {
redirect: `${pages.error}${
pages.error.includes("?") ? "&" : "?"
}error=${error}`,
cookies,
}
}
return render.error({ error: error as ErrorType })
default:
}
} else if (method === "POST") {
switch (action) {
case "signin":
// Verified CSRF Token required for all sign in routes
if (options.csrfTokenVerified && options.provider) {
const signin = await routes.signin({
query: req.query,
body: req.body,
options,
})
if (signin.cookies) cookies.push(...signin.cookies)
return { ...signin, cookies }
}
return { redirect: `${options.url}/signin?csrf=true`, cookies }
case "signout":
// Verified CSRF Token required for signout
if (options.csrfTokenVerified) {
const signout = await routes.signout({ options, sessionStore })
if (signout.cookies) cookies.push(...signout.cookies)
return { ...signout, cookies }
}
return { redirect: `${options.url}/signout?csrf=true`, cookies }
case "callback":
if (options.provider) {
// Verified CSRF Token required for credentials providers only
if (
options.provider.type === "credentials" &&
!options.csrfTokenVerified
) {
return { redirect: `${options.url}/signin?csrf=true`, cookies }
}
const callback = await routes.callback({
body: req.body,
query: req.query,
headers: req.headers,
cookies: req.cookies,
method,
options,
sessionStore,
})
if (callback.cookies) cookies.push(...callback.cookies)
return { ...callback, cookies }
}
break
case "_log":
if (authOptions.logger) {
try {
const { code, level, ...metadata } = req.body ?? {}
logger[level](code, metadata)
} catch (error) {
// If logging itself failed...
logger.error("LOGGER_ERROR", error as Error)
}
}
return {}
default:
}
}
return {
status: 400,
body: `Error: This action with HTTP ${method} is not supported by NextAuth.js` as any,
}
}
/**
* The core functionality of Auth.js.
* It receives a standard [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
* and returns a standard [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response).
*/
export async function AuthHandler(
request: Request,
options: AuthOptions
): Promise<Response> {
setLogger(options.logger, options.debug)
if (!options.trustHost) {
const error = new UntrustedHost(
`Host must be trusted. URL was: ${request.url}`
)
logger.error(error.code, error)
return new Response(JSON.stringify({ message: configErrorMessage }), {
status: 500,
headers: { "Content-Type": "application/json" },
})
}
const req = await toInternalRequest(request)
if (req instanceof Error) {
logger.error((req as any).code, req)
return new Response(
`Error: This action with HTTP ${request.method} is not supported.`,
{ status: 400 }
)
}
const internalResponse = await AuthHandlerInternal({ req, options })
const response = await toResponse(internalResponse)

@@ -291,1 +152,210 @@

}
/**
* Configure the {@link Auth} method.
*
* @example
* ```ts
* import Auth, { type AuthConfig } from "@auth/core"
*
* export const authConfig: AuthConfig = {...}
*
* const request = new Request("https://example.com")
* const resposne = await AuthHandler(request, authConfig)
*
* ```
*
* @see [Initiailzation](https://authjs.dev/reference/configuration/auth-options)
*/
export interface AuthConfig {
/**
* List of authentication providers for signing in
* (e.g. Google, Facebook, Twitter, GitHub, Email, etc) in any order.
* This can be one of the built-in providers or an object with a custom provider.
* * **Default value**: `[]`
* * **Required**: *Yes*
*
* [Documentation](https://next-auth.js.org/configuration/options#providers) | [Providers documentation](https://next-auth.js.org/configuration/providers)
*/
providers: Provider[]
/**
* A random string used to hash tokens, sign cookies and generate cryptographic keys.
* If not specified, it falls back to `AUTH_SECRET` or `NEXTAUTH_SECRET` from environment variables.
* To generate a random string, you can use the following command:
*
* On Unix systems: `openssl rand -hex 32`
* Or go to https://generate-secret.vercel.app/32
*
* @default process.env.AUTH_SECRET ?? process.env.NEXTAUTH_SECRET
*
* [Documentation](https://next-auth.js.org/configuration/options#secret)
*/
secret?: string
/**
* Configure your session like if you want to use JWT or a database,
* how long until an idle session expires, or to throttle write operations in case you are using a database.
* * **Default value**: See the documentation page
* * **Required**: No
*
* [Documentation](https://next-auth.js.org/configuration/options#session)
*/
session?: Partial<SessionOptions>
/**
* JSON Web Tokens are enabled by default if you have not specified an adapter.
* JSON Web Tokens are encrypted (JWE) by default. We recommend you keep this behaviour.
* * **Default value**: See the documentation page
* * **Required**: *No*
*
* [Documentation](https://next-auth.js.org/configuration/options#jwt)
*/
jwt?: Partial<JWTOptions>
/**
* Specify URLs to be used if you want to create custom sign in, sign out and error pages.
* Pages specified will override the corresponding built-in page.
* * **Default value**: `{}`
* * **Required**: *No*
*
* @example
*
* ```ts
* pages: {
* signIn: '/auth/signin',
* signOut: '/auth/signout',
* error: '/auth/error',
* verifyRequest: '/auth/verify-request',
* newUser: '/auth/new-user'
* }
* ```
*
* [Documentation](https://next-auth.js.org/configuration/options#pages) | [Pages documentation](https://next-auth.js.org/configuration/pages)
*/
pages?: Partial<PagesOptions>
/**
* Callbacks are asynchronous functions you can use to control what happens when an action is performed.
* Callbacks are *extremely powerful*, especially in scenarios involving JSON Web Tokens
* as they **allow you to implement access controls without a database** and to **integrate with external databases or APIs**.
* * **Default value**: See the Callbacks documentation
* * **Required**: *No*
*
* [Documentation](https://next-auth.js.org/configuration/options#callbacks) | [Callbacks documentation](https://next-auth.js.org/configuration/callbacks)
*/
callbacks?: Partial<CallbacksOptions>
/**
* Events are asynchronous functions that do not return a response, they are useful for audit logging.
* You can specify a handler for any of these events below - e.g. for debugging or to create an audit log.
* The content of the message object varies depending on the flow
* (e.g. OAuth or Email authentication flow, JWT or database sessions, etc),
* but typically contains a user object and/or contents of the JSON Web Token
* and other information relevant to the event.
* * **Default value**: `{}`
* * **Required**: *No*
*
* [Documentation](https://next-auth.js.org/configuration/options#events) | [Events documentation](https://next-auth.js.org/configuration/events)
*/
events?: Partial<EventCallbacks>
/**
* You can use the adapter option to pass in your database adapter.
*
* * **Required**: *No*
*
* [Documentation](https://next-auth.js.org/configuration/options#adapter) |
* [Adapters Overview](https://next-auth.js.org/adapters/overview)
*/
adapter?: Adapter
/**
* Set debug to true to enable debug messages for authentication and database operations.
* * **Default value**: `false`
* * **Required**: *No*
*
* - ⚠ If you added a custom `logger`, this setting is ignored.
*
* [Documentation](https://next-auth.js.org/configuration/options#debug) | [Logger documentation](https://next-auth.js.org/configuration/options#logger)
*/
debug?: boolean
/**
* Override any of the logger levels (`undefined` levels will use the built-in logger),
* and intercept logs in NextAuth. You can use this option to send NextAuth logs to a third-party logging service.
* * **Default value**: `console`
* * **Required**: *No*
*
* @example
*
* ```ts
* // /pages/api/auth/[...nextauth].js
* import log from "logging-service"
* export default NextAuth({
* logger: {
* error(code, ...message) {
* log.error(code, message)
* },
* warn(code, ...message) {
* log.warn(code, message)
* },
* debug(code, ...message) {
* log.debug(code, message)
* }
* }
* })
* ```
*
* - ⚠ When set, the `debug` option is ignored
*
* [Documentation](https://next-auth.js.org/configuration/options#logger) |
* [Debug documentation](https://next-auth.js.org/configuration/options#debug)
*/
logger?: Partial<LoggerInstance>
/**
* Changes the theme of pages.
* Set to `"light"` if you want to force pages to always be light.
* Set to `"dark"` if you want to force pages to always be dark.
* Set to `"auto"`, (or leave this option out)if you want the pages to follow the preferred system theme.
* * **Default value**: `"auto"`
* * **Required**: *No*
*
* [Documentation](https://next-auth.js.org/configuration/options#theme) | [Pages documentation]("https://next-auth.js.org/configuration/pages")
*/
theme?: Theme
/**
* When set to `true` then all cookies set by NextAuth.js will only be accessible from HTTPS URLs.
* This option defaults to `false` on URLs that start with `http://` (e.g. http://localhost:3000) for developer convenience.
* You can manually set this option to `false` to disable this security feature and allow cookies
* to be accessible from non-secured URLs (this is not recommended).
* * **Default value**: `true` for HTTPS and `false` for HTTP sites
* * **Required**: No
*
* [Documentation](https://next-auth.js.org/configuration/options#usesecurecookies)
*
* - ⚠ **This is an advanced option.** Advanced options are passed the same way as basic options,
* but **may have complex implications** or side effects.
* You should **try to avoid using advanced options** unless you are very comfortable using them.
*/
useSecureCookies?: boolean
/**
* You can override the default cookie names and options for any of the cookies used by NextAuth.js.
* You can specify one or more cookies with custom properties,
* but if you specify custom options for a cookie you must provide all the options for that cookie.
* If you use this feature, you will likely want to create conditional behavior
* to support setting different cookies policies in development and production builds,
* as you will be opting out of the built-in dynamic policy.
* * **Default value**: `{}`
* * **Required**: No
*
* - ⚠ **This is an advanced option.** Advanced options are passed the same way as basic options,
* but **may have complex implications** or side effects.
* You should **try to avoid using advanced options** unless you are very comfortable using them.
*
* [Documentation](https://next-auth.js.org/configuration/options#cookies) | [Usage example](https://next-auth.js.org/configuration/options#example)
*/
cookies?: Partial<CookiesOptions>
/**
* If set to `true`, NextAuth.js will use either the `x-forwarded-host` or `host` headers,
* instead of `NEXTAUTH_URL`
* Make sure that reading `x-forwarded-host` on your hosting platform can be trusted.
* - ⚠ **This is an advanced option.** Advanced options are passed the same way as basic options,
* but **may have complex implications** or side effects.
* You should **try to avoid using advanced options** unless you are very comfortable using them.
*
* @default Boolean(process.env.NEXTAUTH_URL ?? process.env.AUTH_TRUST_HOST ?? process.env.VERCEL)
*/
trustHost?: boolean
}

@@ -0,1 +1,2 @@

import { defaultCookies } from "./cookie.js"
import {

@@ -6,22 +7,19 @@ InvalidCallbackUrl,

MissingAdapterMethods,
MissingAPIRoute,
MissingAuthorize,
MissingSecret,
UnsupportedStrategy,
} from "./errors.js"
import { defaultCookies } from "./cookie.js"
UntrustedHost,
} from "../errors.js"
import type { AuthOptions, RequestInternal } from "../index.js"
import type { AuthConfig, RequestInternal } from "../types.js"
import type { WarningCode } from "./utils/logger.js"
type ConfigError =
| InvalidCallbackUrl
| InvalidEndpoints
| MissingAdapter
| MissingAdapterMethods
| MissingAPIRoute
| MissingAuthorize
| MissingSecret
| InvalidCallbackUrl
| UnsupportedStrategy
| InvalidEndpoints
| UnsupportedStrategy

@@ -43,16 +41,14 @@ let warned = false

* Good place to mention deprecations as well.
*
* REVIEW: Make some of these and corresponding docs less Next.js specific?
*/
export function assertConfig(params: {
options: AuthOptions
req: RequestInternal
}): ConfigError | WarningCode[] {
const { options, req } = params
const { url } = req
export function assertConfig(
request: RequestInternal,
options: AuthConfig
): ConfigError | WarningCode[] {
const { url } = request
const warnings: WarningCode[] = []
if (!warned) {
if (!url.origin) warnings.push("NEXTAUTH_URL")
if (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}`)
}

@@ -64,11 +60,4 @@

// req.query isn't defined when asserting `unstable_getServerSession` for example
if (!req.query?.nextauth && !req.action) {
return new MissingAPIRoute(
"Cannot find [...nextauth].{js,ts} in `/pages/api/auth`. Make sure the filename is written correctly."
)
}
const callbackUrlParam = request.query?.callbackUrl as string | undefined
const callbackUrlParam = req.query?.callbackUrl as string | undefined
if (callbackUrlParam && !isValidHttpUrl(callbackUrlParam, url.origin)) {

@@ -84,3 +73,5 @@ return new InvalidCallbackUrl(

const callbackUrlCookie =
req.cookies?.[options.cookies?.callbackUrl?.name ?? defaultCallbackUrl.name]
request.cookies?.[
options.cookies?.callbackUrl?.name ?? defaultCallbackUrl.name
]

@@ -87,0 +78,0 @@ if (callbackUrlCookie && !isValidHttpUrl(callbackUrlCookie, url.origin)) {

@@ -1,7 +0,7 @@

import { AccountNotLinkedError } from "./errors.js"
import { AccountNotLinked } from "../errors.js"
import { fromDate } from "./utils/date.js"
import type { Account, InternalOptions, User } from "../index.js"
import type { AdapterSession, AdapterUser } from "../adapters.js"
import type { JWT } from "../jwt/index.js"
import type { Account, InternalOptions, User } from "../types.js"
import type { JWT } from "../jwt.js"
import type { OAuthConfig } from "../providers/index.js"

@@ -22,9 +22,8 @@ import type { SessionToken } from "./cookie.js"

*/
export default async function callbackHandler(params: {
sessionToken?: SessionToken
profile: User | AdapterUser | { email: string }
account: Account | null
export async function handleLogin(
sessionToken: SessionToken,
_profile: User | AdapterUser | { email: string },
account: Account | null,
options: InternalOptions
}) {
const { sessionToken, profile: _profile, account, options } = params
) {
// Input validation

@@ -122,3 +121,3 @@ if (!account?.providerAccountId || !account.type)

return { session, user, isNewUser }
} else if (account.type === "oauth") {
} else if (account.type === "oauth" || account.type === "oidc") {
// If signing in with OAuth account, check to see if the account exists already

@@ -138,3 +137,3 @@ const userByAccount = await getUserByAccount({

// and need to return an error.
throw new AccountNotLinkedError(
throw new AccountNotLinked(
"The account is already associated with another user"

@@ -199,3 +198,3 @@ )

// to sign in via email to verify their identity and then link the accounts.
throw new AccountNotLinkedError(
throw new AccountNotLinked(
"Another account already exists with the same e-mail address"

@@ -202,0 +201,0 @@ )

@@ -1,2 +0,2 @@

import type { InternalOptions } from "../index.js"
import type { InternalOptions } from "../types.js"

@@ -3,0 +3,0 @@ interface CreateCallbackUrlParams {

@@ -6,3 +6,3 @@ import type {

SessionStrategy,
} from "../index.js"
} from "../types.js"

@@ -164,2 +164,6 @@ // Uncomment to recalculate the estimated size

/**
* The JWT Session or database Session ID
* constructed from the cookie chunks.
*/
get value() {

@@ -166,0 +170,0 @@ return Object.values(this.#chunks)?.join("")

import { createHash, randomString } from "./web.js"
import type { InternalOptions } from "./types.js"
import type { InternalOptions } from "../types.js"
interface CreateCSRFTokenParams {

@@ -5,0 +5,0 @@ options: InternalOptions

@@ -1,2 +0,2 @@

import type { CallbacksOptions } from "../index.js"
import type { CallbacksOptions } from "../types.js"

@@ -3,0 +3,0 @@ export const defaultCallbacks: CallbacksOptions = {

@@ -1,4 +0,4 @@

import { randomString, createHash } from "../web.js"
import type { InternalOptions } from "../../index.js"
import { createHash, randomString } from "../web.js"
import type { InternalOptions } from "../../types.js"
/**

@@ -13,3 +13,2 @@ * Starts an e-mail login flow, by generating a token,

const { url, adapter, provider, callbackUrl, theme } = options
// Generate token
const token =

@@ -29,3 +28,2 @@ (await provider.generateVerificationToken?.()) ?? randomString(32)

await Promise.all([
// Send to user
provider.sendVerificationRequest({

@@ -39,4 +37,4 @@ identifier,

}),
// Save in database
adapter.createVerificationToken({
// @ts-expect-error -- Verified in `assertConfig`.
adapter.createVerificationToken?.({
identifier,

@@ -43,0 +41,0 @@ token: await createHash(`${token}${secret}`),

@@ -1,3 +0,2 @@

import { adapterErrorHandler, eventsErrorHandler } from "./errors.js"
import * as jwt from "../jwt/index.js"
import * as jwt from "../jwt.js"
import { createCallbackUrl } from "./callback-url.js"

@@ -7,11 +6,17 @@ import * as cookie from "./cookie.js"

import { defaultCallbacks } from "./default-callbacks.js"
import { AdapterError, EventError } from "../errors.js"
import parseProviders from "./providers.js"
import logger from "./utils/logger.js"
import { logger, type LoggerInstance } from "./utils/logger.js"
import parseUrl from "./utils/parse-url.js"
import type { AuthOptions, InternalOptions, RequestInternal } from "../index.js"
import type {
AuthConfig,
EventCallbacks,
InternalOptions,
RequestInternal,
} from "../types.js"
interface InitParams {
url: URL
authOptions: AuthOptions
authOptions: AuthConfig
providerId?: string

@@ -154,1 +159,44 @@ action: InternalOptions["action"]

}
type Method = (...args: any[]) => Promise<any>
/** Wraps an object of methods and adds error handling. */
function eventsErrorHandler(
methods: Partial<EventCallbacks>,
logger: LoggerInstance
): Partial<EventCallbacks> {
return Object.keys(methods).reduce<any>((acc, name) => {
acc[name] = async (...args: any[]) => {
try {
const method: Method = methods[name as keyof Method]
return await method(...args)
} catch (e) {
logger.error(new EventError(e))
}
}
return acc
}, {})
}
/** Handles adapter induced errors. */
function adapterErrorHandler<TAdapter>(
adapter: TAdapter | undefined,
logger: LoggerInstance
): TAdapter | undefined {
if (!adapter) return
return Object.keys(adapter).reduce<any>((acc, name) => {
acc[name] = async (...args: any[]) => {
try {
logger.debug(`adapter_${name}`, { args })
const method: Method = adapter[name as keyof Method]
return await method(...args)
} catch (e) {
const error = new AdapterError(e)
logger.error(error)
throw error
}
}
return acc
}, {})
}

@@ -8,3 +8,3 @@ import * as o from "oauth4webapi"

ResponseInternal,
} from "../../index.js"
} from "../../types.js"
import type { Cookie } from "../cookie.js"

@@ -17,9 +17,6 @@

*/
export async function getAuthorizationUrl({
options,
query,
}: {
export async function getAuthorizationUrl(
query: RequestInternal["query"],
options: InternalOptions<"oauth">
query: RequestInternal["query"]
}): Promise<ResponseInternal> {
): Promise<ResponseInternal> {
const { logger, provider } = options

@@ -90,4 +87,2 @@

url.searchParams.delete("nextauth")
// TODO: This does not work in normalizeOAuth because authorization endpoint can come from discovery

@@ -99,3 +94,3 @@ // Need to make normalizeOAuth async

logger.debug("GET_AUTHORIZATION_URL", { url, cookies, provider })
logger.debug("authorization url is ready", { url, cookies, provider })
return { redirect: url, cookies }

@@ -102,0 +97,0 @@ }

@@ -1,6 +0,6 @@

import { OAuthCallbackError } from "../errors.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 * as o from "oauth4webapi"

@@ -13,187 +13,169 @@ import type {

TokenSet,
} from "../../index.js"
} from "../../types.js"
import type { OAuthConfigInternal } from "../../providers/index.js"
import type { Cookie } from "../cookie.js"
export async function handleOAuthCallback(params: {
/**
* Handles the following OAuth steps.
* https://www.rfc-editor.org/rfc/rfc6749#section-4.1.1
* https://www.rfc-editor.org/rfc/rfc6749#section-4.1.3
* https://openid.net/specs/openid-connect-core-1_0.html#UserInfoRequest
*
* @note Although requesting userinfo is not required by the OAuth2.0 spec,
* we fetch it anyway. This is because we always want a user profile.
*/
export async function handleOAuth(
query: RequestInternal["query"],
cookies: RequestInternal["cookies"],
options: InternalOptions<"oauth">
query: RequestInternal["query"]
body: RequestInternal["body"]
cookies: RequestInternal["cookies"]
}) {
const { options, query, body, cookies } = params
) {
const { logger, provider } = options
let as: o.AuthorizationServer
const errorMessage = body?.error ?? query?.error
if (errorMessage) {
const error = new Error(errorMessage)
logger.error("OAUTH_CALLBACK_HANDLER_ERROR", {
error,
error_description: query?.error_description,
providerId: provider.id,
})
logger.debug("OAUTH_CALLBACK_HANDLER_ERROR", { body })
throw error
}
if (!provider.token?.url && !provider.userinfo?.url) {
// We assume that issuer is always defined as this has been asserted earlier
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const issuer = new URL(provider.issuer!)
const discoveryResponse = await o.discoveryRequest(issuer)
const discoveredAs = await o.processDiscoveryResponse(
issuer,
discoveryResponse
)
try {
let as: o.AuthorizationServer
if (!discoveredAs.token_endpoint)
throw new TypeError(
"TODO: Authorization server did not provide a token endpoint."
)
if (!provider.token?.url && !provider.userinfo?.url) {
// We assume that issuer is always defined as this has been asserted earlier
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const issuer = new URL(provider.issuer!)
const discoveryResponse = await o.discoveryRequest(issuer)
const discoveredAs = await o.processDiscoveryResponse(
issuer,
discoveryResponse
if (!discoveredAs.userinfo_endpoint)
throw new TypeError(
"TODO: Authorization server did not provide a userinfo endpoint."
)
if (!discoveredAs.token_endpoint)
throw new TypeError(
"TODO: Authorization server did not provide a token endpoint."
)
as = discoveredAs
} else {
as = {
issuer: provider.issuer ?? "https://a", // TODO: review fallback issuer
token_endpoint: provider.token?.url.toString(),
userinfo_endpoint: provider.userinfo?.url.toString(),
}
}
if (!discoveredAs.userinfo_endpoint)
throw new TypeError(
"TODO: Authorization server did not provide a userinfo endpoint."
)
const client: o.Client = {
client_id: provider.clientId,
client_secret: provider.clientSecret,
...provider.client,
}
as = discoveredAs
} else {
as = {
issuer: provider.issuer ?? "https://a", // TODO: review fallback issuer
token_endpoint: provider.token?.url.toString(),
userinfo_endpoint: provider.userinfo?.url.toString(),
}
}
const resCookies: Cookie[] = []
const client: o.Client = {
client_id: provider.clientId,
client_secret: provider.clientSecret,
...provider.client,
}
const state = await useState(cookies, resCookies, options)
const resCookies: Cookie[] = []
const parameters = o.validateAuthResponse(
as,
client,
new URLSearchParams(query),
provider.checks.includes("state") ? state : o.skipStateCheck
)
const state = await useState(cookies?.[options.cookies.state.name], options)
if (state) resCookies.push(state.cookie)
/** https://www.rfc-editor.org/rfc/rfc6749#section-4.1.2.1 */
if (o.isOAuth2Error(parameters)) {
logger.debug("OAuthCallbackError", {
providerId: provider.id,
...parameters,
})
throw new OAuthCallbackError(parameters.error)
}
const codeVerifier = await usePKCECodeVerifier(
cookies?.[options.cookies.pkceCodeVerifier.name],
options
)
if (codeVerifier) resCookies.push(codeVerifier.cookie)
const codeVerifier = await usePKCECodeVerifier(
cookies?.[options.cookies.pkceCodeVerifier.name],
options
)
// TODO:
const nonce = await useNonce(cookies?.[options.cookies.nonce.name], options)
if (nonce && provider.type === "oidc") {
resCookies.push(nonce.cookie)
if (codeVerifier) resCookies.push(codeVerifier.cookie)
// TODO:
const nonce = await useNonce(cookies?.[options.cookies.nonce.name], options)
if (nonce && provider.type === "oidc") {
resCookies.push(nonce.cookie)
}
const codeGrantResponse = await o.authorizationCodeGrantRequest(
as,
client,
parameters,
provider.callbackUrl,
codeVerifier?.codeVerifier ?? "auth" // TODO: review fallback code verifier
)
let challenges: o.WWWAuthenticateChallenge[] | undefined
if ((challenges = o.parseWwwAuthenticateChallenges(codeGrantResponse))) {
for (const challenge of challenges) {
console.log("challenge", challenge)
}
throw new Error("TODO: Handle www-authenticate challenges as needed")
}
const parameters = o.validateAuthResponse(
let profile: Profile = {}
let tokens: TokenSet
if (provider.type === "oidc") {
const result = await o.processAuthorizationCodeOpenIDResponse(
as,
client,
new URLSearchParams(query),
provider.checks.includes("state") ? state?.value : o.skipStateCheck
codeGrantResponse
)
if (o.isOAuth2Error(parameters)) {
console.log("error", parameters)
throw new Error("TODO: Handle OAuth 2.0 redirect error")
if (o.isOAuth2Error(result)) {
console.log("error", result)
throw new Error("TODO: Handle OIDC response body error")
}
const codeGrantResponse = await o.authorizationCodeGrantRequest(
profile = o.getValidatedIdTokenClaims(result)
tokens = result
} else {
tokens = await o.processAuthorizationCodeOAuth2Response(
as,
client,
parameters,
provider.callbackUrl,
codeVerifier?.codeVerifier ?? "auth" // TODO: review fallback code verifier
codeGrantResponse
)
let challenges: o.WWWAuthenticateChallenge[] | undefined
if ((challenges = o.parseWwwAuthenticateChallenges(codeGrantResponse))) {
for (const challenge of challenges) {
console.log("challenge", challenge)
}
throw new Error("TODO: Handle www-authenticate challenges as needed")
if (o.isOAuth2Error(tokens as any)) {
console.log("error", tokens)
throw new Error("TODO: Handle OAuth 2.0 response body error")
}
let profile: Profile = {}
let tokens: TokenSet
if (provider.type === "oidc") {
const result = await o.processAuthorizationCodeOpenIDResponse(
if (provider.userinfo?.request) {
profile = await provider.userinfo.request({ tokens, provider })
} else if (provider.userinfo?.url) {
const userinfoResponse = await o.userInfoRequest(
as,
client,
codeGrantResponse
(tokens as any).access_token
)
if (o.isOAuth2Error(result)) {
console.log("error", result)
throw new Error("TODO: Handle OIDC response body error")
}
profile = o.getValidatedIdTokenClaims(result)
tokens = result
} else {
tokens = await o.processAuthorizationCodeOAuth2Response(
as,
client,
codeGrantResponse
)
if (o.isOAuth2Error(tokens as any)) {
console.log("error", tokens)
throw new Error("TODO: Handle OAuth 2.0 response body error")
}
if (provider.userinfo?.request) {
profile = await provider.userinfo.request({ tokens, provider })
} else if (provider.userinfo?.url) {
const userinfoResponse = await o.userInfoRequest(
as,
client,
(tokens as any).access_token
)
profile = await userinfoResponse.json()
}
profile = await userinfoResponse.json()
}
}
const profileResult = await getProfile({
profile,
provider,
tokens,
logger,
})
const profileResult = await getProfile(profile, provider, tokens, logger)
return { ...profileResult, cookies: resCookies }
} catch (error) {
throw new OAuthCallbackError(error as Error)
}
return { ...profileResult, cookies: resCookies }
}
interface GetProfileParams {
profile: Profile
tokens: TokenSet
provider: OAuthConfigInternal<any>
/** Returns profile, raw profile and auth provider details */
async function getProfile(
OAuthProfile: Profile,
provider: OAuthConfigInternal<any>,
tokens: TokenSet,
logger: LoggerInstance
}
/** Returns profile, raw profile and auth provider details */
async function getProfile({
profile: OAuthProfile,
tokens,
provider,
logger,
}: GetProfileParams) {
) {
try {
logger.debug("PROFILE_DATA", { OAuthProfile })
const profile = await provider.profile(OAuthProfile, tokens)
profile.email = profile.email?.toLowerCase()
if (!profile.id)
if (!profile.id) {
throw new TypeError(
`Profile id is missing in ${provider.name} OAuth profile response`
)
}
// Return profile, raw profile and auth provider details
return {

@@ -217,7 +199,5 @@ profile,

// who might be trying to debug this when configuring a new provider.
logger.error("OAUTH_PARSE_PROFILE_ERROR", {
error: error as Error,
OAuthProfile,
})
logger.debug("getProfile error details", OAuthProfile)
logger.error(new OAuthProfileParseError(error))
}
}
import * as o from "oauth4webapi"
import * as jwt from "../../jwt/index.js"
import * as jwt from "../../jwt.js"
import type { InternalOptions } from "../../index.js"
import type { InternalOptions } from "../../types.js"
import type { Cookie } from "../cookie.js"

@@ -11,3 +11,4 @@

* Returns nonce if the provider supports it
* and saves it in a cookie */
* and saves it in a cookie
*/
export async function createNonce(options: InternalOptions<"oauth">): Promise<

@@ -14,0 +15,0 @@ | undefined

import * as o from "oauth4webapi"
import * as jwt from "../../jwt/index.js"
import * as jwt from "../../jwt.js"
import type { InternalOptions } from "../../index.js"
import type { InternalOptions } from "../../types.js"
import type { Cookie } from "../cookie.js"

@@ -6,0 +6,0 @@

@@ -1,4 +0,5 @@

import type { InternalOptions } from "../../index.js"
import * as o from "oauth4webapi"
import type { InternalOptions, RequestInternal } from "../../types.js"
import type { Cookie } from "../cookie.js"
import * as o from "oauth4webapi"
import { InvalidState } from "../../errors.js"

@@ -42,23 +43,31 @@ const STATE_MAX_AGE = 60 * 15 // 15 minutes in seconds

/**
* Returns state from if the provider supports states,
* Returns state from the saved cookie
* if the provider supports states,
* and clears the container cookie afterwards.
*/
export async function useState(
state: string | undefined,
cookies: RequestInternal["cookies"],
resCookies: Cookie[],
options: InternalOptions<"oauth">
): Promise<{ value: string; cookie: Cookie } | undefined> {
const { cookies, provider, jwt } = options
): Promise<string | undefined> {
const { provider, jwt } = options
if (!provider.checks.includes("state")) return
if (!provider.checks?.includes("state") || !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
return {
value: value?.value ?? undefined,
cookie: {
name: cookies.state.name,
value: "",
options: { ...cookies.pkceCodeVerifier.options, maxAge: 0 },
},
}
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
}
import { renderToString } from "preact-render-to-string"
import css from "../styles/index.js"
import ErrorPage from "./error.js"
import SigninPage from "./signin.js"
import SignoutPage from "./signout.js"
import css from "./styles.js"
import VerifyRequestPage from "./verify-request.js"
import type {
ErrorPageParam,
InternalOptions,
RequestInternal,
ResponseInternal,
} from "../../index.js"
} from "../../types.js"
import type { Cookie } from "../cookie.js"
import type { ErrorType } from "./error.js"
function send({ html, title, status, cookies, theme }: any): ResponseInternal {
return {
cookies,
status,
headers: { "Content-Type": "text/html" },
body: `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><style>${css}</style><title>${title}</title></head><body class="__next-auth-theme-${
theme?.colorScheme ?? "auto"
}"><div class="page">${renderToString(html)}</div></body></html>`,
}
}
type RenderPageParams = {

@@ -27,3 +38,3 @@ query?: RequestInternal["query"]

/**
* Unless the user defines their [own pages](https://next-auth.js.org/configuration/pages),
* Unless the user defines their [own pages](https://authjs.dev/guides/basics/pages),
* we render a set of default ones, using Preact SSR.

@@ -34,16 +45,7 @@ */

function send({ html, title, status }: any): ResponseInternal {
return {
cookies,
status,
headers: { "Content-Type": "text/html" },
body: `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><style>${css}</style><title>${title}</title></head><body class="__next-auth-theme-${
theme?.colorScheme ?? "auto"
}"><div class="page">${renderToString(html)}</div></body></html>`,
}
}
return {
signin(props?: any) {
return send({
cookies,
theme,
html: SigninPage({

@@ -71,2 +73,4 @@ csrfToken: params.csrfToken,

return send({
cookies,
theme,
html: SignoutPage({

@@ -83,2 +87,4 @@ csrfToken: params.csrfToken,

return send({
cookies,
theme,
html: VerifyRequestPage({ url, theme, ...props }),

@@ -88,4 +94,6 @@ title: "Verify Request",

},
error(props?: { error?: ErrorType }) {
error(props?: { error?: ErrorPageParam }) {
return send({
cookies,
theme,
...ErrorPage({ url, theme, ...props }),

@@ -92,0 +100,0 @@ title: "Error",

import { merge } from "./utils/merge.js"
import type { InternalProvider } from "../index.js"
import type { InternalProvider } from "../types.js"
import type {

@@ -5,0 +5,0 @@ OAuthConfig,

@@ -1,10 +0,15 @@

import callbackHandler from "../callback-handler.js"
import getAdapterUserFromEmail from "../email/getUserFromEmail.js"
import { handleOAuthCallback } from "../oauth/callback.js"
import { handleLogin } from "../callback-handler.js"
import { CallbackRouteError } from "../../errors.js"
import { handleOAuth } from "../oauth/callback.js"
import { createHash } from "../web.js"
import { handleAuthorized } from "./shared.js"
import type { RequestInternal, ResponseInternal, User } from "../../index.js"
import type { AdapterSession } from "../../adapters.js"
import type {
RequestInternal,
ResponseInternal,
User,
InternalOptions,
} from "../../types.js"
import type { Cookie, SessionStore } from "../cookie.js"
import type { InternalOptions } from "../types.js"

@@ -39,162 +44,112 @@ /** Handle callbacks from login services */

if (provider.type === "oauth" || provider.type === "oidc") {
try {
const {
profile,
account,
OAuthProfile,
cookies: oauthCookies,
} = await handleOAuthCallback({
try {
if (provider.type === "oauth" || provider.type === "oidc") {
const authorizationResult = await handleOAuth(
query,
body,
options,
cookies: params.cookies,
})
params.cookies,
options
)
if (oauthCookies.length) cookies.push(...oauthCookies)
if (authorizationResult.cookies.length) {
cookies.push(...authorizationResult.cookies)
}
try {
// Make it easier to debug when adding a new provider
logger.debug("OAUTH_CALLBACK_RESPONSE", {
profile,
account,
OAuthProfile,
logger.debug("authroization result", authorizationResult)
const { profile, account, OAuthProfile } = authorizationResult
// If we don't have a profile object then either something went wrong
// or the user cancelled signing in. We don't know which, so we just
// direct the user to the signin page for now. We could do something
// else in future.
// TODO: Handle user cancelling signin
if (!profile || !account || !OAuthProfile) {
return { redirect: `${url}/signin`, cookies }
}
// Check if user is allowed to sign in
// Attempt to get Profile from OAuth provider details before invoking
// signIn callback - but if no user object is returned, that is fine
// (that just means it's a new user signing in for the first time).
let userOrProfile = profile
if (adapter) {
const { getUserByAccount } = adapter
const userByAccount = await getUserByAccount({
providerAccountId: account.providerAccountId,
provider: provider.id,
})
// If we don't have a profile object then either something went wrong
// or the user cancelled signing in. We don't know which, so we just
// direct the user to the signin page for now. We could do something
// else in future.
//
// Note: In oAuthCallback an error is logged with debug info, so it
// should at least be visible to developers what happened if it is an
// error with the provider.
if (!profile || !account || !OAuthProfile) {
return { redirect: `${url}/signin`, cookies }
}
if (userByAccount) userOrProfile = userByAccount
}
// Check if user is allowed to sign in
// Attempt to get Profile from OAuth provider details before invoking
// signIn callback - but if no user object is returned, that is fine
// (that just means it's a new user signing in for the first time).
let userOrProfile = profile
if (adapter) {
const { getUserByAccount } = adapter
const userByAccount = await getUserByAccount({
providerAccountId: account.providerAccountId,
provider: provider.id,
})
const unauthorizedOrError = await handleAuthorized(
{ user: userOrProfile, account, profile: OAuthProfile },
options
)
if (userByAccount) userOrProfile = userByAccount
}
if (unauthorizedOrError) return { ...unauthorizedOrError, cookies }
try {
const isAllowed = await callbacks.signIn({
user: userOrProfile,
account,
profile: OAuthProfile,
})
if (!isAllowed) {
return { redirect: `${url}/error?error=AccessDenied`, cookies }
} else if (typeof isAllowed === "string") {
return { redirect: isAllowed, cookies }
}
} catch (error) {
return {
redirect: `${url}/error?error=${encodeURIComponent(
(error as Error).message
)}`,
cookies,
}
// Sign user in
const { user, session, isNewUser } = await handleLogin(
sessionStore.value,
profile,
account,
options
)
if (useJwtSession) {
const defaultToken = {
name: user.name,
email: user.email,
picture: user.image,
sub: user.id?.toString(),
}
// Sign user in
const { user, session, isNewUser } = await callbackHandler({
sessionToken: sessionStore.value,
profile,
const token = await callbacks.jwt({
token: defaultToken,
user,
account,
options,
profile: OAuthProfile,
isNewUser,
})
if (useJwtSession) {
const defaultToken = {
name: user.name,
email: user.email,
picture: user.image,
sub: user.id?.toString(),
}
const token = await callbacks.jwt({
token: defaultToken,
user,
account,
profile: OAuthProfile,
isNewUser,
})
// Encode token
const newToken = await jwt.encode({ ...jwt, token })
// Encode token
const newToken = await jwt.encode({ ...jwt, token })
// Set cookie expiry date
const cookieExpires = new Date()
cookieExpires.setTime(cookieExpires.getTime() + sessionMaxAge * 1000)
// Set cookie expiry date
const cookieExpires = new Date()
cookieExpires.setTime(cookieExpires.getTime() + sessionMaxAge * 1000)
const sessionCookies = sessionStore.chunk(newToken, {
expires: cookieExpires,
})
cookies.push(...sessionCookies)
} else {
// Save Session Token in cookie
cookies.push({
name: options.cookies.sessionToken.name,
value: (session as AdapterSession).sessionToken,
options: {
...options.cookies.sessionToken.options,
expires: (session as AdapterSession).expires,
},
})
}
const sessionCookies = sessionStore.chunk(newToken, {
expires: cookieExpires,
})
cookies.push(...sessionCookies)
} else {
// Save Session Token in cookie
cookies.push({
name: options.cookies.sessionToken.name,
value: (session as AdapterSession).sessionToken,
options: {
...options.cookies.sessionToken.options,
expires: (session as AdapterSession).expires,
},
})
}
// @ts-expect-error
await events.signIn?.({ user, account, profile, isNewUser })
// @ts-expect-error
await events.signIn?.({ user, account, profile, isNewUser })
// Handle first logins on new accounts
// e.g. option to send users to a new account landing page on initial login
// Note that the callback URL is preserved, so the journey can still be resumed
if (isNewUser && pages.newUser) {
return {
redirect: `${pages.newUser}${
pages.newUser.includes("?") ? "&" : "?"
}callbackUrl=${encodeURIComponent(callbackUrl)}`,
cookies,
}
// Handle first logins on new accounts
// e.g. option to send users to a new account landing page on initial login
// Note that the callback URL is preserved, so the journey can still be resumed
if (isNewUser && pages.newUser) {
return {
redirect: `${pages.newUser}${
pages.newUser.includes("?") ? "&" : "?"
}callbackUrl=${encodeURIComponent(callbackUrl)}`,
cookies,
}
}
// Callback URL is already verified at this point, so safe to use if specified
return { redirect: callbackUrl, cookies }
} catch (error) {
if ((error as Error).name === "AccountNotLinkedError") {
// If the email on the account is already linked, but not with this OAuth account
return {
redirect: `${url}/error?error=OAuthAccountNotLinked`,
cookies,
}
} else if ((error as Error).name === "CreateUserError") {
return { redirect: `${url}/error?error=OAuthCreateAccount`, cookies }
}
logger.error("OAUTH_CALLBACK_HANDLER_ERROR", error as Error)
return { redirect: `${url}/error?error=Callback`, cookies }
}
} catch (error) {
if ((error as Error).name === "OAuthCallbackError") {
logger.error("OAUTH_CALLBACK_ERROR", {
error: error as Error,
providerId: provider.id,
})
return { redirect: `${url}/error?error=OAuthCallback`, cookies }
}
logger.error("OAUTH_CALLBACK_ERROR", error as Error)
return { redirect: `${url}/error?error=Callback`, cookies }
}
} else if (provider.type === "email") {
try {
return { redirect: callbackUrl, cookies }
} else if (provider.type === "email") {
const token = query?.token as string | undefined

@@ -209,3 +164,3 @@ const identifier = query?.email as string | undefined

const secret = provider.secret ?? options.secret
// @ts-expect-error -- Verified in `assertConfig`. adapter: Adapter<true>
// @ts-expect-error -- Verified in `assertConfig`.
const invite = await adapter.useVerificationToken({

@@ -221,7 +176,4 @@ identifier,

const profile = await getAdapterUserFromEmail({
email: identifier,
// @ts-expect-error -- Verified in `assertConfig`. adapter: Adapter<true>
adapter,
})
// @ts-expect-error -- Verified in `assertConfig`.
const profile = await getAdapterUserFromEmail(identifier, adapter)

@@ -235,28 +187,16 @@ const account = {

// Check if user is allowed to sign in
try {
const signInCallbackResponse = await callbacks.signIn({
user: profile,
account,
})
if (!signInCallbackResponse) {
return { redirect: `${url}/error?error=AccessDenied`, cookies }
} else if (typeof signInCallbackResponse === "string") {
return { redirect: signInCallbackResponse, cookies }
}
} catch (error) {
return {
redirect: `${url}/error?error=${encodeURIComponent(
(error as Error).message
)}`,
cookies,
}
}
const unauthorizedOrError = await handleAuthorized(
{ user: profile, account },
options
)
if (unauthorizedOrError) return { ...unauthorizedOrError, cookies }
// Sign user in
const { user, session, isNewUser } = await callbackHandler({
sessionToken: sessionStore.value,
const { user, session, isNewUser } = await handleLogin(
sessionStore.value,
profile,
account,
options,
})
options
)

@@ -316,110 +256,97 @@ if (useJwtSession) {

return { redirect: callbackUrl, cookies }
} catch (error) {
if ((error as Error).name === "CreateUserError") {
return { redirect: `${url}/error?error=EmailCreateAccount`, cookies }
}
logger.error("CALLBACK_EMAIL_ERROR", error as Error)
return { redirect: `${url}/error?error=Callback`, cookies }
}
} else if (provider.type === "credentials" && method === "POST") {
const credentials = body
} else if (provider.type === "credentials" && method === "POST") {
const credentials = body
let user: User | null
try {
user = await provider.authorize(credentials, {
query,
body,
headers,
method,
})
if (!user) {
let user: User | null
try {
// TODO: Forward the original request as is, instead of reconstructing it
// prettier-ignore
Object.entries(query ?? {}).forEach(([k, v]) => url.searchParams.set(k, v))
user = await provider.authorize(
credentials,
// prettier-ignore
new Request(url, { headers, method, body: JSON.stringify(body) })
)
if (!user) {
return {
status: 401,
redirect: `${url}/error?${new URLSearchParams({
error: "CredentialsSignin",
provider: provider.id,
})}`,
cookies,
}
}
} catch (error) {
return {
status: 401,
redirect: `${url}/error?${new URLSearchParams({
error: "CredentialsSignin",
provider: provider.id,
})}`,
redirect: `${url}/error?error=${encodeURIComponent(
(error as Error).message
)}`,
cookies,
}
}
} catch (error) {
return {
status: 401,
redirect: `${url}/error?error=${encodeURIComponent(
(error as Error).message
)}`,
cookies,
/** @type {import("src").Account} */
const account = {
providerAccountId: user.id,
type: "credentials",
provider: provider.id,
}
}
/** @type {import("src").Account} */
const account = {
providerAccountId: user.id,
type: "credentials",
provider: provider.id,
}
const unauthorizedOrError = await handleAuthorized(
{ user, account, credentials },
options
)
try {
const isAllowed = await callbacks.signIn({
if (unauthorizedOrError) return { ...unauthorizedOrError, cookies }
const defaultToken = {
name: user.name,
email: user.email,
picture: user.image,
sub: user.id?.toString(),
}
const token = await callbacks.jwt({
token: defaultToken,
user,
// @ts-expect-error
account,
credentials,
isNewUser: false,
})
if (!isAllowed) {
return {
status: 403,
redirect: `${url}/error?error=AccessDenied`,
cookies,
}
} else if (typeof isAllowed === "string") {
return { redirect: isAllowed, cookies }
}
} catch (error) {
return {
redirect: `${url}/error?error=${encodeURIComponent(
(error as Error).message
)}`,
cookies,
}
}
const defaultToken = {
name: user.name,
email: user.email,
picture: user.image,
sub: user.id?.toString(),
}
// Encode token
const newToken = await jwt.encode({ ...jwt, token })
const token = await callbacks.jwt({
token: defaultToken,
user,
// @ts-expect-error
account,
isNewUser: false,
})
// Set cookie expiry date
const cookieExpires = new Date()
cookieExpires.setTime(cookieExpires.getTime() + sessionMaxAge * 1000)
// Encode token
const newToken = await jwt.encode({ ...jwt, token })
const sessionCookies = sessionStore.chunk(newToken, {
expires: cookieExpires,
})
// Set cookie expiry date
const cookieExpires = new Date()
cookieExpires.setTime(cookieExpires.getTime() + sessionMaxAge * 1000)
cookies.push(...sessionCookies)
const sessionCookies = sessionStore.chunk(newToken, {
expires: cookieExpires,
})
// @ts-expect-error
await events.signIn?.({ user, account })
cookies.push(...sessionCookies)
return { redirect: callbackUrl, cookies }
}
// @ts-expect-error
await events.signIn?.({ user, account })
return {
status: 500,
body: `Error: Callback for provider type ${provider.type} not supported`,
cookies,
}
} catch (e) {
const error = new CallbackRouteError(e, { provider: provider.id })
return { redirect: callbackUrl, cookies }
logger.error(error)
url.searchParams.set("error", CallbackRouteError.name)
url.pathname += "/error"
return { redirect: url, cookies }
}
return {
status: 500,
body: `Error: Callback for provider type ${provider.type} not supported`,
cookies,
}
}

@@ -1,2 +0,2 @@

import type { InternalProvider, ResponseInternal } from "../../index.js"
import type { InternalProvider, ResponseInternal } from "../../types.js"

@@ -3,0 +3,0 @@ export interface PublicProvider {

@@ -0,21 +1,13 @@

import { JWTSessionError, SessionTokenError } from "../../errors.js"
import { fromDate } from "../utils/date.js"
import type { InternalOptions, ResponseInternal, Session } from "../../index.js"
import type { Adapter } from "../../adapters.js"
import type { InternalOptions, ResponseInternal, Session } from "../../types.js"
import type { SessionStore } from "../cookie.js"
interface SessionParams {
/** Return a session object filtered via `callbacks.session` */
export async function session(
sessionStore: SessionStore,
options: InternalOptions
sessionStore: SessionStore
}
/**
* Return a session object (without any private fields)
* for Single Page App clients
*/
export async function session(
params: SessionParams
): Promise<ResponseInternal<Session | {}>> {
const { options, sessionStore } = params
const {

@@ -42,6 +34,3 @@ adapter,

try {
const decodedToken = await jwt.decode({
...jwt,
token: sessionToken,
})
const decodedToken = await jwt.decode({ ...jwt, token: sessionToken })

@@ -85,80 +74,85 @@ const newExpires = fromDate(sessionMaxAge)

} catch (error) {
// If JWT not verifiable, make sure the cookie for it is removed and return empty object
logger.error("JWT_SESSION_ERROR", error as Error)
logger.error(new JWTSessionError(error))
// If the JWT is not verifiable remove the broken session cookie(s).
response.cookies?.push(...sessionStore.clean())
}
} else {
try {
const { getSessionAndUser, deleteSession, updateSession } =
adapter as Adapter
let userAndSession = await getSessionAndUser(sessionToken)
// If session has expired, clean up the database
if (
userAndSession &&
userAndSession.session.expires.valueOf() < Date.now()
) {
await deleteSession(sessionToken)
userAndSession = null
}
return response
}
if (userAndSession) {
const { user, session } = userAndSession
// Retrieve session from database
try {
const { getSessionAndUser, deleteSession, updateSession } =
adapter as Adapter
let userAndSession = await getSessionAndUser(sessionToken)
const sessionUpdateAge = options.session.updateAge
// Calculate last updated date to throttle write updates to database
// Formula: ({expiry date} - sessionMaxAge) + sessionUpdateAge
// e.g. ({expiry date} - 30 days) + 1 hour
const sessionIsDueToBeUpdatedDate =
session.expires.valueOf() -
sessionMaxAge * 1000 +
sessionUpdateAge * 1000
// If session has expired, clean up the database
if (
userAndSession &&
userAndSession.session.expires.valueOf() < Date.now()
) {
await deleteSession(sessionToken)
userAndSession = null
}
const newExpires = fromDate(sessionMaxAge)
// Trigger update of session expiry date and write to database, only
// if the session was last updated more than {sessionUpdateAge} ago
if (sessionIsDueToBeUpdatedDate <= Date.now()) {
await updateSession({ sessionToken, expires: newExpires })
}
if (userAndSession) {
const { user, session } = userAndSession
// Pass Session through to the session callback
// @ts-expect-error
const sessionPayload = await callbacks.session({
// By default, only exposes a limited subset of information to the client
// as needed for presentation purposes (e.g. "you are logged in as...").
session: {
user: {
name: user.name,
email: user.email,
image: user.image,
},
expires: session.expires.toISOString(),
},
user,
const sessionUpdateAge = options.session.updateAge
// Calculate last updated date to throttle write updates to database
// Formula: ({expiry date} - sessionMaxAge) + sessionUpdateAge
// e.g. ({expiry date} - 30 days) + 1 hour
const sessionIsDueToBeUpdatedDate =
session.expires.valueOf() -
sessionMaxAge * 1000 +
sessionUpdateAge * 1000
const newExpires = fromDate(sessionMaxAge)
// Trigger update of session expiry date and write to database, only
// if the session was last updated more than {sessionUpdateAge} ago
if (sessionIsDueToBeUpdatedDate <= Date.now()) {
await updateSession({
sessionToken: sessionToken,
expires: newExpires,
})
}
// Return session payload as response
response.body = sessionPayload
// Set cookie again to update expiry
response.cookies?.push({
name: options.cookies.sessionToken.name,
value: sessionToken,
options: {
...options.cookies.sessionToken.options,
expires: newExpires,
// Pass Session through to the session callback
// @ts-expect-error
const sessionPayload = await callbacks.session({
// By default, only exposes a limited subset of information to the client
// as needed for presentation purposes (e.g. "you are logged in as...").
session: {
user: {
name: user.name,
email: user.email,
image: user.image,
},
})
expires: session.expires.toISOString(),
},
user,
})
// @ts-expect-error
await events.session?.({ session: sessionPayload })
} else if (sessionToken) {
// If `sessionToken` was found set but it's not valid for a session then
// remove the sessionToken cookie from browser.
response.cookies?.push(...sessionStore.clean())
}
} catch (error) {
logger.error("SESSION_ERROR", error as Error)
// Return session payload as response
response.body = sessionPayload
// Set cookie again to update expiry
response.cookies?.push({
name: options.cookies.sessionToken.name,
value: sessionToken,
options: {
...options.cookies.sessionToken.options,
expires: newExpires,
},
})
// @ts-expect-error
await events.session?.({ session: sessionPayload })
} else if (sessionToken) {
// If `sessionToken` was found set but it's not valid for a session then
// remove the sessionToken cookie from browser.
response.cookies?.push(...sessionStore.clean())
}
} catch (error) {
logger.error(new SessionTokenError(error))
}

@@ -165,0 +159,0 @@

@@ -1,4 +0,5 @@

import getAdapterUserFromEmail from "../email/getUserFromEmail.js"
import emailSignin from "../email/signin.js"
import { SignInError } from "../../errors.js"
import { getAuthorizationUrl } from "../oauth/authorization-url.js"
import { getAdapterUserFromEmail, handleAuthorized } from "./shared.js"

@@ -10,95 +11,61 @@ import type {

ResponseInternal,
} from "../../index.js"
} from "../../types.js"
/** Handle requests to /api/auth/signin */
export async function signin(params: {
/**
* Initiates the sign in process for OAuth and Email flows .
* For OAuth, redirects to the provider's authorization URL.
* For Email, sends an email with a sign in link.
*/
export async function signin(
query: RequestInternal["query"],
body: RequestInternal["body"],
options: InternalOptions<"oauth" | "email">
query: RequestInternal["query"]
body: RequestInternal["body"]
}): Promise<ResponseInternal> {
const { options, query, body } = params
const { url, callbacks, logger, provider } = options
): Promise<ResponseInternal> {
const { url, logger, provider } = options
try {
if (provider.type === "oauth" || provider.type === "oidc") {
return await getAuthorizationUrl(query, options)
} else if (provider.type === "email") {
const normalizer = provider.normalizeIdentifier ?? defaultNormalizer
const email = normalizer(body?.email)
if (!provider.type) {
return {
status: 500,
// @ts-expect-error
text: `Error: Type not specified for ${provider.name}`,
}
}
// @ts-expect-error -- Verified in `assertConfig`
const user = await getAdapterUserFromEmail(email, options.adapter)
if (provider.type === "oauth" || provider.type === "oidc") {
try {
return await getAuthorizationUrl({ options, query })
} catch (error) {
logger.error("SIGNIN_OAUTH_ERROR", {
error: error as Error,
providerId: provider.id,
})
return { redirect: `${url}/error?error=OAuthSignin` }
}
} else if (provider.type === "email") {
let email: string = body?.email
if (!email) return { redirect: `${url}/error?error=EmailSignin` }
const normalizer: (identifier: string) => string =
provider.normalizeIdentifier ??
((identifier) => {
// Get the first two elements only,
// separated by `@` from user input.
let [local, domain] = identifier.toLowerCase().trim().split("@")
// The part before "@" can contain a ","
// but we remove it on the domain part
domain = domain.split(",")[0]
return `${local}@${domain}`
})
const account: Account = {
providerAccountId: email,
userId: email,
type: "email",
provider: provider.id,
}
try {
email = normalizer(body?.email)
} catch (error) {
logger.error("SIGNIN_EMAIL_ERROR", { error, providerId: provider.id })
return { redirect: `${url}/error?error=EmailSignin` }
}
const unauthorizedOrError = await handleAuthorized(
{ user, account, email: { verificationRequest: true } },
options
)
const user = await getAdapterUserFromEmail({
email,
// @ts-expect-error -- Verified in `assertConfig`. adapter: Adapter<true>
adapter: options.adapter,
})
if (unauthorizedOrError) return unauthorizedOrError
const account: Account = {
providerAccountId: email,
userId: email,
type: "email",
provider: provider.id,
}
// Check if user is allowed to sign in
try {
const signInCallbackResponse = await callbacks.signIn({
user,
account,
email: { verificationRequest: true },
})
if (!signInCallbackResponse) {
return { redirect: `${url}/error?error=AccessDenied` }
} else if (typeof signInCallbackResponse === "string") {
return { redirect: signInCallbackResponse }
}
} catch (error) {
return {
redirect: `${url}/error?${new URLSearchParams({
error: error as string,
})}`,
}
}
try {
const redirect = await emailSignin(email, options)
return { redirect }
} catch (error) {
logger.error("SIGNIN_EMAIL_ERROR", { error, providerId: provider.id })
return { redirect: `${url}/error?error=EmailSignin` }
}
return { redirect: `${url}/signin` }
} catch (e) {
const error = new SignInError(e, { provider: provider.id })
logger.error(error)
url.searchParams.set("error", error.name)
url.pathname += "/error"
return { redirect: url }
}
return { redirect: `${url}/signin` }
}
function defaultNormalizer(email?: string) {
if (!email) throw new Error("Missing email from request body.")
// Get the first two elements only,
// separated by `@` from user input.
let [local, domain] = email.toLowerCase().trim().split("@")
// The part before "@" can contain a ","
// but we remove it on the domain part
domain = domain.split(",")[0]
return `${local}@${domain}`
}

@@ -1,44 +0,35 @@

import type { InternalOptions, ResponseInternal } from "../../index.js"
import type { Adapter } from "../../adapters.js"
import { SignOutError } from "../../errors.js"
import type { InternalOptions, ResponseInternal } from "../../types.js"
import type { SessionStore } from "../cookie.js"
/** Handle requests to /api/auth/signout */
export async function signout(params: {
/**
* Destroys the session.
* 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.
*/
export async function signout(
sessionStore: SessionStore,
options: InternalOptions
sessionStore: SessionStore
}): Promise<ResponseInternal> {
const { options, sessionStore } = params
const { adapter, events, jwt, callbackUrl, logger, session } = options
): Promise<ResponseInternal> {
const { jwt, events, callbackUrl: redirect, logger, session } = options
const sessionToken = sessionStore?.value
if (!sessionToken) {
return { redirect: callbackUrl }
}
const sessionToken = sessionStore.value
if (!sessionToken) return { redirect }
if (session.strategy === "jwt") {
// Dispatch signout event
try {
const decodedJwt = await jwt.decode({ ...jwt, token: sessionToken })
// @ts-expect-error
await events.signOut?.({ token: decodedJwt })
} catch (error) {
// Do nothing if decoding the JWT fails
logger.error("SIGNOUT_ERROR", error)
}
} else {
try {
const session = await (adapter as Adapter).deleteSession(sessionToken)
// Dispatch signout event
// @ts-expect-error
try {
if (session.strategy === "jwt") {
const token = await jwt.decode({ ...jwt, token: sessionToken })
await events.signOut?.({ token })
} else {
const session = await options.adapter?.deleteSession(sessionToken)
await events.signOut?.({ session })
} catch (error) {
// If error, log it but continue
logger.error("SIGNOUT_ERROR", error as Error)
}
} catch (error) {
logger.error(new SignOutError(error))
}
// Remove Session Token
const sessionCookies = sessionStore.clean()
return { redirect: callbackUrl, cookies: sessionCookies }
return { redirect, cookies: sessionStore.clean() }
}

@@ -1,61 +0,40 @@

import { UnknownError } from "../errors.js"
import { AuthError } from "../../errors.js"
/** Makes sure that error is always serializable */
function formatError(o: unknown): unknown {
if (o instanceof Error && !(o instanceof UnknownError)) {
return { message: o.message, stack: o.stack, name: o.name }
}
if (hasErrorProperty(o)) {
o.error = formatError(o.error) as Error
o.message = o.message ?? o.error.message
}
return o
}
export type WarningCode = "debug_enabled"
function hasErrorProperty(
x: unknown
): x is { error: Error; [key: string]: unknown } {
return !!(x as any)?.error
}
export type WarningCode = "NEXTAUTH_URL" | "DEBUG_ENABLED"
/**
* Override any of the methods, and the rest will use the default logger.
*
* [Documentation](https://next-auth.js.org/configuration/options#logger)
* [Documentation](https://authjs.dev/reference/configuration/auth-config#logger)
*/
export interface LoggerInstance extends Record<string, Function> {
warn: (code: WarningCode) => void
error: (
code: string,
/**
* Either an instance of (JSON serializable) Error
* or an object that contains some debug information.
* (Error is still available through `metadata.error`)
*/
metadata: Error | { error: Error; [key: string]: unknown }
) => void
debug: (code: string, metadata: unknown) => void
error: (error: AuthError) => void
debug: (message: string, metadata?: unknown) => void
}
const _logger: LoggerInstance = {
error(code, metadata) {
metadata = formatError(metadata) as Error
const red = "\x1b[31m"
const yellow = "\x1b[33m"
const grey = "\x1b[90m"
const reset = "\x1b[0m"
export const logger: LoggerInstance = {
error(error: AuthError) {
const url = `https://errors.authjs.dev#${error.name.toLowerCase()}`
console.error(error.stack)
console.error(
`[next-auth][error][${code}]`,
`\nhttps://next-auth.js.org/errors#${code.toLowerCase()}`,
metadata.message,
metadata
`${red}[auth][error][${error.name}]${reset}: Read more at ${url}`
)
error.metadata && console.error(JSON.stringify(error.metadata, null, 2))
},
warn(code) {
console.warn(
`[next-auth][warn][${code}]`,
`\nhttps://next-auth.js.org/warnings#${code.toLowerCase()}`
const url = `https://errors.authjs.dev#${code}`
console.warn(`${yellow}[auth][warn][${code}]${reset}`, `Read more: ${url}`)
},
debug(message, metadata) {
console.log(
`${grey}[auth][debug]:${reset} ${message}`,
JSON.stringify(metadata, null, 2)
)
},
debug(code, metadata) {
console.log(`[next-auth][debug][${code}]`, metadata)
},
}

@@ -72,42 +51,7 @@

// Turn off debug logging if `debug` isn't set to `true`
if (!debug) _logger.debug = () => {}
if (!debug) logger.debug = () => {}
if (newLogger.error) _logger.error = newLogger.error
if (newLogger.warn) _logger.warn = newLogger.warn
if (newLogger.debug) _logger.debug = newLogger.debug
if (newLogger.error) logger.error = newLogger.error
if (newLogger.warn) logger.warn = newLogger.warn
if (newLogger.debug) logger.debug = newLogger.debug
}
export default _logger
/** Serializes client-side log messages and sends them to the server */
export function proxyLogger(
logger: LoggerInstance = _logger,
basePath?: string
): LoggerInstance {
try {
if (typeof window === "undefined") {
return logger
}
const clientLogger: Record<string, unknown> = {}
for (const level in logger) {
clientLogger[level] = (code: string, metadata: Error) => {
_logger[level](code, metadata) // Logs to console
if (level === "error") {
metadata = formatError(metadata) as Error
}
;(metadata as any).client = true
const url = `${basePath}/_log`
const body = new URLSearchParams({ level, code, ...(metadata as any) })
if (navigator.sendBeacon) {
return navigator.sendBeacon(url, body)
}
return fetch(url, { method: "POST", body, keepalive: true })
}
}
return clientLogger as unknown as LoggerInstance
} catch {
return _logger
}
}
import { parse as parseCookie, serialize } from "cookie"
import type { RequestInternal, ResponseInternal } from "../index.js"
import { UnknownAction } from "./errors.js"
import type { AuthAction } from "./types.js"
import { AuthError, UnknownAction } from "../errors.js"
import type { AuthAction, RequestInternal, ResponseInternal } from "../types.js"
async function getBody(req: Request): Promise<Record<string, any> | undefined> {

@@ -17,8 +17,17 @@ if (!("body" in req) || !req.body || req.method !== "POST") return

}
// prettier-ignore
const actions: AuthAction[] = [ "providers", "session", "csrf", "signin", "signout", "callback", "verify-request", "error", "_log" ]
const actions: AuthAction[] = [
"providers",
"session",
"csrf",
"signin",
"signout",
"callback",
"verify-request",
"error",
]
export async function toInternalRequest(
req: Request
): Promise<RequestInternal | Error> {
): Promise<RequestInternal | AuthError> {
try {

@@ -35,2 +44,6 @@ // TODO: url.toString() should not include action and providerId

if (req.method !== "GET" && req.method !== "POST") {
throw new UnknownAction("Only GET and POST requests are supported.")
}
const providerIdOrAction = pathname.split("/").pop()

@@ -50,3 +63,3 @@ let providerId

providerId,
method: req.method ?? "GET",
method: req.method,
headers: Object.fromEntries(req.headers),

@@ -53,0 +66,0 @@ body: req.body ? await getBody(req) : undefined,

@@ -1,2 +0,2 @@

import { OAuthConfig, OAuthUserConfig } from "./index.js"
import type { OAuthConfig, OAuthUserConfig } from "./index.js"

@@ -3,0 +3,0 @@ /**

@@ -14,2 +14,3 @@ import type { OAuthConfig, OAuthUserConfig } from "./index.js"

* https://docs.microsoft.com/en-us/graph/api/profilephoto-get?view=graph-rest-1.0#examples
*
* @default 48

@@ -16,0 +17,0 @@ */

import type { CommonProviderOptions } from "./index.js"
import type { Awaitable, RequestInternal, User } from "../index.js"
import type { Awaitable, User } from "../types.js"
import type { JSXInternal } from "preact/src/jsx.js"
export interface CredentialInput {
/**
* Besieds providing type safety inside {@link CredentialsConfig.authorize}
* it also determines how the credentials input fields will be rendered
* on the default sign in page.
*/
export interface CredentialInput
extends Partial<JSXInternal.IntrinsicElements["input"]> {
label?: string
type?: string
value?: string
placeholder?: string
}
/** The Credentials Provider needs to be configured. */
export interface CredentialsConfig<
C extends Record<string, CredentialInput> = Record<string, CredentialInput>
CredentialsInputs extends Record<string, CredentialInput> = Record<
string,
CredentialInput
>
> extends CommonProviderOptions {
type: "credentials"
credentials: C
credentials: CredentialsInputs
/**
* Gives full control over how you handle the credentials received from the user.
*
* :::warning
* There is no validation on the user inputs by default, so make sure you do so
* by a popular library like [Zod](https://zod.dev)
* :::
*
* @example
* ```ts
* //...
* async authorize(, request) {
* const response = await fetch(request)
* if(!response.ok) return null
* return await response.json() ?? null
* }
* //...
*/
authorize: (
credentials: Record<keyof C, string> | undefined,
req: Pick<RequestInternal, "body" | "query" | "headers" | "method">
/** See {@link CredentialInput} */
credentials: Record<keyof CredentialsInputs, string> | undefined,
/** The original request is forward for convenience */
request: Request
) => Awaitable<User | null>
}
export type CredentialsProvider = <C extends Record<string, CredentialInput>>(
options: Partial<CredentialsConfig<C>>
) => CredentialsConfig<C>
export type CredentialsProviderType = "Credentials"
type UserCredentialsConfig<C extends Record<string, CredentialInput>> = Partial<
Omit<CredentialsConfig<C>, "options">
> &
Pick<CredentialsConfig<C>, "authorize" | "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).
*
* It is intended to support use cases where you have an existing system you need to authenticate users against.
*
* It comes with the constraint that users authenticated in this manner are not persisted in the database,
* and consequently that the Credentials provider can only be used if JSON Web Tokens are enabled for sessions.
*
* :::warning **NOTE**
*
* The functionality provided for credentials based authentication is
* **intentionally limited** to _discourage_ use of passwords
* due to the _inherent security risks_ associated with them
* and the _additional complexity_ associated
* with supporting usernames and passwords.
*
* :::
*
* @example
* ```js
* import Auth from "@auth/core"
* import { Credentials } from "@auth/core/providers/credentials"
*
* const request = new Request("https://example.com")
* const resposne = await AuthHandler(request, {
* providers: [
* Credentials({
* credentials: {
* username: { label: "Username" },
* password: { label: "Password", type: "password" }
* },
* async authorize({ request }) {
* const response = await fetch(request)
* if(!response.ok) return null
* return await response.json() ?? null
* }
* })
* ],
* secret: "...",
* trustHost: true,
* })
* ```
* @see [Username/Password Example](https://authjs.dev/guides/providers/credentials#example---username--password)
* @see [Web3/Signin With Ethereum Example](https://authjs.dev/guides/providers/credentials#example---web3--signin-with-ethereum)
*/
export default function Credentials<
C extends Record<string, CredentialInput> = Record<string, CredentialInput>
>(options: UserCredentialsConfig<C>): CredentialsConfig<C> {
CredentialsInputs extends Record<string, CredentialInput> = Record<
string,
CredentialInput
>
>(config: Partial<CredentialsConfig<CredentialsInputs>>): CredentialsConfig {
return {

@@ -40,6 +112,7 @@ id: "credentials",

type: "credentials",
credentials: {} as any,
credentials: {},
authorize: () => null,
options,
// @ts-expect-error
options: config,
}
}

@@ -26,3 +26,3 @@ /**

* *Resources:*
* - [NextAuth.js Documentation](https://next-auth.js.org/providers/dropbox)
* - [NextAuth.js Documentation](https://authjs.dev/reference/oauth-providers/dropbox)
* - [Dropbox Documentation](https://developers.dropbox.com/oauth-guide)

@@ -29,0 +29,0 @@ * - [Configuration](https://www.dropbox.com/developers/apps)

@@ -1,2 +0,2 @@

import type { OAuthConfig, OAuthUserConfig } from "./oauth"
import type { OAuthConfig, OAuthUserConfig } from "./oauth.js"

@@ -3,0 +3,0 @@ export interface DuendeISUser extends Record<string, any> {

@@ -5,3 +5,3 @@ import { createTransport } from "nodemailer"

import type { Options as SMTPTransportOptions } from "nodemailer/lib/smtp-transport"
import type { Awaitable, Theme } from "../index.js"
import type { Awaitable, Theme } from "../types.js"

@@ -17,2 +17,15 @@ export interface SendVerificationRequestParams {

/**
* The Email Provider needs to be configured with an e-mail client.
* By default, it uses `nodemailer`, which you have to install if this
* provider is present.
*
* You can use a other services as well, like:
* - [Postmark](https://postmarkapp.com)
* - [Mailgun](https://www.mailgun.com)
* - [SendGrid](https://sendgrid.com)
* - etc.
*
* @see [Custom email service with Auth.js](https://authjs.dev/guides/providers/email#custom-email-service)
*/
export interface EmailConfig extends CommonProviderOptions {

@@ -22,3 +35,3 @@ type: "email"

server: string | SMTPTransportOptions
/** @default "NextAuth <no-reply@example.com>" */
/** @default `"Auth.js <no-reply@authjs.dev>"` */
from?: string

@@ -28,6 +41,7 @@ /**

* in seconds. Defaults to 1 day
*
* @default 86400
*/
maxAge?: number
/** [Documentation](https://next-auth.js.org/providers/email#customizing-emails) */
/** [Documentation](https://authjs.dev/reference/providers/email#customizing-emails) */
sendVerificationRequest: (

@@ -39,4 +53,5 @@ params: SendVerificationRequestParams

* You can make it predictable or modify it as you like with this method.
*
* @example
* ```js
* ```ts
* Providers.Email({

@@ -48,3 +63,3 @@ * async generateVerificationToken() {

* ```
* [Documentation](https://next-auth.js.org/providers/email#customizing-the-verification-token)
* [Documentation](https://authjs.dev/reference/providers/email#customizing-the-verification-token)
*/

@@ -66,17 +81,13 @@ generateVerificationToken?: () => Awaitable<string>

*
* [Documentation](https://next-auth.js.org/providers/email#normalizing-the-e-mail-address) | [RFC 2821](https://tools.ietf.org/html/rfc2821) | [Email syntax](https://en.wikipedia.org/wiki/Email_address#Syntax)
* [Documentation](https://authjs.dev/reference/providers/email#normalizing-the-e-mail-address) | [RFC 2821](https://tools.ietf.org/html/rfc2821) | [Email syntax](https://en.wikipedia.org/wiki/Email_address#Syntax)
*/
normalizeIdentifier?: (identifier: string) => string
options: EmailUserConfig
}
export type EmailUserConfig = Partial<Omit<EmailConfig, "options">>
export type EmailProvider = (options: EmailUserConfig) => EmailConfig
// TODO: Rename to Token provider
// when started working on https://github.com/nextauthjs/next-auth/discussions/1465
export type EmailProviderType = "Email"
export type EmailProviderType = "email"
export default function Email(options: EmailUserConfig): EmailConfig {
/** TODO: */
export function Email(config: EmailConfig): EmailConfig {
return {

@@ -86,5 +97,4 @@ id: "email",

name: "Email",
// Server can be an SMTP connection string or a nodemailer config object
server: { host: "localhost", port: 25, auth: { user: "", pass: "" } },
from: "NextAuth <no-reply@example.com>",
from: "Auth.js <no-reply@authjs.dev>",
maxAge: 24 * 60 * 60,

@@ -107,3 +117,4 @@ async sendVerificationRequest(params) {

},
options,
// @ts-expect-error
options: config,
}

@@ -110,0 +121,0 @@ }

@@ -1,4 +0,5 @@

import { OAuthConfig, OAuthUserConfig } from "./oauth"
import type { OAuthConfig, OAuthUserConfig } from "./oauth.js"
/** This is the default openid signature returned from FusionAuth
/**
* This is the default openid signature returned from FusionAuth
* it can be customized using [lambda functions](https://fusionauth.io/docs/v1/tech/lambdas)

@@ -5,0 +6,0 @@ */

import type { OAuthConfig, OAuthUserConfig } from "./index.js"
/** @see https://docs.github.com/en/rest/users/users#get-the-authenticated-user */
export interface GithubEmail extends Record<string, any> {
email: string
primary: boolean
verified: boolean
visibility: "public" | "private"
}
/** @see [Get the authenticated user](https://docs.github.com/en/rest/users/users#get-the-authenticated-user) */
export interface GithubProfile extends Record<string, any> {

@@ -52,12 +59,52 @@ login: string

export interface GithubEmail extends Record<string, any> {
email: string
primary: boolean
verified: boolean
visibility: "public" | "private"
}
export default function Github<P extends GithubProfile>(
options: OAuthUserConfig<P>
): OAuthConfig<P> {
/**
* Add GitHub login to your page and make requests to [GitHub APIs](https://docs.github.com/en/rest).
*
* ## Example
*
* @example
*
* ```ts
* import Auth from "@auth/core"
* import { GitHub } from "@auth/core/providers/github"
*
* const request = new Request("https://example.com")
* const resposne = await AuthHandler(request, {
* providers: [GitHub({ clientId: "", clientSecret: "" })],
* })
* ```
*
* ## Resources
*
* @see [GitHub - Creating an OAuth App](https://docs.github.com/en/developers/apps/building-oauth-apps/creating-an-oauth-app)
* @see [GitHub - Authorizing OAuth Apps](https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps)
* @see [GitHub - Configure your GitHub OAuth Apps](https://github.com/settings/developers)
* @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/github.ts)
*
* ## Notes
*
* By default, Auth.js assumes that the GitHub provider is
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
*
* :::tip
*
* The GitHub provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/github.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 GitHub<Profile extends GithubProfile>(
options: OAuthUserConfig<Profile>
): OAuthConfig<Profile> {
return {

@@ -64,0 +111,0 @@ id: "github",

@@ -48,2 +48,47 @@ import type { OAuthConfig, OAuthUserConfig } from "./index.js"

/**
* Add GitLab login to your page.
*
* ## Example
*
* @example
*
* ```js
* import Auth from "@auth/core"
* import { GitLab } from "@auth/core/providers/gitlab"
*
* const request = new Request("https://example.com")
* const resposne = await AuthHandler(request, {
* providers: [
* GitLab({clientId: "", clientSecret: ""})
* ]
* })
* ```
*
* ## Resources
*
* @see [Link 1](https://example.com)
*
* ## Notes
*
* By default, Auth.js assumes that the GitLab provider is
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
*
* :::tip
*
* The GitLab provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/gitlab.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 GitLab<P extends GitLabProfile>(

@@ -50,0 +95,0 @@ options: OAuthUserConfig<P>

@@ -1,8 +0,18 @@

import type { OAuthConfig, OAuthProvider, OAuthProviderType } from "./oauth.js"
import type { EmailConfig, EmailProvider, EmailProviderType } from "./email.js"
import { Profile } from "../types.js"
import CredentialsProvider from "./credentials.js"
import type {
CredentialsConfig,
CredentialsProvider,
CredentialsProviderType,
} from "./credentials.js"
import type {
Email as EmailProvider,
EmailConfig,
EmailProviderType,
} from "./email.js"
import type {
OAuth2Config,
OAuthConfig,
OAuthProviderType,
OIDCConfig,
} from "./oauth.js"

@@ -13,16 +23,56 @@ export * from "./credentials.js"

/**
* Providers passed to Auth.js must define one of these types.
*
* @see [RFC 6749 - The OAuth 2.0 Authorization Framework](https://www.rfc-editor.org/rfc/rfc6749.html#section-2.3)
* @see [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication)
* @see [Email or Passwordless Authentication](https://authjs.dev/concepts/oauth)
* @see [Credentials-based Authentication](https://authjs.dev/concepts/credentials)
*/
export type ProviderType = "oidc" | "oauth" | "email" | "credentials"
/** Shared across all {@link ProviderType} */
export interface CommonProviderOptions {
/**
* Uniquely identifies the provider in {@link AuthConfig.providers}
* It's also part of the URL
*/
id: string
/**
* The provider name used on the default sign-in page's sign-in button.
* For example if it's "Google", the corresponding button will say:
* "Sign in with Google"
*/
name: string
/** See {@link ProviderType} */
type: ProviderType
options?: Record<string, unknown>
}
export type Provider = OAuthConfig<any> | EmailConfig | CredentialsConfig
/**
* Must be a supported authentication provider config:
* - {@link OAuthConfig}
* - {@link EmailConfigInternal}
* - {@link CredentialsConfigInternal}
*
* For more information, see the guides:
*
* @see [OAuth/OIDC guide](https://authjs.dev/guides/providers/custom-provider)
* @see [Email (Passwordless) guide](https://authjs.dev/guides/providers/email)
* @see [Credentials guide](https://authjs.dev/guides/providers/credentials)
*/
export type Provider<P extends Profile = Profile> = (
| OIDCConfig<P>
| OAuth2Config<P>
| EmailConfig
| CredentialsConfig
) & {
options: Record<string, unknown>
}
export type BuiltInProviders = Record<OAuthProviderType, OAuthProvider> &
Record<CredentialsProviderType, CredentialsProvider> &
Record<EmailProviderType, EmailProvider>
export type BuiltInProviders = Record<
OAuthProviderType,
(options: Partial<OAuthConfig<any>>) => OAuthConfig<any>
> &
Record<CredentialsProviderType, typeof CredentialsProvider> &
Record<EmailProviderType, typeof EmailProvider>

@@ -29,0 +79,0 @@ export type AppProviders = Array<

@@ -25,3 +25,3 @@ /**

* ```
* [NextAuth.js Documentation](https://next-auth.js.org/providers/instagram) | [Instagram Documentation](https://developers.facebook.com/docs/instagram-basic-display-api/getting-started) | [Configuration](https://developers.facebook.com/apps)
* [NextAuth.js Documentation](https://authjs.dev/reference/providers/instagram) | [Instagram Documentation](https://developers.facebook.com/docs/instagram-basic-display-api/getting-started) | [Configuration](https://developers.facebook.com/apps)
*/

@@ -28,0 +28,0 @@ /** @type {import(".").OAuthProvider} */

@@ -1,4 +0,3 @@

// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
export type OAuthProviderType =
export type OAuthProviderType =
| "42-school"

@@ -44,3 +43,3 @@ | "apple"

| "netlify"
| "oauth-types"
| "oauth-types.js"
| "oauth"

@@ -71,2 +70,2 @@ | "okta"

| "zoho"
| "zoom"
| "zoom"

@@ -0,4 +1,4 @@

import type { Client } from "oauth4webapi"
import type { Awaitable, Profile, TokenSet, User } from "../types.js"
import type { CommonProviderOptions } from "../providers/index.js"
import type { Profile, TokenSet, User, Awaitable } from "../index.js"
import type { Client } from "oauth4webapi"

@@ -98,2 +98,3 @@ // TODO:

/** TODO: */
export interface OAuth2Config<P> extends CommonProviderOptions, PartialIssuer {

@@ -103,4 +104,5 @@ /**

* a specific provider.
*
* @example
* ```js
* ```ts
* signIn('github') // "github" is the provider ID

@@ -137,3 +139,3 @@ * ```

*
* [Documentation](https://next-auth.js.org/adapters/models#user)
* [Documentation](https://authjs.dev/reference/adapters/models#user)
*/

@@ -155,10 +157,11 @@ profile?: ProfileCallback<P>

/**
* [Documentation](https://next-auth.js.org/configuration/providers/oauth#allowdangerousemailaccountlinking-option)
* [Documentation](https://authjs.dev/reference/providers/oauth#allowdangerousemailaccountlinking-option)
*/
allowDangerousEmailAccountLinking?: boolean
/**
* @internal
* The options provided by the user.
* We will perform a deep-merge of these values
* with the default configuration.
*
* @internal
*/

@@ -168,2 +171,3 @@ options?: OAuthUserConfig<P>

/** TODO: */
export interface OIDCConfig<P> extends Omit<OAuth2Config<P>, "type"> {

@@ -198,5 +202,1 @@ type: "oidc"

Required<Pick<OAuthConfig<P>, "clientId" | "clientSecret">>
export type OAuthProvider = (
options: Partial<OAuthConfig<any>>
) => OAuthConfig<any>

@@ -1,2 +0,2 @@

import { OAuthConfig, OAuthUserConfig } from "./index.js"
import type { OAuthConfig, OAuthUserConfig } from "./index.js"

@@ -3,0 +3,0 @@ export interface PinterestProfile extends Record<string, any> {

@@ -13,2 +13,51 @@ import type { OAuthConfig, OAuthUserConfig } from "./index.js"

}
/**
* Add Spotify login to your page.
*
* ## Example
*
* @example
*
* ```ts
* import Auth from "@auth/core"
* import { Spotify } from "@auth/core/providers/spotify"
*
* const request = new Request("https://example.com")
* const resposne = await AuthHandler(request, {
* providers: [
* Spotify({clientId: "", clientSecret: ""})
* ]
* })
* ```
*
* ---
*
* ## Resources
* @see [Link 1](https://example.com)
*
* ---
*
* ## Notes
*
* By default, Auth.js assumes that the Spotify provider is
* based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification.
*
* :::tip
*
* The Spotify provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/spotify.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/github-discussions).
*
* :::
*/
export default function Spotify<P extends SpotifyProfile>(

@@ -15,0 +64,0 @@ options: OAuthUserConfig<P>

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc