Browse Source

feat: create auth-error-filter and include filters to oauth error listener

* Create abstarct-auth-filter
* Create auth-events model
* Create oauth-error-filter.service
pull/18577/head
masumulu28 3 years ago
parent
commit
65b03e63f7
  1. 5
      npm/ng-packs/packages/core/package.json
  2. 34
      npm/ng-packs/packages/core/src/lib/abstracts/auth-error-filter.ts
  3. 3
      npm/ng-packs/packages/core/src/lib/abstracts/auth.service.ts
  4. 1
      npm/ng-packs/packages/core/src/lib/abstracts/index.ts
  5. 4
      npm/ng-packs/packages/core/src/lib/core.module.ts
  6. 60
      npm/ng-packs/packages/core/src/lib/models/auth-events.ts
  7. 11
      npm/ng-packs/packages/core/src/lib/models/auth.ts
  8. 1
      npm/ng-packs/packages/core/src/lib/models/index.ts
  9. 6
      npm/ng-packs/packages/oauth/package.json
  10. 6
      npm/ng-packs/packages/oauth/src/lib/oauth.module.ts
  11. 1
      npm/ng-packs/packages/oauth/src/lib/services/index.ts
  12. 46
      npm/ng-packs/packages/oauth/src/lib/services/oauth-error-filter.service.ts
  13. 6
      npm/ng-packs/packages/oauth/src/lib/services/oauth.service.ts
  14. 21
      npm/ng-packs/packages/oauth/src/lib/strategies/auth-flow-strategy.ts

5
npm/ng-packs/packages/core/package.json

@ -8,9 +8,8 @@
}, },
"dependencies": { "dependencies": {
"@abp/utils": "~7.4.4", "@abp/utils": "~7.4.4",
"angular-oauth2-oidc": "^15.0.1", "just-clone": "^6.0.0",
"just-clone": "^6.1.1", "just-compare": "^2.0.0",
"just-compare": "^2.3.0",
"ts-toolbelt": "6.15.4", "ts-toolbelt": "6.15.4",
"tslib": "^2.0.0" "tslib": "^2.0.0"
}, },

34
npm/ng-packs/packages/core/src/lib/abstracts/auth-error-filter.ts

@ -0,0 +1,34 @@
import { Signal, signal } from '@angular/core';
import { AuthErrorEvent, AuthErrorFilter } from '../models';
export abstract class AbstractAuthErrorFilter<T, E> {
readonly #filters = signal<Array<T>>([]);
filters = this.#filters.asReadonly();
abstract get(id: string): T;
abstract add(filter: T): void;
abstract patch(item: Partial<T>): void;
abstract remove(id: string): void;
abstract run(event: E): boolean;
}
export class AuthErrorFilterService<
T = AuthErrorEvent,
E = AuthErrorFilter,
> extends AbstractAuthErrorFilter<T, E> {
get(id: string): T {
throw new Error('Import AbpOAuthModule from @abp/ng.oauth or custom implementation');
}
add(filter: T): void {
throw new Error('Import AbpOAuthModule from @abp/ng.oauth or custom implementation');
}
patch(item: Partial<T>): void {
throw new Error('Import AbpOAuthModule from @abp/ng.oauth or custom implementation');
}
remove(id: string): void {
throw new Error('Import AbpOAuthModule from @abp/ng.oauth or custom implementation');
}
run(event: E): boolean {
throw new Error('Import AbpOAuthModule from @abp/ng.oauth or custom implementation');
}
}

3
npm/ng-packs/packages/core/src/lib/abstracts/auth.service.ts

