UNPKG

alepha

Version:

Alepha is a convention-driven TypeScript framework for building robust, end-to-end type-safe applications, from serverless APIs to full-stack React apps.

597 lines (594 loc) 19 kB
import * as _alepha_core2 from "alepha"; import * as _alepha_core3 from "alepha"; import * as _alepha_core1 from "alepha"; import * as _alepha_core0 from "alepha"; import { Alepha, Descriptor, KIND, Static } from "alepha"; import { DateTimeProvider, Duration, DurationLike } from "alepha/datetime"; import { CryptoKey, FlattenedJWSInput, JSONWebKeySet, JWSHeaderParameters, JWTHeaderParameters, JWTPayload, JWTVerifyResult, KeyObject } from "jose"; import * as _sinclair_typebox0 from "@sinclair/typebox"; import * as _sinclair_typebox13 from "@sinclair/typebox"; import * as _sinclair_typebox23 from "@sinclair/typebox"; import { JWTVerifyOptions } from "jose/jwt/verify"; //#region src/schemas/userAccountInfoSchema.d.ts declare const userAccountInfoSchema: _sinclair_typebox0.TObject<{ id: _sinclair_typebox0.TString; name: _sinclair_typebox0.TOptional<_sinclair_typebox0.TString>; email: _sinclair_typebox0.TOptional<_sinclair_typebox0.TString>; picture: _sinclair_typebox0.TOptional<_sinclair_typebox0.TString>; organizations: _sinclair_typebox0.TOptional<_sinclair_typebox0.TArray<_sinclair_typebox0.TString>>; roles: _sinclair_typebox0.TOptional<_sinclair_typebox0.TArray<_sinclair_typebox0.TString>>; }>; type UserAccountInfo = Static<typeof userAccountInfoSchema>; //# sourceMappingURL=userAccountInfoSchema.d.ts.map //#endregion //#region src/interfaces/UserAccountToken.d.ts interface UserAccountToken extends UserAccountInfo { /** * Access token for the user. */ token?: string; realm?: string; /** * Is user dedicated to his own resources for this scope ? * Mostly, Admin is false and Customer is true. */ ownership?: string | boolean; } //# sourceMappingURL=UserAccountToken.d.ts.map //#endregion //#region src/schemas/permissionSchema.d.ts declare const permissionSchema: _sinclair_typebox13.TObject<{ name: _sinclair_typebox13.TString; group: _sinclair_typebox13.TOptional<_sinclair_typebox13.TString>; description: _sinclair_typebox13.TOptional<_sinclair_typebox13.TString>; method: _sinclair_typebox13.TOptional<_sinclair_typebox13.TString>; path: _sinclair_typebox13.TOptional<_sinclair_typebox13.TString>; }>; type Permission = Static<typeof permissionSchema>; //# sourceMappingURL=permissionSchema.d.ts.map //#endregion //#region src/schemas/roleSchema.d.ts declare const roleSchema: _sinclair_typebox23.TObject<{ name: _sinclair_typebox23.TString; description: _sinclair_typebox23.TOptional<_sinclair_typebox23.TString>; default: _sinclair_typebox23.TOptional<_sinclair_typebox23.TBoolean>; permissions: _sinclair_typebox23.TArray<_sinclair_typebox23.TObject<{ name: _sinclair_typebox23.TString; ownership: _sinclair_typebox23.TOptional<_sinclair_typebox23.TBoolean>; exclude: _sinclair_typebox23.TOptional<_sinclair_typebox23.TArray<_sinclair_typebox23.TString>>; }>>; }>; type Role = Static<typeof roleSchema>; //# sourceMappingURL=roleSchema.d.ts.map //#endregion //#region src/providers/JwtProvider.d.ts /** * Provides utilities for working with JSON Web Tokens (JWT). */ declare class JwtProvider { protected readonly log: _alepha_core2.Logger; protected readonly keystore: KeyLoaderHolder[]; protected readonly dateTimeProvider: DateTimeProvider; protected readonly encoder: TextEncoder; /** * Adds a key loader to the embedded keystore. * * @param name * @param secretKeyOrJwks */ setKeyLoader(name: string, secretKeyOrJwks: string | JSONWebKeySet): void; /** * Retrieves the payload from a JSON Web Token (JWT). * * @param token - The JWT to extract the payload from. * * @return A Promise that resolves with the payload object from the token. */ parse(token: string, keyName?: string, options?: JWTVerifyOptions): Promise<JwtParseResult>; /** * Creates a JWT token with the provided payload and secret key. * * @param payload - The payload to be encoded in the token. * It should include the `realm_access` property which contains an array of roles. * @param keyName - The name of the key to use when signing the token. * * @returns The signed JWT token. */ create(payload: ExtendedJWTPayload, keyName?: string, signOptions?: JwtSignOptions): Promise<string>; /** * Determines if the provided key is a secret key. * * @param key * @protected */ protected isSecretKey(key: string): boolean; } type KeyLoader = (protectedHeader?: JWSHeaderParameters, token?: FlattenedJWSInput) => Promise<CryptoKey | KeyObject>; interface KeyLoaderHolder { name: string; keyLoader: KeyLoader; secretKey?: string; } interface JwtSignOptions { header?: Partial<JWTHeaderParameters>; } interface ExtendedJWTPayload extends JWTPayload { name?: string; roles?: string[]; email?: string; organizations?: string[]; realm_access?: { roles: string[]; }; } interface JwtParseResult { keyName: string; result: JWTVerifyResult<ExtendedJWTPayload>; } //# sourceMappingURL=JwtProvider.d.ts.map //#endregion //#region src/providers/SecurityProvider.d.ts declare const envSchema: _alepha_core3.TObject<{ SECURITY_SECRET_KEY: _alepha_core3.TString; }>; declare module "alepha" { interface Env extends Partial<Static<typeof envSchema>> {} } declare class SecurityProvider { protected readonly UNKNOWN_USER_NAME = "Unknown User"; protected readonly PERMISSION_REGEXP: RegExp; protected readonly PERMISSION_REGEXP_WILDCARD: RegExp; protected readonly log: _alepha_core3.Logger; protected readonly jwt: JwtProvider; protected readonly env: { SECURITY_SECRET_KEY: string; }; protected readonly alepha: Alepha; /** * The permissions configured for the security provider. */ protected readonly permissions: Permission[]; /** * The realms configured for the security provider. */ protected readonly realms: Realm[]; protected configure: _alepha_core3.HookDescriptor<"start">; protected ready: _alepha_core3.HookDescriptor<"ready">; /** * Adds a role to one or more realms. * * @param role * @param realms */ createRole(role: Role, ...realms: string[]): Role; /** * Adds a permission to the security provider. * * @param raw - The permission to add. */ createPermission(raw: Permission | string): Permission; createRealm(realm: Realm): void; /** * Updates the roles for a realm then synchronizes the user account provider if available. * * Only available when the app is started. * * @param realm - The realm to update the roles for. * @param roles - The roles to update. */ updateRealm(realm: string, roles: Role[]): Promise<void>; /** * Creates a user account from the provided payload. * * @param payload - The payload to create the user account from. * @param [realmName] - The realm containing the roles. Default is all. * * @returns The user info created from the payload. */ createUserFromPayload(payload: JWTPayload, realmName?: string): UserAccountInfo; /** * Checks if the user has the specified permission. * * Bonus: we check also if the user has "ownership" flag. * * @param permissionLike - The permission to check for. * @param roleEntries - The roles to check for the permission. */ checkPermission(permissionLike: string | Permission, ...roleEntries: string[]): SecurityCheckResult; /** * Creates a user account from the provided payload. * * @param headerOrToken * @param permissionLike */ createUserFromToken(headerOrToken?: string, options?: { permission?: Permission | string; realm?: string; verify?: JWTVerifyOptions; }): Promise<UserAccountToken>; /** * Checks if a user has a specific role. * * @param roleName - The role to check for. * @param permission - The permission to check for. * @returns True if the user has the role, false otherwise. */ can(roleName: string, permission: string | Permission): boolean; /** * Checks if a user has ownership of a specific permission. */ ownership(roleName: string, permission: string | Permission): string | boolean | undefined; /** * Converts a permission object to a string. * * @param permission */ permissionToString(permission: Permission | string): string; getRealms(): Realm[]; /** * Retrieves the user account from the provided user ID. * * @param realm */ getRoles(realm?: string): Role[]; /** * Returns all permissions. * * @param user - Filter permissions by user. * * @return An array containing all permissions. */ getPermissions(user?: { roles?: Array<Role | string>; realm?: string; }): Permission[]; /** * Retrieves the user ID from the provided payload object. * * @param payload - The payload object from which to extract the user ID. * @return The user ID as a string. */ getIdFromPayload(payload: Record<string, any>): string; /** * Retrieves the roles from the provided payload object. * @param payload - The payload object from which to extract the roles. * @return An array of role strings. */ getRolesFromPayload(payload: Record<string, any>): string[]; getPictureFromPayload(payload: Record<string, any>): string | undefined; getEmailFromPayload(payload: Record<string, any>): string | undefined; /** * Returns the name from the given payload. * * @param payload - The payload object. * @returns The name extracted from the payload, or an empty string if the payload is falsy or no name is found. */ getNameFromPayload(payload: Record<string, any>): string; getOrganizationsFromPayload(payload: Record<string, any>): string[] | undefined; } /** * A realm definition. */ interface Realm { name: string; roles: Role[]; /** * The secret key for the realm. * * Can be also a JWKS URL. */ secret?: string | JSONWebKeySet | (() => string); /** * Attach a user provider to the realm. * * This is useful when you want to use a custom user provider for a specific realm. */ userAccountProvider?: SecurityUserAccountProvider; onLoadUser?: (user: UserAccountInfo) => Promise<void> | void; /** * Function to create a user profile from the raw JWT user data. */ profile?: (raw: Record<string, any>) => UserAccountInfo; } interface SecurityUserAccountProvider { jwks: string | undefined; synchronize(config: RealmConfig): Promise<void>; } interface SecurityCheckResult { isAuthorized: boolean; ownership: string | boolean | undefined; } interface RealmConfig { roles?: Array<Role>; smtp?: { host?: string; }; } //#endregion //#region src/descriptors/$permission.d.ts /** * Create a new permission. */ declare const $permission: { (options?: PermissionDescriptorOptions): PermissionDescriptor; [KIND]: typeof PermissionDescriptor; }; interface PermissionDescriptorOptions { /** * Name of the permission. Use Property name is not provided. */ name?: string; /** * Group of the permission. Use Class name is not provided. */ group?: string; /** * Describe the permission. */ description?: string; } declare class PermissionDescriptor extends Descriptor<PermissionDescriptorOptions> { protected readonly securityProvider: SecurityProvider; get name(): string; get group(): string; protected onInit(): void; /** * Check if the user has the permission. */ can(user: UserAccountInfo): boolean; } //# sourceMappingURL=$permission.d.ts.map //#endregion //#region src/descriptors/$realm.d.ts /** * Create a new realm. */ declare const $realm: { (options: RealmDescriptorOptions): RealmDescriptor; [KIND]: typeof RealmDescriptor; }; type RealmDescriptorOptions = { /** * Define the realm name. * If not provided, it will use the property key. */ name?: string; /** * Short description about the realm. */ description?: string; /** * All roles available in the realm. Role is a string (role name) or a Role object (embedded role). */ roles?: Array<string | Role>; settings?: RealmSettings; /** * Parse the JWT payload to create a user account info. */ profile?: (jwtPayload: Record<string, any>) => UserAccountInfo; } & (RealmInternal | RealmExternal); interface RealmSettings { accessToken?: { /** * Lifetime of the access token. * @default 15 minutes */ expiration?: DurationLike; }; refreshToken?: { /** * Lifetime of the refresh token. * @default 30 days */ expiration?: DurationLike; /** * If true, no refresh token will be created. */ disabled?: boolean; create?: (user: UserAccountInfo, refreshToken?: string) => Promise<{ refresh_token: string; expires_in: number; }>; }; } type RealmInternal = { /** * Internal secret to sign JWT tokens and verify them. */ secret: string; }; interface RealmExternal { /** * URL to the JWKS (JSON Web Key Set) to verify JWT tokens from external providers. */ jwks: (() => string) | JSONWebKeySet; /** * Attach a user account provider to the realm to manage roles. * * For example, you can use a KeycloakUserProvider to automatically create/update realm roles inside Keycloak. */ userAccountProvider?: SecurityUserAccountProvider | (() => SecurityUserAccountProvider); } declare class RealmDescriptor extends Descriptor<RealmDescriptorOptions> { protected readonly securityProvider: SecurityProvider; protected readonly dateTimeProvider: DateTimeProvider; protected readonly jwt: JwtProvider; protected readonly log: _alepha_core1.Logger; get name(): string; get accessTokenExpiration(): Duration; get refreshTokenExpiration(): Duration; protected onInit(): void; /** * Get all roles in the realm. */ getRoles(): Role[]; /** * Set all roles in the realm. */ setRoles(roles: Role[]): Promise<void>; /** * Get a role by name, throws an error if not found. */ getRoleByName(name: string): Role; parseToken(token: string): Promise<JWTPayload>; /** * Create a token for the subject. */ createToken(user: UserAccountInfo, refreshToken?: string): Promise<AccessTokenResponse>; } interface CreateTokenOptions { sub: string; roles?: string[]; email?: string; } interface AccessTokenResponse { access_token: string; token_type: string; expires_in?: number; issued_at: number; refresh_token?: string; refresh_token_expires_in?: number; scope?: string; } //# sourceMappingURL=$realm.d.ts.map //#endregion //#region src/descriptors/$role.d.ts /** * Create a new role. */ declare const $role: { (options?: RoleDescriptorOptions): RoleDescriptor; [KIND]: typeof RoleDescriptor; }; interface RoleDescriptorOptions { /** * Name of the role. */ name?: string; /** * Describe the role. */ description?: string; realm?: string | RealmDescriptor; permissions?: Array<string | { name: string; ownership?: boolean; }>; } declare class RoleDescriptor extends Descriptor<RoleDescriptorOptions> { protected readonly securityProvider: SecurityProvider; get name(): string; protected onInit(): void; /** * Get the realm of the role. */ get realm(): string | RealmDescriptor | undefined; } //# sourceMappingURL=$role.d.ts.map //#endregion //#region src/descriptors/$serviceAccount.d.ts /** * Allow to get an access token for a service account. * * You have some options to configure the service account: * - a OAUTH2 URL using client credentials grant type * - a JWT secret shared between the services * * @example * ```ts * import { $serviceAccount } from "alepha/security"; * * class MyService { * serviceAccount = $serviceAccount({ * oauth2: { * url: "https://example.com/oauth2/token", * clientId: "your-client-id", * clientSecret: "your-client-secret", * } * }); * * async fetchData() { * const token = await this.serviceAccount.token(); * // or * const response = await this.serviceAccount.fetch("https://api.example.com/data"); * } * } * ``` */ declare const $serviceAccount: (options: ServiceAccountDescriptorOptions) => ServiceAccountDescriptor; type ServiceAccountDescriptorOptions = { gracePeriod?: number; } & ({ oauth2: Oauth2ServiceAccountDescriptorOptions; } | { realm: RealmDescriptor; user: UserAccountInfo; }); interface Oauth2ServiceAccountDescriptorOptions { /** * Get Token URL. */ url: string; /** * Client ID. */ clientId: string; /** * Client Secret. */ clientSecret: string; } interface ServiceAccountDescriptor { token: () => Promise<string>; } interface ServiceAccountStore { response?: AccessTokenResponse; } //# sourceMappingURL=$serviceAccount.d.ts.map //#endregion //#region src/errors/InvalidPermissionError.d.ts declare class InvalidPermissionError extends Error { constructor(name: string); } //# sourceMappingURL=InvalidPermissionError.d.ts.map //#endregion //#region src/errors/SecurityError.d.ts declare class SecurityError extends Error { name: string; readonly status = 403; } //# sourceMappingURL=SecurityError.d.ts.map //#endregion //#region src/providers/CryptoProvider.d.ts declare class CryptoProvider { hashPassword(password: string): Promise<string>; verifyPassword(password: string, stored: string): Promise<boolean>; } //# sourceMappingURL=CryptoProvider.d.ts.map //#endregion //#region src/index.d.ts declare module "alepha" { interface Hooks { "security:user:created": { realm: string; user: UserAccountInfo; }; } } /** * Provides comprehensive authentication and authorization capabilities with JWT tokens, role-based access control, and user management. * * The security module enables building secure applications using descriptors like `$realm`, `$role`, and `$permission` * on class properties. It offers JWT-based authentication, fine-grained permissions, service accounts, and seamless * integration with various authentication providers and user management systems. * * @see {@link $realm} * @see {@link $role} * @see {@link $permission} * @module alepha.security */ declare const AlephaSecurity: _alepha_core0.Service<_alepha_core0.Module>; //# sourceMappingURL=index.d.ts.map //#endregion export { $permission, $realm, $role, $serviceAccount, AccessTokenResponse, AlephaSecurity, CreateTokenOptions, CryptoProvider, ExtendedJWTPayload, InvalidPermissionError, JwtParseResult, JwtProvider, JwtSignOptions, KeyLoader, KeyLoaderHolder, Oauth2ServiceAccountDescriptorOptions, Permission, PermissionDescriptor, PermissionDescriptorOptions, Realm, RealmConfig, RealmDescriptor, RealmDescriptorOptions, RealmExternal, RealmInternal, RealmSettings, Role, RoleDescriptor, RoleDescriptorOptions, SecurityCheckResult, SecurityError, SecurityProvider, SecurityUserAccountProvider, ServiceAccountDescriptor, ServiceAccountDescriptorOptions, ServiceAccountStore, UserAccountInfo, UserAccountToken, permissionSchema, roleSchema, userAccountInfoSchema }; //# sourceMappingURL=index.d.ts.map