UNPKG

@auth/core

Version:

Authentication for the Web.

288 lines (280 loc) 11.6 kB
/** * Auth.js can be integrated with _any_ data layer (database, ORM, or backend API, HTTP client) * in order to automatically create users, handle account linking automatically, support passwordless login, * and to store session information. * * This module contains utility functions and types to create an Auth.js compatible adapter. * * Auth.js supports 2 session strategies 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. * * Before you continue, Auth.js has a list of {@link https://authjs.dev/reference/adapters/overview official database adapters}. If your database is listed there, you * probably do not need to create your own. If you are using a data solution that cannot be integrated with an official adapter, this module will help you create a compatible adapter. * * :::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 framework/runtime (e.g. it might rely on [Node.js APIs](https://nodejs.org/docs/latest/api)). * Related issues should be reported to the corresponding package maintainers. * ::: * * ## Installation * * ```bash npm2yarn2pnpm * npm install @auth/core * ``` * * Then, you can import this submodule from `@auth/core/adapters`. * * ## Usage * * Each adapter method and its function signature is documented in the {@link Adapter} interface. * * ```ts title=my-adapter.ts * import { type Adapter } from "@auth/core/adapters" * * // 1. Simplest form, a plain object. * export const MyAdapter: Adapter { * // implement the adapter methods here * } * * // or * * // 2. A function that returns an object. Official adapters use this pattern. * export function MyAdapter(config: any): Adapter { * // Instantiate a client/ORM here with the provided config, or pass it in as a parameter. * // Usually, you might already have a client instance elsewhere in your application, * // so you should only create a new instance if you need to or you don't have one. * * return { * // implement the adapter methods * } * } * * ``` * * Then, you can pass your adapter to Auth.js as the `adapter` option. * * ```ts title=index.ts * import { MyAdapter } from "./my-adapter" * * const response = await Auth(..., { * adapter: MyAdapter, // 1. * // or * adapter: MyAdapter({ /* config *\/ }), // 2. * ... * }) * ``` * * Note, you might be able to tweak an existing adapter to work with your data layer, instead of creating one from scratch. * * ```ts title=my-adapter.ts * import { type Adapter } from "@auth/core/adapters" * import { PrismaAdapter } from "@auth/prisma-adapter" * import { PrismaClient } from "@prisma/client" * * const prisma = new PrismaClient() * * const adapter: Adapter = { * ...PrismaAdapter(prisma), * // Add your custom methods here * } * * const request = new Request("https://example.com") * const response = await Auth(request, { adapter, ... }) * ``` * * ## Testing * * 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. * * ## Known issues * * The following are missing built-in features in Auth.js but can be solved in user land. If you would like to help implement these features, please reach out. * * ### Token rotation * * Auth.js _currently_ does not support {@link https://authjs.dev/concepts/oauth#token-rotation `access_token` rotation} out of the box. * The necessary information (`refresh_token`, expiry, etc.) is being stored in the database, but the logic to rotate the token is not implemented * in the core library. * [This guide](https://authjs.dev/guides/basics/refresh-token-rotation#database-strategy) should provide the necessary steps to do this in user land. * * ### Federated logout * * Auth.js _currently_ does not support {@link https://authjs.dev/concepts/oauth#federated-logout federated logout} out of the box. * This means that even if an active session is deleted from the database, the user will still be signed in to the identity provider, * they will only be signed out of the application. * Eg. if you use Google as an identity provider, and you delete the session from the database, * the user will still be signed in to Google, but they will be signed out of your application. * * If your users might be using the application from a publicly shared computer (eg: library), you might want to implement federated logout. * {@link https://authjs.dev/guides/providers/federated-logout This guide} should provide the necessary steps. * * @module adapters */ import { ProviderType } from "./providers/index.js" 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. /** * A user represents a person who can sign in to the application. * If a user does not exist yet, it will be created when they sign in for the first time, * using the information (profile data) returned by the identity provider. * A corresponding account is also created and linked to the user. */ export interface AdapterUser extends User { /** A unique identifier for the user. */ id: string /** The user's email address. */ email: string /** * Whether the user has verified their email address via an [Email provider](https://authjs.dev/reference/core/providers_email). * It is `null` if the user has not signed in with the Email provider yet, or the date of the first successful signin. */ emailVerified: Date | null } /** * An account is a connection between a user and a provider. * * There are two types of accounts: * - OAuth/OIDC accounts, which are created when a user signs in with an OAuth provider. * - Email accounts, which are created when a user signs in with an [Email provider](https://authjs.dev/reference/core/providers_email). * * One user can have multiple accounts. */ export interface AdapterAccount extends Account { userId: string type: Extract<ProviderType, "oauth" | "oidc" | "email"> } /** * A session holds information about a user's current signin state. */ export interface AdapterSession { /** * A randomly generated value that is used to look up the session in the database * when using `"database"` `AuthConfig.strategy` option. * This value is saved in a secure, HTTP-Only cookie on the client. */ sessionToken: string /** 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 `SessionOptions.maxAge`. * It is never extended more than once in a period defined by `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 } /** * A verification token is a temporary token that is used to sign in a user via their email address. * It is created when a user signs in with an [Email provider](https://authjs.dev/reference/core/providers_email). * When the user clicks the link in the email, the token and email is sent back to the server * where it is hashed and compared to the value in the database. * If the tokens and emails match, and the token hasn't expired yet, the user is signed in. * The token is then deleted from the database. */ export interface VerificationToken { /** The user's email address. */ identifier: string /** The absolute date when the token expires. */ expires: Date /** * A [hashed](https://authjs.dev/concepts/hashing) token, using the `AuthConfig.secret` value. */ token: string } /** * An adapter is an object with function properties (methods) that read and write data from a data source. * Think of these methods as a way to normalize the data layer to common interfaces that Auth.js can understand. * * This is what makes Auth.js very flexible and allows it to be used with any data layer. * * The adapter methods are used to perform the following operations: * - Create/update/delete a user * - Link/unlink an account to/from a user * - Handle active sessions * - Support passwordless authentication across multiple devices * * :::note * If any of the methods are not implemented, but are called by Auth.js, * an error will be shown to the user and the operation will fail. * ::: */ 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> & Pick<AdapterUser, 'id'>): Awaitable<AdapterUser> /** @todo This method is currently not invoked yet. */ deleteUser?( userId: string ): Promise<void> | Awaitable<AdapterUser | null | undefined> /** * This method is invoked internally (but optionally can be used for manual linking). * It creates an [Account](https://authjs.dev/reference/adapters#models) in the database. */ linkAccount?( account: AdapterAccount ): Promise<void> | Awaitable<AdapterAccount | null | undefined> /** @todo This method is currently not invoked yet. */ unlinkAccount?( providerAccountId: Pick<AdapterAccount, "provider" | "providerAccountId"> ): Promise<void> | Awaitable<AdapterAccount | undefined> /** Creates a session for the user and returns it. */ createSession?(session: { sessionToken: string userId: string expires: Date }): Awaitable<AdapterSession> getSessionAndUser?( sessionToken: string ): Awaitable<{ session: AdapterSession; user: AdapterUser } | null> 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. */ 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. */ useVerificationToken?(params: { identifier: string token: string }): Awaitable<VerificationToken | null> } // For compatibility with older versions of NextAuth.js // @ts-expect-error declare module "next-auth/adapters" { type JsonObject = { [Key in string]?: JsonValue } type JsonArray = JsonValue[] type JsonPrimitive = string | number | boolean | null type JsonValue = JsonPrimitive | JsonObject | JsonArray interface AdapterAccount { type: "oauth" | "email" | "oidc" [key: string]: JsonValue | undefined } }