@pvway/alpha-oas
Version:
Professional Angular 20+ OAuth2 authentication and authorization library. Provides robust session and refresh token management, secure HTTP interceptors, strongly-typed user and principal models, and full support for standalone components and signals. Des
955 lines (943 loc) • 35.2 kB
JavaScript
import * as i0 from '@angular/core';
import { Injectable } from '@angular/core';
import { HttpHeaders } from '@angular/common/http';
import { Observable, map, catchError, throwError, mergeMap } from 'rxjs';
import { v4 } from 'uuid';
/**
* Represents the authentication status of the principal.
* Used to track the current state in OAuth flows.
*/
var AlphaAuthStatusEnum;
(function (AlphaAuthStatusEnum) {
AlphaAuthStatusEnum[AlphaAuthStatusEnum["Undefined"] = 0] = "Undefined";
AlphaAuthStatusEnum[AlphaAuthStatusEnum["Anonymous"] = 1] = "Anonymous";
AlphaAuthStatusEnum[AlphaAuthStatusEnum["Authenticating"] = 2] = "Authenticating";
AlphaAuthStatusEnum[AlphaAuthStatusEnum["Refreshing"] = 3] = "Refreshing";
AlphaAuthStatusEnum[AlphaAuthStatusEnum["Authenticated"] = 4] = "Authenticated";
})(AlphaAuthStatusEnum || (AlphaAuthStatusEnum = {}));
// noinspection JSUnresolvedReference
/**
* Represents the authentication principal for the current session.
* Tracks authentication status, user identity, and session language.
*
* Responsibilities:
* - Maintains the current authentication status (anonymous, authenticating, authenticated, etc.).
* - Stores and manages the authenticated user object.
* - Persists and retrieves the session language code for localization and HTTP header enrichment.
* - Provides flags for authentication state (isAuthenticated, isAnonymous, isAuthenticating).
* - Supports clearing user identity and session language on sign-out.
*
* Usage:
* - Used by authentication services to manage and expose principal state.
* - Interacts with browser sessionStorage for language code persistence.
*/
class AlphaPrincipal {
/**
* Current authentication status of the principal.
*/
mStatus;
/**
* Gets the current authentication status.
*/
get status() {
return this.mStatus;
}
/**
* Sets the authentication status.
* @param status - New authentication status.
*/
setStatus(status) {
this.mStatus = status;
}
/**
* Current authenticated user, or null if anonymous.
*/
mUser;
/**
* Gets the current user object, or null if not authenticated.
*/
get user() {
return this.mUser;
}
/**
* Sets the current user and updates the session language code.
* @param user - Authenticated user object.
*/
setUser(user) {
this.mUser = user;
this.setSessionLanguageCode(user.languageCode);
}
/**
* Persists the session language code for localization and HTTP header enrichment.
* @param lc - Language code to persist.
*/
setSessionLanguageCode(lc) {
// interceptor will use this value for inserting
// the language-code header on each outgoing request
sessionStorage.setItem('alphaLanguageCode', lc);
}
/**
* Clears the user identity and removes the session language code from storage.
*/
clearUser() {
this.mUser = null;
sessionStorage.removeItem('alphaLanguageCode');
}
/**
* Gets the current session language code.
* Returns the user's language if authenticated, or falls back to sessionStorage or browser language.
*/
get languageCode() {
if (this.mUser) {
return this.mUser.languageCode;
}
const lsLc = sessionStorage.getItem('alphaLanguageCode');
if (lsLc) {
return lsLc;
}
const nav = window.navigator;
const userLang = (nav.language || nav.userLanguage);
return userLang
? userLang.substring(0, 2).toLowerCase()
: 'en';
}
/**
* True if the principal is authenticated.
*/
get isAuthenticated() {
return this.mStatus === AlphaAuthStatusEnum.Authenticated;
}
/**
* True if the principal is anonymous (not authenticated).
*/
get isAnonymous() {
return this.mStatus === AlphaAuthStatusEnum.Undefined
|| this.mStatus === AlphaAuthStatusEnum.Anonymous;
}
/**
* True if the principal is in the process of authenticating or refreshing.
*/
get isAuthenticating() {
return this.mStatus === AlphaAuthStatusEnum.Authenticating
|| this.mStatus === AlphaAuthStatusEnum.Refreshing;
}
/**
* Constructs a new principal with undefined status and no user.
*/
constructor() {
this.mStatus = AlphaAuthStatusEnum.Undefined;
this.mUser = null;
}
}
/**
* Represents the session token data for OAuth authentication.
*
* Responsibilities:
* - Stores, retrieves, and clears session token and metadata in browser storage.
* - Tracks token reception and expiration timestamps for validity checks.
* - Provides static methods for managing session token lifecycle.
* - Used by authentication services to persist and access session tokens securely.
*
* Usage:
* - Call `store()` to persist the session token and metadata in storage.
* - Use `retrieve()` to get the session token and metadata from storage.
* - Use `clear()` to remove the session token and metadata from storage on sign-out.
* - Use `isExpiredOrExpiring` to check if the token is near expiration.
* - Use `getTimestamps()` to calculate reception and expiration timestamps from expiry duration.
*/
class AlphaSessionData {
/**
* Storage key for the rememberMe flag.
*/
static rememberMeFieldName = 'alphaRememberMe';
/**
* Storage key for the access token.
*/
static accessTokenFieldName = 'alphaAccessToken';
/**
* Storage key for the token reception timestamp.
*/
static receptionTsFieldName = 'alphaReceptionTs';
/**
* Storage key for the token expiration timestamp.
*/
static expirationTsFieldName = 'alphaExpirationTs';
/**
* Indicates if the user chose "remember me" during authentication.
*/
rememberMe;
/**
* The access token string.
*/
accessToken;
/**
* Timestamp of token reception in milliseconds.
*/
receptionTs;
/**
* Timestamp of token expiration in milliseconds.
*/
expirationTs;
/**
* Returns true if the token is expired or will expire within 60 seconds.
*/
get isExpiredOrExpiring() {
const nowTs = new Date().getTime();
return this.expirationTs - nowTs < 60000;
}
/**
* Constructs a new AlphaSessionData instance with the given properties.
* @param rememberMe - Indicates if "remember me" was selected.
* @param accessToken - The access token string.
* @param receptionTs - Timestamp of token reception in ms.
* @param expirationTs - Timestamp of token expiration in ms.
*/
constructor(rememberMe, accessToken, receptionTs, // timestamp of token reception in ms
expirationTs) {
this.rememberMe = rememberMe;
this.accessToken = accessToken;
this.receptionTs = receptionTs;
this.expirationTs = expirationTs;
}
/**
* Calculates reception and expiration timestamps from expiry duration in seconds.
* @param expiresIn - Expiry duration in seconds.
* @returns Object containing receptionTs and expirationTs in ms.
*/
static getTimestamps(expiresIn) {
const receptionTs = new Date().getTime();
const expirationTs = receptionTs + expiresIn * 1000;
return {
receptionTs,
expirationTs
};
}
/**
* Retrieves the session data from storage and returns an AlphaSessionData instance.
* @param mStorage - Storage object to use (default: browser sessionStorage).
* @returns AlphaSessionData instance if data exists, otherwise null.
*/
static retrieve(mStorage = sessionStorage) {
const rmString = mStorage
.getItem(AlphaSessionData.rememberMeFieldName);
if (rmString == null)
return null;
const rm = rmString === 'true';
const at = mStorage
.getItem(AlphaSessionData.accessTokenFieldName) ?? '';
const rTsString = mStorage
.getItem(AlphaSessionData.receptionTsFieldName) ?? '0';
const rTs = parseInt(rTsString, 10);
const xTsString = mStorage
.getItem(AlphaSessionData.expirationTsFieldName) ?? '0';
const xTs = parseInt(xTsString, 10);
return new AlphaSessionData(rm, at, rTs, xTs);
}
/**
* Removes the session data from storage.
* @param mStorage - Storage object to use (default: browser sessionStorage).
*/
static clear(mStorage = sessionStorage) {
mStorage.removeItem(AlphaSessionData.rememberMeFieldName);
mStorage.removeItem(AlphaSessionData.accessTokenFieldName);
mStorage.removeItem(AlphaSessionData.receptionTsFieldName);
mStorage.removeItem(AlphaSessionData.expirationTsFieldName);
}
/**
* Stores the session data in storage.
* @param mStorage - Storage object to use (default: browser sessionStorage).
*/
store(mStorage = sessionStorage) {
mStorage.setItem(AlphaSessionData.rememberMeFieldName, this.rememberMe.toString());
mStorage.setItem(AlphaSessionData.accessTokenFieldName, this.accessToken);
mStorage.setItem(AlphaSessionData.receptionTsFieldName, this.receptionTs.toString());
mStorage.setItem(AlphaSessionData.expirationTsFieldName, this.expirationTs.toString());
}
}
/**
* Represents the refresh token data for OAuth authentication.
*
* Responsibilities:
* - Stores, retrieves, and clears the refresh token in browser storage.
* - Provides static methods for managing refresh token lifecycle.
* - Used by authentication services to persist and access refresh tokens securely.
*
* Usage:
* - Call `store()` to persist the refresh token in storage.
* - Use `retrieve()` to get the refresh token from storage.
* - Use `clear()` to remove the refresh token from storage on sign-out.
*/
class AlphaRefreshData {
/**
* The key used to store the refresh token in browser storage.
*/
static refreshTokenFieldName = 'alphaRefreshToken';
/**
* The refresh token string.
*/
refreshToken;
/**
* Constructs a new AlphaRefreshData instance with the given refresh token.
* @param refreshToken - The refresh token string.
*/
constructor(refreshToken) {
this.refreshToken = refreshToken;
}
/**
* Retrieves the refresh token from storage and returns an AlphaRefreshData instance.
* @param mStorage - Storage object to use (default: browser localStorage).
* @returns AlphaRefreshData instance if token exists, otherwise null.
*/
static retrieve(mStorage = localStorage) {
const rt = mStorage
.getItem(AlphaRefreshData.refreshTokenFieldName);
if (rt == null) {
return null;
}
return new AlphaRefreshData(rt);
}
/**
* Removes the refresh token from storage.
* @param mStorage - Storage object to use (default: browser localStorage).
*/
static clear(mStorage = localStorage) {
mStorage.removeItem(AlphaRefreshData.refreshTokenFieldName);
}
/**
* Stores the refresh token in storage.
* @param mStorage - Storage object to use (default: browser localStorage).
*/
store(mStorage = localStorage) {
mStorage.setItem(AlphaRefreshData.refreshTokenFieldName, this.refreshToken);
}
}
// noinspection JSUnresolvedReference
/**
* Factory for creating IAlphaUser instances from a raw data source object (DSO).
* Converts backend user responses into strongly-typed user objects.
*
* Usage:
* - Call `factorFromDso()` with a DSO to get an IAlphaUser instance.
*/
class AlphaUserFactory {
/**
* Converts a raw DSO into an IAlphaUser instance.
* @param dso - The raw user data source object, typically from an API response.
* @returns A strongly-typed user object implementing IAlphaUser.
*/
static factorFromDso(dso) {
const ds = new DsoSlicer$1(dso);
return new AlphaUser(ds.SingleFragment);
}
}
/**
* Utility class for extracting user fragments from a raw DSO.
* Used internally by the factory to map backend fields to user properties.
*/
let DsoSlicer$1 = class DsoSlicer {
_dso;
/**
* Initializes the slicer with a raw data source object.
* @param dso - The raw user data source object.
*/
constructor(dso) {
this._dso = dso;
}
/**
* Extracts and maps user properties from the DSO to a SingleFragment.
*/
get SingleFragment() {
const propertyArray = Object.entries(this._dso.userProperties);
return {
userId: this._dso.userId,
username: this._dso.username,
languageCode: this._dso.languageCode,
properties: new Map(propertyArray)
};
}
};
/**
* Concrete implementation of IAlphaUser.
* Encapsulates user identity, language, and custom properties.
*/
class AlphaUser {
userId;
username;
languageCode;
properties;
/**
* Constructs a user object from a SingleFragment.
* @param f0 - The user fragment containing all required properties.
*/
constructor(f0) {
this.userId = f0.userId;
this.username = f0.username;
this.languageCode = f0.languageCode;
this.properties = f0.properties;
}
}
// noinspection JSUnresolvedReference
// FACTORY
/**
* Factory class for creating authentication envelope instances from a raw data source object (DSO).
* Provides a static method to convert backend authentication responses into a strongly-typed envelope.
*/
class AlphaAuthEnvelopFactory {
/**
* Converts a raw DSO into an IAlphaAuthEnvelop instance.
* @param dso - The raw authentication data source object, typically from an API response.
* @returns A strongly-typed authentication envelope implementing IAlphaAuthEnvelop.
*/
static factorFromDso(dso) {
const ds = new DsoSlicer(dso);
return new AuthEnvelop(ds.SingleFragment);
}
}
// DSO SLICER
/**
* Utility class for extracting authentication fragments from a raw DSO.
* Used internally by the factory to map backend fields to envelope properties.
*/
class DsoSlicer {
_dso;
/**
* Initializes the slicer with a raw data source object.
* @param dso - The raw authentication data source object.
*/
constructor(dso) {
this._dso = dso;
}
/**
* Extracts and maps authentication properties from the DSO to a SingleFragment.
*/
get SingleFragment() {
return {
accessToken: this._dso.access_token,
expiresIn: this._dso.expires_in,
refreshToken: this._dso.refresh_token,
user: AlphaUserFactory.factorFromDso(this._dso.user)
};
}
}
// CONCRETES
/**
* Concrete implementation of IAlphaAuthEnvelop.
* Encapsulates authentication tokens and user information.
*/
class AuthEnvelop {
accessToken;
expiresIn;
refreshToken;
user;
/**
* Constructs an authentication envelope from a SingleFragment.
* @param f0 - The authentication fragment containing all required properties.
*/
constructor(f0) {
this.accessToken = f0.accessToken;
this.expiresIn = f0.expiresIn;
this.refreshToken = f0.refreshToken;
this.user = f0.user;
}
}
/**
* AlphaOasService provides authentication and authorization logic for OAuth-based flows.
* Handles sign-in, token refresh, user retrieval, and principal state management.
*
* Key Features:
* - Supports custom implementations for sign-in, refresh, and authorize via injection methods.
* - Manages session and refresh tokens using browser storage.
* - Emits principal updates and error logs via callback functions.
* - Designed for use as a singleton service (providedIn: 'root').
*
* Usage:
* - Call `init()` to initialize the service with required URLs, HttpClient, and optional callbacks/storage.
* - Use `signIn()`, `refresh()`, and `getMe()` for authentication flows.
* - Use `authorize()` to wrap protected HTTP requests.
* - Listen for principal updates via the `onPrincipalUpdated` callback.
*/
class AlphaOasService {
/**
* HttpClient instance for making HTTP requests. Set via init().
*/
mHttp;
/**
* Service context string for error logging.
*/
mContext = 'OAuthService';
/**
* Principal representing the current authenticated user and status.
*/
mPrincipal;
/**
* OAuth endpoint URLs. Set via init().
*/
mSignInUrl;
mRefreshUrl;
mGetMeUrl;
/**
* Storage objects for session and refresh tokens. Default to browser storage, can be injected.
*/
mSessionStorage = sessionStorage;
mLocalStorage = localStorage;
/**
* Callback for principal updates. Set via init().
*/
mOnPrincipalUpdated = () => { };
/**
* Callback for error logging. Set via init().
*/
mPostErrorLog = () => { };
/**
* Returns the current principal (user and status).
*/
get principal() {
return this.mPrincipal;
}
/**
* Constructs the service and initializes the principal.
*/
constructor() {
this.mPrincipal = new AlphaPrincipal();
}
/**
* Initializes the service with required dependencies and URLs.
* Determines authentication state from session/refresh storage, or sets anonymous mode.
*
* @param httpClient - Angular HttpClient instance.
* @param getMeUrl - URL for retrieving user info.
* @param refreshUrl - URL for token refresh.
* @param signInUrl - URL for sign-in.
* @param postErrorLog - Optional error logging callback.
* @param onPrincipalUpdated - Optional principal update callback.
* @param sStorage - Optional session storage (default: browser sessionStorage).
* @param lStorage - Optional local storage (default: browser localStorage).
* @returns Observable emitting the result of initialization.
*/
init(httpClient, getMeUrl, refreshUrl, signInUrl, postErrorLog, onPrincipalUpdated, sStorage = sessionStorage, lStorage = localStorage) {
this.mHttp = httpClient;
this.mSignInUrl = signInUrl;
this.mRefreshUrl = refreshUrl;
this.mGetMeUrl = getMeUrl;
this.initStorage(sStorage, lStorage);
if (postErrorLog) {
this.mPostErrorLog = postErrorLog;
}
if (onPrincipalUpdated) {
this.mOnPrincipalUpdated = onPrincipalUpdated;
}
// let's first see if there is still a session data
const sd = AlphaSessionData.retrieve(sStorage);
if (sd != null) {
return this.initFromSd();
}
// then check if there is a refresh data available
const rd = AlphaRefreshData.retrieve(lStorage);
if (rd != null) {
return this.initFromRd();
}
// no sd and no rd found let's start as anonymous
return this.initAsAnonymous();
}
/**
* Sets the session and local storage objects used for token management.
* @param sStorage - Session storage.
* @param lStorage - Local storage.
*/
initStorage(sStorage, lStorage) {
this.mSessionStorage = sStorage;
this.mLocalStorage = lStorage;
}
/**
* Initializes authentication from session data (if present).
* @returns Observable emitting result string.
*/
initFromSd() {
console.log('Sd found... calling getMe');
return new Observable((subscriber) => {
this.getMe().subscribe({
next: () => subscriber.next('principal reloaded'),
error: e => subscriber.error(e)
});
});
}
/**
* Initializes authentication from refresh data (if present).
* @returns Observable emitting result string.
*/
initFromRd() {
return new Observable((subscriber) => {
console.log('rd active... calling refresh');
this.mPrincipal.setStatus(AlphaAuthStatusEnum.Refreshing);
this.refresh().subscribe({
next: refreshed => {
if (refreshed) {
subscriber.next('identity refreshed');
}
else {
this.mPrincipal.setStatus(AlphaAuthStatusEnum.Anonymous);
subscriber.next('refresh failed');
}
},
error: (e) => {
subscriber.error(e);
}
});
});
}
/**
* Initializes authentication in anonymous mode (no session/refresh data).
* @returns Observable emitting result string.
*/
initAsAnonymous() {
return new Observable((subscriber) => {
this.mPrincipal.setStatus(AlphaAuthStatusEnum.Anonymous);
subscriber.next('anonymous');
});
}
/**
* Allows injection of a custom sign-in implementation.
* @param signIn - Function implementing sign-in logic.
*/
useSignIn(signIn) {
this.internalSignIn = signIn;
}
/**
* Allows injection of a custom refresh implementation.
* @param refresh - Function implementing refresh logic.
*/
useRefresh(refresh) {
this.internalRefresh = refresh;
}
/**
* Allows injection of a custom authorize implementation.
* @param authorize - Function implementing authorization logic.
*/
useAuthorize(authorize) {
this.internalAuthorize = authorize;
}
/**
* Default sign-in implementation. Can be overridden via useSignIn().
* @param username - User name.
* @param password - User password.
* @param rememberMe - Whether to persist refresh token.
* @returns Observable emitting authentication envelope.
*/
internalSignIn = (username, password) => {
if (this.mHttp === undefined) {
throw new Error('service is not initialized');
}
const body = 'grant_type=password' +
'&username=' + encodeURIComponent(username) +
'&password=' + encodeURIComponent(password);
const headers = new HttpHeaders()
.set('content-type', 'application/x-www-form-urlencoded');
const url = this.mSignInUrl;
return this.mHttp
.post(url, body, { headers: headers })
.pipe(map(dso => AlphaAuthEnvelopFactory.factorFromDso(dso)), catchError(error => {
this.mPostErrorLog(this.mContext, '_signIn', JSON.stringify(error));
return throwError(() => error);
}));
};
/**
* On successful login call storeIdentity.
* Remark: there is no need to call getMe from signIn as getMe
* is actually returning the same data as signIn
* @param username
* @param password
* @param rememberMe
*/
signIn(username, password, rememberMe) {
return new Observable((subscriber) => {
this.internalSignIn(username, password, rememberMe)
.subscribe({
next: (token) => {
this.storeIdentity(token, rememberMe);
subscriber.next(true);
},
error: (e) => {
this.signOut();
if (e.status === 400 || e.status === 401) {
subscriber.next(false);
}
else {
this.mPostErrorLog(this.mContext, 'login', JSON.stringify(e));
subscriber.error(e);
}
}
});
return;
});
}
/**
* Default refresh implementation. Can be overridden via useRefresh().
* @param refreshToken - Refresh token string.
* @returns Observable emitting authentication envelope.
*/
internalRefresh = (refreshToken) => {
if (this.mHttp === undefined) {
throw new Error('service is not initialized');
}
const body = 'grant_type=refresh_token' +
'&refresh_token=' + encodeURIComponent(refreshToken);
const headers = new HttpHeaders()
.set('content-type', 'application/x-www-form-urlencoded');
const url = this.mRefreshUrl;
return this.mHttp.post(url, body, { headers: headers })
.pipe(map(dso => AlphaAuthEnvelopFactory.factorFromDso(dso)));
};
/**
* Refreshes the access token using the refresh token from local storage.
* Emits true on success, false on authentication error, or errors for other failures.
* @returns Observable emitting boolean result.
*/
refresh() {
const rd = AlphaRefreshData.retrieve(this.mLocalStorage);
if (rd == null) {
this.mPostErrorLog(this.mContext, 'refresh', 'rd should not be null');
return throwError(() => 'rd should not be null');
}
return new Observable((subscriber) => {
this.internalRefresh(rd.refreshToken)
.subscribe({
next: (token) => {
this.storeIdentity(token, true);
subscriber.next(true);
},
error: (e) => {
this.signOut();
if (e.status == 401) {
subscriber.next(false);
}
else {
this.mPostErrorLog(this.mContext, 'refresh', JSON.stringify(e));
subscriber.error(e);
}
}
});
});
}
/**
* called from the init() method when the session data is present
*/
getMe() {
if (!this.mHttp) {
throw new Error('service is not initialized');
}
const url = this.mGetMeUrl;
const call = this.mHttp.get(url)
.pipe(map(dso => {
const user = AlphaUserFactory.factorFromDso(dso);
this.populatePrincipal(user);
return user;
}), catchError((error) => {
this.mPostErrorLog(this.mContext, url, JSON.stringify(error));
return throwError(() => error);
}));
return this.authorize(call);
}
/**
* Edits the principal's user info and emits principal update.
* @param firstName - New first name.
* @param lastName - New last name.
* @param languageCode - New language code.
*/
editUserInfo(firstName, lastName, languageCode) {
if (!this.mPrincipal.user) {
return;
}
this.mPrincipal.user.username = firstName + " " + lastName;
this.mPrincipal.user.languageCode = languageCode;
this.mOnPrincipalUpdated(this.mPrincipal);
}
/**
* Signs out the user, clears tokens and principal, and emits principal update.
*/
signOut() {
AlphaSessionData.clear(this.mSessionStorage);
AlphaRefreshData.clear(this.mLocalStorage);
this.mPrincipal.clearUser();
this.mPrincipal.setStatus(AlphaAuthStatusEnum.Anonymous);
this.mOnPrincipalUpdated(this.mPrincipal);
}
/**
* Default authorization implementation. Can be overridden via useAuthorize().
* Checks token validity and refreshes if needed before firing request.
* @param httpRequest - Observable HTTP request to authorize.
* @returns Observable emitting the result of the request.
*/
internalAuthorize(httpRequest) {
const sd = AlphaSessionData.retrieve(this.mSessionStorage);
if (sd == null || sd.isExpiredOrExpiring) {
return this.refresh()
.pipe(mergeMap(() => httpRequest), catchError(error => {
this.mPostErrorLog(this.mContext, '_authorize', JSON.stringify(error));
return throwError(() => error);
}));
}
else {
return httpRequest;
}
}
/**
* Wraps a protected HTTP request with authorization logic.
* @param httpRequest - Observable HTTP request to authorize.
* @returns Observable emitting the result of the request.
*/
authorize(httpRequest) {
return this.internalAuthorize(httpRequest);
}
/**
* (1) stores the access token in the session storage
* (2) stores the refresh token in the local storage
* (3) populates the principal using the user info
*/
storeIdentity(authEnvelop, rememberMe) {
const ts = AlphaSessionData
.getTimestamps(authEnvelop.expiresIn);
const sd = new AlphaSessionData(true, authEnvelop.accessToken, ts.receptionTs, ts.expirationTs);
sd.store(this.mSessionStorage);
console.log('sd populated');
if (rememberMe) {
const rd = new AlphaRefreshData(authEnvelop.refreshToken);
rd.store(this.mLocalStorage);
console.log('rd populated');
}
this.populatePrincipal(authEnvelop.user);
}
/**
* Populates the principal with user info and emits principal update.
* @param user - User object.
*/
populatePrincipal(user) {
this.mPrincipal.setUser(user);
console.log('principal user is set');
this.mPrincipal.setStatus(AlphaAuthStatusEnum.Authenticated);
this.mOnPrincipalUpdated(this.mPrincipal);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AlphaOasService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AlphaOasService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AlphaOasService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [] });
/**
* HTTP interceptor for OAuth authentication and client identification.
*
* Responsibilities:
* - Adds language, client, and authorization headers to outgoing requests.
* - Generates and persists a unique client ID per browser.
* - Retrieves and attaches access tokens from session storage.
* - Supports custom storage injection for testing or advanced scenarios.
*
* Usage:
* - Use as a provider for HTTP_INTERCEPTORS in Angular.
* - Call `initStorage()` to override default browser storage if needed.
* - Use static `handlerFn()` for functional interceptors in Angular 16+.
*/
class AlphaOasInterceptor {
/**
* Session storage for language and access token. Defaults to browser sessionStorage.
*/
mSessionStorage = sessionStorage;
/**
* Local storage for client ID. Defaults to browser localStorage.
*/
mLocalStorage = localStorage;
/**
* Allows injection of custom storage objects for session and local storage.
* Useful for testing or non-browser environments.
* @param sStorage - Session storage object (default: browser sessionStorage).
* @param lStorage - Local storage object (default: browser localStorage).
*/
initStorage(sStorage = sessionStorage, lStorage = localStorage) {
this.mSessionStorage = sStorage;
this.mLocalStorage = lStorage;
}
/**
* Intercepts outgoing HTTP requests and enriches them with authentication and client headers.
* @param req - The outgoing HTTP request.
* @param next - The next handler in the interceptor chain.
* @returns Observable emitting the HTTP event stream.
*/
intercept(req, next) {
const eReq = AlphaOasInterceptor.enrichReq(req, this.mSessionStorage, this.mLocalStorage);
return next.handle(eReq);
}
/**
* Functional interceptor handler for Angular 16+.
* Allows direct injection of storage for advanced scenarios or testing.
* @param req - The outgoing HTTP request.
* @param next - The next handler function.
* @param sStorage - Session storage object (default: browser sessionStorage).
* @param lStorage - Local storage object (default: browser localStorage).
* @returns Observable emitting the HTTP event stream.
*/
static handlerFn(req, next, sStorage = sessionStorage, lStorage = localStorage) {
const eReq = this.enrichReq(req, sStorage, lStorage);
return next(eReq);
}
/**
* Enriches an HTTP request with language, client, and authorization headers.
* - Adds 'language-code' header based on session or browser language.
* - Adds 'client-id' header, generating and persisting a UUID if needed.
* - Adds 'authorization' header if an access token is present in session storage.
* @param req - The original HTTP request.
* @param sStorage - Session storage object.
* @param lStorage - Local storage object.
* @returns The enriched HTTP request.
*/
static enrichReq(req, sStorage, lStorage) {
let headers = req.headers;
// getting languageCode
// set by the principal when setting the user
let languageCode = sStorage.getItem('alphaLanguageCode');
if (!languageCode) {
const nav = window.navigator;
const userLang = (nav.language || nav.userLanguage);
languageCode = userLang
? userLang.substring(0, 2)?.toLowerCase()
: 'en';
sStorage.setItem('alphaLanguageCode', languageCode);
}
// always add the language-code header
headers = headers.append('language-code', languageCode);
/**
* the ClientId (client-id header) identifies the client.
* There will be one client-id for each browser.
* With this in place it will be possible to
* map a new user to his browsing history
* ClientId is only generated once and stored in
* the browser localstorage associated to the url
*/
let clientId = lStorage.getItem('alphaClientId');
if (clientId == null) {
clientId = v4();
lStorage.setItem('alphaClientId', clientId);
}
// always add the client-id header
headers = headers.append('client-id', clientId);
/**
* when an accessToken is present insert the authorization header
* using the accessToken with the bearer scheme
*/
const sd = AlphaSessionData.retrieve(sStorage);
const token = sd?.accessToken;
if (token) {
headers = headers.append('authorization', `bearer ${token}`);
}
return req.clone({ headers });
}
}
/*
* Public API Surface of alpha-oas
*/
/**
* Generated bundle index. Do not edit.
*/
export { AlphaAuthEnvelopFactory, AlphaAuthStatusEnum, AlphaOasInterceptor, AlphaOasService, AlphaPrincipal, AlphaSessionData, AlphaUserFactory };
//# sourceMappingURL=pvway-alpha-oas.mjs.map