UNPKG

@j2blasco/ts-auth

Version:

TypeScript authentication abstraction library that eliminates vendor lock-in and provides mock-free testing for both frontend and backend authentication systems

157 lines (156 loc) 5.64 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AuthTesting = void 0; const rxjs_1 = require("rxjs"); const ts_result_1 = require("@j2blasco/ts-result"); /** * A testing implementation of IAuth for testing purposes. * This implementation simulates real authentication behavior without external dependencies. */ class AuthTesting { users = new Map(); currentUser = null; authState = new rxjs_1.BehaviorSubject(undefined); passwordResetTokens = new Map(); rateLimitTracker = new Map(); idTokens = new Map(); constructor() { // Initialize with undefined state this.authState.next(undefined); } get authState$() { return this.authState.asObservable(); } async signInWithEmailAndPassword(args) { // Validate email format if (!this.isValidEmail(args.email)) { return ts_result_1.resultError.withCode('invalid-email'); } const user = this.users.get(args.email); if (!user) { return ts_result_1.resultError.withCode('user-not-found'); } if (user.password !== args.password) { return ts_result_1.resultError.withCode('wrong-password'); } this.currentUser = { uid: user.uid }; this.authState.next(this.currentUser); this.idTokens.set(user.uid, `fake-id-token-${user.uid}-${Date.now()}`); return (0, ts_result_1.resultSuccessVoid)(); } async getIdToken() { if (!this.currentUser) { throw new Error('No user signed in'); } const token = this.idTokens.get(this.currentUser.uid); if (!token) { throw new Error('No token available'); } return token; } async signOut() { if (this.currentUser) { this.idTokens.delete(this.currentUser.uid); } this.currentUser = null; this.authState.next(null); } async isEmailAvailable(email) { return !this.users.has(email); } async changeEmail(email) { if (!this.currentUser) { return ts_result_1.resultError.unknown('No user signed in'); } if (!(await this.isEmailAvailable(email))) { return ts_result_1.resultError.withCode('email-not-available'); } // Find current user and update email for (const [oldEmail, user] of this.users.entries()) { if (user.uid === this.currentUser.uid) { this.users.delete(oldEmail); this.users.set(email, { ...user, email }); break; } } return (0, ts_result_1.resultSuccess)(undefined); } async triggerResetPasswordFlow(email) { // Check rate limiting const lastRequest = this.rateLimitTracker.get(email) || 0; const now = Date.now(); if (now - lastRequest < 60000) { // 1 minute rate limit return ts_result_1.resultError.withCode('rate-limit-exceeded'); } if (!this.users.has(email)) { return ts_result_1.resultError.withCode('email-not-in-database'); } // Generate fake token const token = `fake-reset-token-${Math.random().toString(36).substring(2)}`; this.passwordResetTokens.set(token, { token, email, expiresAt: now + 3600000, // 1 hour expiry }); this.rateLimitTracker.set(email, now); return (0, ts_result_1.resultSuccessVoid)(); } async requestChangePassword(args) { const resetToken = this.passwordResetTokens.get(args.passwordToken); if (!resetToken) { return ts_result_1.resultError.withCode('token-not-found'); } if (Date.now() > resetToken.expiresAt) { this.passwordResetTokens.delete(args.passwordToken); return ts_result_1.resultError.withCode('token-expired'); } // Update password const user = this.users.get(resetToken.email); if (user) { this.users.set(resetToken.email, { ...user, password: args.newPassword }); } // Clean up token this.passwordResetTokens.delete(args.passwordToken); return (0, ts_result_1.resultSuccessVoid)(); } async deleteAccount() { if (!this.currentUser) { throw new Error('No user signed in'); } // Find and remove user for (const [email, user] of this.users.entries()) { if (user.uid === this.currentUser.uid) { this.users.delete(email); break; } } // Clean up tokens this.idTokens.delete(this.currentUser.uid); // Sign out this.currentUser = null; this.authState.next(null); } async signUp(email, password) { if (this.users.has(email)) { throw new Error('Email already in use'); } const uid = `fake-user-${Math.random().toString(36).substring(2)}`; this.users.set(email, { uid, email, password }); return uid; } // Helper methods for testing addTestUser(email, password, uid) { const userId = uid || `fake-user-${Math.random().toString(36).substring(2)}`; this.users.set(email, { uid: userId, email, password }); return userId; } getPasswordResetTokens() { return Array.from(this.passwordResetTokens.keys()); } isValidEmail(email) { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return emailRegex.test(email); } } exports.AuthTesting = AuthTesting;