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.
579 lines (578 loc) • 17.8 kB
TypeScript
import * as _alepha_core1 from "alepha";
import { Alepha, Descriptor, KIND, Static } from "alepha";
import * as _alepha_logger0 from "alepha/logger";
import { DateTimeProvider, Duration, DurationLike } from "alepha/datetime";
import { CryptoKey, FlattenedJWSInput, JSONWebKeySet, JWSHeaderParameters, JWTHeaderParameters, JWTPayload, JWTVerifyResult, KeyObject } from "jose";
import * as typebox0 from "typebox";
import { JWTVerifyOptions } from "jose/jwt/verify";
//#region src/schemas/userAccountInfoSchema.d.ts
declare const userAccountInfoSchema: typebox0.TObject<{
id: typebox0.TString;
name: typebox0.TOptional<typebox0.TString>;
email: typebox0.TOptional<typebox0.TString>;
username: typebox0.TOptional<typebox0.TString>;
picture: typebox0.TOptional<typebox0.TString>;
sessionId: typebox0.TOptional<typebox0.TString>;
organizations: typebox0.TOptional<typebox0.TArray<typebox0.TString>>;
roles: typebox0.TOptional<typebox0.TArray<typebox0.TString>>;
}>;
type UserAccount = Static<typeof userAccountInfoSchema>;
//#endregion
//#region src/interfaces/UserAccountToken.d.ts
/**
* Add contextual metadata to a user account info.
* E.g. UserAccountToken is a UserAccountInfo during a request.
*/
interface UserAccountToken extends UserAccount {
/**
* Access token for the user.
*/
token?: string;
/**
* Realm name of the user.
*/
realm?: string;
/**
* Is user dedicated to his own resources for this scope ?
* Mostly, Admin is false and Customer is true.
*/
ownership?: string | boolean;
}
//#endregion
//#region src/schemas/permissionSchema.d.ts
declare const permissionSchema: typebox0.TObject<{
name: typebox0.TString;
group: typebox0.TOptional<typebox0.TString>;
description: typebox0.TOptional<typebox0.TString>;
method: typebox0.TOptional<typebox0.TString>;
path: typebox0.TOptional<typebox0.TString>;
}>;
type Permission = Static<typeof permissionSchema>;
//#endregion
//#region src/schemas/roleSchema.d.ts
declare const roleSchema: typebox0.TObject<{
name: typebox0.TString;
description: typebox0.TOptional<typebox0.TString>;
default: typebox0.TOptional<typebox0.TBoolean>;
permissions: typebox0.TArray<typebox0.TObject<{
name: typebox0.TString;
ownership: typebox0.TOptional<typebox0.TBoolean>;
exclude: typebox0.TOptional<typebox0.TArray<typebox0.TString>>;
}>>;
}>;
type Role = Static<typeof roleSchema>;
//#endregion
//#region src/providers/JwtProvider.d.ts
/**
* Provides utilities for working with JSON Web Tokens (JWT).
*/
declare class JwtProvider {
protected readonly log: _alepha_logger0.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 {
sid?: string;
name?: string;
roles?: string[];
email?: string;
organizations?: string[];
realm_access?: {
roles: string[];
};
}
interface JwtParseResult {
keyName: string;
result: JWTVerifyResult<ExtendedJWTPayload>;
}
//#endregion
//#region src/providers/SecurityProvider.d.ts
declare const envSchema: _alepha_core1.TObject<{
SECURITY_SECRET_KEY: _alepha_core1.TString;
}>;
declare module "alepha" {
interface Env extends Partial<Static<typeof envSchema>> {}
}
declare class SecurityProvider {
protected readonly UNKNOWN_USER_NAME = "Anonymous User";
protected readonly PERMISSION_REGEXP: RegExp;
protected readonly PERMISSION_REGEXP_WILDCARD: RegExp;
protected readonly log: _alepha_logger0.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 start: _alepha_core1.HookDescriptor<"start">;
/**
* 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): UserAccount;
/**
* 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;
getSessionIdFromPayload(payload: Record<string, any>): string | undefined;
/**
* 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;
getUsernameFromPayload(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);
/**
* Create the user account info based on the raw JWT payload.
* By default, SecurityProvider has his own implementation, but this method allow to override it.
*/
profile?: (raw: Record<string, any>) => UserAccount;
}
interface SecurityCheckResult {
isAuthorized: boolean;
ownership: string | boolean | undefined;
}
//#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: UserAccount): boolean;
}
//#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>) => UserAccount;
} & (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;
};
onCreateSession?: (user: UserAccount, config: {
expiresIn: number;
}) => Promise<{
refreshToken: string;
sessionId?: string;
}>;
onRefreshSession?: (refreshToken: string) => Promise<{
user: UserAccount;
expiresIn: number;
sessionId?: string;
}>;
onDeleteSession?: (refreshToken: string) => Promise<void>;
}
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;
}
declare class RealmDescriptor extends Descriptor<RealmDescriptorOptions> {
protected readonly securityProvider: SecurityProvider;
protected readonly dateTimeProvider: DateTimeProvider;
protected readonly jwt: JwtProvider;
protected readonly log: _alepha_logger0.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: UserAccount, refreshToken?: {
sid?: string;
refresh_token?: string;
refresh_token_expires_in?: number;
}): Promise<AccessTokenResponse>;
refreshToken(refreshToken: string, accessToken?: string): Promise<{
tokens: AccessTokenResponse;
user: UserAccount;
}>;
}
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;
}
//#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;
}
//#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: UserAccount;
});
interface Oauth2ServiceAccountDescriptorOptions {
/**
* Get Token URL.
*/
url: string;
/**
* Client ID.
*/
clientId: string;
/**
* Client Secret.
*/
clientSecret: string;
}
interface ServiceAccountDescriptor {
token: () => Promise<string>;
}
interface ServiceAccountStore {
response?: AccessTokenResponse;
}
//#endregion
//#region src/errors/InvalidPermissionError.d.ts
declare class InvalidPermissionError extends Error {
constructor(name: string);
}
//#endregion
//#region src/errors/SecurityError.d.ts
declare class SecurityError extends Error {
name: string;
readonly status = 403;
}
//#endregion
//#region src/providers/CryptoProvider.d.ts
declare class CryptoProvider {
hashPassword(password: string): Promise<string>;
verifyPassword(password: string, stored: string): Promise<boolean>;
}
//#endregion
//#region src/index.d.ts
declare module "alepha" {
interface Hooks {
"security:user:created": {
realm: string;
user: UserAccount;
};
}
}
/**
* 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_core1.Service<_alepha_core1.Module>;
//#endregion
export { $permission, $realm, $role, $serviceAccount, AccessTokenResponse, AlephaSecurity, CreateTokenOptions, CryptoProvider, ExtendedJWTPayload, InvalidPermissionError, JwtParseResult, JwtProvider, JwtSignOptions, KeyLoader, KeyLoaderHolder, Oauth2ServiceAccountDescriptorOptions, Permission, PermissionDescriptor, PermissionDescriptorOptions, Realm, RealmDescriptor, RealmDescriptorOptions, RealmExternal, RealmInternal, RealmSettings, Role, RoleDescriptor, RoleDescriptorOptions, SecurityCheckResult, SecurityError, SecurityProvider, ServiceAccountDescriptor, ServiceAccountDescriptorOptions, ServiceAccountStore, UserAccount, UserAccountToken, permissionSchema, roleSchema, userAccountInfoSchema };
//# sourceMappingURL=index.d.ts.map