UNPKG

@adonisjs/auth

Version:

Official authentication provider for Adonis framework

338 lines (337 loc) 9.02 kB
import { n as E_UNAUTHORIZED_ACCESS } from "../../errors-eDV8ejO0.js"; import "../../symbols-C5QEqFvJ.js"; import { RuntimeException } from "@adonisjs/core/exceptions"; import { base64 } from "@adonisjs/core/helpers"; import auth from "basic-auth"; //#region modules/basic_auth_guard/guard.ts /** * BasicAuth guard implements the HTTP Authentication protocol * * @template UserProvider - The user provider contract * * @example * const guard = new BasicAuthGuard( * 'basic', * ctx, * emitter, * userProvider * ) * * const user = await guard.authenticate() * console.log('Authenticated user:', user.email) */ var BasicAuthGuard = class { /** * A unique name for the guard. */ #name; /** * Reference to the current HTTP context */ #ctx; /** * Provider to lookup user details */ #userProvider; /** * Emitter to emit events */ #emitter; /** * Driver name of the guard */ driverName = "basic_auth"; /** * Whether or not the authentication has been attempted * during the current request. */ authenticationAttempted = false; /** * A boolean to know if the current request has * been authenticated */ isAuthenticated = false; /** * Reference to an instance of the authenticated user. * The value only exists after calling one of the * following methods. * * - authenticate * - check * * You can use the "getUserOrFail" method to throw an exception if * the request is not authenticated. */ user; /** * Creates a new BasicAuthGuard instance * * @param name - Unique name for the guard instance * @param ctx - HTTP context for the current request * @param emitter - Event emitter for guard events * @param userProvider - User provider for credential verification * * @example * const guard = new BasicAuthGuard( * 'basic', * ctx, * emitter, * new BasicAuthLucidUserProvider() * ) */ constructor(name, ctx, emitter, userProvider) { this.#name = name; this.#ctx = ctx; this.#emitter = emitter; this.#userProvider = userProvider; } /** * Emits authentication failure, updates the local state, * and returns an exception to end the authentication * cycle. */ #authenticationFailed() { this.isAuthenticated = false; this.user = void 0; const error = new E_UNAUTHORIZED_ACCESS("Invalid basic auth credentials", { guardDriverName: this.driverName }); this.#emitter.emit("basic_auth:authentication_failed", { ctx: this.#ctx, guardName: this.#name, error }); return error; } /** * Emits the authentication succeeded event and updates * the local state to reflect successful authentication */ #authenticationSucceeded(user) { this.isAuthenticated = true; this.user = user; this.#emitter.emit("basic_auth:authentication_succeeded", { ctx: this.#ctx, guardName: this.#name, user }); } /** * Returns an instance of the authenticated user. Or throws * an exception if the request is not authenticated. * * @throws {E_UNAUTHORIZED_ACCESS} When user is not authenticated * * @example * const user = guard.getUserOrFail() * console.log('User:', user.email) */ getUserOrFail() { if (!this.user) throw new E_UNAUTHORIZED_ACCESS("Invalid basic auth credentials", { guardDriverName: this.driverName }); return this.user; } /** * Authenticates the incoming HTTP request by looking for BasicAuth * credentials inside the request authorization header. * * @throws {E_UNAUTHORIZED_ACCESS} When authentication fails * * @example * try { * const user = await guard.authenticate() * console.log('Authenticated as:', user.email) * } catch (error) { * console.log('Authentication failed') * } */ async authenticate() { /** * Avoid re-authenticating when already authenticated */ if (this.authenticationAttempted) return this.getUserOrFail(); /** * Beginning authentication attempt */ this.authenticationAttempted = true; this.#emitter.emit("basic_auth:authentication_attempted", { ctx: this.#ctx, guardName: this.#name }); /** * Fetch credentials from the header or fail */ const credentials = auth(this.#ctx.request.request); if (!credentials) throw this.#authenticationFailed(); /** * Verify user credentials or fail */ const user = await this.#userProvider.verifyCredentials(credentials.name, credentials.pass); if (!user) throw this.#authenticationFailed(); /** * Mark user as authenticated */ this.#authenticationSucceeded(user.getOriginal()); return this.getUserOrFail(); } /** * Silently attempt to authenticate the user. * * The method returns a boolean indicating if the authentication * succeeded or failed. * * @example * const isAuthenticated = await guard.check() * if (isAuthenticated) { * console.log('User is authenticated:', guard.user.email) * } */ async check() { try { await this.authenticate(); return true; } catch (error) { if (error instanceof E_UNAUTHORIZED_ACCESS) return false; throw error; } } /** * Returns the Authorization header clients can use to authenticate * the request using basic auth. * * @param uid - The username or user identifier * @param password - The user's password * * @example * const clientAuth = await guard.authenticateAsClient('user@example.com', 'secret') * // Use clientAuth.headers.authorization in API tests */ async authenticateAsClient(uid, password) { return { headers: { authorization: `Basic ${base64.encode(`${uid}:${password}`)}` } }; } }; //#endregion //#region modules/basic_auth_guard/user_providers/lucid.ts /** * Uses a Lucid model to verify access tokens and find a user during * authentication * * @template UserModel - The Lucid model representing the user * * @example * const userProvider = new BasicAuthLucidUserProvider({ * model: () => import('#models/user') * }) */ var BasicAuthLucidUserProvider = class { /** * Reference to the lazily imported model */ model; /** * Creates a new BasicAuthLucidUserProvider instance * * @param options - Configuration options for the user provider * * @example * const provider = new BasicAuthLucidUserProvider({ * model: () => import('#models/user') * }) */ constructor(options) { this.options = options; } /** * Imports the model from the provider, returns and caches it * for further operations. * * @example * const UserModel = await provider.getModel() * const user = await UserModel.find(1) */ async getModel() { if (this.model && !("hot" in import.meta)) return this.model; this.model = (await this.options.model()).default; return this.model; } /** * Creates an adapter user for the guard * * @param user - The user model instance * * @example * const guardUser = await provider.createUserForGuard(user) * console.log('User ID:', guardUser.getId()) */ async createUserForGuard(user) { const model = await this.getModel(); if (user instanceof model === false) throw new RuntimeException(`Invalid user object. It must be an instance of the "${model.name}" model`); return { getId() { /** * Ensure user has a primary key */ if (!user.$primaryKeyValue) throw new RuntimeException(`Cannot use "${model.name}" model for authentication. The value of column "${model.primaryKey}" is undefined or null`); return user.$primaryKeyValue; }, getOriginal() { return user; } }; } /** * Verifies credentials using the underlying model * * @param uid - The username or user identifier * @param password - The password to verify * * @example * const guardUser = await provider.verifyCredentials('user@example.com', 'secret') * if (guardUser) { * console.log('Valid credentials') * } */ async verifyCredentials(uid, password) { const model = await this.getModel(); try { const user = await model.verifyCredentials(uid, password); return this.createUserForGuard(user); } catch { return null; } } }; //#endregion //#region modules/basic_auth_guard/define_config.ts /** * Configures basic auth guard for authentication using HTTP Basic Authentication * * @param config - Configuration object containing the user provider * * @example * const guard = basicAuthGuard({ * provider: basicAuthUserProvider({ * model: () => import('#models/user') * }) * }) */ function basicAuthGuard(config) { return { async resolver(name, app) { const emitter = await app.container.make("emitter"); const provider = "resolver" in config.provider ? await config.provider.resolver(app) : config.provider; return (ctx) => new BasicAuthGuard(name, ctx, emitter, provider); } }; } /** * Configures user provider that uses Lucid models to authenticate * users using basic auth credentials * * @param config - Configuration options for the Lucid user provider * * @example * const userProvider = basicAuthUserProvider({ * model: () => import('#models/user') * }) */ function basicAuthUserProvider(config) { return new BasicAuthLucidUserProvider(config); } //#endregion export { BasicAuthGuard, BasicAuthLucidUserProvider, basicAuthGuard, basicAuthUserProvider };