UNPKG

@convo-lang/convo-lang

Version:
114 lines 4.12 kB
import { BehaviorSubject } from "rxjs"; import { ConvoEmailOtpSignRequestSchema, ConvoEmailPasswordSignRequestSchema, ConvoSignInJwtSchema } from "./convo-db-auth-schemas.js"; import { ConvoDbPermissionBoundary } from "./ConvoDbPermissionBoundary.js"; const localStorageKey = 'ConvoDbAuthJwt::'; /** * An auth helper class. Auth is not directly handled by ConvoDbAuth, it relies on stored database * function for auth operations. */ export class ConvoDbAuthManager { get jwtSubject() { return this._jwt; } get jwt() { return this._jwt.value; } constructor(db) { this._jwt = new BehaviorSubject(null); this.maxCachedBoundaries = 200; this.boundaryCacheDropRatio = 0.3; this.boundaryCacheCount = 0; this.boundaryCache = {}; this.db = db; try { if (globalThis.localStorage) { const key = localStorageKey + this.db.dbName; const json = globalThis.localStorage.getItem(key); if (json) { const v = JSON.parse(json); if (v) { this._jwt.next(v); } } } } catch (ex) { console.error('Failed to load JWT from local storage'); } } async signOutAsync() { this.setJwt(null); } async signInEmailOtpAsync(request) { const r = await this.db.callFunctionWithSchemaAsync(ConvoEmailOtpSignRequestSchema, ConvoSignInJwtSchema, '/bin/sign-in-email-otp', request); if (!r.success) { return r; } this.setJwt(r.result); return { success: true, result: r.result, }; } async signInEmailPasswordAsync(request) { const r = await this.db.callFunctionWithSchemaAsync(ConvoEmailPasswordSignRequestSchema, ConvoSignInJwtSchema, '/bin/sign-in-email-password', request); if (!r.success) { return r; } this.setJwt(r.result); return { success: true, result: r.result, }; } setJwt(jwt) { this._jwt.next(jwt); try { if (globalThis.localStorage) { const key = localStorageKey + this.db.dbName; globalThis.localStorage.setItem(key, JSON.stringify(jwt)); } } catch (ex) { console.error('Failed to store JWT in local storage'); } } /** * Create a proxy db with all public methods permissions scoped to the given `permissionFrom` * argument. * @param permissionFrom The node path where to scope permissions from * @param byPass If true no proxy will be created and the db of the auth manager will be returned. */ createBoundary(permissionFrom, byPass = false) { if (byPass) { return this.db; } const cached = this.boundaryCache[permissionFrom]; if (cached) { return cached; } return this._createBoundary(permissionFrom); } _createBoundary(permissionFrom) { const boundary = new ConvoDbPermissionBoundary({ proxiedDb: this.db, auth: this, identityPath: permissionFrom, }); this.boundaryCache[permissionFrom] = boundary; this.boundaryCacheCount++; if (this.boundaryCacheCount > this.maxCachedBoundaries) { const all = Object.values(this.boundaryCache); all.sort((a, b) => a.lastUsed - b.lastUsed); const end = Math.floor(this.maxCachedBoundaries * this.boundaryCacheDropRatio); for (let i = 0; i < end; i++) { const b = all[i]; if (!b || b === boundary) { continue; } if (this.boundaryCache[b.identityPath]) { delete this.boundaryCache[b.identityPath]; this.boundaryCacheCount--; } } } return boundary; } } //# sourceMappingURL=ConvoDbAuthManager.js.map