release: @marketplaces/auth 0.1.0 (built from main)
This commit is contained in:
107
dist/ed25519/auth.service.js
vendored
Normal file
107
dist/ed25519/auth.service.js
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
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 };
|
||||
Reference in New Issue
Block a user