@xcelsior/auth 0.1.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +13 -13
- package/.turbo/turbo-lint.log +2 -2
- package/.turbo/turbo-test.log +3 -7
- package/dist/index.d.mts +263 -34
- package/dist/index.d.ts +263 -34
- package/dist/index.js +306 -182
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +303 -188
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -9
- package/src/email/smtp.ts +3 -2
- package/src/email/types.ts +1 -1
- package/src/index.ts +1 -0
- package/src/middleware/auth.ts +2 -4
- package/src/services/auth.ts +338 -27
- package/src/storage/index.ts +0 -17
- package/src/storage/types.ts +49 -24
- package/src/types/index.ts +57 -6
- package/src/storage/dynamodb.ts +0 -153
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/services/auth.ts","../src/storage/dynamodb.ts","../src/storage/index.ts","../src/email/smtp.ts","../src/email/ses.ts","../src/email/defaultTemplates.ts","../src/email/index.ts","../src/middleware/auth.ts","../src/types/index.ts"],"sourcesContent":["import bcrypt from 'bcryptjs';\nimport jwt from 'jsonwebtoken';\nimport { v4 as uuidv4 } from 'uuid';\nimport type { AuthConfig, User, UserRole } from '../types';\nimport type { IStorageProvider } from '../storage/types';\nimport type { IEmailProvider } from '../email/types';\nimport { createStorageProvider } from '../storage';\nimport { createEmailProvider } from '../email';\n\nexport class AuthService {\n private storage: IStorageProvider;\n private email: IEmailProvider;\n private config: AuthConfig;\n\n constructor(config: AuthConfig) {\n this.config = config;\n this.storage = createStorageProvider(config.storage);\n this.email = createEmailProvider(config.email);\n }\n\n private generateToken(user: User): string {\n // @ts-ignore\n return jwt.sign(\n {\n id: user.id,\n email: user.email,\n roles: user.roles,\n isEmailVerified: user.isEmailVerified,\n },\n this.config.jwt.privateKey,\n {\n algorithm: 'RS256',\n expiresIn: this.config.jwt.expiresIn,\n keyid: this.config.jwt.keyId,\n }\n );\n }\n\n private async hashPassword(password: string): Promise<string> {\n return bcrypt.hash(password, 10);\n }\n\n async signup(\n email: string,\n password: string,\n roles: UserRole[] = ['USER']\n ): Promise<{ user: User; token: string }> {\n const existingUser = await this.storage.getUserByEmail(email);\n if (existingUser) {\n throw new Error('User already exists');\n }\n\n const verificationToken = uuidv4();\n const user: User = {\n id: uuidv4(),\n email,\n passwordHash: await this.hashPassword(password),\n roles,\n isEmailVerified: false,\n verificationToken,\n createdAt: Date.now(),\n updatedAt: Date.now(),\n };\n\n await this.storage.createUser(user);\n await this.email.sendVerificationEmail(email, verificationToken);\n\n const token = this.generateToken(user);\n return { user, token };\n }\n\n async signin(email: string, password: string): Promise<{ user: User; token: string }> {\n const user = await this.storage.getUserByEmail(email);\n if (!user) {\n throw new Error('Invalid credentials');\n }\n\n const isValidPassword = await bcrypt.compare(password, user.passwordHash);\n if (!isValidPassword) {\n throw new Error('Invalid credentials');\n }\n\n if (!user.isEmailVerified) {\n throw new Error('Please verify your email before signing in');\n }\n\n const token = this.generateToken(user);\n return { user, token };\n }\n\n async verifyEmail(token: string): Promise<void> {\n const user = await this.storage.getUserByVerifyEmailToken(token);\n if (!user || user.verificationToken !== token) {\n throw new Error('Invalid verification token');\n }\n\n await this.storage.updateUser(user.id, {\n isEmailVerified: true,\n verificationToken: undefined,\n updatedAt: Date.now(),\n });\n }\n\n async initiatePasswordReset(email: string): Promise<void> {\n const user = await this.storage.getUserByEmail(email);\n if (!user) {\n throw new Error('User not found');\n }\n\n const resetToken = uuidv4();\n const resetExpires = Date.now() + 3600000; // 1 hour\n\n await this.storage.updateUser(user.id, {\n resetPasswordToken: resetToken,\n resetPasswordExpires: resetExpires,\n updatedAt: Date.now(),\n });\n\n await this.email.sendPasswordResetEmail(email, resetToken);\n }\n\n async resetPassword(token: string, newPassword: string): Promise<void> {\n const user = await this.storage.getUserByResetPasswordToken(token);\n if (\n !user ||\n !user.resetPasswordToken ||\n user.resetPasswordToken !== token ||\n !user.resetPasswordExpires ||\n user.resetPasswordExpires < Date.now()\n ) {\n throw new Error('Invalid or expired reset token');\n }\n\n await this.storage.updateUser(user.id, {\n passwordHash: await this.hashPassword(newPassword),\n resetPasswordToken: undefined,\n resetPasswordExpires: undefined,\n updatedAt: Date.now(),\n });\n }\n\n async verifyToken(\n token: string\n ): Promise<{ id: string; email: string; roles: UserRole[]; isEmailVerified: boolean }> {\n try {\n return jwt.verify(token, this.config.jwt.publicKey, { algorithms: ['RS256'] }) as {\n id: string;\n email: string;\n roles: UserRole[];\n isEmailVerified: boolean;\n };\n } catch (_error) {\n throw new Error('Invalid token');\n }\n }\n\n async hasRole(userId: string, requiredRoles: UserRole[]): Promise<boolean> {\n const user = await this.storage.getUserById(userId);\n if (!user) {\n return false;\n }\n\n return requiredRoles.some((role) => user.roles.includes(role));\n }\n}\n","import { DynamoDBClient } from '@aws-sdk/client-dynamodb';\nimport {\n DynamoDBDocumentClient,\n PutCommand,\n GetCommand,\n UpdateCommand,\n DeleteCommand,\n QueryCommand,\n} from '@aws-sdk/lib-dynamodb';\nimport type { User } from '../types';\nimport type { IStorageProvider, DynamoDBConfig } from './types';\n\nexport class DynamoDBStorageProvider implements IStorageProvider {\n private client: DynamoDBDocumentClient;\n private tableName: string;\n\n constructor(config: DynamoDBConfig) {\n const dbClient = new DynamoDBClient({ region: config.region });\n this.client = DynamoDBDocumentClient.from(dbClient, {});\n this.tableName = config.tableName;\n }\n\n async createUser(user: User): Promise<void> {\n await this.client.send(\n new PutCommand({\n TableName: this.tableName,\n Item: user,\n ConditionExpression: 'attribute_not_exists(email)',\n })\n );\n }\n\n async getUserByResetPasswordToken(resetPasswordToken: string): Promise<User | null> {\n const response = await this.client.send(\n new QueryCommand({\n TableName: this.tableName,\n IndexName: 'ResetPasswordTokenIndex',\n KeyConditionExpression: 'resetPasswordToken = :token',\n ExpressionAttributeValues: {\n ':token': resetPasswordToken,\n },\n })\n );\n\n return response.Items?.[0] as User | null;\n }\n\n async getUserByVerifyEmailToken(verifyEmailToken: string): Promise<User | null> {\n const response = await this.client.send(\n new QueryCommand({\n TableName: this.tableName,\n IndexName: 'VerifyEmailTokenIndex',\n KeyConditionExpression: 'verificationToken = :token',\n ExpressionAttributeValues: {\n ':token': verifyEmailToken,\n },\n })\n );\n\n return response.Items?.[0] as User | null;\n }\n\n async getUserById(id: string): Promise<User | null> {\n const response = await this.client.send(\n new GetCommand({\n TableName: this.tableName,\n Key: { id },\n })\n );\n\n return response.Item as User | null;\n }\n\n async getUserByEmail(email: string): Promise<User | null> {\n const response = await this.client.send(\n new QueryCommand({\n TableName: this.tableName,\n IndexName: 'EmailIndex',\n KeyConditionExpression: 'email = :email',\n ExpressionAttributeValues: {\n ':email': email,\n },\n })\n );\n\n return response.Items?.[0] as User | null;\n }\n\n async updateUser(id: string, updates: Partial<User>): Promise<void> {\n const toSet: Record<string, any> = {};\n const toRemove: string[] = [];\n\n // Separate attributes to set and remove\n Object.entries(updates).forEach(([key, value]) => {\n if (value === undefined) {\n toRemove.push(key);\n } else {\n toSet[key] = value;\n }\n });\n\n // If no updates at all, return early\n if (Object.keys(toSet).length === 0 && toRemove.length === 0) {\n return;\n }\n\n // Build the update expression\n const setPart =\n Object.keys(toSet).length > 0\n ? `SET ${Object.keys(toSet)\n .map((key) => `#${key} = :${key}`)\n .join(', ')}`\n : '';\n\n const removePart =\n toRemove.length > 0 ? `REMOVE ${toRemove.map((key) => `#${key}`).join(', ')}` : '';\n\n const updateExpression = [setPart, removePart].filter(Boolean).join(' ');\n\n // Build expression attribute names (needed for both SET and REMOVE)\n const expressionAttributeNames = [...Object.keys(toSet), ...toRemove].reduce(\n (acc, key) => ({ ...acc, [`#${key}`]: key }),\n {}\n );\n\n // Build expression attribute values (only needed for SET)\n const expressionAttributeValues = Object.entries(toSet).reduce(\n (acc, [key, value]) => ({ ...acc, [`:${key}`]: value }),\n {}\n );\n\n await this.client.send(\n new UpdateCommand({\n TableName: this.tableName,\n Key: { id },\n UpdateExpression: updateExpression,\n ExpressionAttributeNames: expressionAttributeNames,\n ...(Object.keys(expressionAttributeValues).length > 0 && {\n ExpressionAttributeValues: expressionAttributeValues,\n }),\n })\n );\n }\n\n async deleteUser(id: string): Promise<void> {\n await this.client.send(\n new DeleteCommand({\n TableName: this.tableName,\n Key: { id },\n })\n );\n }\n}\n","import type { DynamoDBConfig, IStorageProvider, StorageConfig } from './types';\nimport { DynamoDBStorageProvider } from './dynamodb';\n\nexport function createStorageProvider(config: StorageConfig): IStorageProvider {\n switch (config.type) {\n case 'dynamodb':\n return new DynamoDBStorageProvider(config.options as DynamoDBConfig);\n case 'mongodb':\n throw new Error('MongoDB storage provider not implemented yet');\n case 'postgres':\n throw new Error('PostgreSQL storage provider not implemented yet');\n default:\n throw new Error(`Unsupported storage type: ${config.type}`);\n }\n}\n\nexport * from './types';\nexport * from './dynamodb';\n","import nodemailer from 'nodemailer';\nimport type { IEmailProvider, SMTPConfig, EmailTemplates } from './types';\n\nexport class SMTPEmailProvider implements IEmailProvider {\n private transporter: nodemailer.Transporter;\n private config: { from: string; templates: EmailTemplates };\n\n constructor(from: string, config: SMTPConfig, templates: EmailTemplates) {\n this.transporter = nodemailer.createTransport(config);\n this.config = { from, templates };\n }\n\n async sendVerificationEmail(email: string, token: string): Promise<void> {\n const { subject, html } = this.config.templates.verification;\n\n await this.transporter.sendMail({\n from: this.config.from,\n to: email,\n subject,\n html: html(token),\n });\n }\n\n async sendPasswordResetEmail(email: string, token: string): Promise<void> {\n const { subject, html } = this.config.templates.resetPassword;\n\n await this.transporter.sendMail({\n from: this.config.from,\n to: email,\n subject,\n html: html(token),\n });\n }\n\n async verifyConnection(): Promise<boolean> {\n try {\n await this.transporter.verify();\n return true;\n } catch (_error) {\n return false;\n }\n }\n}\n","import { SESv2Client, SendEmailCommand } from '@aws-sdk/client-sesv2';\nimport type { IEmailProvider, SESConfig, EmailTemplates } from './types';\nimport { defaultTemplates } from './defaultTemplates';\n\nexport class SESEmailProvider implements IEmailProvider {\n private client: SESv2Client;\n private config: { from: string; templates: EmailTemplates; sourceArn?: string };\n\n constructor(from: string, config: SESConfig, templates?: EmailTemplates) {\n this.client = new SESv2Client({\n region: config.region,\n credentials: config.credentials,\n });\n this.config = {\n from,\n templates: templates ?? defaultTemplates,\n sourceArn: config.sourceArn,\n };\n }\n\n private async sendEmail(to: string, subject: string, html: string): Promise<void> {\n const command = new SendEmailCommand({\n FromEmailAddress: this.config.from,\n FromEmailAddressIdentityArn: this.config.sourceArn,\n Destination: {\n ToAddresses: [to],\n },\n Content: {\n Simple: {\n Subject: {\n Data: subject,\n Charset: 'UTF-8',\n },\n Body: {\n Html: {\n Data: html,\n Charset: 'UTF-8',\n },\n },\n },\n },\n });\n\n await this.client.send(command);\n }\n\n async sendVerificationEmail(email: string, token: string): Promise<void> {\n const { subject, html } = this.config.templates.verification;\n await this.sendEmail(email, subject, html(token));\n }\n\n async sendPasswordResetEmail(email: string, token: string): Promise<void> {\n const { subject, html } = this.config.templates.resetPassword;\n await this.sendEmail(email, subject, html(token));\n }\n\n async verifyConnection(): Promise<boolean> {\n try {\n // SES doesn't have a direct verify method, so we'll check if we can describe our sending status\n await this.client.send(\n new SendEmailCommand({\n FromEmailAddress: this.config.from,\n FromEmailAddressIdentityArn: this.config.sourceArn,\n Destination: {\n ToAddresses: [this.config.from], // Send to ourselves as a test\n },\n Content: {\n Simple: {\n Subject: { Data: 'Test Connection', Charset: 'UTF-8' },\n Body: { Text: { Data: 'Test', Charset: 'UTF-8' } },\n },\n },\n })\n );\n return true;\n } catch (_error) {\n return false;\n }\n }\n}\n","import type { EmailTemplates } from './types';\n\nexport const defaultTemplates: EmailTemplates = {\n verification: {\n subject: 'Verify your email address',\n html: (token: string) => `\n<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <title>Verify your email address</title>\n <style>\n body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; }\n .button { display: inline-block; padding: 12px 24px; background-color: #007bff; color: white; text-decoration: none; border-radius: 4px; margin: 20px 0; }\n .footer { margin-top: 30px; font-size: 0.9em; color: #666; }\n </style>\n</head>\n<body>\n <h1>Verify your email address</h1>\n <p>Thank you for signing up! Please click the button below to verify your email address:</p>\n <a href=\"${token}\" class=\"button\">Verify Email Address</a>\n <p>Or copy and paste this link in your browser:</p>\n <p>${token}</p>\n <div class=\"footer\">\n <p>If you didn't create an account, you can safely ignore this email.</p>\n </div>\n</body>\n</html>`,\n },\n resetPassword: {\n subject: 'Reset your password',\n html: (token: string) => `\n<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <title>Reset your password</title>\n <style>\n body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; }\n .button { display: inline-block; padding: 12px 24px; background-color: #007bff; color: white; text-decoration: none; border-radius: 4px; margin: 20px 0; }\n .footer { margin-top: 30px; font-size: 0.9em; color: #666; }\n </style>\n</head>\n<body>\n <h1>Reset your password</h1>\n <p>We received a request to reset your password. Click the button below to create a new password:</p>\n <a href=\"${token}\" class=\"button\">Reset Password</a>\n <p>Or copy and paste this link in your browser:</p>\n <p>${token}</p>\n <div class=\"footer\">\n <p>If you didn't request a password reset, you can safely ignore this email.</p>\n <p>This link will expire in 24 hours.</p>\n </div>\n</body>\n</html>`,\n },\n};\n","import type { IEmailProvider, EmailConfig, SMTPConfig, SESConfig } from './types';\nimport { SMTPEmailProvider } from './smtp';\nimport { SESEmailProvider } from './ses';\n\nexport function createEmailProvider(config: EmailConfig): IEmailProvider {\n switch (config.type) {\n case 'smtp':\n return new SMTPEmailProvider(\n config.from,\n config.options as SMTPConfig,\n config.templates\n );\n case 'ses':\n return new SESEmailProvider(config.from, config.options as SESConfig, config.templates);\n default:\n throw new Error(`Unsupported email provider type: ${config.type}`);\n }\n}\n\nexport * from './types';\nexport * from './smtp';\nexport * from './ses';\n","import type { AuthService } from '../services/auth';\nimport type { UserRole } from '../types';\n\nexport class AuthMiddleware {\n private authService: AuthService;\n\n constructor(authService: AuthService) {\n this.authService = authService;\n }\n\n verifyToken() {\n return async (req: any, res: any, next: any) => {\n try {\n const token = req.headers.authorization?.split(' ')[1];\n if (!token) {\n throw new Error('No token provided');\n }\n\n const decoded = await this.authService.verifyToken(token);\n req.user = decoded;\n next();\n } catch (_error) {\n res.status(401).json({ error: 'Unauthorized' });\n }\n };\n }\n\n requireRoles(roles: UserRole[]) {\n return async (req: any, res: any, next: any) => {\n try {\n const hasRole = await this.authService.hasRole(req.user.id, roles);\n if (!hasRole) {\n throw new Error('Insufficient permissions');\n }\n next();\n } catch (_error) {\n res.status(403).json({ error: 'Forbidden' });\n }\n };\n }\n\n requireEmailVerified() {\n return (req: any, res: any, next: any) => {\n if (!req.user.isEmailVerified) {\n return res.status(403).json({ error: 'Email not verified' });\n }\n next();\n };\n }\n}\n","import { z } from 'zod';\nimport type { StorageConfig } from '../storage/types';\nimport type { EmailConfig } from '../email/types';\n\nexport const UserRoleSchema = z.enum(['ADMIN', 'USER', 'GUEST']);\nexport type UserRole = z.infer<typeof UserRoleSchema>;\n\nexport const UserSchema = z.object({\n id: z.string(),\n email: z.string().email(),\n passwordHash: z.string(),\n roles: z.array(UserRoleSchema),\n isEmailVerified: z.boolean(),\n verificationToken: z.string().optional(),\n resetPasswordToken: z.string().optional(),\n resetPasswordExpires: z.number().optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n});\n\nexport type User = z.infer<typeof UserSchema>;\n\nexport interface AuthConfig {\n jwt: {\n privateKey: string;\n publicKey: string;\n keyId: string;\n expiresIn: string;\n };\n storage: StorageConfig;\n email: EmailConfig;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/services/auth.ts","../src/email/smtp.ts","../src/email/defaultTemplates.ts","../src/email/ses.ts","../src/email/index.ts","../src/middleware/auth.ts","../src/types/index.ts"],"sourcesContent":["import bcrypt from 'bcryptjs';\nimport jwt from 'jsonwebtoken';\nimport { v4 as uuidv4 } from 'uuid';\nimport crypto from 'crypto';\nimport type {\n AuthConfig,\n CreateSessionInput,\n CreateUserInput,\n Session,\n SessionId,\n SessionInfo,\n User,\n UserId,\n} from '../types';\nimport type {\n FindUsersOptions,\n FindUsersResult,\n IStorageProvider,\n UserFilter,\n} from '../storage/types';\nimport type { IEmailProvider } from '../email/types';\nimport { createEmailProvider } from '../email';\n\nexport interface AuthTokens {\n accessToken: string;\n refreshToken: string;\n}\n\nexport interface LoginOptions {\n userAgent?: string;\n ipAddress?: string;\n deviceName?: string;\n}\n\ninterface RefreshTokenPayload {\n sessionId: SessionId;\n userId: UserId;\n type: 'refresh';\n}\n\n/** Allowed fields for user updates */\nexport interface UserUpdateInput {\n email?: string;\n firstName?: string;\n lastName?: string;\n roles?: string[];\n isEmailVerified?: boolean;\n [key: string]: unknown;\n}\n\nexport class AuthService {\n private storage: IStorageProvider;\n private email: IEmailProvider;\n private config: AuthConfig;\n\n constructor(config: AuthConfig) {\n this.config = config;\n this.storage = config.storage;\n this.email = createEmailProvider(config.email);\n }\n\n private generateAccessToken(user: User): string {\n // @ts-ignore\n return jwt.sign(\n {\n id: user.id,\n email: user.email,\n roles: user.roles,\n isEmailVerified: user.isEmailVerified,\n },\n this.config.jwt.privateKey,\n {\n algorithm: 'RS256',\n expiresIn: this.config.jwt.expiresIn,\n keyid: this.config.jwt.keyId,\n }\n );\n }\n\n private generateRefreshToken(sessionId: SessionId, userId: UserId): string {\n const payload: RefreshTokenPayload = {\n sessionId,\n userId,\n type: 'refresh',\n };\n\n // @ts-ignore\n return jwt.sign(payload, this.config.jwt.privateKey, {\n algorithm: 'RS256',\n expiresIn: this.config.jwt.refreshTokenExpiresIn || '7d',\n keyid: this.config.jwt.keyId,\n });\n }\n\n private verifyRefreshToken(token: string): RefreshTokenPayload {\n try {\n const payload = jwt.verify(token, this.config.jwt.publicKey, {\n algorithms: ['RS256'],\n }) as RefreshTokenPayload;\n\n if (payload.type !== 'refresh') {\n throw new Error('Invalid token type');\n }\n\n return payload;\n } catch (_error) {\n throw new Error('Invalid refresh token');\n }\n }\n\n private hashToken(token: string): string {\n return crypto.createHash('sha256').update(token).digest('hex');\n }\n\n private getRefreshTokenExpiryMs(): number {\n const expiresIn = this.config.jwt.refreshTokenExpiresIn || '7d';\n const match = expiresIn.match(/^(\\d+)([smhd])$/);\n if (!match) {\n return 7 * 24 * 60 * 60 * 1000; // Default 7 days\n }\n\n const value = parseInt(match[1], 10);\n const unit = match[2];\n const multipliers: Record<string, number> = {\n s: 1000,\n m: 60 * 1000,\n h: 60 * 60 * 1000,\n d: 24 * 60 * 60 * 1000,\n };\n\n return value * multipliers[unit];\n }\n\n private async hashPassword(password: string): Promise<string> {\n return bcrypt.hash(password, 10);\n }\n\n private async createSession(\n userId: UserId,\n options: LoginOptions = {}\n ): Promise<{ session: Session; refreshToken: string }> {\n const now = Date.now();\n const sessionId = this.config.idGeneration === 'uuid' ? uuidv4() : undefined;\n\n const tempSession: CreateSessionInput = {\n ...(sessionId ? { id: sessionId } : {}),\n userId,\n refreshTokenHash: '',\n userAgent: options.userAgent,\n ipAddress: options.ipAddress,\n deviceName: options.deviceName,\n createdAt: now,\n lastUsedAt: now,\n expiresAt: now + this.getRefreshTokenExpiryMs(),\n };\n\n const session = await this.storage.createSession(tempSession);\n const refreshToken = this.generateRefreshToken(session.id, userId);\n await this.storage.updateSession(session.id, {\n refreshTokenHash: this.hashToken(refreshToken),\n });\n\n return {\n session: { ...session, refreshTokenHash: this.hashToken(refreshToken) },\n refreshToken,\n };\n }\n\n async signup(\n createUserInput: CreateUserInput,\n password: string,\n options: LoginOptions = {}\n ): Promise<{ user: User; tokens: AuthTokens }> {\n const existingUser = await this.storage.getUserByEmail(createUserInput.email);\n if (existingUser) {\n throw new Error('User already exists');\n }\n\n const verificationToken = uuidv4();\n const id = this.config.idGeneration === 'uuid' ? uuidv4() : undefined;\n const user = {\n ...createUserInput,\n id,\n passwordHash: await this.hashPassword(password),\n isEmailVerified: false,\n verificationToken,\n createdAt: Date.now(),\n updatedAt: Date.now(),\n };\n\n const createdUser = await this.storage.createUser(user);\n await this.email.sendVerificationEmail(createUserInput.email, verificationToken);\n\n const { refreshToken } = await this.createSession(createdUser.id!, options);\n const accessToken = this.generateAccessToken(createdUser);\n return { user: createdUser, tokens: { accessToken, refreshToken } };\n }\n\n async signin(\n email: string,\n password: string,\n options: LoginOptions = {}\n ): Promise<{ user: User; tokens: AuthTokens }> {\n const user = await this.storage.getUserByEmail(email);\n if (!user) {\n throw new Error('Invalid credentials');\n }\n\n const isValidPassword = await bcrypt.compare(password, user.passwordHash);\n if (!isValidPassword) {\n throw new Error('Invalid credentials');\n }\n\n if (!user.isEmailVerified) {\n throw new Error('Please verify your email before signing in');\n }\n\n const { refreshToken } = await this.createSession(user.id, options);\n const accessToken = this.generateAccessToken(user);\n return { user, tokens: { accessToken, refreshToken } };\n }\n\n async refreshAccessToken(refreshToken: string): Promise<AuthTokens> {\n const payload = this.verifyRefreshToken(refreshToken);\n\n const session = await this.storage.getSessionById(payload.sessionId);\n if (!session) {\n throw new Error('Session not found');\n }\n\n // Verify token hash matches\n const tokenHash = this.hashToken(refreshToken);\n if (session.refreshTokenHash !== tokenHash) {\n throw new Error('Invalid refresh token');\n }\n\n // Check if session is expired\n if (session.expiresAt < Date.now()) {\n await this.storage.deleteSession(session.id!);\n throw new Error('Session expired');\n }\n\n const user = await this.storage.getUserById(session.userId);\n if (!user) {\n throw new Error('User not found');\n }\n\n // Generate new refresh token (rotation)\n const newRefreshToken = this.generateRefreshToken(session.id, user.id);\n\n // Update session with new token hash and last used time\n await this.storage.updateSession(session.id, {\n refreshTokenHash: this.hashToken(newRefreshToken),\n lastUsedAt: Date.now(),\n expiresAt: Date.now() + this.getRefreshTokenExpiryMs(),\n });\n\n const accessToken = this.generateAccessToken(user);\n return { accessToken, refreshToken: newRefreshToken };\n }\n\n /**\n * Logout from current session only\n */\n async logout(refreshToken: string): Promise<void> {\n try {\n const payload = this.verifyRefreshToken(refreshToken);\n await this.storage.deleteSession(payload.sessionId);\n } catch {\n // Token invalid - already logged out or expired\n }\n }\n\n /**\n * Revoke a specific session by ID\n */\n async revokeSession(userId: UserId, sessionId: SessionId): Promise<void> {\n const session = await this.storage.getSessionById(sessionId);\n if (!session || session.userId !== userId) {\n throw new Error('Session not found');\n }\n await this.storage.deleteSession(sessionId);\n }\n\n /**\n * Revoke all sessions for a user (logout from all devices)\n */\n async revokeAllSessions(userId: UserId): Promise<void> {\n await this.storage.deleteAllUserSessions(userId);\n }\n\n /**\n * Get all active sessions for a user\n */\n async getSessions(userId: UserId, currentRefreshToken?: string): Promise<SessionInfo[]> {\n const sessions = await this.storage.getSessionsByUserId(userId);\n const now = Date.now();\n\n let currentSessionId: SessionId | undefined;\n if (currentRefreshToken) {\n try {\n const payload = this.verifyRefreshToken(currentRefreshToken);\n currentSessionId = payload.sessionId;\n } catch {\n // Invalid token, no current session to mark\n }\n }\n\n return sessions\n .filter((s) => s.expiresAt > now)\n .map((session) => ({\n id: session.id!,\n deviceName: session.deviceName,\n userAgent: session.userAgent,\n ipAddress: session.ipAddress,\n createdAt: session.createdAt,\n lastUsedAt: session.lastUsedAt,\n isCurrent: session.id === currentSessionId,\n }));\n }\n\n async verifyEmail(token: string): Promise<void> {\n const user = await this.storage.getUserByVerifyEmailToken(token);\n if (!user || user.verificationToken !== token) {\n throw new Error('Invalid verification token');\n }\n\n await this.storage.updateUser(user.id!, {\n isEmailVerified: true,\n verificationToken: undefined,\n updatedAt: Date.now(),\n });\n }\n\n async initiatePasswordReset(email: string): Promise<void> {\n const user = await this.storage.getUserByEmail(email);\n if (!user) {\n throw new Error('User not found');\n }\n\n const resetToken = uuidv4();\n const resetExpires = Date.now() + 3600000; // 1 hour\n\n await this.storage.updateUser(user.id!, {\n resetPasswordToken: resetToken,\n resetPasswordExpires: resetExpires,\n updatedAt: Date.now(),\n });\n\n await this.email.sendPasswordResetEmail(email, resetToken);\n }\n\n async resetPassword(token: string, newPassword: string): Promise<void> {\n const user = await this.storage.getUserByResetPasswordToken(token);\n if (\n !user ||\n !user.resetPasswordToken ||\n user.resetPasswordToken !== token ||\n !user.resetPasswordExpires ||\n user.resetPasswordExpires < Date.now()\n ) {\n throw new Error('Invalid or expired reset token');\n }\n\n await this.storage.updateUser(user.id!, {\n passwordHash: await this.hashPassword(newPassword),\n resetPasswordToken: undefined,\n resetPasswordExpires: undefined,\n updatedAt: Date.now(),\n });\n }\n\n async verifyToken(\n token: string\n ): Promise<{ id: UserId; email: string; roles: string[]; isEmailVerified: boolean }> {\n try {\n return jwt.verify(token, this.config.jwt.publicKey, { algorithms: ['RS256'] }) as {\n id: UserId;\n email: string;\n roles: string[];\n isEmailVerified: boolean;\n };\n } catch (_error) {\n console.log('Token verification failed:', _error);\n throw new Error('Invalid token');\n }\n }\n\n async hasRole(userId: UserId, requiredRoles: string[]): Promise<boolean> {\n const user = await this.storage.getUserById(userId);\n if (!user) {\n return false;\n }\n\n return requiredRoles.some((role) => user.roles.includes(role));\n }\n\n // ==================== User Management ====================\n\n /**\n * Get a user by ID\n */\n async getUserById(id: UserId): Promise<User | null> {\n return this.storage.getUserById(id);\n }\n\n /**\n * Get a user by email\n */\n async getUserByEmail(email: string): Promise<User | null> {\n return this.storage.getUserByEmail(email);\n }\n\n /**\n * Update a user's profile information.\n * Supports predefined fields (email, firstName, lastName, roles, isEmailVerified)\n * as well as any additional custom fields.\n */\n async updateUser(id: UserId, updates: UserUpdateInput): Promise<User> {\n const user = await this.storage.getUserById(id);\n if (!user) {\n throw new Error('User not found');\n }\n\n if (updates.email !== undefined && updates.email !== user.email) {\n const existingUser = await this.storage.getUserByEmail(updates.email);\n if (existingUser) {\n throw new Error('Email already in use');\n }\n }\n\n const { email, firstName, lastName, roles, isEmailVerified, ...additionalFields } = updates;\n const mergedUpdates: Record<string, unknown> = {\n updatedAt: Date.now(),\n ...additionalFields,\n };\n\n if (email !== undefined) mergedUpdates.email = email;\n if (firstName !== undefined) mergedUpdates.firstName = firstName;\n if (lastName !== undefined) mergedUpdates.lastName = lastName;\n if (roles !== undefined) mergedUpdates.roles = roles;\n if (isEmailVerified !== undefined) mergedUpdates.isEmailVerified = isEmailVerified;\n\n await this.storage.updateUser(id, mergedUpdates);\n\n return { ...user, ...mergedUpdates } as User;\n }\n\n /**\n * Delete a user and all their sessions\n */\n async deleteUser(id: UserId): Promise<void> {\n const user = await this.storage.getUserById(id);\n if (!user) {\n throw new Error('User not found');\n }\n await this.storage.deleteUser(id);\n }\n\n /**\n * Find users with filtering and pagination\n *\n * @example\n * // Find all admins\n * const { users } = await authService.findUsers({ hasAnyRole: ['ADMIN'] });\n *\n * // Find verified users with email containing '@company.com'\n * const result = await authService.findUsers({\n * emailContains: '@company.com',\n * isEmailVerified: true\n * }, { limit: 20 });\n */\n async findUsers(filter: UserFilter, options?: FindUsersOptions): Promise<FindUsersResult> {\n return this.storage.findUsers(filter, options);\n }\n}\n","import nodemailer from 'nodemailer';\nimport type { IEmailProvider, SMTPConfig, EmailTemplates } from './types';\nimport { defaultTemplates } from './defaultTemplates';\n\nexport class SMTPEmailProvider implements IEmailProvider {\n private transporter: nodemailer.Transporter;\n private config: { from: string; templates: EmailTemplates };\n\n constructor(from: string, config: SMTPConfig, templates?: EmailTemplates) {\n this.transporter = nodemailer.createTransport(config);\n this.config = { from, templates: templates ?? defaultTemplates };\n }\n\n async sendVerificationEmail(email: string, token: string): Promise<void> {\n const { subject, html } = this.config.templates.verification;\n\n await this.transporter.sendMail({\n from: this.config.from,\n to: email,\n subject,\n html: html(token),\n });\n }\n\n async sendPasswordResetEmail(email: string, token: string): Promise<void> {\n const { subject, html } = this.config.templates.resetPassword;\n\n await this.transporter.sendMail({\n from: this.config.from,\n to: email,\n subject,\n html: html(token),\n });\n }\n\n async verifyConnection(): Promise<boolean> {\n try {\n await this.transporter.verify();\n return true;\n } catch (_error) {\n return false;\n }\n }\n}\n","import type { EmailTemplates } from './types';\n\nexport const defaultTemplates: EmailTemplates = {\n verification: {\n subject: 'Verify your email address',\n html: (token: string) => `\n<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <title>Verify your email address</title>\n <style>\n body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; }\n .button { display: inline-block; padding: 12px 24px; background-color: #007bff; color: white; text-decoration: none; border-radius: 4px; margin: 20px 0; }\n .footer { margin-top: 30px; font-size: 0.9em; color: #666; }\n </style>\n</head>\n<body>\n <h1>Verify your email address</h1>\n <p>Thank you for signing up! Please click the button below to verify your email address:</p>\n <a href=\"${token}\" class=\"button\">Verify Email Address</a>\n <p>Or copy and paste this link in your browser:</p>\n <p>${token}</p>\n <div class=\"footer\">\n <p>If you didn't create an account, you can safely ignore this email.</p>\n </div>\n</body>\n</html>`,\n },\n resetPassword: {\n subject: 'Reset your password',\n html: (token: string) => `\n<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <title>Reset your password</title>\n <style>\n body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; }\n .button { display: inline-block; padding: 12px 24px; background-color: #007bff; color: white; text-decoration: none; border-radius: 4px; margin: 20px 0; }\n .footer { margin-top: 30px; font-size: 0.9em; color: #666; }\n </style>\n</head>\n<body>\n <h1>Reset your password</h1>\n <p>We received a request to reset your password. Click the button below to create a new password:</p>\n <a href=\"${token}\" class=\"button\">Reset Password</a>\n <p>Or copy and paste this link in your browser:</p>\n <p>${token}</p>\n <div class=\"footer\">\n <p>If you didn't request a password reset, you can safely ignore this email.</p>\n <p>This link will expire in 24 hours.</p>\n </div>\n</body>\n</html>`,\n },\n};\n","import { SESv2Client, SendEmailCommand } from '@aws-sdk/client-sesv2';\nimport type { IEmailProvider, SESConfig, EmailTemplates } from './types';\nimport { defaultTemplates } from './defaultTemplates';\n\nexport class SESEmailProvider implements IEmailProvider {\n private client: SESv2Client;\n private config: { from: string; templates: EmailTemplates; sourceArn?: string };\n\n constructor(from: string, config: SESConfig, templates?: EmailTemplates) {\n this.client = new SESv2Client({\n region: config.region,\n credentials: config.credentials,\n });\n this.config = {\n from,\n templates: templates ?? defaultTemplates,\n sourceArn: config.sourceArn,\n };\n }\n\n private async sendEmail(to: string, subject: string, html: string): Promise<void> {\n const command = new SendEmailCommand({\n FromEmailAddress: this.config.from,\n FromEmailAddressIdentityArn: this.config.sourceArn,\n Destination: {\n ToAddresses: [to],\n },\n Content: {\n Simple: {\n Subject: {\n Data: subject,\n Charset: 'UTF-8',\n },\n Body: {\n Html: {\n Data: html,\n Charset: 'UTF-8',\n },\n },\n },\n },\n });\n\n await this.client.send(command);\n }\n\n async sendVerificationEmail(email: string, token: string): Promise<void> {\n const { subject, html } = this.config.templates.verification;\n await this.sendEmail(email, subject, html(token));\n }\n\n async sendPasswordResetEmail(email: string, token: string): Promise<void> {\n const { subject, html } = this.config.templates.resetPassword;\n await this.sendEmail(email, subject, html(token));\n }\n\n async verifyConnection(): Promise<boolean> {\n try {\n // SES doesn't have a direct verify method, so we'll check if we can describe our sending status\n await this.client.send(\n new SendEmailCommand({\n FromEmailAddress: this.config.from,\n FromEmailAddressIdentityArn: this.config.sourceArn,\n Destination: {\n ToAddresses: [this.config.from], // Send to ourselves as a test\n },\n Content: {\n Simple: {\n Subject: { Data: 'Test Connection', Charset: 'UTF-8' },\n Body: { Text: { Data: 'Test', Charset: 'UTF-8' } },\n },\n },\n })\n );\n return true;\n } catch (_error) {\n return false;\n }\n }\n}\n","import type { IEmailProvider, EmailConfig, SMTPConfig, SESConfig } from './types';\nimport { SMTPEmailProvider } from './smtp';\nimport { SESEmailProvider } from './ses';\n\nexport function createEmailProvider(config: EmailConfig): IEmailProvider {\n switch (config.type) {\n case 'smtp':\n return new SMTPEmailProvider(\n config.from,\n config.options as SMTPConfig,\n config.templates\n );\n case 'ses':\n return new SESEmailProvider(config.from, config.options as SESConfig, config.templates);\n default:\n throw new Error(`Unsupported email provider type: ${config.type}`);\n }\n}\n\nexport * from './types';\nexport * from './smtp';\nexport * from './ses';\n","import type { AuthService } from '../services/auth';\n\nexport class AuthMiddleware {\n private authService: AuthService;\n\n constructor(authService: AuthService) {\n this.authService = authService;\n }\n\n verifyToken() {\n return async (req: any, res: any, next: any) => {\n try {\n const token = req.headers.authorization?.split(' ')[1];\n if (!token) {\n throw new Error('No token provided');\n }\n\n req.user = await this.authService.verifyToken(token);\n next();\n } catch (_error) {\n res.status(401).json({ error: 'Unauthorized' });\n }\n };\n }\n\n requireRoles(roles: string[]) {\n return async (req: any, res: any, next: any) => {\n try {\n const hasRole = await this.authService.hasRole(req.user.id, roles);\n if (!hasRole) {\n throw new Error('Insufficient permissions');\n }\n next();\n } catch (_error) {\n res.status(403).json({ error: 'Forbidden' });\n }\n };\n }\n\n requireEmailVerified() {\n return (req: any, res: any, next: any) => {\n if (!req.user.isEmailVerified) {\n return res.status(403).json({ error: 'Email not verified' });\n }\n next();\n };\n }\n}\n","import { z } from 'zod';\nimport type { IStorageProvider } from '../storage/types';\nimport type { EmailConfig } from '../email/types';\n\n/** ID can be either auto-incremented number or UUID string */\nexport type UserId = string | number;\nexport type SessionId = string | number;\n\nexport const UserSchema = z.object({\n id: z.union([z.string(), z.number()]),\n email: z.string().email(),\n firstName: z.string().optional(),\n lastName: z.string().optional(),\n passwordHash: z.string(),\n roles: z.array(z.string()),\n isEmailVerified: z.boolean(),\n verificationToken: z.string().optional(),\n resetPasswordToken: z.string().optional(),\n resetPasswordExpires: z.number().optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n});\n\nexport const CreateUserSchema = UserSchema.omit({\n id: true,\n createdAt: true,\n updatedAt: true,\n}).extend({\n createdAt: z.number().optional(),\n updatedAt: z.number().optional(),\n id: z.union([z.string(), z.number()]).optional(),\n});\n\nexport type User = z.infer<typeof UserSchema>;\nexport type CreateUserInput = z.infer<typeof CreateUserSchema>;\n\nexport const SessionSchema = z.object({\n id: z.union([z.string(), z.number()]),\n userId: z.union([z.string(), z.number()]),\n refreshTokenHash: z.string(),\n userAgent: z.string().optional(),\n ipAddress: z.string().optional(),\n deviceName: z.string().optional(),\n createdAt: z.number(),\n lastUsedAt: z.number(),\n expiresAt: z.number(),\n});\n\nexport const CreateSessionSchema = SessionSchema.omit({\n id: true,\n createdAt: true,\n lastUsedAt: true,\n}).extend({\n createdAt: z.number().optional(),\n lastUsedAt: z.number().optional(),\n id: z.union([z.string(), z.number()]).optional(),\n});\nexport type Session = z.infer<typeof SessionSchema>;\nexport type CreateSessionInput = z.infer<typeof CreateSessionSchema>;\n\n/** Session info returned to clients (without sensitive data) */\nexport interface SessionInfo {\n id: SessionId;\n deviceName?: string;\n userAgent?: string;\n ipAddress?: string;\n createdAt: number;\n lastUsedAt: number;\n isCurrent: boolean;\n}\n\nexport interface AuthConfig {\n jwt: {\n privateKey: string;\n publicKey: string;\n keyId: string;\n expiresIn: string;\n refreshTokenExpiresIn?: string;\n };\n storage: IStorageProvider;\n idGeneration: 'uuid' | 'increment';\n email: EmailConfig;\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xcelsior/auth",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Reusable serverless authentication system with RBAC and configurable email notifications",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -10,10 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"main": "src/index.ts",
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@aws-sdk/client-dynamodb": "^3.0.0",
|
|
14
13
|
"@aws-sdk/client-sesv2": "^3.0.0",
|
|
15
|
-
"@aws-sdk/lib-dynamodb": "^3.0.0",
|
|
16
|
-
"@aws-sdk/util-dynamodb": "^3.0.0",
|
|
17
14
|
"bcryptjs": "^2.4.3",
|
|
18
15
|
"jsonwebtoken": "^9.0.0",
|
|
19
16
|
"nodemailer": "^6.9.0",
|
|
@@ -27,13 +24,10 @@
|
|
|
27
24
|
"@types/nodemailer": "^6.4.17",
|
|
28
25
|
"@types/uuid": "^9.0.0"
|
|
29
26
|
},
|
|
30
|
-
"peerDependencies": {
|
|
31
|
-
"@aws-sdk/client-dynamodb": "^3.0.0",
|
|
32
|
-
"@aws-sdk/lib-dynamodb": "^3.0.0"
|
|
33
|
-
},
|
|
34
27
|
"scripts": {
|
|
35
|
-
"build": "tsup",
|
|
28
|
+
"build": "tsup && tsc --noEmit",
|
|
36
29
|
"dev": "tsup --watch",
|
|
30
|
+
"prepublish": "npm run build",
|
|
37
31
|
"test": "jest --passWithNoTests",
|
|
38
32
|
"lint": "biome check ."
|
|
39
33
|
}
|
package/src/email/smtp.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import nodemailer from 'nodemailer';
|
|
2
2
|
import type { IEmailProvider, SMTPConfig, EmailTemplates } from './types';
|
|
3
|
+
import { defaultTemplates } from './defaultTemplates';
|
|
3
4
|
|
|
4
5
|
export class SMTPEmailProvider implements IEmailProvider {
|
|
5
6
|
private transporter: nodemailer.Transporter;
|
|
6
7
|
private config: { from: string; templates: EmailTemplates };
|
|
7
8
|
|
|
8
|
-
constructor(from: string, config: SMTPConfig, templates
|
|
9
|
+
constructor(from: string, config: SMTPConfig, templates?: EmailTemplates) {
|
|
9
10
|
this.transporter = nodemailer.createTransport(config);
|
|
10
|
-
this.config = { from, templates };
|
|
11
|
+
this.config = { from, templates: templates ?? defaultTemplates };
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
async sendVerificationEmail(email: string, token: string): Promise<void> {
|
package/src/email/types.ts
CHANGED
package/src/index.ts
CHANGED
package/src/middleware/auth.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { AuthService } from '../services/auth';
|
|
2
|
-
import type { UserRole } from '../types';
|
|
3
2
|
|
|
4
3
|
export class AuthMiddleware {
|
|
5
4
|
private authService: AuthService;
|
|
@@ -16,8 +15,7 @@ export class AuthMiddleware {
|
|
|
16
15
|
throw new Error('No token provided');
|
|
17
16
|
}
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
req.user = decoded;
|
|
18
|
+
req.user = await this.authService.verifyToken(token);
|
|
21
19
|
next();
|
|
22
20
|
} catch (_error) {
|
|
23
21
|
res.status(401).json({ error: 'Unauthorized' });
|
|
@@ -25,7 +23,7 @@ export class AuthMiddleware {
|
|
|
25
23
|
};
|
|
26
24
|
}
|
|
27
25
|
|
|
28
|
-
requireRoles(roles:
|
|
26
|
+
requireRoles(roles: string[]) {
|
|
29
27
|
return async (req: any, res: any, next: any) => {
|
|
30
28
|
try {
|
|
31
29
|
const hasRole = await this.authService.hasRole(req.user.id, roles);
|