var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; import { Injectable, inject, signal } from '@angular/core'; import { HttpErrorResponse } from '@angular/common/http'; import { catchError, switchMap, tap, throwError } from 'rxjs'; import { Observable } from 'rxjs'; import { authErrorCodeFromBackendCode, authErrorCodeFromStatus } from './models/auth-error.model'; import { AuthApiService } from './auth-api.service'; import { Ed25519KeypairService } from './ed25519-keypair.service'; import { SessionService } from './session.service'; /** * Orchestrates the Ed25519 challenge/response admin auth flow end to end: * * GET /api/admin/auth/challenge -> { nonce } * sign(nonce) with local Ed25519 key -> signature * POST /api/admin/auth/verify -> { token, refreshToken } * * This is the lowest-level orchestrator; components should go through * AuthFacade rather than calling this directly. Exported from the package * barrel as `Ed25519AuthService` to avoid colliding with the telegram * module's `AuthService`. */ let AuthService = class AuthService { constructor() { this.api = inject(AuthApiService); this.keypair = inject(Ed25519KeypairService); this.session = inject(SessionService); this.loginPhaseSignal = signal('idle'); this.lastErrorSignal = signal(null); this.loginPhase = this.loginPhaseSignal.asReadonly(); this.lastError = this.lastErrorSignal.asReadonly(); this.session.onRefreshDue(() => this.refresh().subscribe()); } /** Restores a persisted session on app bootstrap. Call once from an APP_INITIALIZER or root component. */ restoreSession() { this.session.restore(); } login() { this.lastErrorSignal.set(null); this.loginPhaseSignal.set('requesting-challenge'); return this.api.requestChallenge().pipe(switchMap(challenge => this.signChallenge(challenge.nonce).pipe(switchMap(({ publicKeyBase64, signature }) => { this.loginPhaseSignal.set('verifying'); return this.api.verifySignature({ publicKey: publicKeyBase64, signature, nonce: challenge.nonce }); }))), tap(tokens => { this.session.activate(tokens); this.loginPhaseSignal.set('done'); }), catchError(error => this.handleAuthError(error, 'invalid-signature'))); } refresh() { const refreshToken = this.session.getRefreshToken(); if (!refreshToken) { this.session.markExpired(); return throwError(() => this.toAuthError({ code: 'session-expired', message: 'No refresh token available.' })); } return this.api.refresh({ refreshToken }).pipe(tap(tokens => this.session.activate(tokens)), catchError(error => this.handleAuthError(error, 'session-expired', () => this.session.markExpired()))); } logout() { const refreshToken = this.session.getRefreshToken(); this.session.clear(); if (!refreshToken) { return new Observable(subscriber => { subscriber.next(); subscriber.complete(); }); } return this.api.logout(refreshToken).pipe(catchError(() => throwError(() => null))); } signChallenge(nonce) { this.loginPhaseSignal.set('signing'); return new Observable(subscriber => { this.keypair .getOrCreateKeyPair() .then(({ publicKeyBase64 }) => this.keypair.sign(nonce).then(signature => { subscriber.next({ publicKeyBase64, signature }); subscriber.complete(); })) .catch(error => subscriber.error(error)); }); } handleAuthError(error, fallbackCode, onError) { onError?.(); return throwError(() => this.toAuthError(this.toAuthErrorShape(error, fallbackCode))); } toAuthErrorShape(error, fallbackCode) { if (error instanceof HttpErrorResponse) { const bodyCode = error.error?.error?.code; const code = authErrorCodeFromBackendCode(bodyCode) ?? authErrorCodeFromStatus(error.status); return { code, message: error.message, status: error.status }; } if (error instanceof Error) { return { code: fallbackCode, message: error.message }; } return { code: fallbackCode, message: 'Unknown authentication error.' }; } toAuthError(error) { this.lastErrorSignal.set(error); return error; } }; AuthService = __decorate([ Injectable({ providedIn: 'root' }) ], AuthService); export { AuthService };