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