UNPKG

@adonisjs/auth

Version:

Official authentication provider for Adonis framework

385 lines (384 loc) 11.2 kB
import { n as E_UNAUTHORIZED_ACCESS } from "./errors-eDV8ejO0.js"; import { t as debug_default } from "./debug-CrTUB4zl.js"; import { RuntimeException } from "@adonisjs/core/exceptions"; import { HttpContextFactory } from "@adonisjs/core/factories/http"; //#region src/authenticator.ts /** * Authenticator is used to authenticate incoming HTTP requests * using one or more known guards. */ var Authenticator = class { /** * Registered guards */ #config; /** * Cache of guards created during the HTTP request */ #guardsCache = {}; /** * Last guard that was used to perform the authentication via * the "authenticateUsing" method. * * @note * Reset on every call made to "authenticate", "check" and * "authenticateUsing" method. */ #authenticationAttemptedViaGuard; /** * Name of the guard using which the request has * been authenticated successfully. * * @note * Reset on every call made to "authenticate", "check" and * "authenticateUsing" method. */ #authenticatedViaGuard; /** * Reference to HTTP context */ #ctx; /** * Name of the default guard configured in the auth configuration * * @example * const defaultGuard = auth.defaultGuard * console.log(defaultGuard) // 'web' */ get defaultGuard() { return this.#config.default; } /** * Reference to the guard using which the current * request has been authenticated. Returns undefined if * authentication has not been attempted or failed. * * @example * await auth.authenticate() * console.log(auth.authenticatedViaGuard) // 'web' */ get authenticatedViaGuard() { return this.#authenticatedViaGuard; } /** * A boolean to know if the current request has been authenticated. The * property returns false when "authenticate" or "authenticateUsing" * methods are not used. * * @example * await auth.authenticate() * console.log(auth.isAuthenticated) // true */ get isAuthenticated() { if (!this.#authenticationAttemptedViaGuard) return false; return this.use(this.#authenticationAttemptedViaGuard).isAuthenticated; } /** * Reference to the currently authenticated user. The property returns * undefined when "authenticate" or "authenticateUsing" methods are * not used. * * @example * await auth.authenticate() * console.log(auth.user?.email) */ get user() { if (!this.#authenticationAttemptedViaGuard) return; return this.use(this.#authenticationAttemptedViaGuard).user; } /** * Whether or not the authentication has been attempted during * the current request. The property returns false when the * "authenticate" or "authenticateUsing" methods are not * used. * * @example * await auth.check() * console.log(auth.authenticationAttempted) // true */ get authenticationAttempted() { if (!this.#authenticationAttemptedViaGuard) return false; return this.use(this.#authenticationAttemptedViaGuard).authenticationAttempted; } /** * Creates a new Authenticator instance * * @param ctx - The HTTP context for the current request * @param config - Configuration object containing default guard and available guards * * @example * const authenticator = new Authenticator(ctx, { * default: 'web', * guards: { web: sessionGuard } * }) */ constructor(ctx, config) { this.#ctx = ctx; this.#config = config; debug_default("creating authenticator. config %O", this.#config); } /** * Returns an instance of the logged-in user or throws an exception * * @throws {RuntimeException} When authentication has not been attempted * * @example * const user = auth.getUserOrFail() * console.log(user.id) */ getUserOrFail() { if (!this.#authenticationAttemptedViaGuard) throw new RuntimeException("Cannot access authenticated user. Please call \"auth.authenticate\" method first."); return this.use(this.#authenticationAttemptedViaGuard).getUserOrFail(); } /** * Returns an instance of a known guard. Guards instances are * cached during the lifecycle of an HTTP request. * * @param guard - Optional guard name. Uses default guard if not provided * * @example * const sessionGuard = auth.use('session') * const defaultGuard = auth.use() */ use(guard) { const guardToUse = guard || this.#config.default; /** * Use cached copy if exists */ const cachedGuard = this.#guardsCache[guardToUse]; if (cachedGuard) { debug_default("authenticator: using guard from cache. name: \"%s\"", guardToUse); return cachedGuard; } const guardFactory = this.#config.guards[guardToUse]; /** * Construct guard and cache it */ debug_default("authenticator: creating guard. name: \"%s\"", guardToUse); const guardInstance = guardFactory(this.#ctx); this.#guardsCache[guardToUse] = guardInstance; return guardInstance; } /** * Authenticate current request using the default guard. Calling this * method multiple times triggers multiple authentication with the * guard. * * @throws {E_UNAUTHORIZED_ACCESS} When authentication fails * * @example * const user = await auth.authenticate() * console.log('Authenticated user:', user.email) */ async authenticate() { await this.authenticateUsing(); return this.getUserOrFail(); } /** * Silently attempt to authenticate the request using the default * guard. Calling this method multiple times triggers multiple * authentication with the guard. * * @example * const isAuthenticated = await auth.check() * if (isAuthenticated) { * console.log('User is authenticated') * } */ async check() { this.#authenticationAttemptedViaGuard = this.defaultGuard; const isAuthenticated = await this.use().check(); if (isAuthenticated) this.#authenticatedViaGuard = this.defaultGuard; return isAuthenticated; } /** * Authenticate the request using all of the mentioned guards * or the default guard. * * The authentication process will stop after any of the mentioned * guards is able to authenticate the request successfully. * * Otherwise, "E_UNAUTHORIZED_ACCESS" will be raised. * * @param guards - Array of guard names to try for authentication * @param options - Options object with optional loginRoute for redirects * * @throws {E_UNAUTHORIZED_ACCESS} When none of the guards can authenticate * * @example * const user = await auth.authenticateUsing(['session', 'api']) * const userWithRedirect = await auth.authenticateUsing(['web'], { loginRoute: '/login' }) */ async authenticateUsing(guards, options) { const guardsToUse = guards || [this.defaultGuard]; let lastUsedDriver; for (let guardName of guardsToUse) { debug_default("attempting to authenticate using guard \"%s\"", guardName); this.#authenticationAttemptedViaGuard = guardName; const guard = this.use(guardName); lastUsedDriver = guard.driverName; if (await guard.check()) { this.#authenticatedViaGuard = guardName; return this.getUserOrFail(); } } throw new E_UNAUTHORIZED_ACCESS("Unauthorized access", { guardDriverName: lastUsedDriver, redirectTo: options?.loginRoute }); } /** * Silently attempt to authenticate the request using all of the mentioned guards * or the default guard. Calling this method multiple times triggers multiple * authentication with the guard. * * @param guards - Array of guard names to check. Defaults to default guard * * @example * const isAuthenticated = await auth.checkUsing(['session', 'api']) * if (isAuthenticated) { * const user = auth.user * } */ async checkUsing(guards = [this.defaultGuard]) { for (const name of guards) { this.#authenticationAttemptedViaGuard = name; if (await this.use(name).check()) { this.#authenticatedViaGuard = name; return true; } } return false; } }; //#endregion //#region src/authenticator_client.ts /** * Authenticator client is used to create guard instances for testing. * It passes a fake HTTPContext to the guards, so make sure to not * call server side APIs that might be relying on a real * HTTPContext instance. */ var AuthenticatorClient = class { /** * Registered guards */ #config; /** * Cache of guards */ #guardsCache = {}; /** * Name of the default guard configured in the auth configuration * * @example * const client = new AuthenticatorClient({ default: 'web', guards: {} }) * console.log(client.defaultGuard) // 'web' */ get defaultGuard() { return this.#config.default; } /** * Creates a new AuthenticatorClient instance for testing * * @param config - Configuration object containing default guard and available guards * * @example * const client = new AuthenticatorClient({ * default: 'web', * guards: { web: sessionGuard } * }) */ constructor(config) { this.#config = config; debug_default("creating authenticator client. config %O", this.#config); } /** * Returns an instance of a known guard. Guards instances are * cached during the lifecycle of an HTTP request. * * @param guard - Optional guard name. Uses default guard if not provided * * @example * const sessionGuard = client.use('session') * const defaultGuard = client.use() */ use(guard) { const guardToUse = guard || this.#config.default; /** * Use cached copy if exists */ const cachedGuard = this.#guardsCache[guardToUse]; if (cachedGuard) { debug_default("authenticator client: using guard from cache. name: \"%s\"", guardToUse); return cachedGuard; } const guardFactory = this.#config.guards[guardToUse]; /** * Construct guard and cache it */ debug_default("authenticator client: creating guard. name: \"%s\"", guardToUse); const guardInstance = guardFactory(new HttpContextFactory().create()); this.#guardsCache[guardToUse] = guardInstance; return guardInstance; } }; //#endregion //#region src/auth_manager.ts /** * Auth manager exposes the API to register and manage authentication * guards from the config */ var AuthManager = class { /** * Name of the default guard configured in the auth configuration * * @example * const manager = new AuthManager({ default: 'web', guards: {} }) * console.log(manager.defaultGuard) // 'web' */ get defaultGuard() { return this.config.default; } /** * Creates a new AuthManager instance * * @param config - Configuration object containing default guard and available guards * * @example * const manager = new AuthManager({ * default: 'web', * guards: { web: sessionGuard, api: tokenGuard } * }) */ constructor(config) { this.config = config; this.config = config; } /** * Create an authenticator for a given HTTP request. The authenticator * is used to authenticate incoming HTTP requests * * @param ctx - The HTTP context for the current request * * @example * const authenticator = manager.createAuthenticator(ctx) * const user = await authenticator.authenticate() */ createAuthenticator(ctx) { return new Authenticator(ctx, this.config); } /** * Creates an instance of the authenticator client. The client is * used to setup authentication state during testing. * * @example * const client = manager.createAuthenticatorClient() * const guard = client.use('session') */ createAuthenticatorClient() { return new AuthenticatorClient(this.config); } }; //#endregion export { AuthenticatorClient as n, Authenticator as r, AuthManager as t };