ngx-auth
Version:
Angular 20+ Authentication module
142 lines (134 loc) • 4.92 kB
JavaScript
import { HttpClient } from '@angular/common/http';
import { InjectionToken, signal, inject, makeEnvironmentProviders } from '@angular/core';
import { Subject, switchMap, catchError, from, first, map, throwError, combineLatest, tap } from 'rxjs';
import { Router } from '@angular/router';
/**
* Essential service for authentication
*/
class NgxAuthService {
}
const AUTH_SERVICE = new InjectionToken('ngx-auth--service');
const NgxRefreshInProgress = signal(false, ...(ngDevMode ? [{ debugName: "NgxRefreshInProgress" }] : []));
const delay$ = new Subject();
const ngxAuthInterceptor = (req, next) => {
const authService = inject(AUTH_SERVICE);
const http = inject(HttpClient);
if (authService.skipRequest(req)) {
return next(req);
}
const request$ = NgxRefreshInProgress()
? delayRequest(authService, req)
: addToken(authService, req);
return request$.pipe(switchMap(req => next(req)), catchError(res => responseError(authService, http, req, res)));
};
/**
* Add access token to headers or the request
*/
function addToken(authService, req) {
return from(authService.getAccessToken())
.pipe(first(), map(token => {
if (token) {
return req.clone({
setHeaders: authService.getHeaders?.(token) ?? { Authorization: `Bearer ${token}` },
});
}
return req;
}));
}
/**
* Delay request, by subscribing on refresh event, once it finished, process it
* otherwise throw error
*/
function delayRequest(authService, req) {
return delay$.pipe(first(), switchMap(canDelay => canDelay
? addToken(authService, req)
: throwError(() => req)));
}
/**
* Failed request interceptor, check if it has to be processed with refresh
*/
function responseError(authService, http, req, res) {
const refreshShouldHappen$ = authService.refreshShouldHappen(res, req);
return combineLatest([
from(typeof refreshShouldHappen$ === 'boolean'
? Promise.resolve(refreshShouldHappen$)
: refreshShouldHappen$),
from(authService.isAuthenticated()),
])
.pipe(switchMap(([refreshShouldHappen, isAuthenticated]) => {
if (refreshShouldHappen && isAuthenticated && !NgxRefreshInProgress()) {
NgxRefreshInProgress.set(true);
from(authService.refreshToken())
.subscribe({
error: () => {
NgxRefreshInProgress.set(false);
delay$.next(false);
},
next: () => {
NgxRefreshInProgress.set(false);
delay$.next(true);
},
});
}
if (refreshShouldHappen && isAuthenticated && NgxRefreshInProgress()) {
return delay$.pipe(first(), switchMap(canRetry => canRetry
? http.request(req)
: throwError(() => res || req)));
}
return throwError(() => res);
}));
}
const PUBLIC_REDIRECT_URI = new InjectionToken('ngx-auth--public-redirect-uri');
/**
* Guard, checks access token availability and allows or disallows access to page,
* and redirects out
*/
const ngxProtectedGuard = (_, state) => {
const authService = inject(AUTH_SERVICE);
const publicUri = inject(PUBLIC_REDIRECT_URI);
const router = inject(Router);
return from(authService.isAuthenticated()).pipe(tap(async (isAllowed) => {
if (isAllowed) {
return;
}
authService.setInterruptedUrl?.(state.url);
await router.navigateByUrl(publicUri);
}));
};
const PROTECTED_REDIRECT_URI = new InjectionToken('ngx-auth--protected-redirect-uri');
/**
* Guard, checks access token availability and allows or disallows access to page,
* and redirects out
*/
const ngxPublicGuard = () => {
const authService = inject(AUTH_SERVICE);
const protectedUri = inject(PROTECTED_REDIRECT_URI);
const router = inject(Router);
return from(authService.isAuthenticated()).pipe(map(isAuthenticated => !isAuthenticated), tap(async (isAllowed) => {
if (isAllowed) {
return;
}
await router.navigateByUrl(protectedUri);
}));
};
function provideNgxAuthProviders(options) {
return makeEnvironmentProviders([
{
provide: AUTH_SERVICE,
useExisting: options.authService,
},
{
provide: PROTECTED_REDIRECT_URI,
useValue: options.protectedRedirectUri,
},
{
provide: PUBLIC_REDIRECT_URI,
useValue: options.publicRedirectUri,
},
]);
}
/**
* Generated bundle index. Do not edit.
*/
export { AUTH_SERVICE, NgxAuthService, NgxRefreshInProgress, PROTECTED_REDIRECT_URI, PUBLIC_REDIRECT_URI, ngxAuthInterceptor, ngxProtectedGuard, ngxPublicGuard, provideNgxAuthProviders };
//# sourceMappingURL=ngx-auth.mjs.map