Loading demos/angular/src/app/app.config.ts +5 −2 Original line number Diff line number Diff line Loading @@ -12,7 +12,10 @@ import { withEnabledBlockingInitialNavigation, } from '@angular/router'; import { provideServiceWorker } from '@angular/service-worker'; import { BearerTokenInterceptor } from '@rxap/authentication'; import { BearerTokenInterceptor, withDisabledAuthentication, } from '@rxap/authentication'; import { ProvideEnvironment } from '@rxap/environment'; import { ProvideChangelog } from '@rxap/ngx-changelog'; import { Loading Loading @@ -58,7 +61,7 @@ export const appConfig: ApplicationConfig = { dummyClientSecret: 'geheim', redirectUri: window.location.origin + '/index.html', // silentRefreshRedirectUri: window.location.origin + '/silent-refresh.html', })), }), withDisabledAuthentication()), provideServiceWorker( 'ngsw-worker.js', { enabled: environment.serviceWorker, registrationStrategy: 'registerWhenStable:30000' }, Loading packages/angular/authentication/src/lib/tokens.ts +11 −1 Original line number Diff line number Diff line import { InjectionToken } from '@angular/core'; import { InjectionToken, Provider, } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; export const RXAP_AUTHENTICATION_DEACTIVATED = new InjectionToken<boolean>('rxap/authentication/deactivated'); Loading @@ -20,3 +23,10 @@ export const RXAP_INITIAL_AUTHENTICATION_STATE = new InjectionToken<boolean>('rx providedIn: 'root', factory: () => false, }); export function withDisabledAuthentication(disabled = true): Provider { return { provide: RXAP_AUTHENTICATION_DEACTIVATED, useValue: disabled, }; } packages/angular/oauth/src/lib/authentication-guard.ts +10 −1 Original line number Diff line number Diff line Loading @@ -3,11 +3,20 @@ import { ActivatedRouteSnapshot, RouterStateSnapshot, } from '@angular/router'; import { RxapAuthenticationService } from '@rxap/authentication'; import { RXAP_AUTHENTICATION_DEACTIVATED, RxapAuthenticationService, } from '@rxap/authentication'; import { AuthenticationService } from './authentication.service'; export async function AuthenticationGuard(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> { const isAuthDisabled = inject(RXAP_AUTHENTICATION_DEACTIVATED, { optional: true }) ?? false; if (isAuthDisabled) { console.warn('Authentication is disabled!'); return true; } const authService = inject(RxapAuthenticationService); Loading packages/angular/oauth/src/lib/authentication.service.ts +61 −14 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ import { AuthenticationEventType, IAuthenticationService, RXAP_AUTHENTICATION_ACCESS_TOKEN, RXAP_AUTHENTICATION_DEACTIVATED, } from '@rxap/authentication'; import { isDefined } from '@rxap/rxjs'; import { Loading @@ -20,27 +21,56 @@ import { JwksValidationHandler } from 'angular-oauth2-oidc-jwks'; import { BehaviorSubject, distinctUntilChanged, EMPTY, firstValueFrom, Observable, startWith, Subject, } from 'rxjs'; import { map } from 'rxjs/operators'; const JONE_DOE_JWT = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'; @Injectable() export class AuthenticationService implements IAuthenticationService { events$: Observable<AuthenticationEvent>; isAuthenticated$ = new BehaviorSubject<boolean | null>(null); events$: Observable<AuthenticationEvent> = EMPTY; public readonly isAuthenticated$ = new BehaviorSubject<boolean | null>(null); /** * Is only used when authentication is disabled to emit the events * @private */ private readonly _events$ = new Subject<AuthenticationEvent>(); constructor( private readonly oauthService: OAuthService, @Optional() @Inject(AUTH_CONFIG) authConfig: AuthConfig, private readonly authConfig: AuthConfig, @Inject(RXAP_AUTHENTICATION_ACCESS_TOKEN) private readonly authenticationAccessToken: BehaviorSubject<AuthenticationAccessToken | null>, @Inject(RXAP_AUTHENTICATION_DEACTIVATED) private readonly isAuthDisabled: boolean, ) { if (authConfig) { this.oauthService.configure(authConfig); if (this.isAuthDisabled) { this.events$ = this._events$.asObservable(); this.linkEventsAndIsAuthenticated(); this._events$.next({ type: AuthenticationEventType.OnAuthSuccess, payload: null, }); this.authenticationAccessToken.next({ token: JONE_DOE_JWT, valid: true, }); } else { this.init(); } } init() { if (this.authConfig) { this.oauthService.configure(this.authConfig); } this.events$ = this.oauthService.events.pipe( map(event => { Loading Loading @@ -74,11 +104,7 @@ export class AuthenticationService implements IAuthenticationService { }), isDefined(), ); this.events$.pipe( map(event => event ? event.type === AuthenticationEventType.OnAuthSuccess : null), distinctUntilChanged(), startWith(null), ).subscribe(this.isAuthenticated$); this.linkEventsAndIsAuthenticated(); // Use setStorage to use sessionStorage or another implementation of the TS-type Storage // instead of localStorage Loading @@ -92,16 +118,37 @@ export class AuthenticationService implements IAuthenticationService { this.oauthService.loadDiscoveryDocumentAndTryLogin().then(status => { this.isAuthenticated$.next(status); }); } async signOut(): Promise<void> { if (this.isAuthDisabled) { this._events$.next({ type: AuthenticationEventType.OnLogout, payload: null, }); } else { this.oauthService.logOut(); } } signIn() { if (this.isAuthDisabled) { this._events$.next({ type: AuthenticationEventType.OnAuthSuccess, payload: null, }); } else { this.oauthService.initLoginFlow(); } } private linkEventsAndIsAuthenticated() { this.events$.pipe( map(event => event ? event.type === AuthenticationEventType.OnAuthSuccess : null), distinctUntilChanged(), startWith(null), ).subscribe(this.isAuthenticated$); } isAuthenticated(): Promise<boolean> { return firstValueFrom(this.isAuthenticated$.pipe(isDefined())); Loading Loading
demos/angular/src/app/app.config.ts +5 −2 Original line number Diff line number Diff line Loading @@ -12,7 +12,10 @@ import { withEnabledBlockingInitialNavigation, } from '@angular/router'; import { provideServiceWorker } from '@angular/service-worker'; import { BearerTokenInterceptor } from '@rxap/authentication'; import { BearerTokenInterceptor, withDisabledAuthentication, } from '@rxap/authentication'; import { ProvideEnvironment } from '@rxap/environment'; import { ProvideChangelog } from '@rxap/ngx-changelog'; import { Loading Loading @@ -58,7 +61,7 @@ export const appConfig: ApplicationConfig = { dummyClientSecret: 'geheim', redirectUri: window.location.origin + '/index.html', // silentRefreshRedirectUri: window.location.origin + '/silent-refresh.html', })), }), withDisabledAuthentication()), provideServiceWorker( 'ngsw-worker.js', { enabled: environment.serviceWorker, registrationStrategy: 'registerWhenStable:30000' }, Loading
packages/angular/authentication/src/lib/tokens.ts +11 −1 Original line number Diff line number Diff line import { InjectionToken } from '@angular/core'; import { InjectionToken, Provider, } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; export const RXAP_AUTHENTICATION_DEACTIVATED = new InjectionToken<boolean>('rxap/authentication/deactivated'); Loading @@ -20,3 +23,10 @@ export const RXAP_INITIAL_AUTHENTICATION_STATE = new InjectionToken<boolean>('rx providedIn: 'root', factory: () => false, }); export function withDisabledAuthentication(disabled = true): Provider { return { provide: RXAP_AUTHENTICATION_DEACTIVATED, useValue: disabled, }; }
packages/angular/oauth/src/lib/authentication-guard.ts +10 −1 Original line number Diff line number Diff line Loading @@ -3,11 +3,20 @@ import { ActivatedRouteSnapshot, RouterStateSnapshot, } from '@angular/router'; import { RxapAuthenticationService } from '@rxap/authentication'; import { RXAP_AUTHENTICATION_DEACTIVATED, RxapAuthenticationService, } from '@rxap/authentication'; import { AuthenticationService } from './authentication.service'; export async function AuthenticationGuard(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> { const isAuthDisabled = inject(RXAP_AUTHENTICATION_DEACTIVATED, { optional: true }) ?? false; if (isAuthDisabled) { console.warn('Authentication is disabled!'); return true; } const authService = inject(RxapAuthenticationService); Loading
packages/angular/oauth/src/lib/authentication.service.ts +61 −14 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ import { AuthenticationEventType, IAuthenticationService, RXAP_AUTHENTICATION_ACCESS_TOKEN, RXAP_AUTHENTICATION_DEACTIVATED, } from '@rxap/authentication'; import { isDefined } from '@rxap/rxjs'; import { Loading @@ -20,27 +21,56 @@ import { JwksValidationHandler } from 'angular-oauth2-oidc-jwks'; import { BehaviorSubject, distinctUntilChanged, EMPTY, firstValueFrom, Observable, startWith, Subject, } from 'rxjs'; import { map } from 'rxjs/operators'; const JONE_DOE_JWT = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'; @Injectable() export class AuthenticationService implements IAuthenticationService { events$: Observable<AuthenticationEvent>; isAuthenticated$ = new BehaviorSubject<boolean | null>(null); events$: Observable<AuthenticationEvent> = EMPTY; public readonly isAuthenticated$ = new BehaviorSubject<boolean | null>(null); /** * Is only used when authentication is disabled to emit the events * @private */ private readonly _events$ = new Subject<AuthenticationEvent>(); constructor( private readonly oauthService: OAuthService, @Optional() @Inject(AUTH_CONFIG) authConfig: AuthConfig, private readonly authConfig: AuthConfig, @Inject(RXAP_AUTHENTICATION_ACCESS_TOKEN) private readonly authenticationAccessToken: BehaviorSubject<AuthenticationAccessToken | null>, @Inject(RXAP_AUTHENTICATION_DEACTIVATED) private readonly isAuthDisabled: boolean, ) { if (authConfig) { this.oauthService.configure(authConfig); if (this.isAuthDisabled) { this.events$ = this._events$.asObservable(); this.linkEventsAndIsAuthenticated(); this._events$.next({ type: AuthenticationEventType.OnAuthSuccess, payload: null, }); this.authenticationAccessToken.next({ token: JONE_DOE_JWT, valid: true, }); } else { this.init(); } } init() { if (this.authConfig) { this.oauthService.configure(this.authConfig); } this.events$ = this.oauthService.events.pipe( map(event => { Loading Loading @@ -74,11 +104,7 @@ export class AuthenticationService implements IAuthenticationService { }), isDefined(), ); this.events$.pipe( map(event => event ? event.type === AuthenticationEventType.OnAuthSuccess : null), distinctUntilChanged(), startWith(null), ).subscribe(this.isAuthenticated$); this.linkEventsAndIsAuthenticated(); // Use setStorage to use sessionStorage or another implementation of the TS-type Storage // instead of localStorage Loading @@ -92,16 +118,37 @@ export class AuthenticationService implements IAuthenticationService { this.oauthService.loadDiscoveryDocumentAndTryLogin().then(status => { this.isAuthenticated$.next(status); }); } async signOut(): Promise<void> { if (this.isAuthDisabled) { this._events$.next({ type: AuthenticationEventType.OnLogout, payload: null, }); } else { this.oauthService.logOut(); } } signIn() { if (this.isAuthDisabled) { this._events$.next({ type: AuthenticationEventType.OnAuthSuccess, payload: null, }); } else { this.oauthService.initLoginFlow(); } } private linkEventsAndIsAuthenticated() { this.events$.pipe( map(event => event ? event.type === AuthenticationEventType.OnAuthSuccess : null), distinctUntilChanged(), startWith(null), ).subscribe(this.isAuthenticated$); } isAuthenticated(): Promise<boolean> { return firstValueFrom(this.isAuthenticated$.pipe(isDefined())); Loading