@ -33,9 +33,8 @@ export class AuthService implements IAuthService {
navigateToLogin(queryParams?: Params): void {} navigateToLogin(queryParams?: Params): void {}
get isInternalAuth() { get isInternalAuth(): boolean {
throw new Error('not implemented'); throw new Error('not implemented');
return false;
} }
get isAuthenticated(): boolean { get isAuthenticated(): boolean {

1
npm/ng-packs/packages/core/src/lib/abstracts/index.ts

@ -3,3 +3,4 @@ export * from './ng-model.component';
export * from './auth.guard'; export * from './auth.guard';
export * from './auth.service'; export * from './auth.service';
export * from './auth-response.model'; export * from './auth-response.model';
export * from './auth-error-filter';

4
npm/ng-packs/packages/core/src/lib/core.module.ts

@ -39,6 +39,7 @@ import { QUEUE_MANAGER } from './tokens/queue.token';
import { DefaultQueueManager } from './utils/queue'; import { DefaultQueueManager } from './utils/queue';
import { IncludeLocalizationResourcesProvider } from './providers/include-localization-resources.provider'; import { IncludeLocalizationResourcesProvider } from './providers/include-localization-resources.provider';
import { SORT_COMPARE_FUNC, compareFuncFactory } from './tokens/compare-func.token'; import { SORT_COMPARE_FUNC, compareFuncFactory } from './tokens/compare-func.token';
import { AuthErrorFilterService } from './abstracts';
/** /**
* BaseCoreModule is the module that holds * BaseCoreModule is the module that holds
@ -179,7 +180,7 @@ export class CoreModule {
}, },
{ {
provide: SORT_COMPARE_FUNC, provide: SORT_COMPARE_FUNC,
useFactory: compareFuncFactory useFactory: compareFuncFactory,
}, },
{ {
provide: QUEUE_MANAGER, provide: QUEUE_MANAGER,
@ -189,6 +190,7 @@ export class CoreModule {
provide: OTHERS_GROUP, provide: OTHERS_GROUP,
useValue: options.othersGroup || 'AbpUi::OthersGroup', useValue: options.othersGroup || 'AbpUi::OthersGroup',
}, },
AuthErrorFilterService,
IncludeLocalizationResourcesProvider, IncludeLocalizationResourcesProvider,
], ],
}; };

60
npm/ng-packs/packages/core/src/lib/models/auth-events.ts

@ -0,0 +1,60 @@
export type EventType =
| 'discovery_document_loaded'
| 'jwks_load_error'
| 'invalid_nonce_in_state'
| 'discovery_document_load_error'
| 'discovery_document_validation_error'
| 'user_profile_loaded'
| 'user_profile_load_error'
| 'token_received'
| 'token_error'
| 'code_error'
| 'token_refreshed'
| 'token_refresh_error'
| 'silent_refresh_error'
| 'silently_refreshed'
| 'silent_refresh_timeout'
| 'token_validation_error'
| 'token_expires'
| 'session_changed'
| 'session_error'
| 'session_terminated'
| 'session_unchanged'
| 'logout'
| 'popup_closed'
| 'popup_blocked'
| 'token_revoke_error';
export abstract class AuthEvent {
readonly type: EventType;
constructor(type: EventType) {
this.type = type;
}
}
export class AuthSuccessEvent extends AuthEvent {
readonly info: any;
constructor(type: EventType, info?: any) {
super(type);
this.info = info;
}
}
export class AuthInfoEvent extends AuthEvent {
readonly info: any;
constructor(type: EventType, info?: any) {
super(type);
this.info = info;
}
}
export class AuthErrorEvent extends AuthEvent {
readonly reason: object;
readonly params: object;
constructor(type: EventType, reason: object, params?: object) {
super(type);
this.reason = reason;
this.params = params;
}
}

11
npm/ng-packs/packages/core/src/lib/models/auth.ts

@ -1,5 +1,6 @@
import { UnaryFunction } from 'rxjs';
import { Injector } from '@angular/core'; import { Injector } from '@angular/core';
import { UnaryFunction } from 'rxjs';
import { AuthErrorEvent } from './auth-events';
export interface LoginParams { export interface LoginParams {
username: string; username: string;
@ -13,7 +14,13 @@ export type PipeToLoginFn = (
injector: Injector, injector: Injector,
) => UnaryFunction<any, any>; ) => UnaryFunction<any, any>;
/** /**
* @deprecated The interface should not be used anymore. * @deprecated The interface should not be used anymore.
*/ */
export type SetTokenResponseToStorageFn<T = any> = (tokenRes: T) => void; export type SetTokenResponseToStorageFn<T = any> = (tokenRes: T) => void;
export type CheckAuthenticationStateFn = (injector: Injector) => void; export type CheckAuthenticationStateFn = (injector: Injector) => void;
export interface AuthErrorFilter<T = AuthErrorEvent> {
id: string;
executable: boolean;
execute: (event: T) => boolean;
}

1
npm/ng-packs/packages/core/src/lib/models/index.ts

@ -7,3 +7,4 @@ export * from './rest';
export * from './session'; export * from './session';
export * from './utility'; export * from './utility';
export * from './auth'; export * from './auth';
export * from './auth-events';

6
npm/ng-packs/packages/oauth/package.json

@ -9,9 +9,9 @@
"dependencies": { "dependencies": {
"@abp/ng.core": "~7.4.4", "@abp/ng.core": "~7.4.4",
"@abp/utils": "~7.4.4", "@abp/utils": "~7.4.4",
"angular-oauth2-oidc": "^15.0.1", "angular-oauth2-oidc": "^15.0.0",
"just-clone": "^6.1.1", "just-clone": "^6.0.0",
"just-compare": "^2.3.0", "just-compare": "^2.0.0",
"tslib": "^2.0.0" "tslib": "^2.0.0"
}, },
"publishConfig": { "publishConfig": {

6
npm/ng-packs/packages/oauth/src/lib/oauth.module.ts

@ -1,18 +1,19 @@
import { APP_INITIALIZER, ModuleWithProviders, NgModule, Provider } from '@angular/core'; import { APP_INITIALIZER, ModuleWithProviders, NgModule, Provider } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { OAuthModule, OAuthStorage } from 'angular-oauth2-oidc'; import { OAuthModule, OAuthStorage } from 'angular-oauth2-oidc';
import { import {
AbpLocalStorageService, AbpLocalStorageService,
ApiInterceptor, ApiInterceptor,
AuthErrorFilterService,
AuthGuard, AuthGuard,
AuthService, AuthService,
CHECK_AUTHENTICATION_STATE_FN_KEY, CHECK_AUTHENTICATION_STATE_FN_KEY,
noop, noop,
PIPE_TO_LOGIN_FN_KEY, PIPE_TO_LOGIN_FN_KEY,
} from '@abp/ng.core'; } from '@abp/ng.core';
import { AbpOAuthService } from './services'; import { AbpOAuthService, OAuthErrorFilterService } from './services';
import { OAuthConfigurationHandler } from './handlers/oauth-configuration.handler'; import { OAuthConfigurationHandler } from './handlers/oauth-configuration.handler';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { OAuthApiInterceptor } from './interceptors/api.interceptor'; import { OAuthApiInterceptor } from './interceptors/api.interceptor';
import { AbpOAuthGuard } from './guards/oauth.guard'; import { AbpOAuthGuard } from './guards/oauth.guard';
import { NavigateToManageProfileProvider } from './providers'; import { NavigateToManageProfileProvider } from './providers';
@ -60,6 +61,7 @@ export class AbpOAuthModule {
}, },
OAuthModule.forRoot().providers as Provider[], OAuthModule.forRoot().providers as Provider[],
{ provide: OAuthStorage, useClass: AbpLocalStorageService }, { provide: OAuthStorage, useClass: AbpLocalStorageService },
{ provide: AuthErrorFilterService, useExisting: OAuthErrorFilterService },
], ],
}; };
} }

