@taukala/xs-ctrl
Version:
A flexible and powerful access control library for JavaScript applications with dynamic validation support
196 lines (179 loc) • 5.98 kB
TypeScript
declare module '@taukala/xs-ctrl' {
/**
* Session object type (generic to allow custom session types)
*/
export type Session = any;
/**
* Claims object structure representing user permissions and attributes
* @example
* {
* role: ['admin', 'user'],
* department: ['IT'],
* businessOwner: ['business-1', 'business-2']
* }
*/
export interface Claims {
[key: string]: (string | number)[];
}
/**
* Context object for validation containing session, claims, and custom resources
*/
export interface ValidationContext {
session?: Session;
claims?: Claims;
resources?: Record<string, any>;
[key: string]: any;
}
/**
* Static condition type representing [key, allowedValues] pairs
*/
export type Condition = [string, (string | number)[]];
/**
* Dynamic validator function type for complex authorization rules
*/
export type DynamicValidator = (context: ValidationContext) => boolean | Promise<boolean>;
/**
* Access rule structure containing static conditions and dynamic validators
*/
export interface AccessRule {
conditions: Condition[];
dynamicValidators: DynamicValidator[];
}
/**
* Configuration options for creating a permission validator
*/
export interface PermissionValidatorOptions {
/**
* Async function to retrieve the current session
* Should return null/undefined if no session exists
*/
getSession: () => Promise<Session | null | undefined>;
/**
* Async function to retrieve user claims
* Receives the session object and should return claims object
* @example
* async (session) => ({
* role: ['business'],
* businessOwner: ['business-1', 'business-2']
* })
*/
getClaims: (session: Session) => Promise<Claims>;
/**
* Callback for handling unauthenticated users
* Usually redirects to login page or returns 401 response
*/
onUnauthenticated: () => void | Promise<void>;
/**
* Callback for handling unauthorized access
* Usually redirects to home page or returns 403 response
*/
onUnauthorized: () => void | Promise<void>;
}
/**
* Result object returned by permission validator
*/
export interface PermissionValidatorResult {
session?: Session;
claims?: Claims;
status: 200 | 401 | 403;
}
/**
* Interface for building access rules using a fluent API
*/
export interface AccessRuleBuilder {
/**
* Adds a static condition to the access rule
* @param key The condition key (e.g., 'role', 'department')
* @param values One or more acceptable values for the condition
* @throws {Error} If key is empty or no values are provided
*/
addCondition(key: string, ...values: (string | number)[]): AccessRuleBuilder;
/**
* Adds a dynamic condition to the access rule
* @param validator Function that performs dynamic validation
* @throws {Error} If validator is not a function
*/
addDynamicCondition(validator: DynamicValidator): AccessRuleBuilder;
/**
* Builds and returns the final rule object
*/
build(): AccessRule;
}
/**
* Creates a permission validator that combines authentication and authorization checks.
* This factory function generates a validator that can be used to protect routes or resources.
*
* @param options Configuration options
* @returns An async validator function that accepts access rules and context
*
* @example
* // Basic usage with Next.js and static rules
* const validatePermission = createPermissionValidator({
* getSession: auth,
* getClaims: async (session) => ({
* role: ['admin'],
* department: ['IT']
* }),
* onUnauthenticated: () => redirect('/auth/signin'),
* onUnauthorized: () => redirect('/')
* });
*
* @example
* // Usage with dynamic business rules
* const validatePermission = createPermissionValidator({
* getSession: auth,
* getClaims: async (session) => ({
* role: ['business'],
* businessOwner: ['business-1', 'business-2']
* }),
* onUnauthenticated: () => redirect('/auth/signin'),
* onUnauthorized: () => redirect('/')
* });
*/
export function createPermissionValidator(
options: PermissionValidatorOptions
): (
criteria: AccessRule[],
context?: ValidationContext
) => Promise<PermissionValidatorResult>;
/**
* Validates user claims against a set of access rules using OR/AND logic.
* Supports both static claim validation and dynamic resource-based validation.
*
* @param accessRules Array of rule groups
* @param userClaims Object containing user's claims
* @param context Context object passed to dynamic validators
* @throws {Error} If accessRules is not an array
* @throws {Error} If user's claims don't match any rule group
* @returns Returns true if validation passes
*/
export function validateClaim(
accessRules: AccessRule[],
userClaims: Claims,
context?: ValidationContext
): Promise<boolean>;
/**
* Creates an access rule builder for constructing authorization rules.
* The builder follows a fluent interface pattern allowing method chaining
* for creating complex access conditions.
*
* @returns Rule builder object with methods for building access rules
*
* @example
* // Static only - single condition
* const adminRule = createAccessRule()
* .addCondition('role', 'admin')
* .build();
*
* @example
* // Mixed - static and dynamic conditions
* const complexRule = createAccessRule()
* .addCondition('role', 'business')
* .addDynamicCondition(({ claims, resources }) => {
* const businessIds = claims.businessOwner || [];
* return businessIds.includes(resources.business?.id);
* })
* .build();
*/
export function createAccessRule(): AccessRuleBuilder;
}