import { ErrorCode } from '../types/error-codes'; import { AppError } from './app-error'; import type { ErrorContext } from './app-error'; type AuthErrorCode = | ErrorCode.AUTHENTICATION_REQUIRED | ErrorCode.INVALID_TOKEN | ErrorCode.TOKEN_EXPIRED | ErrorCode.PERMISSION_DENIED | ErrorCode.RESOURCE_NOT_OWNED; /** * Error for authentication and authorization failures. * HTTP Status: 401 (auth) or 403 (authorization) * * @example * ```typescript * // Authentication errors (401) * return err(AuthError.unauthorized()); * return err(AuthError.invalidToken('Token has been revoked')); * return err(AuthError.tokenExpired()); * * // Authorization errors (403) * return err(AuthError.forbidden('Admin access required')); * return err(AuthError.notOwned('Story', storyId)); * ``` */ export class AuthError extends AppError { constructor(code: AuthErrorCode, message: string, context?: ErrorContext) { super({ code, message, context }); this.name = 'AuthError'; } /** * Create an error for missing authentication. * HTTP 401 Unauthorized */ static unauthorized(message = 'Authentication required'): AuthError { return new AuthError(ErrorCode.AUTHENTICATION_REQUIRED, message); } /** * Create an error for an invalid token. * HTTP 401 Unauthorized */ static invalidToken(message = 'Invalid or malformed token'): AuthError { return new AuthError(ErrorCode.INVALID_TOKEN, message); } /** * Create an error for an expired token. * HTTP 401 Unauthorized */ static tokenExpired(message = 'Token has expired'): AuthError { return new AuthError(ErrorCode.TOKEN_EXPIRED, message); } /** * Create an error for insufficient permissions. * HTTP 403 Forbidden */ static forbidden(message = 'Permission denied'): AuthError { return new AuthError(ErrorCode.PERMISSION_DENIED, message); } /** * Create an error when a user tries to access a resource they don't own. * HTTP 403 Forbidden * * @param resourceType - Type of resource (e.g., 'Story', 'Character') * @param resourceId - ID of the resource */ static notOwned(resourceType: string, resourceId: string): AuthError { return new AuthError(ErrorCode.RESOURCE_NOT_OWNED, `${resourceType} does not belong to you`, { resourceType, resourceId, }); } }