1
npm/ng-packs/packages/oauth/src/lib/services/index.ts

@ -1 +1,2 @@
export * from './oauth.service'; export * from './oauth.service';
export * from './oauth-error-filter.service';

46
npm/ng-packs/packages/oauth/src/lib/services/oauth-error-filter.service.ts

@ -0,0 +1,46 @@
import { AbstractAuthErrorFilter, AuthErrorFilter } from '@abp/ng.core';
import { Injectable, signal } from '@angular/core';
import { OAuthErrorEvent } from 'angular-oauth2-oidc';
@Injectable({ providedIn: 'root' })
export class OAuthErrorFilterService extends AbstractAuthErrorFilter<
AuthErrorFilter<OAuthErrorEvent>,
OAuthErrorEvent
> {
readonly #filters = signal<Array<AuthErrorFilter<OAuthErrorEvent>>>([]);
filters = this.#filters.asReadonly();
get(id: string): AuthErrorFilter<OAuthErrorEvent> {
return this.#filters().find(({ id: _id }) => _id === id);
}
add(filter: AuthErrorFilter<OAuthErrorEvent>): void {
this.#filters.update(items => [...items, filter]);
}
patch(item: Partial<AuthErrorFilter<OAuthErrorEvent>>): void {
const _item = this.filters().find(({ id }) => id === item.id);
if (!_item) {
return;
}
Object.assign(_item, item);
}
remove(id: string): void {
const item = this.filters().find(({ id: _id }) => _id === id);
if (!item) {
return;
}
this.#filters.update(items => items.filter(({ id: _id }) => _id !== id));
}
run(event: OAuthErrorEvent): boolean {
return this.filters()
.filter(({ executable }) => !!executable)
.map(({ execute }) => execute(event))
.some(item => item);
}
}

