@adonisjs/auth
Version:
Official authentication provider for Adonis framework
338 lines (337 loc) • 9.02 kB
JavaScript
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 };