UNPKG

@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
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