UNPKG

mikroauth

Version:

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

553 lines (540 loc) 15.8 kB
import { PikoDB } from 'pikodb'; /** * @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 CreateTokenRequest { email: string; username?: string; role?: string; ip?: string; } 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; metadata?: Record<string, any>; appUrl?: string; subject?: string; } interface MagicLinkRequest { email: string; ip?: string; metadata?: Record<string, any>; appUrl?: string; subject?: 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. * @param metadata - Optional metadata object that can be used to customize the email content. * @returns The formatted text or HTML content for the email. */ type MagicLinkTemplate = (magicLink: string, expiryMinutes: number, metadata?: Record<string, any>) => 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 Creates credentials/tokens directly without sending an email. * This method is useful for programmatic use cases such as SSO integrations, * where you need to authenticate a user and obtain tokens directly without * going through the magic link flow. */ createToken(params: CreateTokenRequest): Promise<TokenResponse>; /** * @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 PikoDB implementation of the StorageProvider interface. * Provides lightweight, reliable key-value storage with optional encryption. */ declare class PikoDBProvider implements StorageProvider { private readonly db; private readonly encryption?; private readonly PREFIX_KV; private readonly PREFIX_COLLECTION; private readonly TABLE_NAME; constructor(pikoDB: PikoDB, encryptionKey?: string); /** * @description Start the PikoDB 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. * Supports wildcards: * (any characters) and ? (single character). */ 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[]>; } /** * @description Resend email provider using their HTTP API. * @see https://resend.com/docs/api-reference/emails/send-email */ declare class ResendProvider implements EmailProvider { private readonly apiKey; private readonly debug; constructor(config: { apiKey: string; debug?: boolean; }); /** * @description Send an email using the Resend API. */ sendMail(message: EmailMessage): Promise<void>; } /** * @description Brevo (formerly Sendinblue) email provider using their HTTP API. * @see https://developers.brevo.com/docs/send-a-transactional-email */ declare class BrevoProvider implements EmailProvider { private readonly apiKey; private readonly debug; constructor(config: { apiKey: string; debug?: boolean; }); /** * @description Send an email using the Brevo API. */ sendMail(message: EmailMessage): Promise<void>; } /** * @description Postmark email provider using their HTTP API. * @see https://postmarkapp.com/developer/api/email-api */ declare class PostmarkProvider implements EmailProvider { private readonly serverToken; private readonly messageStream; private readonly debug; constructor(config: { serverToken: string; messageStream?: string; debug?: boolean; }); /** * @description Send an email using the Postmark API. */ sendMail(message: EmailMessage): Promise<void>; } /** * @description SendGrid email provider using their v3 HTTP API. * @see https://www.twilio.com/docs/sendgrid/api-reference/mail-send/mail-send */ declare class SendGridProvider implements EmailProvider { private readonly apiKey; private readonly debug; constructor(config: { apiKey: string; debug?: boolean; }); /** * @description Send an email using the SendGrid v3 API. */ sendMail(message: EmailMessage): Promise<void>; } /** * @description AWS SES email provider using the SES v2 HTTP API. * @see https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_SendEmail.html */ declare class AWSESProvider implements EmailProvider { private readonly accessKeyId; private readonly secretAccessKey; private readonly region; private readonly debug; constructor(config: { accessKeyId: string; secretAccessKey: string; region: string; debug?: boolean; }); /** * @description Send an email using the AWS SES v2 API. */ sendMail(message: EmailMessage): Promise<void>; /** * @description Calculate SHA256 hash. */ private sha256; /** * @description Calculate HMAC SHA256. */ private hmacSha256; /** * @description Get signing key for AWS Signature V4. */ private getSignatureKey; } export { AWSESProvider, BrevoProvider, type CreateTokenRequest, type EmailProvider, InMemoryEmailProvider, InMemoryStorageProvider, MikroAuth, MikroMailProvider, PikoDBProvider, PostmarkProvider, ResendProvider, SendGridProvider, type StorageProvider };