UNPKG

mikroauth

Version:

Dead-simple magic link authentication that is useful, lightweight, and uncluttered.

427 lines (419 loc) 11.9 kB
import { MikroDB } from 'mikrodb'; /** * @description Storage interface for MikroAuth. */ interface StorageProvider { get(key: string): Promise<string | null>; set(key: string, value: string, expirySeconds?: number): Promise<void>; delete(key: string): Promise<void>; addToCollection(collectionKey: string, item: string, expirySeconds?: number): Promise<void>; removeFromCollection(collectionKey: string, item: string): Promise<void>; getCollection(collectionKey: string): Promise<string[]>; getCollectionSize(collectionKey: string): Promise<number>; removeOldestFromCollection(collectionKey: string): Promise<string | null>; findKeys(pattern: string): Promise<string[]>; } interface EmailMessage { from: string; to: string | string[]; cc?: string | string[]; bcc?: string | string[]; subject: string; text: string; html: string; } interface EmailProvider { sendMail(message: EmailMessage): Promise<void>; } interface JwtPayload { sub: string; jti: string; iat?: number; exp?: number; lastLogin: number; metadata?: { ip?: string; }; } interface JwtPayload { sub: string; username?: string; email?: string; role?: string; jti: string; iat?: number; exp?: number; lastLogin: number; metadata?: { ip?: string; }; } interface RequestContext { body?: Record<string, any>; query?: Record<string, any>; headers?: Record<string, any>; ip?: string; log?: { error: (message: string, error: Error) => void; info?: (message: string, ...args: any[]) => void; }; user?: { email: string; }; } interface RequestContext { body?: Record<string, any>; query?: Record<string, any>; headers?: Record<string, any>; ip?: string; log?: { error: (message: string, error: Error) => void; info?: (message: string, ...args: any[]) => void; }; user?: { email: string; }; } interface MagicLinkRequest { email: string; ip?: string; } interface MagicLinkRequest { email: string; ip?: string; } interface VerifyTokenRequest { token: string; email: string; } interface VerifyTokenRequest { token: string; email: string; } interface SessionInfo { id: string; createdAt: number; lastLogin: number; lastUsed?: number; metadata?: { ip?: string; }; isCurrentSession?: boolean; } interface SessionInfo { id: string; createdAt: number; lastLogin: number; lastUsed?: number; metadata?: { ip?: string; }; isCurrentSession?: boolean; } interface TokenResponse { accessToken: string; refreshToken: string; exp: number; tokenType: string; } interface TokenResponse { accessToken: string; refreshToken: string; exp: number; tokenType: string; } type UserIdentity = { id: string; email: string; username: string; role: string; }; type AuthConfiguration = { /** * The JSON Web Token secret to use. */ jwtSecret: string; /** * How many seconds until a magic link expires? */ magicLinkExpirySeconds: number; /** * How many seconds until the JSON Web Token expires? */ jwtExpirySeconds: number; /** * How many seconds until the refresh token expires? */ refreshTokenExpirySeconds: number; /** * How many sessions can be active? */ maxActiveSessions: number; /** * The URL to the application we are authenticating towards. */ appUrl: string; /** * Custom email templates. */ templates: EmailTemplateConfiguration | null | undefined; /** * Use debug mode? */ debug: boolean; }; /** * @description Options for configuring MikroAuth. */ type AuthOptions = Partial<AuthConfiguration>; type EmailConfiguration = { emailSubject: string; user: string; host: string; password: string; port: number; secure: boolean; maxRetries: number; debug: boolean; }; type EmailOptions = Partial<EmailConfiguration>; /** * @description Configuration for magic link email templates. * Defines the structure for text and HTML versions of authentication emails. */ type EmailTemplateConfiguration = { textVersion: MagicLinkTemplate; htmlVersion: MagicLinkTemplate; }; /** * @description Function that generates the text and HTML version of the email. * @param magicLink - The authentication link to include in the email. * @param expiryMinutes - The number of minutes until the link expires. * @returns The formatted text or HTML content for the email. */ type MagicLinkTemplate = (magicLink: string, expiryMinutes: number) => string; /** * @description MikroAuth is a dead-simple "Magic Link" * authentication service that works with your storage and email. */ declare class MikroAuth { private readonly config; private readonly email; private readonly storage; private readonly jwtService; private readonly templates; constructor(options: { auth: AuthOptions; email: EmailOptions; }, emailProvider?: EmailProvider, storageProvider?: StorageProvider); /** * @description Verify that we are not using defaults. */ private checkIfUsingDefaultCredentialsInProduction; /** * @description Generates a secure token. */ generateToken(email: string): string; /** * @description Generate a JWT for an authenticated user. */ generateJsonWebToken(user: UserIdentity): string; /** * @description Generates a refresh token. */ private generateRefreshToken; /** * @description Tracks a user session in storage. */ private trackSession; /** * @description Creates the actual magic link URL, using the token and email. */ private generateMagicLinkUrl; /** * @description Creates and sends a magic link to the user. */ createMagicLink(params: MagicLinkRequest): Promise<{ message: string; }>; /** * @description Verifies a magic link token and creates session tokens. */ verifyToken(params: VerifyTokenRequest): Promise<TokenResponse>; /** * @description Refreshes an access token using a refresh token. */ refreshAccessToken(refreshToken: string): Promise<TokenResponse>; /** * @description Verifies a JWT token. */ verify(token: string): JwtPayload; /** * @description Logs out a user by revoking their session token. */ logout(refreshToken: string): Promise<{ message: string; }>; /** * @description Gets all active sessions for a user. */ getSessions(request: RequestContext): Promise<{ sessions: SessionInfo[]; }>; /** * @description Revokes all active sessions for a user. */ revokeSessions(request: RequestContext): Promise<{ message: string; }>; /** * @description Middleware to authenticate requests with JWT tokens. */ authenticate(request: any, next: (error?: Error) => void): void; } /** * @description MikroDB implementation of the StorageProvider interface. */ declare class MikroDBProvider implements StorageProvider { private readonly db; private readonly PREFIX_KV; private readonly PREFIX_COLLECTION; private readonly TABLE_NAME; constructor(mikroDb: MikroDB); /** * @description Start the MikroDB instance. */ start(): Promise<void>; /** * @description Close the database connection and clean up resources. */ close(): Promise<void>; /** * @description Set a value with optional expiry. */ set(key: string, value: string, expirySeconds?: number): Promise<void>; /** * @description Get a value by key. */ get(key: string): Promise<string | null>; /** * @description Delete a key. */ delete(key: string): Promise<void>; /** * @description Add an item to a collection. */ addToCollection(collectionKey: string, item: string, expirySeconds?: number): Promise<void>; /** * @description Remove an item from a collection. */ removeFromCollection(collectionKey: string, item: string): Promise<void>; /** * @description Get all items in a collection. */ getCollection(collectionKey: string): Promise<string[]>; /** * @description Get the number of items in a collection. */ getCollectionSize(collectionKey: string): Promise<number>; /** * @description Remove and return the oldest item from a collection. */ removeOldestFromCollection(collectionKey: string): Promise<string | null>; /** * @description Find keys matching a pattern. */ findKeys(pattern: string): Promise<string[]>; } /** * @description Use MikroMail as the email provider. * @see https://github.com/mikaelvesavuori/mikromail */ declare class MikroMailProvider implements EmailProvider { private readonly email; private readonly sender; constructor(config: Record<string, any>); /** * @description Send an email using the MikroMail provider. */ sendMail(message: EmailMessage): Promise<void>; } /** * @description Mock implementation of EmailProvider * for development and testing purposes. */ declare class InMemoryEmailProvider implements EmailProvider { private options?; private sentEmails; constructor(options?: { logToConsole?: boolean; onSend?: (message: EmailMessage) => void; } | undefined); /** * @description Send an email with the in-memory provider. */ sendMail(message: EmailMessage): Promise<void>; /** * @description Get all sent emails (for testing purposes). */ getSentEmails(): EmailMessage[]; /** * @description Clear sent emails history (for testing purposes). */ clearSentEmails(): void; } /** * @description In-memory implementation of the StorageProvider interface. */ declare class InMemoryStorageProvider implements StorageProvider { private data; private collections; private expiryEmitter; private expiryCheckInterval; constructor(checkIntervalMs?: number); /** * @description Clean up resources. */ destroy(): void; /** * @description Check for and remove expired items. */ private checkExpiredItems; /** * @description Set a value with optional expiry. */ set(key: string, value: string, expirySeconds?: number): Promise<void>; /** * @description Get a value by key. */ get(key: string): Promise<string | null>; /** * @description Delete a key. */ delete(key: string): Promise<void>; /** * @description Add an item to a collection. */ addToCollection(collectionKey: string, item: string, expirySeconds?: number): Promise<void>; /** * @description Remove an item from a collection. */ removeFromCollection(collectionKey: string, item: string): Promise<void>; /** * @description Get all items in a collection. */ getCollection(collectionKey: string): Promise<string[]>; /** * @description Get the number of items in a collection. */ getCollectionSize(collectionKey: string): Promise<number>; /** * @description Remove and return the oldest item from a collection. */ removeOldestFromCollection(collectionKey: string): Promise<string | null>; /** * @description Find keys matching a pattern (simple wildcard support). */ findKeys(pattern: string): Promise<string[]>; } export { type EmailProvider, InMemoryEmailProvider, InMemoryStorageProvider, MikroAuth, MikroDBProvider, MikroMailProvider, type StorageProvider };