6
npm/ng-packs/packages/oauth/src/lib/services/oauth.service.ts

@ -44,10 +44,10 @@ export class AbpOAuthService implements IAuthService {
} }
logout(queryParams?: Params): Observable<any> { logout(queryParams?: Params): Observable<any> {
if (!this.strategy) {
if(!this.strategy){ return EMPTY;
return EMPTY
} }
return this.strategy.logout(queryParams); return this.strategy.logout(queryParams);
} }

21
npm/ng-packs/packages/oauth/src/lib/strategies/auth-flow-strategy.ts

@ -1,13 +1,16 @@
import { Injector } from '@angular/core'; import { Injector } from '@angular/core';
import { HttpErrorResponse } from '@angular/common/http';
import { Params, Router } from '@angular/router'; import { Params, Router } from '@angular/router';
import { Observable, of } from 'rxjs';
import { filter, map, switchMap, take, tap } from 'rxjs/operators';
import { import {
AuthConfig, AuthConfig,
OAuthErrorEvent, OAuthErrorEvent,
OAuthService as OAuthService2, OAuthService as OAuthService2,
OAuthStorage, OAuthStorage,
} from 'angular-oauth2-oidc'; } from 'angular-oauth2-oidc';
import { Observable, of } from 'rxjs';
import { filter, map, switchMap, take, tap } from 'rxjs/operators';
import { import {
AbpLocalStorageService, AbpLocalStorageService,
ConfigStateService, ConfigStateService,
@ -17,9 +20,10 @@ import {
SessionStateService, SessionStateService,
TENANT_KEY, TENANT_KEY,
} from '@abp/ng.core'; } from '@abp/ng.core';
import { clearOAuthStorage } from '../utils/clear-o-auth-storage'; import { clearOAuthStorage } from '../utils/clear-o-auth-storage';
import { oAuthStorage } from '../utils/oauth-storage'; import { oAuthStorage } from '../utils/oauth-storage';
import { HttpErrorResponse } from '@angular/common/http'; import { OAuthErrorFilterService } from '../services';
export abstract class AuthFlowStrategy { export abstract class AuthFlowStrategy {
abstract readonly isInternalAuth: boolean; abstract readonly isInternalAuth: boolean;
@ -34,6 +38,8 @@ export abstract class AuthFlowStrategy {
protected tenantKey: string; protected tenantKey: string;
protected router: Router; protected router: Router;
protected readonly oAuthErrorFilterService: OAuthErrorFilterService;
abstract checkIfInternalAuth(queryParams?: Params): boolean; abstract checkIfInternalAuth(queryParams?: Params): boolean;
abstract navigateToLogin(queryParams?: Params): void; abstract navigateToLogin(queryParams?: Params): void;
abstract logout(queryParams?: Params): Observable<any>; abstract logout(queryParams?: Params): Observable<any>;
@ -54,6 +60,7 @@ export abstract class AuthFlowStrategy {
this.oAuthConfig = this.environment.getEnvironment().oAuthConfig || {}; this.oAuthConfig = this.environment.getEnvironment().oAuthConfig || {};
this.tenantKey = injector.get(TENANT_KEY); this.tenantKey = injector.get(TENANT_KEY);
this.router = injector.get(Router); this.router = injector.get(Router);
this.oAuthErrorFilterService = injector.get(OAuthErrorFilterService);
this.listenToOauthErrors(); this.listenToOauthErrors();
} }
@ -97,6 +104,7 @@ export abstract class AuthFlowStrategy {
if (redirect_uri && redirect_uri !== '/') { if (redirect_uri && redirect_uri !== '/') {
return redirect_uri; return redirect_uri;
} }
return '/'; return '/';
}), }),
switchMap(redirectUri => switchMap(redirectUri =>
@ -118,7 +126,12 @@ export abstract class AuthFlowStrategy {
this.oAuthService.events this.oAuthService.events
.pipe( .pipe(
filter(event => event instanceof OAuthErrorEvent), filter(event => event instanceof OAuthErrorEvent),
tap(() => clearOAuthStorage()), tap((err: OAuthErrorEvent) => {
const shouldSkip = this.oAuthErrorFilterService.run(err);
if (!shouldSkip) {
clearOAuthStorage();
}
}),
switchMap(() => this.configState.refreshAppState()), switchMap(() => this.configState.refreshAppState()),
) )
.subscribe(); .subscribe();

Loading…
Cancel
Save