@travetto/auth
v5.0.13
Published
Authentication scaffolding for the Travetto framework
Downloads
913
Maintainers
Readme
Authentication
Authentication scaffolding for the Travetto framework
Install: @travetto/auth
npm install @travetto/auth
# or
yarn add @travetto/auth
This module provides the high-level backdrop for managing security principals. The goal of this module is to be a centralized location for various security frameworks to plug into. The primary contributions are:
- Standard Types
- Authentication Contract
- Authorization Contract
- Common security-related utilities for
- Checking permissions
- Generating passwords
Standard Types
The module's goal is to be as flexible as possible. To that end, the primary contract that this module defines, is that of the Principal Structure.
Code: Principal Structure
export interface Principal<D = AnyMap> {
/**
* Primary identifier for a user
*/
id: string;
/**
* Date of expiration
*/
expiresAt?: Date;
/**
* Date of issuance
*/
issuedAt?: Date;
/**
* Max age in seconds a principal is valid
*/
maxAge?: number;
/**
* The source of the issuance
*/
issuer?: string;
/**
* Supplemental details
*/
details: D;
/**
* List of all provided permissions
*/
permissions?: string[];
}
As referenced above, a Principal Structure is defined as a user with respect to a security context. This can be information the application knows about the user (authorized) or what a separate service may know about a user (3rd-party authentication).
Authentication
Code: Authenticator
export interface Authenticator<T = unknown, P extends Principal = Principal, C = unknown> {
/**
* Allows for the authenticator to be initialized if needed
* @param ctx
*/
initialize?(ctx: C): Promise<void>;
/**
* Verify the payload, ensuring the payload is correctly identified.
*
* @returns Valid principal if authenticated
* @returns undefined if authentication is valid, but incomplete (multi-step)
* @throws AppError if authentication fails
*/
authenticate(payload: T, ctx?: C): Promise<P | undefined> | P | undefined;
}
The Authenticator only requires one method to be defined, and that is authenticate
. This method receives a generic payload, and a supplemental context as an input. The interface is responsible for converting that to an authenticated principal.
Example
The JWT module is a good example of an authenticator. This is a common use case for simple internal auth.
Authorization
Code: Authorizer
export interface Authorizer<P extends Principal = Principal> {
/**
* Authorize inbound principal, verifying it's permission to access the system.
* @param principal
* @returns New principal that conforms to the required principal shape
*/
authorize(principal: Principal): Promise<P> | P;
}
Authorizers are generally seen as a secondary step post-authentication. Authentication acts as a very basic form of authorization, assuming the principal store is owned by the application.
The Authorizer only requires one method to be defined, and that is authorizer
. This method receives an authenticated principal as an input, and is responsible for converting that to an authorized principal.
Example
The Data Modeling Support extension is a good example of an authenticator. This is a common use case for simple internal auth.
Overall, the structure is simple, but drives home the primary use cases of the framework. The goals are:
- Be able to identify a user uniquely
- To have a reference to a user's set of permissions
- To have access to the principal
Common Utilities
The AuthUtil provides the following functionality:
Code: Auth util structure
import crypto from 'node:crypto';
import util from 'node:util';
import { AppError, Util } from '@travetto/runtime';
const pbkdf2 = util.promisify(crypto.pbkdf2);
/**
* Standard auth utilities
*/
export class AuthUtil {
/**
* Generate a hash for a given value
*
* @param value Value to hash
* @param salt The salt value
* @param iterations Number of iterations on hashing
* @param keylen Length of hash
* @param digest Digest method
*/
static generateHash(value: string, salt: string, iterations = 25000, keylen = 256, digest = 'sha256'): Promise<string>;
/**
* Generate a salted password, with the ability to validate the password
*
* @param password
* @param salt Salt value, or if a number, length of salt
* @param validator Optional function to validate your password
*/
static async generatePassword(password: string, salt: number | string = 32): Promise<{ salt: string, hash: string }>;
}
roleMatcher
is probably the only functionality that needs to be explained. The function extends the core allow/deny matcher functionality from Runtime's Util class.
An example of role checks could be:
- Admin
- !Editor
- Owner+Author The code would check the list in order, which would result in the following logic:
- If the user is an admin, always allow
- If the user has the editor role, deny
- If the user is both an owner and an author allow
- By default, deny due to the presence of positive checks