From 4f58fdb4cc698d53cb6f23cf84b843baf8f0685f Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Tue, 20 Dec 2022 11:26:44 +0300 Subject: [PATCH 01/20] Init new oauth-packages --- npm/ng-packs/packages/oauth/.eslintrc.json | 36 ++++++ npm/ng-packs/packages/oauth/README.md | 7 ++ npm/ng-packs/packages/oauth/jest.config.ts | 22 ++++ npm/ng-packs/packages/oauth/ng-package.json | 12 ++ npm/ng-packs/packages/oauth/package.json | 21 ++++ .../packages/oauth/src/lib/oauth.module.ts | 21 ++++ .../lib/strategies/auth-code-flow-strategy.ts | 39 +++++++ .../src/lib/strategies/auth-flow-strategy.ts | 103 ++++++++++++++++++ .../strategies/auth-password-flow-strategy.ts | 101 +++++++++++++++++ .../oauth/src/lib/strategies/index.ts | 3 + npm/ng-packs/packages/oauth/src/public-api.ts | 0 npm/ng-packs/packages/oauth/src/test-setup.ts | 1 + npm/ng-packs/packages/oauth/tsconfig.json | 17 +++ npm/ng-packs/packages/oauth/tsconfig.lib.json | 15 +++ .../packages/oauth/tsconfig.lib.prod.json | 11 ++ .../packages/oauth/tsconfig.spec.json | 11 ++ 16 files changed, 420 insertions(+) create mode 100644 npm/ng-packs/packages/oauth/.eslintrc.json create mode 100644 npm/ng-packs/packages/oauth/README.md create mode 100644 npm/ng-packs/packages/oauth/jest.config.ts create mode 100644 npm/ng-packs/packages/oauth/ng-package.json create mode 100644 npm/ng-packs/packages/oauth/package.json create mode 100644 npm/ng-packs/packages/oauth/src/lib/oauth.module.ts create mode 100644 npm/ng-packs/packages/oauth/src/lib/strategies/auth-code-flow-strategy.ts create mode 100644 npm/ng-packs/packages/oauth/src/lib/strategies/auth-flow-strategy.ts create mode 100644 npm/ng-packs/packages/oauth/src/lib/strategies/auth-password-flow-strategy.ts create mode 100644 npm/ng-packs/packages/oauth/src/lib/strategies/index.ts create mode 100644 npm/ng-packs/packages/oauth/src/public-api.ts create mode 100644 npm/ng-packs/packages/oauth/src/test-setup.ts create mode 100644 npm/ng-packs/packages/oauth/tsconfig.json create mode 100644 npm/ng-packs/packages/oauth/tsconfig.lib.json create mode 100644 npm/ng-packs/packages/oauth/tsconfig.lib.prod.json create mode 100644 npm/ng-packs/packages/oauth/tsconfig.spec.json diff --git a/npm/ng-packs/packages/oauth/.eslintrc.json b/npm/ng-packs/packages/oauth/.eslintrc.json new file mode 100644 index 0000000000..9c51584494 --- /dev/null +++ b/npm/ng-packs/packages/oauth/.eslintrc.json @@ -0,0 +1,36 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "extends": [ + "plugin:@nrwl/nx/angular", + "plugin:@angular-eslint/template/process-inline-templates" + ], + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "abp", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "abp", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "extends": ["plugin:@nrwl/nx/angular-template"], + "rules": {} + } + ] +} diff --git a/npm/ng-packs/packages/oauth/README.md b/npm/ng-packs/packages/oauth/README.md new file mode 100644 index 0000000000..e42bbefb70 --- /dev/null +++ b/npm/ng-packs/packages/oauth/README.md @@ -0,0 +1,7 @@ +# oauth + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test oauth` to execute the unit tests. diff --git a/npm/ng-packs/packages/oauth/jest.config.ts b/npm/ng-packs/packages/oauth/jest.config.ts new file mode 100644 index 0000000000..4dd4b3eaf8 --- /dev/null +++ b/npm/ng-packs/packages/oauth/jest.config.ts @@ -0,0 +1,22 @@ +/* eslint-disable */ +export default { + displayName: 'oauth', + preset: '../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + globals: { + 'ts-jest': { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + }, + coverageDirectory: '../../coverage/packages/oauth', + transform: { + '^.+\\.(ts|mjs|js|html)$': 'jest-preset-angular', + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/npm/ng-packs/packages/oauth/ng-package.json b/npm/ng-packs/packages/oauth/ng-package.json new file mode 100644 index 0000000000..2e78d8fbe5 --- /dev/null +++ b/npm/ng-packs/packages/oauth/ng-package.json @@ -0,0 +1,12 @@ +{ + "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../dist/packages/core", + "lib": { + "entryFile": "src/public-api.ts" + }, + "allowedNonPeerDependencies": [ + "@abp/utils", + "@abp/ng.core", + "angular-oauth2-oidc" + ] +} diff --git a/npm/ng-packs/packages/oauth/package.json b/npm/ng-packs/packages/oauth/package.json new file mode 100644 index 0000000000..9f127e7701 --- /dev/null +++ b/npm/ng-packs/packages/oauth/package.json @@ -0,0 +1,21 @@ +{ + "name": "@abp/ng.core", + "version": "7.0.0-rc.4", + "homepage": "https://abp.io", + "repository": { + "type": "git", + "url": "https://github.com/abpframework/abp.git" + }, + "dependencies": { + "@abp/utils": "~7.0.0-rc.4", + "@abp/ng.core": "~7.0.0-rc.4", + "angular-oauth2-oidc": "^15.0.1", + "just-clone": "^6.1.1", + "just-compare": "^1.4.0", + "ts-toolbelt": "6.15.4", + "tslib": "^2.0.0" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/npm/ng-packs/packages/oauth/src/lib/oauth.module.ts b/npm/ng-packs/packages/oauth/src/lib/oauth.module.ts new file mode 100644 index 0000000000..471e4e3d78 --- /dev/null +++ b/npm/ng-packs/packages/oauth/src/lib/oauth.module.ts @@ -0,0 +1,21 @@ +import { ModuleWithProviders, NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { OAuthModule as OAuthModule2, OAuthService, OAuthStorage } from 'angular-oauth2-oidc'; +import { TimeoutLimitedOAuthService } from '@abp/ng.core'; +import { storageFactory } from './utils/storage.factory'; + +@NgModule({ + imports: [CommonModule, OAuthModule2], +}) +export class OAuthModule { + static forRoot(): ModuleWithProviders { + return { + ngModule: OAuthModule, + providers: [ + OAuthModule2.forRoot().providers, + { provide: OAuthStorage, useFactory: storageFactory }, + { provide: OAuthService, useClass: TimeoutLimitedOAuthService }, + ], + }; + } +} diff --git a/npm/ng-packs/packages/oauth/src/lib/strategies/auth-code-flow-strategy.ts b/npm/ng-packs/packages/oauth/src/lib/strategies/auth-code-flow-strategy.ts new file mode 100644 index 0000000000..946d1295ca --- /dev/null +++ b/npm/ng-packs/packages/oauth/src/lib/strategies/auth-code-flow-strategy.ts @@ -0,0 +1,39 @@ +import { noop } from '@abp/ng.core'; +import { Params } from '@angular/router'; +import { from, of } from 'rxjs'; +import { AuthFlowStrategy } from './auth-flow-strategy'; + +export class AuthCodeFlowStrategy extends AuthFlowStrategy { + readonly isInternalAuth = false; + + async init() { + return super + .init() + .then(() => this.oAuthService.tryLogin().catch(noop)) + .then(() => this.oAuthService.setupAutomaticSilentRefresh({}, 'access_token')); + } + + navigateToLogin(queryParams?: Params) { + this.oAuthService.initCodeFlow('', this.getCultureParams(queryParams)); + } + + checkIfInternalAuth(queryParams?: Params) { + this.oAuthService.initCodeFlow('', this.getCultureParams(queryParams)); + return false; + } + + logout(queryParams?: Params) { + return from(this.oAuthService.revokeTokenAndLogout(this.getCultureParams(queryParams))); + } + + login(queryParams?: Params) { + this.oAuthService.initCodeFlow('', this.getCultureParams(queryParams)); + return of(null); + } + + private getCultureParams(queryParams?: Params) { + const lang = this.sessionState.getLanguage(); + const culture = { culture: lang, 'ui-culture': lang }; + return { ...(lang && culture), ...queryParams }; + } +} diff --git a/npm/ng-packs/packages/oauth/src/lib/strategies/auth-flow-strategy.ts b/npm/ng-packs/packages/oauth/src/lib/strategies/auth-flow-strategy.ts new file mode 100644 index 0000000000..f74a223fbd --- /dev/null +++ b/npm/ng-packs/packages/oauth/src/lib/strategies/auth-flow-strategy.ts @@ -0,0 +1,103 @@ +import { Injector } from '@angular/core'; +import { Params } from '@angular/router'; +import { AuthConfig, OAuthErrorEvent, OAuthService, OAuthStorage } from 'angular-oauth2-oidc'; +import { Observable, of } from 'rxjs'; +import { filter, switchMap, tap } from 'rxjs/operators'; +import { LoginParams } from '../models/auth'; +import { + ConfigStateService, + EnvironmentService, + HttpErrorReporterService, + SessionStateService, + TENANT_KEY, +} from '@abp/ng.core'; +import { oAuthStorage } from './oauth-storage'; +import { clearOAuthStorage } from './clear-o-auth-storage'; + +export abstract class AuthFlowStrategy { + abstract readonly isInternalAuth: boolean; + + protected httpErrorReporter: HttpErrorReporterService; + protected environment: EnvironmentService; + protected configState: ConfigStateService; + protected oAuthService: OAuthService; + protected oAuthConfig: AuthConfig; + protected sessionState: SessionStateService; + protected tenantKey: string; + + abstract checkIfInternalAuth(queryParams?: Params): boolean; + + abstract navigateToLogin(queryParams?: Params): void; + + abstract logout(queryParams?: Params): Observable; + + abstract login(params?: LoginParams | Params): Observable; + + private catchError = err => { + this.httpErrorReporter.reportError(err); + return of(null); + }; + + constructor(protected injector: Injector) { + this.httpErrorReporter = injector.get(HttpErrorReporterService); + this.environment = injector.get(EnvironmentService); + this.configState = injector.get(ConfigStateService); + this.oAuthService = injector.get(OAuthService); + this.sessionState = injector.get(SessionStateService); + this.oAuthConfig = this.environment.getEnvironment().oAuthConfig; + this.tenantKey = injector.get(TENANT_KEY); + + this.listenToOauthErrors(); + } + + async init(): Promise { + const shouldClear = shouldStorageClear( + this.environment.getEnvironment().oAuthConfig.clientId, + oAuthStorage, + ); + if (shouldClear) clearOAuthStorage(oAuthStorage); + + this.oAuthService.configure(this.oAuthConfig); + + this.oAuthService.events + .pipe(filter(event => event.type === 'token_refresh_error')) + .subscribe(() => this.navigateToLogin()); + + return this.oAuthService + .loadDiscoveryDocument() + .then(() => { + if (this.oAuthService.hasValidAccessToken() || !this.oAuthService.getRefreshToken()) { + return Promise.resolve(); + } + + return this.refreshToken(); + }) + .catch(this.catchError); + } + + protected refreshToken() { + return this.oAuthService.refreshToken().catch(() => clearOAuthStorage()); + } + + protected listenToOauthErrors() { + this.oAuthService.events + .pipe( + filter(event => event instanceof OAuthErrorEvent), + tap(() => clearOAuthStorage()), + switchMap(() => this.configState.refreshAppState()), + ) + .subscribe(); + } +} + +function shouldStorageClear(clientId: string, storage: OAuthStorage): boolean { + const key = 'abpOAuthClientId'; + if (!storage.getItem(key)) { + storage.setItem(key, clientId); + return false; + } + + const shouldClear = storage.getItem(key) !== clientId; + if (shouldClear) storage.setItem(key, clientId); + return shouldClear; +} diff --git a/npm/ng-packs/packages/oauth/src/lib/strategies/auth-password-flow-strategy.ts b/npm/ng-packs/packages/oauth/src/lib/strategies/auth-password-flow-strategy.ts new file mode 100644 index 0000000000..703042956d --- /dev/null +++ b/npm/ng-packs/packages/oauth/src/lib/strategies/auth-password-flow-strategy.ts @@ -0,0 +1,101 @@ +import { filter, switchMap, tap } from 'rxjs/operators'; +import { OAuthInfoEvent } from 'angular-oauth2-oidc'; +import { Params, Router } from '@angular/router'; +import { from, Observable, pipe } from 'rxjs'; +import { HttpHeaders } from '@angular/common/http'; +import { AuthFlowStrategy } from './auth-flow-strategy'; +import { removeRememberMe, setRememberMe } from './auth-utils'; +import { LoginParams } from '../models'; +import { clearOAuthStorage } from './clear-o-auth-storage'; + +function getCookieValueByName(name: string) { + const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)')); + return match ? match[2] : ''; +} + +export class AuthPasswordFlowStrategy extends AuthFlowStrategy { + readonly isInternalAuth = true; + private cookieKey = 'rememberMe'; + private storageKey = 'passwordFlow'; + + private listenToTokenExpiration() { + this.oAuthService.events + .pipe( + filter( + event => + event instanceof OAuthInfoEvent && + event.type === 'token_expires' && + event.info === 'access_token', + ), + ) + .subscribe(() => { + if (this.oAuthService.getRefreshToken()) { + this.refreshToken(); + } else { + this.oAuthService.logOut(); + removeRememberMe(); + this.configState.refreshAppState().subscribe(); + } + }); + } + + async init() { + if (!getCookieValueByName(this.cookieKey) && localStorage.getItem(this.storageKey)) { + this.oAuthService.logOut(); + } + + return super.init().then(() => this.listenToTokenExpiration()); + } + + navigateToLogin(queryParams?: Params) { + const router = this.injector.get(Router); + return router.navigate(['/account/login'], { queryParams }); + } + + checkIfInternalAuth() { + return true; + } + + login(params: LoginParams): Observable { + const tenant = this.sessionState.getTenant(); + + return from( + this.oAuthService.fetchTokenUsingPasswordFlow( + params.username, + params.password, + new HttpHeaders({ ...(tenant && tenant.id && { [this.tenantKey]: tenant.id }) }), + ), + ).pipe(this.pipeToLogin(params)); + } + + pipeToLogin(params: Pick) { + const router = this.injector.get(Router); + + return pipe( + switchMap(() => this.configState.refreshAppState()), + tap(() => { + setRememberMe(params.rememberMe); + if (params.redirectUrl) router.navigate([params.redirectUrl]); + }), + ); + } + + logout(queryParams?: Params) { + const router = this.injector.get(Router); + + return from(this.oAuthService.revokeTokenAndLogout(queryParams)).pipe( + switchMap(() => this.configState.refreshAppState()), + tap(() => { + router.navigateByUrl('/'); + removeRememberMe(); + }), + ); + } + + protected refreshToken() { + return this.oAuthService.refreshToken().catch(() => { + clearOAuthStorage(); + removeRememberMe(); + }); + } +} diff --git a/npm/ng-packs/packages/oauth/src/lib/strategies/index.ts b/npm/ng-packs/packages/oauth/src/lib/strategies/index.ts new file mode 100644 index 0000000000..6def1d4dce --- /dev/null +++ b/npm/ng-packs/packages/oauth/src/lib/strategies/index.ts @@ -0,0 +1,3 @@ +export * from './auth-flow-strategy'; +export * from './auth-code-flow-strategy'; +export * from './auth-password-flow-strategy'; diff --git a/npm/ng-packs/packages/oauth/src/public-api.ts b/npm/ng-packs/packages/oauth/src/public-api.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/npm/ng-packs/packages/oauth/src/test-setup.ts b/npm/ng-packs/packages/oauth/src/test-setup.ts new file mode 100644 index 0000000000..1100b3e8a6 --- /dev/null +++ b/npm/ng-packs/packages/oauth/src/test-setup.ts @@ -0,0 +1 @@ +import 'jest-preset-angular/setup-jest'; diff --git a/npm/ng-packs/packages/oauth/tsconfig.json b/npm/ng-packs/packages/oauth/tsconfig.json new file mode 100644 index 0000000000..2f1f4daffb --- /dev/null +++ b/npm/ng-packs/packages/oauth/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "target": "es2020" + } +} diff --git a/npm/ng-packs/packages/oauth/tsconfig.lib.json b/npm/ng-packs/packages/oauth/tsconfig.lib.json new file mode 100644 index 0000000000..58b88ce56c --- /dev/null +++ b/npm/ng-packs/packages/oauth/tsconfig.lib.json @@ -0,0 +1,15 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "target": "ES2022", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [], + "lib": ["dom", "es2018"], + "useDefineForClassFields": false + }, + "exclude": ["src/test-setup.ts", "**/*.spec.ts", "jest.config.ts"], + "include": ["**/*.ts"] +} diff --git a/npm/ng-packs/packages/oauth/tsconfig.lib.prod.json b/npm/ng-packs/packages/oauth/tsconfig.lib.prod.json new file mode 100644 index 0000000000..0e06848ce5 --- /dev/null +++ b/npm/ng-packs/packages/oauth/tsconfig.lib.prod.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false, + "target": "ES2022", + "useDefineForClassFields": false + }, + "angularCompilerOptions": { + "compilationMode": "partial" + } +} diff --git a/npm/ng-packs/packages/oauth/tsconfig.spec.json b/npm/ng-packs/packages/oauth/tsconfig.spec.json new file mode 100644 index 0000000000..be72f24e9b --- /dev/null +++ b/npm/ng-packs/packages/oauth/tsconfig.spec.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"], + "esModuleInterop": true + }, + "files": ["src/test-setup.ts"], + "include": ["**/*.ts"] +} From 05c5a4a0adadb4d1ffa0b8fdbff3dcd7d40a0b86 Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Wed, 21 Dec 2022 13:58:20 +0300 Subject: [PATCH 02/20] Move auth logic to ng-oauth packages --- npm/ng-packs/angular.json | 285 ++++++------------ .../apps/dev-app/src/app/app.module.ts | 2 + .../dev-app/src/app/home/home.component.ts | 4 +- npm/ng-packs/package.json | 12 +- .../core/src/lib/abstracts/auth.guard.ts | 15 + .../core/src/lib/abstracts/auth.service.ts | 59 ++++ .../packages/core/src/lib/abstracts/index.ts | 2 + .../packages/core/src/lib/core.module.ts | 31 +- .../packages/core/src/lib/guards/index.ts | 1 - .../packages/core/src/lib/handlers/index.ts | 1 - .../packages/core/src/lib/models/index.ts | 2 +- .../core/src/lib/services/auth.service.ts | 52 ---- .../packages/core/src/lib/services/index.ts | 2 - .../src/lib/strategies/auth-flow.strategy.ts | 262 ---------------- .../packages/core/src/lib/utils/index.ts | 1 - .../core/src/lib/utils/initial-utils.ts | 45 +-- npm/ng-packs/packages/core/src/public-api.ts | 1 - npm/ng-packs/packages/oauth/ng-package.json | 6 +- npm/ng-packs/packages/oauth/package.json | 3 +- .../packages/oauth/src/lib/guards/index.ts | 1 + .../src/lib/guards/oauth.guard.ts} | 4 +- .../packages/oauth/src/lib/handlers/index.ts | 1 + .../handlers/oauth-configuration.handler.ts | 4 +- .../src/lib/interceptors/api.interceptor.ts | 4 +- .../src/lib/interceptors/index.ts | 0 .../packages/oauth/src/lib/oauth.module.ts | 50 ++- .../packages/oauth/src/lib/services/index.ts | 2 + .../oauth/src/lib/services/oauth.service.ts | 57 ++++ .../services/timeout-limited-oauth.service.ts | 3 +- .../src/lib/strategies/auth-flow-strategy.ts | 17 +- .../strategies/auth-password-flow-strategy.ts | 6 +- .../oauth/src/lib/tests/sample.spec.ts | 5 + .../src/lib/tokens/ auth-flow-strategy.ts | 12 + .../packages/oauth/src/lib/tokens/index.ts | 1 + .../src/lib/utils/auth-utils.ts | 3 +- .../src/lib/utils/clear-o-auth-storage.ts | 21 ++ .../packages/oauth/src/lib/utils/index.ts | 5 + .../oauth/src/lib/utils/init-factory.ts | 31 ++ .../oauth/src/lib/utils/oauth-storage.ts | 1 + .../oauth/src/lib/utils/storage.factory.ts | 6 + npm/ng-packs/packages/oauth/src/public-api.ts | 8 + .../packages/schematics/collection.json | 40 +++ .../packages/theme-shared/ng-package.json | 3 +- .../src/lib/handlers/error.handler.ts | 4 +- npm/ng-packs/tsconfig.base.json | 3 +- 45 files changed, 468 insertions(+), 610 deletions(-) create mode 100644 npm/ng-packs/packages/core/src/lib/abstracts/auth.guard.ts create mode 100644 npm/ng-packs/packages/core/src/lib/abstracts/auth.service.ts delete mode 100644 npm/ng-packs/packages/core/src/lib/services/auth.service.ts delete mode 100644 npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts create mode 100644 npm/ng-packs/packages/oauth/src/lib/guards/index.ts rename npm/ng-packs/packages/{core/src/lib/guards/auth.guard.ts => oauth/src/lib/guards/oauth.guard.ts} (82%) create mode 100644 npm/ng-packs/packages/oauth/src/lib/handlers/index.ts rename npm/ng-packs/packages/{core => oauth}/src/lib/handlers/oauth-configuration.handler.ts (83%) rename npm/ng-packs/packages/{core => oauth}/src/lib/interceptors/api.interceptor.ts (88%) rename npm/ng-packs/packages/{core => oauth}/src/lib/interceptors/index.ts (100%) create mode 100644 npm/ng-packs/packages/oauth/src/lib/services/index.ts create mode 100644 npm/ng-packs/packages/oauth/src/lib/services/oauth.service.ts rename npm/ng-packs/packages/{core => oauth}/src/lib/services/timeout-limited-oauth.service.ts (70%) create mode 100644 npm/ng-packs/packages/oauth/src/lib/tests/sample.spec.ts create mode 100644 npm/ng-packs/packages/oauth/src/lib/tokens/ auth-flow-strategy.ts create mode 100644 npm/ng-packs/packages/oauth/src/lib/tokens/index.ts rename npm/ng-packs/packages/{core => oauth}/src/lib/utils/auth-utils.ts (93%) create mode 100644 npm/ng-packs/packages/oauth/src/lib/utils/clear-o-auth-storage.ts create mode 100644 npm/ng-packs/packages/oauth/src/lib/utils/index.ts create mode 100644 npm/ng-packs/packages/oauth/src/lib/utils/init-factory.ts create mode 100644 npm/ng-packs/packages/oauth/src/lib/utils/oauth-storage.ts create mode 100644 npm/ng-packs/packages/oauth/src/lib/utils/storage.factory.ts create mode 100644 npm/ng-packs/packages/schematics/collection.json diff --git a/npm/ng-packs/angular.json b/npm/ng-packs/angular.json index cd9029142c..01e482cd85 100644 --- a/npm/ng-packs/angular.json +++ b/npm/ng-packs/angular.json @@ -10,9 +10,7 @@ "architect": { "build": { "builder": "@nrwl/angular:package", - "outputs": [ - "{workspaceRoot}/dist/packages/account" - ], + "outputs": ["{workspaceRoot}/dist/packages/account"], "options": { "project": "packages/account/ng-package.json" }, @@ -28,9 +26,7 @@ }, "test": { "builder": "@nrwl/jest:jest", - "outputs": [ - "{workspaceRoot}/coverage/packages/account" - ], + "outputs": ["{workspaceRoot}/coverage/packages/account"], "options": { "jestConfig": "packages/account/jest.config.ts", "passWithNoTests": true @@ -39,22 +35,13 @@ "lint": { "builder": "@nrwl/linter:eslint", "options": { - "lintFilePatterns": [ - "packages/account/src/**/*.ts", - "packages/account/src/**/*.html" - ] + "lintFilePatterns": ["packages/account/src/**/*.ts", "packages/account/src/**/*.html"] }, - "outputs": [ - "{options.outputFile}" - ] + "outputs": ["{options.outputFile}"] } }, "tags": [], - "implicitDependencies": [ - "core", - "theme-shared", - "account-core" - ] + "implicitDependencies": ["core", "theme-shared", "account-core"] }, "account-core": { "$schema": "../../node_modules/nx/schemas/project-schema.json", @@ -65,9 +52,7 @@ "architect": { "build": { "builder": "@nrwl/angular:package", - "outputs": [ - "{workspaceRoot}/dist/packages/account-core" - ], + "outputs": ["{workspaceRoot}/dist/packages/account-core"], "options": { "project": "packages/account-core/ng-package.json" }, @@ -83,9 +68,7 @@ }, "test": { "builder": "@nrwl/jest:jest", - "outputs": [ - "{workspaceRoot}/coverage/packages/account-core" - ], + "outputs": ["{workspaceRoot}/coverage/packages/account-core"], "options": { "jestConfig": "packages/account-core/jest.config.ts", "passWithNoTests": true @@ -99,16 +82,11 @@ "packages/account-core/src/**/*.html" ] }, - "outputs": [ - "{options.outputFile}" - ] + "outputs": ["{options.outputFile}"] } }, "tags": [], - "implicitDependencies": [ - "core", - "theme-shared" - ] + "implicitDependencies": ["core", "theme-shared"] }, "components": { "$schema": "../../node_modules/nx/schemas/project-schema.json", @@ -119,9 +97,7 @@ "architect": { "build": { "builder": "@nrwl/angular:package", - "outputs": [ - "{workspaceRoot}/dist/packages/components" - ], + "outputs": ["{workspaceRoot}/dist/packages/components"], "options": { "project": "packages/components/ng-package.json" }, @@ -137,9 +113,7 @@ }, "test": { "builder": "@nrwl/jest:jest", - "outputs": [ - "{workspaceRoot}/coverage/packages/components" - ], + "outputs": ["{workspaceRoot}/coverage/packages/components"], "options": { "jestConfig": "packages/components/jest.config.ts", "passWithNoTests": true @@ -153,16 +127,11 @@ "packages/components/src/**/*.html" ] }, - "outputs": [ - "{options.outputFile}" - ] + "outputs": ["{options.outputFile}"] } }, "tags": [], - "implicitDependencies": [ - "core", - "theme-shared" - ] + "implicitDependencies": ["core", "theme-shared"] }, "core": { "$schema": "../../node_modules/nx/schemas/project-schema.json", @@ -173,9 +142,7 @@ "architect": { "build": { "builder": "@nrwl/angular:package", - "outputs": [ - "{workspaceRoot}/dist/packages/core" - ], + "outputs": ["{workspaceRoot}/dist/packages/core"], "options": { "project": "packages/core/ng-package.json" }, @@ -191,9 +158,7 @@ }, "test": { "builder": "@nrwl/jest:jest", - "outputs": [ - "{workspaceRoot}/coverage/packages/core" - ], + "outputs": ["{workspaceRoot}/coverage/packages/core"], "options": { "jestConfig": "packages/core/jest.config.ts", "passWithNoTests": true @@ -202,14 +167,9 @@ "lint": { "builder": "@nrwl/linter:eslint", "options": { - "lintFilePatterns": [ - "packages/core/src/**/*.ts", - "packages/core/src/**/*.html" - ] + "lintFilePatterns": ["packages/core/src/**/*.ts", "packages/core/src/**/*.html"] }, - "outputs": [ - "{options.outputFile}" - ] + "outputs": ["{options.outputFile}"] } }, "tags": [] @@ -223,9 +183,7 @@ "architect": { "build": { "builder": "@angular-devkit/build-angular:browser", - "outputs": [ - "{options.outputPath}" - ], + "outputs": ["{options.outputPath}"], "options": { "outputPath": "dist/apps/dev-app", "index": "apps/dev-app/src/index.html", @@ -233,14 +191,8 @@ "polyfills": "apps/dev-app/src/polyfills.ts", "tsConfig": "apps/dev-app/tsconfig.app.json", "inlineStyleLanguage": "scss", - "allowedCommonJsDependencies": [ - "chart.js", - "js-sha256" - ], - "assets": [ - "apps/dev-app/src/favicon.ico", - "apps/dev-app/src/assets" - ], + "allowedCommonJsDependencies": ["chart.js", "js-sha256"], + "assets": ["apps/dev-app/src/favicon.ico", "apps/dev-app/src/assets"], "styles": [ { "input": "node_modules/bootstrap/dist/css/bootstrap.rtl.min.css", @@ -341,17 +293,12 @@ "lint": { "builder": "@nrwl/linter:eslint", "options": { - "lintFilePatterns": [ - "apps/dev-app/src/**/*.ts", - "apps/dev-app/src/**/*.html" - ] + "lintFilePatterns": ["apps/dev-app/src/**/*.ts", "apps/dev-app/src/**/*.html"] } }, "test": { "builder": "@nrwl/jest:jest", - "outputs": [ - "{workspaceRoot}/coverage/apps/dev-app" - ], + "outputs": ["{workspaceRoot}/coverage/apps/dev-app"], "options": { "jestConfig": "apps/dev-app/jest.config.ts", "passWithNoTests": true @@ -381,19 +328,13 @@ "lint": { "builder": "@nrwl/linter:eslint", "options": { - "lintFilePatterns": [ - "apps/dev-app-e2e/**/*.{js,ts}" - ] + "lintFilePatterns": ["apps/dev-app-e2e/**/*.{js,ts}"] }, - "outputs": [ - "{options.outputFile}" - ] + "outputs": ["{options.outputFile}"] } }, "tags": [], - "implicitDependencies": [ - "dev-app" - ] + "implicitDependencies": ["dev-app"] }, "feature-management": { "$schema": "../../node_modules/nx/schemas/project-schema.json", @@ -404,9 +345,7 @@ "architect": { "build": { "builder": "@nrwl/angular:package", - "outputs": [ - "{workspaceRoot}/dist/packages/feature-management" - ], + "outputs": ["{workspaceRoot}/dist/packages/feature-management"], "options": { "project": "packages/feature-management/ng-package.json" }, @@ -422,9 +361,7 @@ }, "test": { "builder": "@nrwl/jest:jest", - "outputs": [ - "{workspaceRoot}/coverage/packages/feature-management" - ], + "outputs": ["{workspaceRoot}/coverage/packages/feature-management"], "options": { "jestConfig": "packages/feature-management/jest.config.ts", "passWithNoTests": true @@ -438,16 +375,11 @@ "packages/feature-management/src/**/*.html" ] }, - "outputs": [ - "{options.outputFile}" - ] + "outputs": ["{options.outputFile}"] } }, "tags": [], - "implicitDependencies": [ - "core", - "theme-shared" - ] + "implicitDependencies": ["core", "theme-shared"] }, "identity": { "$schema": "../../node_modules/nx/schemas/project-schema.json", @@ -458,9 +390,7 @@ "architect": { "build": { "builder": "@nrwl/angular:package", - "outputs": [ - "{workspaceRoot}/dist/packages/identity" - ], + "outputs": ["{workspaceRoot}/dist/packages/identity"], "options": { "project": "packages/identity/ng-package.json" }, @@ -476,9 +406,7 @@ }, "test": { "builder": "@nrwl/jest:jest", - "outputs": [ - "{workspaceRoot}/coverage/packages/identity" - ], + "outputs": ["{workspaceRoot}/coverage/packages/identity"], "options": { "jestConfig": "packages/identity/jest.config.ts", "passWithNoTests": true @@ -487,22 +415,54 @@ "lint": { "builder": "@nrwl/linter:eslint", "options": { - "lintFilePatterns": [ - "packages/identity/src/**/*.ts", - "packages/identity/src/**/*.html" - ] + "lintFilePatterns": ["packages/identity/src/**/*.ts", "packages/identity/src/**/*.html"] + }, + "outputs": ["{options.outputFile}"] + } + }, + "tags": [], + "implicitDependencies": ["core", "theme-shared", "permission-management"] + }, + "oauth": { + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "projectType": "library", + "root": "packages/oauth", + "sourceRoot": "packages/oauth/src", + "prefix": "abp", + "architect": { + "build": { + "builder": "@nrwl/angular:package", + "outputs": ["{workspaceRoot}/dist/packages/oauth"], + "options": { + "project": "packages/oauth/ng-package.json" }, - "outputs": [ - "{options.outputFile}" - ] + "configurations": { + "production": { + "tsConfig": "packages/oauth/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "packages/oauth/tsconfig.lib.json" + } + }, + "defaultConfiguration": "production" + }, + "test": { + "builder": "@nrwl/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "packages/oauth/jest.config.ts", + "passWithNoTests": true + } + }, + "lint": { + "builder": "@nrwl/linter:eslint", + "options": { + "lintFilePatterns": ["packages/oauth/**/*.ts", "packages/oauth/**/*.html"] + } } }, "tags": [], - "implicitDependencies": [ - "core", - "theme-shared", - "permission-management" - ] + "implicitDependencies": ["core"] }, "permission-management": { "$schema": "../../node_modules/nx/schemas/project-schema.json", @@ -513,9 +473,7 @@ "architect": { "build": { "builder": "@nrwl/angular:package", - "outputs": [ - "{workspaceRoot}/dist/packages/permission-management" - ], + "outputs": ["{workspaceRoot}/dist/packages/permission-management"], "options": { "project": "packages/permission-management/ng-package.json" }, @@ -531,9 +489,7 @@ }, "test": { "builder": "@nrwl/jest:jest", - "outputs": [ - "{workspaceRoot}/coverage/packages/permission-management" - ], + "outputs": ["{workspaceRoot}/coverage/packages/permission-management"], "options": { "jestConfig": "packages/permission-management/jest.config.ts", "passWithNoTests": true @@ -547,16 +503,11 @@ "packages/permission-management/src/**/*.html" ] }, - "outputs": [ - "{options.outputFile}" - ] + "outputs": ["{options.outputFile}"] } }, "tags": [], - "implicitDependencies": [ - "core", - "theme-shared" - ] + "implicitDependencies": ["core", "theme-shared"] }, "schematics": { "$schema": "../../node_modules/nx/schemas/project-schema.json", @@ -567,9 +518,7 @@ "architect": { "test": { "builder": "@nrwl/jest:jest", - "outputs": [ - "{workspaceRoot}/coverage/packages/schematics" - ], + "outputs": ["{workspaceRoot}/coverage/packages/schematics"], "options": { "jestConfig": "packages/schematics/jest.config.ts", "passWithNoTests": true @@ -583,9 +532,7 @@ "packages/schematics/src/**/*.html" ] }, - "outputs": [ - "{options.outputFile}" - ] + "outputs": ["{options.outputFile}"] } }, "tags": [] @@ -599,9 +546,7 @@ "architect": { "build": { "builder": "@nrwl/angular:package", - "outputs": [ - "{workspaceRoot}/dist/packages/setting-management" - ], + "outputs": ["{workspaceRoot}/dist/packages/setting-management"], "options": { "project": "packages/setting-management/ng-package.json" }, @@ -617,9 +562,7 @@ }, "test": { "builder": "@nrwl/jest:jest", - "outputs": [ - "{workspaceRoot}/coverage/packages/setting-management" - ], + "outputs": ["{workspaceRoot}/coverage/packages/setting-management"], "options": { "jestConfig": "packages/setting-management/jest.config.ts", "passWithNoTests": true @@ -633,17 +576,11 @@ "packages/setting-management/src/**/*.html" ] }, - "outputs": [ - "{options.outputFile}" - ] + "outputs": ["{options.outputFile}"] } }, "tags": [], - "implicitDependencies": [ - "core", - "theme-shared", - "components" - ] + "implicitDependencies": ["core", "theme-shared", "components"] }, "tenant-management": { "$schema": "../../node_modules/nx/schemas/project-schema.json", @@ -654,9 +591,7 @@ "architect": { "build": { "builder": "@nrwl/angular:package", - "outputs": [ - "{workspaceRoot}/dist/packages/tenant-management" - ], + "outputs": ["{workspaceRoot}/dist/packages/tenant-management"], "options": { "project": "packages/tenant-management/ng-package.json" }, @@ -672,9 +607,7 @@ }, "test": { "builder": "@nrwl/jest:jest", - "outputs": [ - "{workspaceRoot}/coverage/packages/tenant-management" - ], + "outputs": ["{workspaceRoot}/coverage/packages/tenant-management"], "options": { "jestConfig": "packages/tenant-management/jest.config.ts", "passWithNoTests": true @@ -688,17 +621,11 @@ "packages/tenant-management/src/**/*.html" ] }, - "outputs": [ - "{options.outputFile}" - ] + "outputs": ["{options.outputFile}"] } }, "tags": [], - "implicitDependencies": [ - "core", - "theme-shared", - "feature-management" - ] + "implicitDependencies": ["core", "theme-shared", "feature-management"] }, "theme-basic": { "$schema": "../../node_modules/nx/schemas/project-schema.json", @@ -709,9 +636,7 @@ "architect": { "build": { "builder": "@nrwl/angular:package", - "outputs": [ - "{workspaceRoot}/dist/packages/theme-basic" - ], + "outputs": ["{workspaceRoot}/dist/packages/theme-basic"], "options": { "project": "packages/theme-basic/ng-package.json" }, @@ -727,9 +652,7 @@ }, "test": { "builder": "@nrwl/jest:jest", - "outputs": [ - "{workspaceRoot}/coverage/packages/theme-basic" - ], + "outputs": ["{workspaceRoot}/coverage/packages/theme-basic"], "options": { "jestConfig": "packages/theme-basic/jest.config.ts", "passWithNoTests": true @@ -743,17 +666,11 @@ "packages/theme-basic/src/**/*.html" ] }, - "outputs": [ - "{options.outputFile}" - ] + "outputs": ["{options.outputFile}"] } }, "tags": [], - "implicitDependencies": [ - "core", - "theme-shared", - "account-core" - ] + "implicitDependencies": ["core", "theme-shared", "account-core"] }, "theme-shared": { "$schema": "../../node_modules/nx/schemas/project-schema.json", @@ -764,9 +681,7 @@ "architect": { "build": { "builder": "@nrwl/angular:package", - "outputs": [ - "{workspaceRoot}/dist/packages/theme-shared" - ], + "outputs": ["{workspaceRoot}/dist/packages/theme-shared"], "options": { "project": "packages/theme-shared/ng-package.json" }, @@ -782,9 +697,7 @@ }, "test": { "builder": "@nrwl/jest:jest", - "outputs": [ - "{workspaceRoot}/coverage/packages/theme-shared" - ], + "outputs": ["{workspaceRoot}/coverage/packages/theme-shared"], "options": { "jestConfig": "packages/theme-shared/jest.config.ts", "passWithNoTests": true @@ -798,15 +711,11 @@ "packages/theme-shared/src/**/*.html" ] }, - "outputs": [ - "{options.outputFile}" - ] + "outputs": ["{options.outputFile}"] } }, "tags": [], - "implicitDependencies": [ - "core" - ] + "implicitDependencies": ["core","oauth"] } } } diff --git a/npm/ng-packs/apps/dev-app/src/app/app.module.ts b/npm/ng-packs/apps/dev-app/src/app/app.module.ts index cff75b7bfa..52ebd38362 100644 --- a/npm/ng-packs/apps/dev-app/src/app/app.module.ts +++ b/npm/ng-packs/apps/dev-app/src/app/app.module.ts @@ -14,6 +14,7 @@ import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { APP_ROUTE_PROVIDER } from './route.provider'; import { FeatureManagementModule } from '@abp/ng.feature-management'; +import { AbpOAuthModule } from '@abp/ng.oauth'; @NgModule({ imports: [ @@ -26,6 +27,7 @@ import { FeatureManagementModule } from '@abp/ng.feature-management'; sendNullsAsQueryParam: false, skipGetAppConfiguration: false, }), + AbpOAuthModule.forRoot(), ThemeSharedModule.forRoot(), AccountConfigModule.forRoot(), IdentityConfigModule.forRoot(), diff --git a/npm/ng-packs/apps/dev-app/src/app/home/home.component.ts b/npm/ng-packs/apps/dev-app/src/app/home/home.component.ts index c0b4602ee2..c48187728c 100644 --- a/npm/ng-packs/apps/dev-app/src/app/home/home.component.ts +++ b/npm/ng-packs/apps/dev-app/src/app/home/home.component.ts @@ -8,10 +8,10 @@ import { OAuthService } from 'angular-oauth2-oidc'; }) export class HomeComponent { get hasLoggedIn(): boolean { - return this.oAuthService.hasValidAccessToken(); + return this.authService.isAuthenticated; } - constructor(private oAuthService: OAuthService, private authService: AuthService) {} + constructor(private authService: AuthService) {} login() { this.authService.navigateToLogin(); diff --git a/npm/ng-packs/package.json b/npm/ng-packs/package.json index b44f13bbc9..ce78984581 100644 --- a/npm/ng-packs/package.json +++ b/npm/ng-packs/package.json @@ -86,8 +86,8 @@ "@popperjs/core": "~2.11.2", "@schematics/angular": "~15.0.1", "@swimlane/ngx-datatable": "^20.0.0", - "@types/jest": "28.1.8", - "@types/node": "14.14.33", + "@types/jest": "28.1.1", + "@types/node": "16.11.7", "@typescript-eslint/eslint-plugin": "5.44.0", "@typescript-eslint/parser": "5.44.0", "angular-oauth2-oidc": "^15.0.1", @@ -99,7 +99,7 @@ "eslint-config-prettier": "8.1.0", "eslint-plugin-cypress": "^2.10.3", "got": "^11.5.2", - "jest": "28.1.3", + "jest": "28.1.1", "jest-canvas-mock": "^2.3.1", "jest-environment-jsdom": "28.1.1", "jest-preset-angular": "12.2.2", @@ -119,18 +119,18 @@ "protractor": "~7.0.0", "rxjs": "7.5.6", "should-quote": "^1.0.0", - "ts-jest": "28.0.8", + "ts-jest": "28.0.5", "ts-node": "10.9.1", "ts-toolbelt": "6.15.4", "tsickle": "^0.39.1", - "tslib": "^2.0.0", + "tslib": "^2.3.0", "tslint": "~6.1.0", "typescript": "4.8.4", "zone.js": "0.11.4" }, "dependencies": {}, "lint-staged": { - "**/*.{js,jsx,ts,tsx}":[ + "**/*.{js,jsx,ts,tsx}": [ "npx prettier --write --config .prettierrc " ] } diff --git a/npm/ng-packs/packages/core/src/lib/abstracts/auth.guard.ts b/npm/ng-packs/packages/core/src/lib/abstracts/auth.guard.ts new file mode 100644 index 0000000000..023b8110dc --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/abstracts/auth.guard.ts @@ -0,0 +1,15 @@ +import { CanActivate, UrlTree } from '@angular/router'; +import { Observable } from 'rxjs'; +import { Injectable } from '@angular/core'; +@Injectable({ + providedIn: 'root', +}) +export class AuthGuard implements CanActivate, IAuthGuard { + canActivate(): Observable | boolean | UrlTree { + console.error('You should add @abp/ng-oauth packages or create your own auth packages.'); + return false; + } +} +export interface IAuthGuard { + canActivate(): Observable | boolean | UrlTree; +} diff --git a/npm/ng-packs/packages/core/src/lib/abstracts/auth.service.ts b/npm/ng-packs/packages/core/src/lib/abstracts/auth.service.ts new file mode 100644 index 0000000000..97bf0b7b24 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/abstracts/auth.service.ts @@ -0,0 +1,59 @@ +import { Injectable } from '@angular/core'; +import { Params } from '@angular/router'; +import { Observable, of } from 'rxjs'; +import { LoginParams } from '../models/auth'; + +/** + * Abstract service for Authentication. + */ +@Injectable({ + providedIn: 'root', +}) +export class AuthService implements IAuthService { + constructor() {} + + private warningMessage() { + console.error('You should add @abp/ng-oauth packages or create your own auth packages.'); + } + + init(): Promise { + this.warningMessage(); + return Promise.resolve(undefined); + } + + login(params: LoginParams): Observable { + this.warningMessage(); + return of(undefined); + } + + logout(queryParams?: Params): Observable { + this.warningMessage(); + return of(undefined); + } + + navigateToLogin(queryParams?: Params): void {} + + get isInternalAuth() { + throw new Error('not implemented'); + return false; + } + + get isAuthenticated(): boolean { + this.warningMessage(); + return false; + } +} + +export interface IAuthService { + get isInternalAuth(): boolean; + + get isAuthenticated(): boolean; + + init(): Promise; + + logout(queryParams?: Params): Observable; + + navigateToLogin(queryParams?: Params): void; + + login(params: LoginParams): Observable; +} diff --git a/npm/ng-packs/packages/core/src/lib/abstracts/index.ts b/npm/ng-packs/packages/core/src/lib/abstracts/index.ts index a4e501a850..6a32fcf8eb 100644 --- a/npm/ng-packs/packages/core/src/lib/abstracts/index.ts +++ b/npm/ng-packs/packages/core/src/lib/abstracts/index.ts @@ -1 +1,3 @@ export * from './ng-model.component'; +export * from './auth.guard'; +export * from './auth.service'; diff --git a/npm/ng-packs/packages/core/src/lib/core.module.ts b/npm/ng-packs/packages/core/src/lib/core.module.ts index 212bf02654..4cf289a240 100644 --- a/npm/ng-packs/packages/core/src/lib/core.module.ts +++ b/npm/ng-packs/packages/core/src/lib/core.module.ts @@ -1,9 +1,8 @@ import { CommonModule } from '@angular/common'; -import { HTTP_INTERCEPTORS, HttpClientModule, HttpClientXsrfModule } from '@angular/common/http'; +import { HttpClientModule, HttpClientXsrfModule } from '@angular/common/http'; import { APP_INITIALIZER, Injector, ModuleWithProviders, NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; -import { OAuthModule, OAuthService, OAuthStorage } from 'angular-oauth2-oidc'; import { AbstractNgModelComponent } from './abstracts/ng-model.component'; import { DynamicLayoutComponent } from './components/dynamic-layout.component'; import { ReplaceableRouteContainerComponent } from './components/replaceable-route-container.component'; @@ -16,9 +15,7 @@ import { InitDirective } from './directives/init.directive'; import { PermissionDirective } from './directives/permission.directive'; import { ReplaceableTemplateDirective } from './directives/replaceable-template.directive'; import { StopPropagationDirective } from './directives/stop-propagation.directive'; -import { OAuthConfigurationHandler } from './handlers/oauth-configuration.handler'; import { RoutesHandler } from './handlers/routes.handler'; -import { ApiInterceptor } from './interceptors/api.interceptor'; import { LocalizationModule } from './localization.module'; import { ABP } from './models/common'; import { LocalizationPipe } from './pipes/localization.pipe'; @@ -27,7 +24,6 @@ import { ToInjectorPipe } from './pipes/to-injector.pipe'; import { CookieLanguageProvider } from './providers/cookie-language.provider'; import { LocaleProvider } from './providers/locale.provider'; import { LocalizationService } from './services/localization.service'; -import { oAuthStorage } from './strategies/auth-flow.strategy'; import { localizationContributor, LOCALIZATIONS } from './tokens/localization.token'; import { CORE_OPTIONS, coreOptionsFactory } from './tokens/options.token'; import { TENANT_KEY } from './tokens/tenant-key.token'; @@ -37,12 +33,8 @@ import { getInitialData, localeInitializer } from './utils/initial-utils'; import { ShortDateTimePipe } from './pipes/short-date-time.pipe'; import { ShortTimePipe } from './pipes/short-time.pipe'; import { ShortDatePipe } from './pipes/short-date.pipe'; -import { TimeoutLimitedOAuthService } from './services/timeout-limited-oauth.service'; import { IncludeLocalizationResourcesProvider } from './providers/include-localization-resources.provider'; - -export function storageFactory(): OAuthStorage { - return oAuthStorage; -} +import { AuthGuard } from './abstracts/auth.guard'; /** * BaseCoreModule is the module that holds @@ -77,7 +69,6 @@ export function storageFactory(): OAuthStorage { ShortDatePipe, ], imports: [ - OAuthModule, CommonModule, HttpClientModule, FormsModule, @@ -117,7 +108,6 @@ export class BaseCoreModule {} imports: [ BaseCoreModule, LocalizationModule, - OAuthModule, HttpClientXsrfModule.withOptions({ cookieName: 'XSRF-TOKEN', headerName: 'RequestVerificationToken', @@ -138,7 +128,6 @@ export class CoreModule { return { ngModule: RootCoreModule, providers: [ - OAuthModule.forRoot().providers, LocaleProvider, CookieLanguageProvider, { @@ -150,17 +139,6 @@ export class CoreModule { useFactory: coreOptionsFactory, deps: ['CORE_OPTIONS'], }, - { - provide: HTTP_INTERCEPTORS, - useExisting: ApiInterceptor, - multi: true, - }, - { - provide: APP_INITIALIZER, - multi: true, - deps: [OAuthConfigurationHandler], - useFactory: noop, - }, { provide: APP_INITIALIZER, multi: true, @@ -185,8 +163,7 @@ export class CoreModule { deps: [RoutesHandler], useFactory: noop, }, - { provide: OAuthStorage, useFactory: storageFactory }, - { provide: OAuthService, useClass: TimeoutLimitedOAuthService }, + { provide: TENANT_KEY, useValue: options.tenantKey || '__tenant' }, { provide: LOCALIZATIONS, @@ -194,7 +171,7 @@ export class CoreModule { useValue: localizationContributor(options.localizations), deps: [LocalizationService], }, - IncludeLocalizationResourcesProvider + IncludeLocalizationResourcesProvider, ], }; } diff --git a/npm/ng-packs/packages/core/src/lib/guards/index.ts b/npm/ng-packs/packages/core/src/lib/guards/index.ts index 6d31d6bf14..566b7d166d 100644 --- a/npm/ng-packs/packages/core/src/lib/guards/index.ts +++ b/npm/ng-packs/packages/core/src/lib/guards/index.ts @@ -1,2 +1 @@ -export * from './auth.guard'; export * from './permission.guard'; diff --git a/npm/ng-packs/packages/core/src/lib/handlers/index.ts b/npm/ng-packs/packages/core/src/lib/handlers/index.ts index e11b317f45..b666218d93 100644 --- a/npm/ng-packs/packages/core/src/lib/handlers/index.ts +++ b/npm/ng-packs/packages/core/src/lib/handlers/index.ts @@ -1,2 +1 @@ -export * from './oauth-configuration.handler'; export * from './routes.handler'; diff --git a/npm/ng-packs/packages/core/src/lib/models/index.ts b/npm/ng-packs/packages/core/src/lib/models/index.ts index 67e1c4ed38..0549dd0128 100644 --- a/npm/ng-packs/packages/core/src/lib/models/index.ts +++ b/npm/ng-packs/packages/core/src/lib/models/index.ts @@ -1,4 +1,3 @@ -export * from './auth'; export * from './common'; export * from './dtos'; export * from './environment'; @@ -7,3 +6,4 @@ export * from './replaceable-components'; export * from './rest'; export * from './session'; export * from './utility'; +export * from './auth'; diff --git a/npm/ng-packs/packages/core/src/lib/services/auth.service.ts b/npm/ng-packs/packages/core/src/lib/services/auth.service.ts deleted file mode 100644 index aa920c1633..0000000000 --- a/npm/ng-packs/packages/core/src/lib/services/auth.service.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Injectable, Injector } from '@angular/core'; -import { Params } from '@angular/router'; -import { from, Observable } from 'rxjs'; -import { filter, map, switchMap, take, tap } from 'rxjs/operators'; -import { LoginParams } from '../models/auth'; -import { AuthFlowStrategy, AUTH_FLOW_STRATEGY } from '../strategies/auth-flow.strategy'; -import { EnvironmentService } from './environment.service'; - -@Injectable({ - providedIn: 'root', -}) -export class AuthService { - private strategy: AuthFlowStrategy; - - get isInternalAuth() { - return this.strategy.isInternalAuth; - } - - constructor(protected injector: Injector) {} - - async init() { - const environmentService = this.injector.get(EnvironmentService); - - return environmentService - .getEnvironment$() - .pipe( - map(env => env?.oAuthConfig), - filter(oAuthConfig => !!oAuthConfig), - tap(oAuthConfig => { - this.strategy = - oAuthConfig.responseType === 'code' - ? AUTH_FLOW_STRATEGY.Code(this.injector) - : AUTH_FLOW_STRATEGY.Password(this.injector); - }), - switchMap(() => from(this.strategy.init())), - take(1), - ) - .toPromise(); - } - - logout(queryParams?: Params): Observable { - return this.strategy.logout(queryParams); - } - - navigateToLogin(queryParams?: Params) { - this.strategy.navigateToLogin(queryParams); - } - - login(params: LoginParams) { - return this.strategy.login(params); - } -} diff --git a/npm/ng-packs/packages/core/src/lib/services/index.ts b/npm/ng-packs/packages/core/src/lib/services/index.ts index 7584316db0..209d2a881e 100644 --- a/npm/ng-packs/packages/core/src/lib/services/index.ts +++ b/npm/ng-packs/packages/core/src/lib/services/index.ts @@ -1,4 +1,3 @@ -export * from './auth.service'; export * from './config-state.service'; export * from './content-projection.service'; export * from './dom-insertion.service'; @@ -19,4 +18,3 @@ export * from './routes.service'; export * from './session-state.service'; export * from './subscription.service'; export * from './track-by.service'; -export * from './timeout-limited-oauth.service'; diff --git a/npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts b/npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts deleted file mode 100644 index 9ac9283b5d..0000000000 --- a/npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts +++ /dev/null @@ -1,262 +0,0 @@ -import { HttpHeaders } from '@angular/common/http'; -import { Injector } from '@angular/core'; -import { Params, Router } from '@angular/router'; -import { - AuthConfig, - OAuthErrorEvent, - OAuthInfoEvent, - OAuthService, - OAuthStorage -} from 'angular-oauth2-oidc'; -import { from, Observable, of, pipe } from 'rxjs'; -import { filter, switchMap, tap } from 'rxjs/operators'; -import { LoginParams } from '../models/auth'; -import { ConfigStateService } from '../services/config-state.service'; -import { EnvironmentService } from '../services/environment.service'; -import { HttpErrorReporterService } from '../services/http-error-reporter.service'; -import { SessionStateService } from '../services/session-state.service'; -import { TENANT_KEY } from '../tokens/tenant-key.token'; -import { removeRememberMe, setRememberMe } from '../utils/auth-utils'; -import { noop } from '../utils/common-utils'; - -export const oAuthStorage = localStorage; - -export abstract class AuthFlowStrategy { - abstract readonly isInternalAuth: boolean; - - protected httpErrorReporter: HttpErrorReporterService; - protected environment: EnvironmentService; - protected configState: ConfigStateService; - protected oAuthService: OAuthService; - protected oAuthConfig: AuthConfig; - protected sessionState: SessionStateService; - protected tenantKey: string; - - abstract checkIfInternalAuth(queryParams?: Params): boolean; - abstract navigateToLogin(queryParams?: Params): void; - abstract logout(queryParams?: Params): Observable; - abstract login(params?: LoginParams | Params): Observable; - - private catchError = err => { - this.httpErrorReporter.reportError(err); - return of(null); - }; - - constructor(protected injector: Injector) { - this.httpErrorReporter = injector.get(HttpErrorReporterService); - this.environment = injector.get(EnvironmentService); - this.configState = injector.get(ConfigStateService); - this.oAuthService = injector.get(OAuthService); - this.sessionState = injector.get(SessionStateService); - this.oAuthConfig = this.environment.getEnvironment().oAuthConfig; - this.tenantKey = injector.get(TENANT_KEY); - - this.listenToOauthErrors(); - } - - async init(): Promise { - const shouldClear = shouldStorageClear( - this.environment.getEnvironment().oAuthConfig.clientId, - oAuthStorage, - ); - if (shouldClear) clearOAuthStorage(oAuthStorage); - - this.oAuthService.configure(this.oAuthConfig); - - this.oAuthService.events - .pipe(filter(event => event.type === 'token_refresh_error')) - .subscribe(() => this.navigateToLogin()); - - return this.oAuthService - .loadDiscoveryDocument() - .then(() => { - if (this.oAuthService.hasValidAccessToken() || !this.oAuthService.getRefreshToken()) { - return Promise.resolve(); - } - - return this.refreshToken(); - }) - .catch(this.catchError); - } - - protected refreshToken() { - return this.oAuthService.refreshToken().catch(() => clearOAuthStorage()); - } - - protected listenToOauthErrors() { - this.oAuthService.events - .pipe( - filter(event => event instanceof OAuthErrorEvent), - tap(() => clearOAuthStorage()), - switchMap(() => this.configState.refreshAppState()), - ) - .subscribe(); - } -} - -export class AuthCodeFlowStrategy extends AuthFlowStrategy { - readonly isInternalAuth = false; - - async init() { - return super - .init() - .then(() => this.oAuthService.tryLogin().catch(noop)) - .then(() => this.oAuthService.setupAutomaticSilentRefresh({}, 'access_token')); - } - - navigateToLogin(queryParams?: Params) { - this.oAuthService.initCodeFlow('', this.getCultureParams(queryParams)); - } - - checkIfInternalAuth(queryParams?: Params) { - this.oAuthService.initCodeFlow('', this.getCultureParams(queryParams)); - return false; - } - - logout(queryParams?: Params) { - return from(this.oAuthService.revokeTokenAndLogout(this.getCultureParams(queryParams))); - } - - login(queryParams?: Params) { - this.oAuthService.initCodeFlow('', this.getCultureParams(queryParams)); - return of(null); - } - - private getCultureParams(queryParams?: Params) { - const lang = this.sessionState.getLanguage(); - const culture = { culture: lang, 'ui-culture': lang }; - return { ...(lang && culture), ...queryParams }; - } -} - -export class AuthPasswordFlowStrategy extends AuthFlowStrategy { - readonly isInternalAuth = true; - private cookieKey = 'rememberMe'; - private storageKey = 'passwordFlow'; - - private listenToTokenExpiration() { - this.oAuthService.events - .pipe( - filter( - event => - event instanceof OAuthInfoEvent && - event.type === 'token_expires' && - event.info === 'access_token', - ), - ) - .subscribe(() => { - if (this.oAuthService.getRefreshToken()) { - this.refreshToken(); - } else { - this.oAuthService.logOut(); - removeRememberMe(); - this.configState.refreshAppState().subscribe(); - } - }); - } - - async init() { - if (!getCookieValueByName(this.cookieKey) && localStorage.getItem(this.storageKey)) { - this.oAuthService.logOut(); - } - - return super.init().then(() => this.listenToTokenExpiration()); - } - - navigateToLogin(queryParams?: Params) { - const router = this.injector.get(Router); - router.navigate(['/account/login'], { queryParams }); - } - - checkIfInternalAuth() { - return true; - } - - login(params: LoginParams): Observable { - const tenant = this.sessionState.getTenant(); - - return from( - this.oAuthService.fetchTokenUsingPasswordFlow( - params.username, - params.password, - new HttpHeaders({ ...(tenant && tenant.id && { [this.tenantKey]: tenant.id }) }), - ), - ).pipe(this.pipeToLogin(params)); - } - - pipeToLogin(params: Pick) { - const router = this.injector.get(Router); - - return pipe( - switchMap(() => this.configState.refreshAppState()), - tap(() => { - setRememberMe(params.rememberMe); - if (params.redirectUrl) router.navigate([params.redirectUrl]); - }), - ); - } - - logout(queryParams?: Params) { - const router = this.injector.get(Router); - - return from(this.oAuthService.revokeTokenAndLogout(queryParams)).pipe( - switchMap(() => this.configState.refreshAppState()), - tap(() => { - router.navigateByUrl('/'); - removeRememberMe(); - }), - ); - } - - protected refreshToken() { - return this.oAuthService.refreshToken().catch(() => { - clearOAuthStorage(); - removeRememberMe(); - }); - } -} - -export const AUTH_FLOW_STRATEGY = { - Code(injector: Injector) { - return new AuthCodeFlowStrategy(injector); - }, - Password(injector: Injector) { - return new AuthPasswordFlowStrategy(injector); - }, -}; - -export function clearOAuthStorage(storage: OAuthStorage = oAuthStorage) { - const keys = [ - 'access_token', - 'id_token', - 'refresh_token', - 'nonce', - 'PKCE_verifier', - 'expires_at', - 'id_token_claims_obj', - 'id_token_expires_at', - 'id_token_stored_at', - 'access_token_stored_at', - 'granted_scopes', - 'session_state', - ]; - - keys.forEach(key => storage.removeItem(key)); -} - -function shouldStorageClear(clientId: string, storage: OAuthStorage): boolean { - const key = 'abpOAuthClientId'; - if (!storage.getItem(key)) { - storage.setItem(key, clientId); - return false; - } - - const shouldClear = storage.getItem(key) !== clientId; - if (shouldClear) storage.setItem(key, clientId); - return shouldClear; -} - -function getCookieValueByName(name: string) { - const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)')); - return match ? match[2] : ''; -} diff --git a/npm/ng-packs/packages/core/src/lib/utils/index.ts b/npm/ng-packs/packages/core/src/lib/utils/index.ts index 56378f664f..a50b6ef731 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/index.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/index.ts @@ -1,5 +1,4 @@ export * from './array-utils'; -export * from './auth-utils'; export * from './common-utils'; export * from './date-utils'; export * from './environment-utils'; diff --git a/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts index 9c14d14b6d..bc4d11d977 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts @@ -1,16 +1,13 @@ import { registerLocaleData } from '@angular/common'; import { Injector } from '@angular/core'; -import { OAuthService } from 'angular-oauth2-oidc'; import { tap, catchError } from 'rxjs/operators'; -import { throwError } from 'rxjs'; +import { lastValueFrom, throwError } from 'rxjs'; import { ABP } from '../models/common'; import { Environment } from '../models/environment'; import { CurrentTenantDto } from '../proxy/volo/abp/asp-net-core/mvc/multi-tenancy/models'; -import { AuthService } from '../services/auth.service'; import { ConfigStateService } from '../services/config-state.service'; import { EnvironmentService } from '../services/environment.service'; import { SessionStateService } from '../services/session-state.service'; -import { clearOAuthStorage } from '../strategies/auth-flow.strategy'; import { CORE_OPTIONS } from '../tokens/options.token'; import { APP_INIT_ERROR_HANDLERS } from '../tokens/app-config.token'; import { getRemoteEnv } from './environment-utils'; @@ -25,41 +22,29 @@ export function getInitialData(injector: Injector) { environmentService.setState(options.environment as Environment); await getRemoteEnv(injector, options.environment); await parseTenantFromUrl(injector); - await injector.get(AuthService).init(); if (options.skipGetAppConfiguration) return; - return configState - .refreshAppState() - .pipe( - tap(() => checkAccessToken(injector)), - tap(() => { - const currentTenant = configState.getOne('currentTenant') as CurrentTenantDto; - injector.get(SessionStateService).setTenant(currentTenant); - }), - catchError(error => { - const appInitErrorHandlers = injector.get(APP_INIT_ERROR_HANDLERS, null); - if (appInitErrorHandlers && appInitErrorHandlers.length) { - appInitErrorHandlers.forEach(func => func(error)); - } + const result$ = configState.refreshAppState().pipe( + tap(() => { + const currentTenant = configState.getOne('currentTenant') as CurrentTenantDto; + injector.get(SessionStateService).setTenant(currentTenant); + }), + catchError(error => { + const appInitErrorHandlers = injector.get(APP_INIT_ERROR_HANDLERS, null); + if (appInitErrorHandlers && appInitErrorHandlers.length) { + appInitErrorHandlers.forEach(func => func(error)); + } - return throwError(error); - }), - ) - .toPromise(); + return throwError(error); + }), + ); + await lastValueFrom(result$); }; return fn; } -export function checkAccessToken(injector: Injector) { - const configState = injector.get(ConfigStateService); - const oAuth = injector.get(OAuthService); - if (oAuth.hasValidAccessToken() && !configState.getDeep('currentUser.id')) { - clearOAuthStorage(); - } -} - export function localeInitializer(injector: Injector) { const fn = () => { const sessionState = injector.get(SessionStateService); diff --git a/npm/ng-packs/packages/core/src/public-api.ts b/npm/ng-packs/packages/core/src/public-api.ts index a5cbd78e4e..20dec1308b 100644 --- a/npm/ng-packs/packages/core/src/public-api.ts +++ b/npm/ng-packs/packages/core/src/public-api.ts @@ -6,7 +6,6 @@ export * from './lib/core.module'; export * from './lib/directives'; export * from './lib/enums'; export * from './lib/guards'; -export * from './lib/interceptors'; export * from './lib/localization.module'; export * from './lib/models'; export * from './lib/pipes'; diff --git a/npm/ng-packs/packages/oauth/ng-package.json b/npm/ng-packs/packages/oauth/ng-package.json index 2e78d8fbe5..f9cb029dc0 100644 --- a/npm/ng-packs/packages/oauth/ng-package.json +++ b/npm/ng-packs/packages/oauth/ng-package.json @@ -1,12 +1,14 @@ { "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", - "dest": "../../dist/packages/core", + "dest": "../../dist/packages/oauth", "lib": { "entryFile": "src/public-api.ts" }, "allowedNonPeerDependencies": [ "@abp/utils", "@abp/ng.core", - "angular-oauth2-oidc" + "angular-oauth2-oidc", + "just-clone", + "just-compare" ] } diff --git a/npm/ng-packs/packages/oauth/package.json b/npm/ng-packs/packages/oauth/package.json index 9f127e7701..e945d95272 100644 --- a/npm/ng-packs/packages/oauth/package.json +++ b/npm/ng-packs/packages/oauth/package.json @@ -1,5 +1,5 @@ { - "name": "@abp/ng.core", + "name": "@abp/ng.oauth", "version": "7.0.0-rc.4", "homepage": "https://abp.io", "repository": { @@ -12,7 +12,6 @@ "angular-oauth2-oidc": "^15.0.1", "just-clone": "^6.1.1", "just-compare": "^1.4.0", - "ts-toolbelt": "6.15.4", "tslib": "^2.0.0" }, "publishConfig": { diff --git a/npm/ng-packs/packages/oauth/src/lib/guards/index.ts b/npm/ng-packs/packages/oauth/src/lib/guards/index.ts new file mode 100644 index 0000000000..b746a80626 --- /dev/null +++ b/npm/ng-packs/packages/oauth/src/lib/guards/index.ts @@ -0,0 +1 @@ +export * from './oauth.guard'; diff --git a/npm/ng-packs/packages/core/src/lib/guards/auth.guard.ts b/npm/ng-packs/packages/oauth/src/lib/guards/oauth.guard.ts similarity index 82% rename from npm/ng-packs/packages/core/src/lib/guards/auth.guard.ts rename to npm/ng-packs/packages/oauth/src/lib/guards/oauth.guard.ts index f1e73ab805..357e861ba0 100644 --- a/npm/ng-packs/packages/core/src/lib/guards/auth.guard.ts +++ b/npm/ng-packs/packages/oauth/src/lib/guards/oauth.guard.ts @@ -2,12 +2,12 @@ import { Injectable } from '@angular/core'; import { CanActivate, UrlTree } from '@angular/router'; import { OAuthService } from 'angular-oauth2-oidc'; import { Observable } from 'rxjs'; -import { AuthService } from '../services/auth.service'; +import { AuthService, IAuthGuard } from '@abp/ng.core'; @Injectable({ providedIn: 'root', }) -export class AuthGuard implements CanActivate { +export class AbpOAuthGuard implements CanActivate, IAuthGuard { constructor(private oauthService: OAuthService, private authService: AuthService) {} canActivate(): Observable | boolean | UrlTree { diff --git a/npm/ng-packs/packages/oauth/src/lib/handlers/index.ts b/npm/ng-packs/packages/oauth/src/lib/handlers/index.ts new file mode 100644 index 0000000000..0faec9db66 --- /dev/null +++ b/npm/ng-packs/packages/oauth/src/lib/handlers/index.ts @@ -0,0 +1 @@ +export * from './oauth-configuration.handler'; diff --git a/npm/ng-packs/packages/core/src/lib/handlers/oauth-configuration.handler.ts b/npm/ng-packs/packages/oauth/src/lib/handlers/oauth-configuration.handler.ts similarity index 83% rename from npm/ng-packs/packages/core/src/lib/handlers/oauth-configuration.handler.ts rename to npm/ng-packs/packages/oauth/src/lib/handlers/oauth-configuration.handler.ts index f82e382fe7..7e4cce28e4 100644 --- a/npm/ng-packs/packages/core/src/lib/handlers/oauth-configuration.handler.ts +++ b/npm/ng-packs/packages/oauth/src/lib/handlers/oauth-configuration.handler.ts @@ -2,9 +2,7 @@ import { Inject, Injectable } from '@angular/core'; import { OAuthService } from 'angular-oauth2-oidc'; import compare from 'just-compare'; import { filter, map } from 'rxjs/operators'; -import { ABP } from '../models/common'; -import { EnvironmentService } from '../services/environment.service'; -import { CORE_OPTIONS } from '../tokens/options.token'; +import { ABP, EnvironmentService, CORE_OPTIONS } from '@abp/ng.core'; @Injectable({ providedIn: 'root', diff --git a/npm/ng-packs/packages/core/src/lib/interceptors/api.interceptor.ts b/npm/ng-packs/packages/oauth/src/lib/interceptors/api.interceptor.ts similarity index 88% rename from npm/ng-packs/packages/core/src/lib/interceptors/api.interceptor.ts rename to npm/ng-packs/packages/oauth/src/lib/interceptors/api.interceptor.ts index d3dbe19ec0..91666b12a9 100644 --- a/npm/ng-packs/packages/core/src/lib/interceptors/api.interceptor.ts +++ b/npm/ng-packs/packages/oauth/src/lib/interceptors/api.interceptor.ts @@ -2,9 +2,7 @@ import { HttpHandler, HttpHeaders, HttpInterceptor, HttpRequest } from '@angular import { Inject, Injectable } from '@angular/core'; import { OAuthService } from 'angular-oauth2-oidc'; import { finalize } from 'rxjs/operators'; -import { SessionStateService } from '../services/session-state.service'; -import { HttpWaitService } from '../services/http-wait.service'; -import { TENANT_KEY } from '../tokens/tenant-key.token'; +import { SessionStateService, HttpWaitService, TENANT_KEY } from '@abp/ng.core'; @Injectable({ providedIn: 'root', diff --git a/npm/ng-packs/packages/core/src/lib/interceptors/index.ts b/npm/ng-packs/packages/oauth/src/lib/interceptors/index.ts similarity index 100% rename from npm/ng-packs/packages/core/src/lib/interceptors/index.ts rename to npm/ng-packs/packages/oauth/src/lib/interceptors/index.ts diff --git a/npm/ng-packs/packages/oauth/src/lib/oauth.module.ts b/npm/ng-packs/packages/oauth/src/lib/oauth.module.ts index 471e4e3d78..ea2874c2f9 100644 --- a/npm/ng-packs/packages/oauth/src/lib/oauth.module.ts +++ b/npm/ng-packs/packages/oauth/src/lib/oauth.module.ts @@ -1,20 +1,52 @@ -import { ModuleWithProviders, NgModule } from '@angular/core'; +import { APP_INITIALIZER, Injector, ModuleWithProviders, NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { OAuthModule as OAuthModule2, OAuthService, OAuthStorage } from 'angular-oauth2-oidc'; -import { TimeoutLimitedOAuthService } from '@abp/ng.core'; +import { OAuthModule, OAuthService, OAuthStorage } from 'angular-oauth2-oidc'; +import { AuthGuard, AuthService, noop } from '@abp/ng.core'; import { storageFactory } from './utils/storage.factory'; +import { AbpOAuthService, TimeoutLimitedOAuthService } from './services'; +import { OAuthConfigurationHandler } from './handlers/oauth-configuration.handler'; +import { initFactory } from './utils/init-factory'; +import { HTTP_INTERCEPTORS } from '@angular/common/http'; +import { ApiInterceptor } from './interceptors/api.interceptor'; +import { AbpOAuthGuard } from './guards/oauth.guard'; @NgModule({ - imports: [CommonModule, OAuthModule2], + imports: [CommonModule, OAuthModule], }) -export class OAuthModule { - static forRoot(): ModuleWithProviders { +export class AbpOAuthModule { + static forRoot(): ModuleWithProviders { return { - ngModule: OAuthModule, + ngModule: AbpOAuthModule, providers: [ - OAuthModule2.forRoot().providers, + { + provide: AuthService, + useClass: AbpOAuthService, + }, + { + provide: AuthGuard, + useClass: AbpOAuthGuard, + }, + { + provide: HTTP_INTERCEPTORS, + useExisting: ApiInterceptor, + multi: true, + }, + { + provide: APP_INITIALIZER, + multi: true, + deps: [OAuthConfigurationHandler], + useFactory: noop, + }, + { + provide: APP_INITIALIZER, + multi: true, + deps: [Injector], + useFactory: initFactory, + }, + + OAuthModule.forRoot().providers, { provide: OAuthStorage, useFactory: storageFactory }, - { provide: OAuthService, useClass: TimeoutLimitedOAuthService }, + // {provide: OAuthService, useClass: TimeoutLimitedOAuthService} ], }; } diff --git a/npm/ng-packs/packages/oauth/src/lib/services/index.ts b/npm/ng-packs/packages/oauth/src/lib/services/index.ts new file mode 100644 index 0000000000..a0e99cbcae --- /dev/null +++ b/npm/ng-packs/packages/oauth/src/lib/services/index.ts @@ -0,0 +1,2 @@ +export * from './oauth.service'; +export * from './timeout-limited-oauth.service'; diff --git a/npm/ng-packs/packages/oauth/src/lib/services/oauth.service.ts b/npm/ng-packs/packages/oauth/src/lib/services/oauth.service.ts new file mode 100644 index 0000000000..9588fef305 --- /dev/null +++ b/npm/ng-packs/packages/oauth/src/lib/services/oauth.service.ts @@ -0,0 +1,57 @@ +import { Injectable, Injector } from '@angular/core'; +import { Params } from '@angular/router'; +import { from, Observable, lastValueFrom } from 'rxjs'; +import { filter, map, switchMap, take, tap } from 'rxjs/operators'; +import { IAuthService, LoginParams } from '@abp/ng.core'; +import { AuthFlowStrategy } from '../strategies'; +import { EnvironmentService } from '@abp/ng.core'; +import { AUTH_FLOW_STRATEGY } from '../tokens/ auth-flow-strategy'; +import { OAuthService } from 'angular-oauth2-oidc'; + +@Injectable({ + providedIn: 'root', +}) +export class AbpOAuthService implements IAuthService { + private strategy: AuthFlowStrategy; + + get isInternalAuth() { + return this.strategy.isInternalAuth; + } + + constructor(protected injector: Injector, private oAuthService: OAuthService) {} + + async init() { + const environmentService = this.injector.get(EnvironmentService); + + const result$ = environmentService.getEnvironment$().pipe( + map(env => env?.oAuthConfig), + filter(oAuthConfig => !!oAuthConfig), + tap(oAuthConfig => { + this.strategy = + oAuthConfig.responseType === 'code' + ? AUTH_FLOW_STRATEGY.Code(this.injector) + : AUTH_FLOW_STRATEGY.Password(this.injector); + }), + switchMap(() => from(this.strategy.init())), + take(1), + ); + + return await lastValueFrom(result$); + } + + logout(queryParams?: Params): Observable { + return this.strategy.logout(queryParams); + } + + navigateToLogin(queryParams?: Params) { + this.strategy.navigateToLogin(queryParams); + } + + login(params: LoginParams) { + return this.strategy.login(params); + } + + get isAuthenticated(): boolean { + return this.oAuthService.hasValidAccessToken(); + } +} diff --git a/npm/ng-packs/packages/core/src/lib/services/timeout-limited-oauth.service.ts b/npm/ng-packs/packages/oauth/src/lib/services/timeout-limited-oauth.service.ts similarity index 70% rename from npm/ng-packs/packages/core/src/lib/services/timeout-limited-oauth.service.ts rename to npm/ng-packs/packages/oauth/src/lib/services/timeout-limited-oauth.service.ts index bdce294968..8b9d4ef393 100644 --- a/npm/ng-packs/packages/core/src/lib/services/timeout-limited-oauth.service.ts +++ b/npm/ng-packs/packages/oauth/src/lib/services/timeout-limited-oauth.service.ts @@ -1,6 +1,7 @@ -import { Injectable, NgZone, Optional } from '@angular/core'; +import { Injectable } from '@angular/core'; import { OAuthService } from 'angular-oauth2-oidc'; +/** * @deprecated No need to override official service. It should be fixed in 15 or never version of angular-oauth2-oidc*/ @Injectable() export class TimeoutLimitedOAuthService extends OAuthService { protected override calcTimeout(storedAt: number, expiration: number): number { diff --git a/npm/ng-packs/packages/oauth/src/lib/strategies/auth-flow-strategy.ts b/npm/ng-packs/packages/oauth/src/lib/strategies/auth-flow-strategy.ts index f74a223fbd..3b5c89d4ac 100644 --- a/npm/ng-packs/packages/oauth/src/lib/strategies/auth-flow-strategy.ts +++ b/npm/ng-packs/packages/oauth/src/lib/strategies/auth-flow-strategy.ts @@ -1,18 +1,23 @@ import { Injector } from '@angular/core'; import { Params } from '@angular/router'; -import { AuthConfig, OAuthErrorEvent, OAuthService, OAuthStorage } from 'angular-oauth2-oidc'; +import { + AuthConfig, + OAuthErrorEvent, + OAuthService as OAuthService2, + OAuthStorage, +} from 'angular-oauth2-oidc'; import { Observable, of } from 'rxjs'; import { filter, switchMap, tap } from 'rxjs/operators'; -import { LoginParams } from '../models/auth'; import { + LoginParams, ConfigStateService, EnvironmentService, HttpErrorReporterService, SessionStateService, TENANT_KEY, } from '@abp/ng.core'; -import { oAuthStorage } from './oauth-storage'; -import { clearOAuthStorage } from './clear-o-auth-storage'; +import { clearOAuthStorage } from '../utils/clear-o-auth-storage'; +import { oAuthStorage } from '../utils/oauth-storage'; export abstract class AuthFlowStrategy { abstract readonly isInternalAuth: boolean; @@ -20,7 +25,7 @@ export abstract class AuthFlowStrategy { protected httpErrorReporter: HttpErrorReporterService; protected environment: EnvironmentService; protected configState: ConfigStateService; - protected oAuthService: OAuthService; + protected oAuthService: OAuthService2; protected oAuthConfig: AuthConfig; protected sessionState: SessionStateService; protected tenantKey: string; @@ -42,7 +47,7 @@ export abstract class AuthFlowStrategy { this.httpErrorReporter = injector.get(HttpErrorReporterService); this.environment = injector.get(EnvironmentService); this.configState = injector.get(ConfigStateService); - this.oAuthService = injector.get(OAuthService); + this.oAuthService = injector.get(OAuthService2); this.sessionState = injector.get(SessionStateService); this.oAuthConfig = this.environment.getEnvironment().oAuthConfig; this.tenantKey = injector.get(TENANT_KEY); diff --git a/npm/ng-packs/packages/oauth/src/lib/strategies/auth-password-flow-strategy.ts b/npm/ng-packs/packages/oauth/src/lib/strategies/auth-password-flow-strategy.ts index 703042956d..ff795fc38d 100644 --- a/npm/ng-packs/packages/oauth/src/lib/strategies/auth-password-flow-strategy.ts +++ b/npm/ng-packs/packages/oauth/src/lib/strategies/auth-password-flow-strategy.ts @@ -4,9 +4,9 @@ import { Params, Router } from '@angular/router'; import { from, Observable, pipe } from 'rxjs'; import { HttpHeaders } from '@angular/common/http'; import { AuthFlowStrategy } from './auth-flow-strategy'; -import { removeRememberMe, setRememberMe } from './auth-utils'; -import { LoginParams } from '../models'; -import { clearOAuthStorage } from './clear-o-auth-storage'; +import { removeRememberMe, setRememberMe } from '../utils/auth-utils'; +import { LoginParams } from '@abp/ng.core'; +import { clearOAuthStorage } from '../utils/clear-o-auth-storage'; function getCookieValueByName(name: string) { const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)')); diff --git a/npm/ng-packs/packages/oauth/src/lib/tests/sample.spec.ts b/npm/ng-packs/packages/oauth/src/lib/tests/sample.spec.ts new file mode 100644 index 0000000000..83925bb9c0 --- /dev/null +++ b/npm/ng-packs/packages/oauth/src/lib/tests/sample.spec.ts @@ -0,0 +1,5 @@ +describe('Test', () => { + it('should be passed', () => { + expect(true).toBe(true); + }); +}); diff --git a/npm/ng-packs/packages/oauth/src/lib/tokens/ auth-flow-strategy.ts b/npm/ng-packs/packages/oauth/src/lib/tokens/ auth-flow-strategy.ts new file mode 100644 index 0000000000..a2c8aa1624 --- /dev/null +++ b/npm/ng-packs/packages/oauth/src/lib/tokens/ auth-flow-strategy.ts @@ -0,0 +1,12 @@ +import { Injector } from '@angular/core'; +import { AuthCodeFlowStrategy } from '../strategies/auth-code-flow-strategy'; +import { AuthPasswordFlowStrategy } from '../strategies/auth-password-flow-strategy'; + +export const AUTH_FLOW_STRATEGY = { + Code(injector: Injector) { + return new AuthCodeFlowStrategy(injector); + }, + Password(injector: Injector) { + return new AuthPasswordFlowStrategy(injector); + }, +}; diff --git a/npm/ng-packs/packages/oauth/src/lib/tokens/index.ts b/npm/ng-packs/packages/oauth/src/lib/tokens/index.ts new file mode 100644 index 0000000000..2ee0bf7fde --- /dev/null +++ b/npm/ng-packs/packages/oauth/src/lib/tokens/index.ts @@ -0,0 +1 @@ +export * from './ auth-flow-strategy'; diff --git a/npm/ng-packs/packages/core/src/lib/utils/auth-utils.ts b/npm/ng-packs/packages/oauth/src/lib/utils/auth-utils.ts similarity index 93% rename from npm/ng-packs/packages/core/src/lib/utils/auth-utils.ts rename to npm/ng-packs/packages/oauth/src/lib/utils/auth-utils.ts index 10fe1e1177..2ac24abf22 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/auth-utils.ts +++ b/npm/ng-packs/packages/oauth/src/lib/utils/auth-utils.ts @@ -3,8 +3,7 @@ import { Router } from '@angular/router'; import { OAuthStorage, TokenResponse } from 'angular-oauth2-oidc'; import { pipe } from 'rxjs'; import { switchMap, tap } from 'rxjs/operators'; -import { LoginParams } from '../models/auth'; -import { ConfigStateService } from '../services/config-state.service'; +import { ConfigStateService, LoginParams } from '@abp/ng.core'; const cookieKey = 'rememberMe'; const storageKey = 'passwordFlow'; diff --git a/npm/ng-packs/packages/oauth/src/lib/utils/clear-o-auth-storage.ts b/npm/ng-packs/packages/oauth/src/lib/utils/clear-o-auth-storage.ts new file mode 100644 index 0000000000..d9b662c833 --- /dev/null +++ b/npm/ng-packs/packages/oauth/src/lib/utils/clear-o-auth-storage.ts @@ -0,0 +1,21 @@ +import { OAuthStorage } from 'angular-oauth2-oidc'; +import { oAuthStorage } from './oauth-storage'; + +export function clearOAuthStorage(storage: OAuthStorage = oAuthStorage) { + const keys = [ + 'access_token', + 'id_token', + 'refresh_token', + 'nonce', + 'PKCE_verifier', + 'expires_at', + 'id_token_claims_obj', + 'id_token_expires_at', + 'id_token_stored_at', + 'access_token_stored_at', + 'granted_scopes', + 'session_state', + ]; + + keys.forEach(key => storage.removeItem(key)); +} diff --git a/npm/ng-packs/packages/oauth/src/lib/utils/index.ts b/npm/ng-packs/packages/oauth/src/lib/utils/index.ts new file mode 100644 index 0000000000..e948a3d6b7 --- /dev/null +++ b/npm/ng-packs/packages/oauth/src/lib/utils/index.ts @@ -0,0 +1,5 @@ +export * from './oauth-storage'; +export * from './storage.factory'; +export * from './auth-utils'; +export * from './clear-o-auth-storage'; +export * from './init-factory'; diff --git a/npm/ng-packs/packages/oauth/src/lib/utils/init-factory.ts b/npm/ng-packs/packages/oauth/src/lib/utils/init-factory.ts new file mode 100644 index 0000000000..4395384139 --- /dev/null +++ b/npm/ng-packs/packages/oauth/src/lib/utils/init-factory.ts @@ -0,0 +1,31 @@ +import { ABP, AuthService, ConfigStateService, CORE_OPTIONS } from '@abp/ng.core'; +import { Injector } from '@angular/core'; +import { OAuthService } from 'angular-oauth2-oidc'; +import { clearOAuthStorage } from './clear-o-auth-storage'; +import { lastValueFrom } from 'rxjs'; +import { tap } from 'rxjs/operators'; + +export function initFactory(injector: Injector): () => Promise { + return async () => { + const authService = injector.get(AuthService); + await authService.init(); + const configState = injector.get(ConfigStateService); + + const options = injector.get(CORE_OPTIONS) as ABP.Root; + + if (options.skipGetAppConfiguration) { + return; + } + + const result$ = configState.refreshAppState().pipe(tap(() => checkAccessToken(injector))); + await lastValueFrom(result$); + }; +} + +export function checkAccessToken(injector: Injector) { + const configState = injector.get(ConfigStateService); + const oAuth = injector.get(OAuthService); + if (oAuth.hasValidAccessToken() && !configState.getDeep('currentUser.id')) { + clearOAuthStorage(); + } +} diff --git a/npm/ng-packs/packages/oauth/src/lib/utils/oauth-storage.ts b/npm/ng-packs/packages/oauth/src/lib/utils/oauth-storage.ts new file mode 100644 index 0000000000..9fb591e1c7 --- /dev/null +++ b/npm/ng-packs/packages/oauth/src/lib/utils/oauth-storage.ts @@ -0,0 +1 @@ +export const oAuthStorage = localStorage; diff --git a/npm/ng-packs/packages/oauth/src/lib/utils/storage.factory.ts b/npm/ng-packs/packages/oauth/src/lib/utils/storage.factory.ts new file mode 100644 index 0000000000..5d76ecf244 --- /dev/null +++ b/npm/ng-packs/packages/oauth/src/lib/utils/storage.factory.ts @@ -0,0 +1,6 @@ +import { OAuthStorage } from 'angular-oauth2-oidc'; +import { oAuthStorage } from './oauth-storage'; + +export function storageFactory(): OAuthStorage { + return oAuthStorage; +} diff --git a/npm/ng-packs/packages/oauth/src/public-api.ts b/npm/ng-packs/packages/oauth/src/public-api.ts index e69de29bb2..d6154d0883 100644 --- a/npm/ng-packs/packages/oauth/src/public-api.ts +++ b/npm/ng-packs/packages/oauth/src/public-api.ts @@ -0,0 +1,8 @@ +export * from './lib/oauth.module'; +export * from './lib/utils'; +export * from './lib/tokens'; +export * from './lib/services'; +export * from './lib/strategies'; +export * from './lib/handlers'; +export * from './lib/interceptors'; +export * from './lib/guards'; diff --git a/npm/ng-packs/packages/schematics/collection.json b/npm/ng-packs/packages/schematics/collection.json new file mode 100644 index 0000000000..16f148d78a --- /dev/null +++ b/npm/ng-packs/packages/schematics/collection.json @@ -0,0 +1,40 @@ +{ + "schematics": { + "proxy-add": { + "description": "ABP Proxy Generator Add Schematics", + "factory": "./commands/proxy-add", + "schema": "./commands/proxy-add/schema.json" + }, + "proxy-index": { + "description": "ABP Proxy Generator Index Schematics", + "factory": "./commands/proxy-index", + "schema": "./commands/proxy-index/schema.json" + }, + "proxy-refresh": { + "description": "ABP Proxy Generator Refresh Schematics", + "factory": "./commands/proxy-refresh", + "schema": "./commands/proxy-refresh/schema.json" + }, + "proxy-remove": { + "description": "ABP Proxy Generator Remove Schematics", + "factory": "./commands/proxy-remove", + "schema": "./commands/proxy-remove/schema.json" + }, + "api": { + "description": "ABP API Generator Schematics", + "factory": "./commands/api", + "schema": "./commands/api/schema.json" + }, + "create-lib": { + "description": "ABP Create Library Schematics", + "factory": "./commands/create-lib", + "schema": "./commands/create-lib/schema.json" + }, + "change-theme": { + "description": "ABP Change Styles of Theme Schematics", + "factory": "./commands/change-theme", + "schema": "./commands/change-theme/schema.json" + + } + } +} diff --git a/npm/ng-packs/packages/theme-shared/ng-package.json b/npm/ng-packs/packages/theme-shared/ng-package.json index bc84e15a22..8082dca0c9 100644 --- a/npm/ng-packs/packages/theme-shared/ng-package.json +++ b/npm/ng-packs/packages/theme-shared/ng-package.json @@ -11,6 +11,7 @@ "@ngx-validate/core", "@swimlane/ngx-datatable", "bootstrap", - "@popperjs/core" + "@popperjs/core", + "@abp/ng.oauth" ] } diff --git a/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts b/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts index a3db3d2017..ce5647bccd 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts @@ -83,6 +83,7 @@ export class ErrorHandler { protected cfRes: ComponentFactoryResolver; protected rendererFactory: RendererFactory2; protected httpErrorConfig: HttpErrorConfig; + private authService: AuthService; constructor(protected injector: Injector) { this.httpErrorReporter = injector.get(HttpErrorReporterService); @@ -91,6 +92,7 @@ export class ErrorHandler { this.cfRes = injector.get(ComponentFactoryResolver); this.rendererFactory = injector.get(RendererFactory2); this.httpErrorConfig = injector.get('HTTP_ERROR_CONFIG'); + this.authService = this.injector.get(AuthService); this.listenToRestError(); this.listenToRouterError(); @@ -284,7 +286,7 @@ export class ErrorHandler { } private navigateToLogin() { - this.injector.get(AuthService).navigateToLogin(); + this.authService.navigateToLogin(); } createErrorComponent(instance: Partial) { diff --git a/npm/ng-packs/tsconfig.base.json b/npm/ng-packs/tsconfig.base.json index e9cd6ac6b6..ac29fd53ad 100644 --- a/npm/ng-packs/tsconfig.base.json +++ b/npm/ng-packs/tsconfig.base.json @@ -43,7 +43,8 @@ "@abp/ng.theme.basic/testing": ["packages/theme-basic/testing/src/public-api.ts"], "@abp/ng.theme.shared": ["packages/theme-shared/src/public-api.ts"], "@abp/ng.theme.shared/extensions": ["packages/theme-shared/extensions/src/public-api.ts"], - "@abp/ng.theme.shared/testing": ["packages/theme-shared/testing/src/public-api.ts"] + "@abp/ng.theme.shared/testing": ["packages/theme-shared/testing/src/public-api.ts"], + "@abp/ng.oauth": ["packages/oauth/src/public-api.ts"] } }, "exclude": ["node_modules", "tmp"] From edabd352622bc958539f4972b50e3dc4c9309f6d Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Wed, 21 Dec 2022 16:59:29 +0300 Subject: [PATCH 03/20] Move tests --- .../core/src/lib/tests/initial-utils.spec.ts | 24 +--- .../src/lib/tests/auth.guard.spec.ts | 10 +- .../oauth/src/lib/tests/initial-utils.spec.ts | 110 ++++++++++++++++++ 3 files changed, 120 insertions(+), 24 deletions(-) rename npm/ng-packs/packages/{core => oauth}/src/lib/tests/auth.guard.spec.ts (81%) create mode 100644 npm/ng-packs/packages/oauth/src/lib/tests/initial-utils.spec.ts diff --git a/npm/ng-packs/packages/core/src/lib/tests/initial-utils.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/initial-utils.spec.ts index 12d27544f8..b28447be99 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/initial-utils.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/initial-utils.spec.ts @@ -1,18 +1,17 @@ import { Component, Injector } from '@angular/core'; import { createComponentFactory, Spectator } from '@ngneat/spectator/jest'; -import { OAuthService } from 'angular-oauth2-oidc'; import { of } from 'rxjs'; import { AbpApplicationConfigurationService } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-configuration.service'; import { ApplicationConfigurationDto } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/models'; import { SessionStateService } from '../services/session-state.service'; import { EnvironmentService } from '../services/environment.service'; -import { AuthService } from '../services/auth.service'; +import { AuthService } from '../abstracts/auth.service'; import { ConfigStateService } from '../services/config-state.service'; -import * as AuthFlowStrategy from '../strategies/auth-flow.strategy'; import { CORE_OPTIONS } from '../tokens/options.token'; -import { checkAccessToken, getInitialData, localeInitializer } from '../utils/initial-utils'; +import { getInitialData, localeInitializer } from '../utils/initial-utils'; import * as environmentUtils from '../utils/environment-utils'; import * as multiTenancyUtils from '../utils/multi-tenancy-utils'; +import { RestService } from '../services/rest.service'; const environment = { oAuthConfig: { issuer: 'test' } }; @@ -31,8 +30,8 @@ describe('InitialUtils', () => { ConfigStateService, AbpApplicationConfigurationService, AuthService, - OAuthService, SessionStateService, + RestService, ], providers: [ { @@ -52,6 +51,7 @@ describe('InitialUtils', () => { const environmentService = spectator.inject(EnvironmentService); const configStateService = spectator.inject(ConfigStateService); const sessionStateService = spectator.inject(SessionStateService); + const parseTenantFromUrlSpy = jest.spyOn(multiTenancyUtils, 'parseTenantFromUrl'); const getRemoteEnvSpy = jest.spyOn(environmentUtils, 'getRemoteEnv'); parseTenantFromUrlSpy.mockReturnValue(Promise.resolve()); @@ -82,20 +82,6 @@ describe('InitialUtils', () => { }); }); - describe('#checkAccessToken', () => { - test('should call logOut fn of OAuthService when token is valid and current user not found', async () => { - const injector = spectator.inject(Injector); - const injectorSpy = jest.spyOn(injector, 'get'); - const clearOAuthStorageSpy = jest.spyOn(AuthFlowStrategy, 'clearOAuthStorage'); - - injectorSpy.mockReturnValueOnce({ getDeep: () => false }); - injectorSpy.mockReturnValueOnce({ hasValidAccessToken: () => true }); - - checkAccessToken(injector); - expect(clearOAuthStorageSpy).toHaveBeenCalled(); - }); - }); - describe('#localeInitializer', () => { test('should resolve registerLocale', async () => { const injector = spectator.inject(Injector); diff --git a/npm/ng-packs/packages/core/src/lib/tests/auth.guard.spec.ts b/npm/ng-packs/packages/oauth/src/lib/tests/auth.guard.spec.ts similarity index 81% rename from npm/ng-packs/packages/core/src/lib/tests/auth.guard.spec.ts rename to npm/ng-packs/packages/oauth/src/lib/tests/auth.guard.spec.ts index 151e0ec639..62c42ae6aa 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/auth.guard.spec.ts +++ b/npm/ng-packs/packages/oauth/src/lib/tests/auth.guard.spec.ts @@ -1,13 +1,13 @@ import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest'; import { OAuthService } from 'angular-oauth2-oidc'; -import { AuthGuard } from '../guards/auth.guard'; -import { AuthService } from '../services/auth.service'; +import { AbpOAuthGuard } from '../guards/oauth.guard'; +import { AuthService } from '@Abp/ng.core'; describe('AuthGuard', () => { - let spectator: SpectatorService; - let guard: AuthGuard; + let spectator: SpectatorService; + let guard: AbpOAuthGuard; const createService = createServiceFactory({ - service: AuthGuard, + service: AbpOAuthGuard, mocks: [OAuthService, AuthService], }); diff --git a/npm/ng-packs/packages/oauth/src/lib/tests/initial-utils.spec.ts b/npm/ng-packs/packages/oauth/src/lib/tests/initial-utils.spec.ts new file mode 100644 index 0000000000..31fa3dfcf7 --- /dev/null +++ b/npm/ng-packs/packages/oauth/src/lib/tests/initial-utils.spec.ts @@ -0,0 +1,110 @@ +import { Component, Injector } from '@angular/core'; +import { createComponentFactory, Spectator } from '@ngneat/spectator/jest'; +import { OAuthService } from 'angular-oauth2-oidc'; + +import { + CORE_OPTIONS, + EnvironmentService, + AuthService, + ConfigStateService, + AbpApplicationConfigurationService, + SessionStateService, + ApplicationConfigurationDto, +} from '@abp/ng.core'; +import * as clearOAuthStorageDefault from '../utils/clear-o-auth-storage'; +import { checkAccessToken, initFactory } from '../utils/init-factory'; +import { of } from 'rxjs'; + +const environment = { oAuthConfig: { issuer: 'test' } }; + +@Component({ + selector: 'abp-dummy', + template: '', +}) +export class DummyComponent {} + +describe('InitialUtils', () => { + let spectator: Spectator; + const createComponent = createComponentFactory({ + component: DummyComponent, + mocks: [ + EnvironmentService, + ConfigStateService, + AbpApplicationConfigurationService, + AuthService, + OAuthService, + SessionStateService, + ], + providers: [ + { + provide: CORE_OPTIONS, + useValue: { + environment, + registerLocaleFn: () => Promise.resolve(), + skipGetAppConfiguration: false, + }, + }, + ], + }); + + beforeEach(() => (spectator = createComponent())); + + describe('#getInitialData', () => { + let mockInjector; + let configStateService; + let authService; + beforeEach(() => { + mockInjector = { + get: spectator.inject, + }; + configStateService = spectator.inject(ConfigStateService); + authService = spectator.inject(AuthService); + }); + + test('should called configStateService.refreshAppState', async () => { + const configRefreshAppStateSpy = jest.spyOn(configStateService, 'refreshAppState'); + const appConfigRes = { + currentTenant: { id: 'test', name: 'testing' }, + } as ApplicationConfigurationDto; + + configRefreshAppStateSpy.mockReturnValue(of(appConfigRes)); + + await initFactory(mockInjector)(); + + expect(configRefreshAppStateSpy).toHaveBeenCalled(); + }); + }); + + describe('#checkAccessToken', () => { + let injector; + let injectorSpy; + let clearOAuthStorageSpy; + beforeEach(() => { + injector = spectator.inject(Injector); + injectorSpy = jest.spyOn(injector, 'get'); + clearOAuthStorageSpy = jest.spyOn(clearOAuthStorageDefault, 'clearOAuthStorage'); + clearOAuthStorageSpy.mockReset(); + }); + + test('should call logOut fn of OAuthService when token is valid and current user not found', async () => { + injectorSpy.mockReturnValueOnce({ getDeep: () => false }); + injectorSpy.mockReturnValueOnce({ hasValidAccessToken: () => true }); + checkAccessToken(injector); + expect(clearOAuthStorageSpy).toHaveBeenCalled(); + }); + + test('should not call logOut fn of OAuthService when token is invalid', async () => { + injectorSpy.mockReturnValueOnce({ getDeep: () => true }); + injectorSpy.mockReturnValueOnce({ hasValidAccessToken: () => false }); + checkAccessToken(injector); + expect(clearOAuthStorageSpy).not.toHaveBeenCalled(); + }); + + test('should not call logOut fn of OAuthService when token is valid but user is not found', async () => { + injectorSpy.mockReturnValueOnce({ getDeep: () => true }); + injectorSpy.mockReturnValueOnce({ hasValidAccessToken: () => true }); + checkAccessToken(injector); + expect(clearOAuthStorageSpy).not.toHaveBeenCalled(); + }); + }); +}); From 1ea74276d127d73cbd2cd2e4e4b98e9f977e443f Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Thu, 22 Dec 2022 10:48:09 +0300 Subject: [PATCH 04/20] add OAuth pack to templates --- .../app-nolayers/angular/src/app/app.module.ts | 2 ++ templates/app/angular/.vscode/extensions.json | 15 --------------- templates/app/angular/src/app/app.module.ts | 2 ++ .../projects/dev-app/src/app/app.module.ts | 2 ++ 4 files changed, 6 insertions(+), 15 deletions(-) delete mode 100644 templates/app/angular/.vscode/extensions.json diff --git a/templates/app-nolayers/angular/src/app/app.module.ts b/templates/app-nolayers/angular/src/app/app.module.ts index 33744f6b00..13d8731b67 100644 --- a/templates/app-nolayers/angular/src/app/app.module.ts +++ b/templates/app-nolayers/angular/src/app/app.module.ts @@ -15,6 +15,7 @@ import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { APP_ROUTE_PROVIDER } from './route.provider'; import { FeatureManagementModule } from '@abp/ng.feature-management'; +import { AbpOAuthModule } from '@abp/ng.oauth'; @NgModule({ imports: [ @@ -25,6 +26,7 @@ import { FeatureManagementModule } from '@abp/ng.feature-management'; environment, registerLocaleFn: registerLocale(), }), + AbpOAuthModule.forRoot(), ThemeSharedModule.forRoot(), AccountConfigModule.forRoot(), IdentityConfigModule.forRoot(), diff --git a/templates/app/angular/.vscode/extensions.json b/templates/app/angular/.vscode/extensions.json deleted file mode 100644 index 5db7bc21ac..0000000000 --- a/templates/app/angular/.vscode/extensions.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "recommendations": [ - "angular.ng-template", - "esbenp.prettier-vscode", - "ms-vscode.vscode-typescript-tslint-plugin", - "visualstudioexptteam.vscodeintellicode", - "christian-kohler.path-intellisense", - "christian-kohler.npm-intellisense", - "Mikael.Angular-BeastCode", - "xabikos.JavaScriptSnippets", - "msjsdiag.debugger-for-chrome", - "donjayamanne.githistory", - "oderwat.indent-rainbow" - ] -} diff --git a/templates/app/angular/src/app/app.module.ts b/templates/app/angular/src/app/app.module.ts index 33744f6b00..13d8731b67 100644 --- a/templates/app/angular/src/app/app.module.ts +++ b/templates/app/angular/src/app/app.module.ts @@ -15,6 +15,7 @@ import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { APP_ROUTE_PROVIDER } from './route.provider'; import { FeatureManagementModule } from '@abp/ng.feature-management'; +import { AbpOAuthModule } from '@abp/ng.oauth'; @NgModule({ imports: [ @@ -25,6 +26,7 @@ import { FeatureManagementModule } from '@abp/ng.feature-management'; environment, registerLocaleFn: registerLocale(), }), + AbpOAuthModule.forRoot(), ThemeSharedModule.forRoot(), AccountConfigModule.forRoot(), IdentityConfigModule.forRoot(), diff --git a/templates/module/angular/projects/dev-app/src/app/app.module.ts b/templates/module/angular/projects/dev-app/src/app/app.module.ts index e71793c2f8..615bc75f2c 100644 --- a/templates/module/angular/projects/dev-app/src/app/app.module.ts +++ b/templates/module/angular/projects/dev-app/src/app/app.module.ts @@ -15,6 +15,7 @@ import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { APP_ROUTE_PROVIDER } from './route.provider'; import { FeatureManagementModule } from '@abp/ng.feature-management'; +import { AbpOAuthModule } from '@abp/ng.oauth'; @NgModule({ imports: [ @@ -27,6 +28,7 @@ import { FeatureManagementModule } from '@abp/ng.feature-management'; sendNullsAsQueryParam: false, skipGetAppConfiguration: false, }), + AbpOAuthModule.forRoot(), ThemeSharedModule.forRoot(), AccountConfigModule.forRoot(), IdentityConfigModule.forRoot(), From def85ef5c25201a86968a6a4637908b6f2096e3a Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Thu, 22 Dec 2022 15:23:16 +0300 Subject: [PATCH 05/20] Add manage-profile provider. Set OAuth config is nullable. --- .../core/src/lib/models/environment.ts | 4 ++-- .../src/lib/tokens/manage-profile.token.ts | 18 +--------------- .../packages/oauth/src/lib/oauth.module.ts | 2 ++ .../packages/oauth/src/lib/providers/index.ts | 1 + .../navigate-to-manage-profile.provider.ts | 21 +++++++++++++++++++ npm/ng-packs/packages/oauth/src/public-api.ts | 1 + 6 files changed, 28 insertions(+), 19 deletions(-) create mode 100644 npm/ng-packs/packages/oauth/src/lib/providers/index.ts create mode 100644 npm/ng-packs/packages/oauth/src/lib/providers/navigate-to-manage-profile.provider.ts diff --git a/npm/ng-packs/packages/core/src/lib/models/environment.ts b/npm/ng-packs/packages/core/src/lib/models/environment.ts index 062b1df82b..252afe1291 100644 --- a/npm/ng-packs/packages/core/src/lib/models/environment.ts +++ b/npm/ng-packs/packages/core/src/lib/models/environment.ts @@ -7,10 +7,10 @@ export interface Environment { hmr?: boolean; test?: boolean; localization?: { defaultResourceName?: string }; - oAuthConfig: AuthConfig; + oAuthConfig?: AuthConfig; production: boolean; remoteEnv?: RemoteEnv; - [key:string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any + [key: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any } export interface ApplicationInfo { name: string; diff --git a/npm/ng-packs/packages/core/src/lib/tokens/manage-profile.token.ts b/npm/ng-packs/packages/core/src/lib/tokens/manage-profile.token.ts index 3bc1c41c14..2083e63c71 100644 --- a/npm/ng-packs/packages/core/src/lib/tokens/manage-profile.token.ts +++ b/npm/ng-packs/packages/core/src/lib/tokens/manage-profile.token.ts @@ -1,21 +1,5 @@ -import { InjectionToken, inject } from '@angular/core'; -import { EnvironmentService } from '../services/environment.service'; +import { InjectionToken } from '@angular/core'; export const NAVIGATE_TO_MANAGE_PROFILE = new InjectionToken<() => void>( 'NAVIGATE_TO_MANAGE_PROFILE', - { - providedIn: 'root', - factory: () => { - const environment = inject(EnvironmentService); - - return () => { - window.open( - `${environment.getEnvironment().oAuthConfig.issuer}/Account/Manage?returnUrl=${ - window.location.href - }`, - '_self', - ); - }; - }, - }, ); diff --git a/npm/ng-packs/packages/oauth/src/lib/oauth.module.ts b/npm/ng-packs/packages/oauth/src/lib/oauth.module.ts index ea2874c2f9..0bc0c887ca 100644 --- a/npm/ng-packs/packages/oauth/src/lib/oauth.module.ts +++ b/npm/ng-packs/packages/oauth/src/lib/oauth.module.ts @@ -9,6 +9,7 @@ import { initFactory } from './utils/init-factory'; import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { ApiInterceptor } from './interceptors/api.interceptor'; import { AbpOAuthGuard } from './guards/oauth.guard'; +import { NavigateToManageProfileProvider } from './providers'; @NgModule({ imports: [CommonModule, OAuthModule], @@ -31,6 +32,7 @@ export class AbpOAuthModule { useExisting: ApiInterceptor, multi: true, }, + NavigateToManageProfileProvider, { provide: APP_INITIALIZER, multi: true, diff --git a/npm/ng-packs/packages/oauth/src/lib/providers/index.ts b/npm/ng-packs/packages/oauth/src/lib/providers/index.ts new file mode 100644 index 0000000000..d938907916 --- /dev/null +++ b/npm/ng-packs/packages/oauth/src/lib/providers/index.ts @@ -0,0 +1 @@ +export * from './navigate-to-manage-profile.provider'; diff --git a/npm/ng-packs/packages/oauth/src/lib/providers/navigate-to-manage-profile.provider.ts b/npm/ng-packs/packages/oauth/src/lib/providers/navigate-to-manage-profile.provider.ts new file mode 100644 index 0000000000..e20bf62fc2 --- /dev/null +++ b/npm/ng-packs/packages/oauth/src/lib/providers/navigate-to-manage-profile.provider.ts @@ -0,0 +1,21 @@ +import { inject, Provider } from '@angular/core'; +import { EnvironmentService, NAVIGATE_TO_MANAGE_PROFILE } from '@abp/ng.core'; + +export const NavigateToManageProfileProvider: Provider = { + provide: NAVIGATE_TO_MANAGE_PROFILE, + useFactory: () => { + const environment = inject(EnvironmentService); + + return () => { + const env = environment.getEnvironment(); + if (!env.oAuthConfig) { + console.warn('The oAuthConfig env is missing on environment.ts'); + return; + } + window.open( + `${env.oAuthConfig.issuer}/Account/Manage?returnUrl=${window.location.href}`, + '_self', + ); + }; + }, +}; diff --git a/npm/ng-packs/packages/oauth/src/public-api.ts b/npm/ng-packs/packages/oauth/src/public-api.ts index d6154d0883..72cc5e0269 100644 --- a/npm/ng-packs/packages/oauth/src/public-api.ts +++ b/npm/ng-packs/packages/oauth/src/public-api.ts @@ -6,3 +6,4 @@ export * from './lib/strategies'; export * from './lib/handlers'; export * from './lib/interceptors'; export * from './lib/guards'; +export * from './lib/providers'; From 8a19fb9e66c97574348adf60daa67a96904e42b5 Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Thu, 22 Dec 2022 15:36:44 +0300 Subject: [PATCH 06/20] Add oauth packages copy to template command --- npm/ng-packs/scripts/copy-packages-to-templates.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/npm/ng-packs/scripts/copy-packages-to-templates.ts b/npm/ng-packs/scripts/copy-packages-to-templates.ts index e71dacbcc0..dd605db994 100644 --- a/npm/ng-packs/scripts/copy-packages-to-templates.ts +++ b/npm/ng-packs/scripts/copy-packages-to-templates.ts @@ -17,7 +17,8 @@ const packageMap = { 'tenant-management': 'ng.tenant-management', 'theme-basic': 'ng.theme.basic', 'theme-shared': 'ng.theme.shared', - 'schematics':'ng.schematics' + 'schematics':'ng.schematics', + oauth:'ng.oauth' }; program.option('-t, --templates ', 'template dirs', false); program.option('-p, --template-path ', 'root template path', false); @@ -25,8 +26,9 @@ program.parse(process.argv); const templates = program.templates ? program.templates.split(',') : defaultTemplates; const templateRootPath = program.templatePath ? program.templatePath : defaultTemplatePath; (async () => { - await execa('yarn', ['build'], { + await execa('yarn', ['build:all'], { stdout: 'inherit', + cwd:'../' }); await installPackages(); From b729c1853b0bdd3c08d286911d94f2cdc6893c1d Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Thu, 22 Dec 2022 15:36:56 +0300 Subject: [PATCH 07/20] Add docs as draft --- docs/en/UI/Angular/OAuth-Module.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 docs/en/UI/Angular/OAuth-Module.md diff --git a/docs/en/UI/Angular/OAuth-Module.md b/docs/en/UI/Angular/OAuth-Module.md new file mode 100644 index 0000000000..a6ad2ac2d8 --- /dev/null +++ b/docs/en/UI/Angular/OAuth-Module.md @@ -0,0 +1,5 @@ +# ABP OAuth Packages + +@abp/core has the abstractions but logics moved @abp/oauth package since v7.0. + + From 9d2079ebf79f24ae3d88a99498fa24405ac1173d Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Thu, 22 Dec 2022 18:11:20 +0300 Subject: [PATCH 08/20] Add docs --- docs/en/UI/Angular/OAuth-Module.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/en/UI/Angular/OAuth-Module.md b/docs/en/UI/Angular/OAuth-Module.md index a6ad2ac2d8..18b38e3e1a 100644 --- a/docs/en/UI/Angular/OAuth-Module.md +++ b/docs/en/UI/Angular/OAuth-Module.md @@ -1,5 +1,14 @@ -# ABP OAuth Packages +# ABP OAuth Package +The authentication functionality has been moved from the @abp/ng.core to the @abp/ng.ouath since v7.0. -@abp/core has the abstractions but logics moved @abp/oauth package since v7.0. +If your app is version 7.0 or higher, you should include "AbpOAuthModule.forRoot()" in your app.module.ts as an import after "CoreModule.forRoot(...)". +Those abstractions can be found in the @abp/ng-core packages. +- `AuthService` (the class that implements the IAuthService interface). +- `NAVIGATE_TO_MANAGE_PROFILE` Inject token. +- `AuthGuard` (the class that implements the IAuthGuard interface). +Those base classes are overridden by the "AbpOAuthModule" for oAuth. +If you want to make your own authentication system, you must also change these 'abstract' classes. + +ApiInterceptor is provided by @abp/ng.oauth. The ApiInterceptor adds the token, accepted-language, and tenant id to the header of the HTTP request. From 4dd6fb08a3c342454b8a3c1b5e1cbb230233382b Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Fri, 23 Dec 2022 09:58:58 +0300 Subject: [PATCH 09/20] Move api.interceptor.spec.ts --- .../{core => oauth}/src/lib/tests/api.interceptor.spec.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) rename npm/ng-packs/packages/{core => oauth}/src/lib/tests/api.interceptor.spec.ts (93%) diff --git a/npm/ng-packs/packages/core/src/lib/tests/api.interceptor.spec.ts b/npm/ng-packs/packages/oauth/src/lib/tests/api.interceptor.spec.ts similarity index 93% rename from npm/ng-packs/packages/core/src/lib/tests/api.interceptor.spec.ts rename to npm/ng-packs/packages/oauth/src/lib/tests/api.interceptor.spec.ts index 405798571a..97fa7035dd 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/api.interceptor.spec.ts +++ b/npm/ng-packs/packages/oauth/src/lib/tests/api.interceptor.spec.ts @@ -4,9 +4,7 @@ import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest'; import { OAuthService } from 'angular-oauth2-oidc'; import { Subject, timer } from 'rxjs'; import { ApiInterceptor } from '../interceptors/api.interceptor'; -import { HttpWaitService } from '../services/http-wait.service'; -import { SessionStateService } from '../services/session-state.service'; -import { TENANT_KEY } from '../tokens/tenant-key.token'; +import { HttpWaitService, SessionStateService, TENANT_KEY } from '@abp/ng.core'; describe('ApiInterceptor', () => { let spectator: SpectatorService; From ab20336910838af0fcba9cf82aecd64fc9716e88 Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Fri, 23 Dec 2022 14:45:16 +0300 Subject: [PATCH 10/20] Remove minor duplicated code --- .../strategies/auth-password-flow-strategy.ts | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/npm/ng-packs/packages/oauth/src/lib/strategies/auth-password-flow-strategy.ts b/npm/ng-packs/packages/oauth/src/lib/strategies/auth-password-flow-strategy.ts index ff795fc38d..2e2e278985 100644 --- a/npm/ng-packs/packages/oauth/src/lib/strategies/auth-password-flow-strategy.ts +++ b/npm/ng-packs/packages/oauth/src/lib/strategies/auth-password-flow-strategy.ts @@ -4,7 +4,7 @@ import { Params, Router } from '@angular/router'; import { from, Observable, pipe } from 'rxjs'; import { HttpHeaders } from '@angular/common/http'; import { AuthFlowStrategy } from './auth-flow-strategy'; -import { removeRememberMe, setRememberMe } from '../utils/auth-utils'; +import { pipeToLogin, removeRememberMe, setRememberMe } from '../utils/auth-utils'; import { LoginParams } from '@abp/ng.core'; import { clearOAuthStorage } from '../utils/clear-o-auth-storage'; @@ -65,21 +65,8 @@ export class AuthPasswordFlowStrategy extends AuthFlowStrategy { params.password, new HttpHeaders({ ...(tenant && tenant.id && { [this.tenantKey]: tenant.id }) }), ), - ).pipe(this.pipeToLogin(params)); + ).pipe(pipeToLogin(params, this.injector)); } - - pipeToLogin(params: Pick) { - const router = this.injector.get(Router); - - return pipe( - switchMap(() => this.configState.refreshAppState()), - tap(() => { - setRememberMe(params.rememberMe); - if (params.redirectUrl) router.navigate([params.redirectUrl]); - }), - ); - } - logout(queryParams?: Params) { const router = this.injector.get(Router); From 121e6ab940f731543cb6fb0b037260a964aadf70 Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Fri, 23 Dec 2022 18:48:13 +0300 Subject: [PATCH 11/20] Convert functions to token --- .../src/lib/interceptors/api.interceptor.ts | 26 ++++++++++++++++++ .../core/src/lib/interceptors/index.ts | 1 + .../packages/core/src/lib/models/auth.ts | 10 +++++++ .../packages/core/src/lib/tokens/index.ts | 4 ++- .../src/lib/tokens/pipe-to-login.token.ts | 4 +++ .../set-token-response-to-storage.token.ts | 6 +++++ npm/ng-packs/packages/core/src/public-api.ts | 1 + .../src/lib/interceptors/api.interceptor.ts | 6 ++--- .../packages/oauth/src/lib/oauth.module.ts | 27 ++++++++++++++++--- .../oauth/src/lib/utils/auth-utils.ts | 25 ++++++++++++----- 10 files changed, 96 insertions(+), 14 deletions(-) create mode 100644 npm/ng-packs/packages/core/src/lib/interceptors/api.interceptor.ts create mode 100644 npm/ng-packs/packages/core/src/lib/interceptors/index.ts create mode 100644 npm/ng-packs/packages/core/src/lib/tokens/pipe-to-login.token.ts create mode 100644 npm/ng-packs/packages/core/src/lib/tokens/set-token-response-to-storage.token.ts diff --git a/npm/ng-packs/packages/core/src/lib/interceptors/api.interceptor.ts b/npm/ng-packs/packages/core/src/lib/interceptors/api.interceptor.ts new file mode 100644 index 0000000000..f7dd03267e --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/interceptors/api.interceptor.ts @@ -0,0 +1,26 @@ +import { + HttpEvent, + HttpHandler, + HttpHeaders, + HttpInterceptor, + HttpRequest, +} from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; + +@Injectable({ + providedIn: 'root', +}) +export class ApiInterceptor implements IApiInterceptor { + getAdditionalHeaders(existingHeaders?: HttpHeaders) { + return existingHeaders; + } + + intercept(req: HttpRequest, next: HttpHandler): Observable> { + return next.handle(req); + } +} + +export interface IApiInterceptor extends HttpInterceptor { + getAdditionalHeaders(existingHeaders?: HttpHeaders): HttpHeaders; +} diff --git a/npm/ng-packs/packages/core/src/lib/interceptors/index.ts b/npm/ng-packs/packages/core/src/lib/interceptors/index.ts new file mode 100644 index 0000000000..d7479a7684 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/interceptors/index.ts @@ -0,0 +1 @@ +export * from './api.interceptor'; diff --git a/npm/ng-packs/packages/core/src/lib/models/auth.ts b/npm/ng-packs/packages/core/src/lib/models/auth.ts index 5621dc8655..4a1a01f357 100644 --- a/npm/ng-packs/packages/core/src/lib/models/auth.ts +++ b/npm/ng-packs/packages/core/src/lib/models/auth.ts @@ -1,6 +1,16 @@ +import { UnaryFunction } from 'rxjs'; +import { Injector } from '@angular/core'; + export interface LoginParams { username: string; password: string; rememberMe?: boolean; redirectUrl?: string; } + +export type PipeToLoginFn = ( + params: Pick, + injector: Injector, +) => UnaryFunction; + +export type SetTokenResponseToStorageFn = (injector: Injector, tokenRes: unknown) => void; diff --git a/npm/ng-packs/packages/core/src/lib/tokens/index.ts b/npm/ng-packs/packages/core/src/lib/tokens/index.ts index 20cab35c28..45b0f9f3cd 100644 --- a/npm/ng-packs/packages/core/src/lib/tokens/index.ts +++ b/npm/ng-packs/packages/core/src/lib/tokens/index.ts @@ -6,4 +6,6 @@ export * from './lodaer-delay.token'; export * from './manage-profile.token'; export * from './options.token'; export * from './tenant-key.token'; -export * from './include-localization-resources.token'; \ No newline at end of file +export * from './include-localization-resources.token'; +export * from './pipe-to-login.token'; +export * from './set-token-response-to-storage.token'; diff --git a/npm/ng-packs/packages/core/src/lib/tokens/pipe-to-login.token.ts b/npm/ng-packs/packages/core/src/lib/tokens/pipe-to-login.token.ts new file mode 100644 index 0000000000..3100ede9bb --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tokens/pipe-to-login.token.ts @@ -0,0 +1,4 @@ +import { InjectionToken } from '@angular/core'; +import { PipeToLoginFn } from '../models/auth'; + +export const PIPE_TO_LOGIN_FN_KEY = new InjectionToken('PIPE_TO_LOGIN_FN_KEY'); diff --git a/npm/ng-packs/packages/core/src/lib/tokens/set-token-response-to-storage.token.ts b/npm/ng-packs/packages/core/src/lib/tokens/set-token-response-to-storage.token.ts new file mode 100644 index 0000000000..0436195654 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tokens/set-token-response-to-storage.token.ts @@ -0,0 +1,6 @@ +import { InjectionToken } from '@angular/core'; +import { SetTokenResponseToStorageFn } from '../models'; + +export const SET_TOKEN_RESPONSE_TO_STORAGE_FN_KEY = new InjectionToken( + 'SET_TOKEN_RESPONSE_TO_STORAGE_FN_KEY', +); diff --git a/npm/ng-packs/packages/core/src/public-api.ts b/npm/ng-packs/packages/core/src/public-api.ts index 20dec1308b..da5fccd8c0 100644 --- a/npm/ng-packs/packages/core/src/public-api.ts +++ b/npm/ng-packs/packages/core/src/public-api.ts @@ -22,3 +22,4 @@ export * from './lib/strategies'; export * from './lib/tokens'; export * from './lib/utils'; export * from './lib/validators'; +export * from './lib/interceptors'; diff --git a/npm/ng-packs/packages/oauth/src/lib/interceptors/api.interceptor.ts b/npm/ng-packs/packages/oauth/src/lib/interceptors/api.interceptor.ts index 91666b12a9..e807133c0d 100644 --- a/npm/ng-packs/packages/oauth/src/lib/interceptors/api.interceptor.ts +++ b/npm/ng-packs/packages/oauth/src/lib/interceptors/api.interceptor.ts @@ -1,13 +1,13 @@ -import { HttpHandler, HttpHeaders, HttpInterceptor, HttpRequest } from '@angular/common/http'; +import { HttpHandler, HttpHeaders, HttpRequest } from '@angular/common/http'; import { Inject, Injectable } from '@angular/core'; import { OAuthService } from 'angular-oauth2-oidc'; import { finalize } from 'rxjs/operators'; -import { SessionStateService, HttpWaitService, TENANT_KEY } from '@abp/ng.core'; +import { SessionStateService, HttpWaitService, TENANT_KEY, IApiInterceptor } from '@abp/ng.core'; @Injectable({ providedIn: 'root', }) -export class ApiInterceptor implements HttpInterceptor { +export class OAuthApiInterceptor implements IApiInterceptor { constructor( private oAuthService: OAuthService, private sessionState: SessionStateService, diff --git a/npm/ng-packs/packages/oauth/src/lib/oauth.module.ts b/npm/ng-packs/packages/oauth/src/lib/oauth.module.ts index 0bc0c887ca..ca9f232015 100644 --- a/npm/ng-packs/packages/oauth/src/lib/oauth.module.ts +++ b/npm/ng-packs/packages/oauth/src/lib/oauth.module.ts @@ -1,15 +1,23 @@ import { APP_INITIALIZER, Injector, ModuleWithProviders, NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { OAuthModule, OAuthService, OAuthStorage } from 'angular-oauth2-oidc'; -import { AuthGuard, AuthService, noop } from '@abp/ng.core'; +import { OAuthModule, OAuthStorage } from 'angular-oauth2-oidc'; +import { + ApiInterceptor, + AuthGuard, + AuthService, + noop, + PIPE_TO_LOGIN_FN_KEY, + SET_TOKEN_RESPONSE_TO_STORAGE_FN_KEY, +} from '@abp/ng.core'; import { storageFactory } from './utils/storage.factory'; import { AbpOAuthService, TimeoutLimitedOAuthService } from './services'; import { OAuthConfigurationHandler } from './handlers/oauth-configuration.handler'; import { initFactory } from './utils/init-factory'; import { HTTP_INTERCEPTORS } from '@angular/common/http'; -import { ApiInterceptor } from './interceptors/api.interceptor'; +import { OAuthApiInterceptor } from './interceptors/api.interceptor'; import { AbpOAuthGuard } from './guards/oauth.guard'; import { NavigateToManageProfileProvider } from './providers'; +import { pipeToLogin, setTokenResponseToStorage } from './utils'; @NgModule({ imports: [CommonModule, OAuthModule], @@ -27,6 +35,18 @@ export class AbpOAuthModule { provide: AuthGuard, useClass: AbpOAuthGuard, }, + { + provide: ApiInterceptor, + useClass: OAuthApiInterceptor, + }, + { + provide: PIPE_TO_LOGIN_FN_KEY, + useValue: pipeToLogin, + }, + { + provide: SET_TOKEN_RESPONSE_TO_STORAGE_FN_KEY, + useValue: setTokenResponseToStorage, + }, { provide: HTTP_INTERCEPTORS, useExisting: ApiInterceptor, @@ -45,7 +65,6 @@ export class AbpOAuthModule { deps: [Injector], useFactory: initFactory, }, - OAuthModule.forRoot().providers, { provide: OAuthStorage, useFactory: storageFactory }, // {provide: OAuthService, useClass: TimeoutLimitedOAuthService} diff --git a/npm/ng-packs/packages/oauth/src/lib/utils/auth-utils.ts b/npm/ng-packs/packages/oauth/src/lib/utils/auth-utils.ts index 2ac24abf22..f620aadf77 100644 --- a/npm/ng-packs/packages/oauth/src/lib/utils/auth-utils.ts +++ b/npm/ng-packs/packages/oauth/src/lib/utils/auth-utils.ts @@ -3,12 +3,17 @@ import { Router } from '@angular/router'; import { OAuthStorage, TokenResponse } from 'angular-oauth2-oidc'; import { pipe } from 'rxjs'; import { switchMap, tap } from 'rxjs/operators'; -import { ConfigStateService, LoginParams } from '@abp/ng.core'; +import { + ConfigStateService, + LoginParams, + PipeToLoginFn, + SetTokenResponseToStorageFn, +} from '@abp/ng.core'; const cookieKey = 'rememberMe'; const storageKey = 'passwordFlow'; -export function pipeToLogin( +export const pipeToLogin: PipeToLoginFn = function ( params: Pick, injector: Injector, ) { @@ -22,10 +27,18 @@ export function pipeToLogin( if (params.redirectUrl) router.navigate([params.redirectUrl]); }), ); -} +}; -export function setTokenResponseToStorage(injector: Injector, tokenRes: TokenResponse) { - const { access_token, refresh_token, scope: grantedScopes, expires_in } = tokenRes; +export const setTokenResponseToStorage: SetTokenResponseToStorageFn = function ( + injector: Injector, + tokenRes: unknown, +) { + const { + access_token, + refresh_token, + scope: grantedScopes, + expires_in, + } = tokenRes as TokenResponse; const storage = injector.get(OAuthStorage); storage.setItem('access_token', access_token); @@ -42,7 +55,7 @@ export function setTokenResponseToStorage(injector: Injector, tokenRes: TokenRes const expiresAt = now.getTime() + expiresInMilliSeconds; storage.setItem('expires_at', '' + expiresAt); } -} +}; export function setRememberMe(remember: boolean) { removeRememberMe(); From e9fb84154059895a06acb3b2f37ed7f6937b84ff Mon Sep 17 00:00:00 2001 From: Hamza Albreem <94292623+braim23@users.noreply.github.com> Date: Mon, 26 Dec 2022 14:09:54 +0300 Subject: [PATCH 12/20] tiny fix for the OAuth-Module doc --- docs/en/UI/Angular/OAuth-Module.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/UI/Angular/OAuth-Module.md b/docs/en/UI/Angular/OAuth-Module.md index 18b38e3e1a..a14b49bed0 100644 --- a/docs/en/UI/Angular/OAuth-Module.md +++ b/docs/en/UI/Angular/OAuth-Module.md @@ -1,5 +1,5 @@ # ABP OAuth Package -The authentication functionality has been moved from the @abp/ng.core to the @abp/ng.ouath since v7.0. +The authentication functionality has been moved from @abp/ng.core to @abp/ng.ouath since v7.0. If your app is version 7.0 or higher, you should include "AbpOAuthModule.forRoot()" in your app.module.ts as an import after "CoreModule.forRoot(...)". From a1c6fed8d46b4d2935ee6a8931f1f5ce99c8ece4 Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Tue, 27 Dec 2022 11:19:01 +0300 Subject: [PATCH 13/20] Nove checkAccessToken function to core, inject fn via provider. --- .../packages/core/src/lib/models/auth.ts | 3 +- .../lib/tokens/check-authentication-state.ts | 6 ++++ .../packages/core/src/lib/tokens/index.ts | 1 + .../core/src/lib/utils/initial-utils.ts | 14 +++++++-- .../packages/oauth/src/lib/oauth.module.ts | 17 +++++----- .../packages/oauth/src/lib/services/index.ts | 1 - .../services/timeout-limited-oauth.service.ts | 12 ------- .../oauth/src/lib/tests/initial-utils.spec.ts | 5 +-- .../oauth/src/lib/utils/auth-utils.ts | 11 ++----- .../oauth/src/lib/utils/check-access-token.ts | 12 +++++++ .../packages/oauth/src/lib/utils/index.ts | 2 +- .../oauth/src/lib/utils/init-factory.ts | 31 ------------------- 12 files changed, 47 insertions(+), 68 deletions(-) create mode 100644 npm/ng-packs/packages/core/src/lib/tokens/check-authentication-state.ts delete mode 100644 npm/ng-packs/packages/oauth/src/lib/services/timeout-limited-oauth.service.ts create mode 100644 npm/ng-packs/packages/oauth/src/lib/utils/check-access-token.ts delete mode 100644 npm/ng-packs/packages/oauth/src/lib/utils/init-factory.ts diff --git a/npm/ng-packs/packages/core/src/lib/models/auth.ts b/npm/ng-packs/packages/core/src/lib/models/auth.ts index 4a1a01f357..f5d532454b 100644 --- a/npm/ng-packs/packages/core/src/lib/models/auth.ts +++ b/npm/ng-packs/packages/core/src/lib/models/auth.ts @@ -13,4 +13,5 @@ export type PipeToLoginFn = ( injector: Injector, ) => UnaryFunction; -export type SetTokenResponseToStorageFn = (injector: Injector, tokenRes: unknown) => void; +export type SetTokenResponseToStorageFn = (injector: Injector, tokenRes: T) => void; +export type CheckAuthenticationStateFn = (injector: Injector) => void; diff --git a/npm/ng-packs/packages/core/src/lib/tokens/check-authentication-state.ts b/npm/ng-packs/packages/core/src/lib/tokens/check-authentication-state.ts new file mode 100644 index 0000000000..0c68b18b52 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tokens/check-authentication-state.ts @@ -0,0 +1,6 @@ +import { InjectionToken } from '@angular/core'; +import { CheckAuthenticationStateFn } from '@abp/ng.core'; + +export const CHECK_AUTHENTICATION_STATE_FN_KEY = new InjectionToken( + 'CHECK_AUTHENTICATION_STATE_FN_KEY', +); diff --git a/npm/ng-packs/packages/core/src/lib/tokens/index.ts b/npm/ng-packs/packages/core/src/lib/tokens/index.ts index 45b0f9f3cd..971ac0fcbd 100644 --- a/npm/ng-packs/packages/core/src/lib/tokens/index.ts +++ b/npm/ng-packs/packages/core/src/lib/tokens/index.ts @@ -9,3 +9,4 @@ export * from './tenant-key.token'; export * from './include-localization-resources.token'; export * from './pipe-to-login.token'; export * from './set-token-response-to-storage.token'; +export * from './check-authentication-state'; diff --git a/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts index bc4d11d977..782af0899d 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts @@ -1,5 +1,5 @@ import { registerLocaleData } from '@angular/common'; -import { Injector } from '@angular/core'; +import { InjectFlags, Injector } from '@angular/core'; import { tap, catchError } from 'rxjs/operators'; import { lastValueFrom, throwError } from 'rxjs'; import { ABP } from '../models/common'; @@ -12,6 +12,9 @@ import { CORE_OPTIONS } from '../tokens/options.token'; import { APP_INIT_ERROR_HANDLERS } from '../tokens/app-config.token'; import { getRemoteEnv } from './environment-utils'; import { parseTenantFromUrl } from './multi-tenancy-utils'; +import { AuthService } from '../abstracts'; +import { CHECK_AUTHENTICATION_STATE_FN_KEY } from '../tokens/check-authentication-state'; +import { noop } from './common-utils'; export function getInitialData(injector: Injector) { const fn = async () => { @@ -22,10 +25,17 @@ export function getInitialData(injector: Injector) { environmentService.setState(options.environment as Environment); await getRemoteEnv(injector, options.environment); await parseTenantFromUrl(injector); - + const authService = injector.get(AuthService, undefined, { optional: true }); + const checkAuthenticationState = injector.get(CHECK_AUTHENTICATION_STATE_FN_KEY, noop, { + optional: true, + }); + if (authService) { + await authService.init(); + } if (options.skipGetAppConfiguration) return; const result$ = configState.refreshAppState().pipe( + tap(() => checkAuthenticationState(injector)), tap(() => { const currentTenant = configState.getOne('currentTenant') as CurrentTenantDto; injector.get(SessionStateService).setTenant(currentTenant); diff --git a/npm/ng-packs/packages/oauth/src/lib/oauth.module.ts b/npm/ng-packs/packages/oauth/src/lib/oauth.module.ts index ca9f232015..753ad815e3 100644 --- a/npm/ng-packs/packages/oauth/src/lib/oauth.module.ts +++ b/npm/ng-packs/packages/oauth/src/lib/oauth.module.ts @@ -5,19 +5,19 @@ import { ApiInterceptor, AuthGuard, AuthService, + CHECK_AUTHENTICATION_STATE_FN_KEY, noop, PIPE_TO_LOGIN_FN_KEY, SET_TOKEN_RESPONSE_TO_STORAGE_FN_KEY, } from '@abp/ng.core'; import { storageFactory } from './utils/storage.factory'; -import { AbpOAuthService, TimeoutLimitedOAuthService } from './services'; +import { AbpOAuthService } from './services'; import { OAuthConfigurationHandler } from './handlers/oauth-configuration.handler'; -import { initFactory } from './utils/init-factory'; import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { OAuthApiInterceptor } from './interceptors/api.interceptor'; import { AbpOAuthGuard } from './guards/oauth.guard'; import { NavigateToManageProfileProvider } from './providers'; -import { pipeToLogin, setTokenResponseToStorage } from './utils'; +import { checkAccessToken, pipeToLogin, setTokenResponseToStorage } from './utils'; @NgModule({ imports: [CommonModule, OAuthModule], @@ -47,6 +47,10 @@ export class AbpOAuthModule { provide: SET_TOKEN_RESPONSE_TO_STORAGE_FN_KEY, useValue: setTokenResponseToStorage, }, + { + provide: CHECK_AUTHENTICATION_STATE_FN_KEY, + useValue: checkAccessToken, + }, { provide: HTTP_INTERCEPTORS, useExisting: ApiInterceptor, @@ -59,15 +63,8 @@ export class AbpOAuthModule { deps: [OAuthConfigurationHandler], useFactory: noop, }, - { - provide: APP_INITIALIZER, - multi: true, - deps: [Injector], - useFactory: initFactory, - }, OAuthModule.forRoot().providers, { provide: OAuthStorage, useFactory: storageFactory }, - // {provide: OAuthService, useClass: TimeoutLimitedOAuthService} ], }; } diff --git a/npm/ng-packs/packages/oauth/src/lib/services/index.ts b/npm/ng-packs/packages/oauth/src/lib/services/index.ts index a0e99cbcae..ddc54916a3 100644 --- a/npm/ng-packs/packages/oauth/src/lib/services/index.ts +++ b/npm/ng-packs/packages/oauth/src/lib/services/index.ts @@ -1,2 +1 @@ export * from './oauth.service'; -export * from './timeout-limited-oauth.service'; diff --git a/npm/ng-packs/packages/oauth/src/lib/services/timeout-limited-oauth.service.ts b/npm/ng-packs/packages/oauth/src/lib/services/timeout-limited-oauth.service.ts deleted file mode 100644 index 8b9d4ef393..0000000000 --- a/npm/ng-packs/packages/oauth/src/lib/services/timeout-limited-oauth.service.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Injectable } from '@angular/core'; -import { OAuthService } from 'angular-oauth2-oidc'; - -/** * @deprecated No need to override official service. It should be fixed in 15 or never version of angular-oauth2-oidc*/ -@Injectable() -export class TimeoutLimitedOAuthService extends OAuthService { - protected override calcTimeout(storedAt: number, expiration: number): number { - const result = super.calcTimeout(storedAt, expiration); - const MAX_TIMEOUT_DURATION = 2147483647; - return result < MAX_TIMEOUT_DURATION ? result : MAX_TIMEOUT_DURATION - 1; - } -} diff --git a/npm/ng-packs/packages/oauth/src/lib/tests/initial-utils.spec.ts b/npm/ng-packs/packages/oauth/src/lib/tests/initial-utils.spec.ts index 31fa3dfcf7..488c14113b 100644 --- a/npm/ng-packs/packages/oauth/src/lib/tests/initial-utils.spec.ts +++ b/npm/ng-packs/packages/oauth/src/lib/tests/initial-utils.spec.ts @@ -12,8 +12,8 @@ import { ApplicationConfigurationDto, } from '@abp/ng.core'; import * as clearOAuthStorageDefault from '../utils/clear-o-auth-storage'; -import { checkAccessToken, initFactory } from '../utils/init-factory'; import { of } from 'rxjs'; +import { checkAccessToken } from '../utils/check-access-token'; const environment = { oAuthConfig: { issuer: 'test' } }; @@ -69,7 +69,8 @@ describe('InitialUtils', () => { configRefreshAppStateSpy.mockReturnValue(of(appConfigRes)); - await initFactory(mockInjector)(); + // Todo: refactor it + // await initFactory(mockInjector)(); expect(configRefreshAppStateSpy).toHaveBeenCalled(); }); diff --git a/npm/ng-packs/packages/oauth/src/lib/utils/auth-utils.ts b/npm/ng-packs/packages/oauth/src/lib/utils/auth-utils.ts index f620aadf77..74df5ce79b 100644 --- a/npm/ng-packs/packages/oauth/src/lib/utils/auth-utils.ts +++ b/npm/ng-packs/packages/oauth/src/lib/utils/auth-utils.ts @@ -29,16 +29,11 @@ export const pipeToLogin: PipeToLoginFn = function ( ); }; -export const setTokenResponseToStorage: SetTokenResponseToStorageFn = function ( +export const setTokenResponseToStorage: SetTokenResponseToStorageFn = function ( injector: Injector, - tokenRes: unknown, + tokenRes: TokenResponse, ) { - const { - access_token, - refresh_token, - scope: grantedScopes, - expires_in, - } = tokenRes as TokenResponse; + const { access_token, refresh_token, scope: grantedScopes, expires_in } = tokenRes; const storage = injector.get(OAuthStorage); storage.setItem('access_token', access_token); diff --git a/npm/ng-packs/packages/oauth/src/lib/utils/check-access-token.ts b/npm/ng-packs/packages/oauth/src/lib/utils/check-access-token.ts new file mode 100644 index 0000000000..0e9249f05e --- /dev/null +++ b/npm/ng-packs/packages/oauth/src/lib/utils/check-access-token.ts @@ -0,0 +1,12 @@ +import { Injector } from '@angular/core'; +import { CheckAuthenticationStateFn, ConfigStateService } from '@abp/ng.core'; +import { OAuthService } from 'angular-oauth2-oidc'; +import { clearOAuthStorage } from './clear-o-auth-storage'; + +export const checkAccessToken: CheckAuthenticationStateFn = function (injector: Injector) { + const configState = injector.get(ConfigStateService); + const oAuth = injector.get(OAuthService); + if (oAuth.hasValidAccessToken() && !configState.getDeep('currentUser.id')) { + clearOAuthStorage(); + } +}; diff --git a/npm/ng-packs/packages/oauth/src/lib/utils/index.ts b/npm/ng-packs/packages/oauth/src/lib/utils/index.ts index e948a3d6b7..63dfd03fe8 100644 --- a/npm/ng-packs/packages/oauth/src/lib/utils/index.ts +++ b/npm/ng-packs/packages/oauth/src/lib/utils/index.ts @@ -2,4 +2,4 @@ export * from './oauth-storage'; export * from './storage.factory'; export * from './auth-utils'; export * from './clear-o-auth-storage'; -export * from './init-factory'; +export * from './check-access-token'; diff --git a/npm/ng-packs/packages/oauth/src/lib/utils/init-factory.ts b/npm/ng-packs/packages/oauth/src/lib/utils/init-factory.ts deleted file mode 100644 index 4395384139..0000000000 --- a/npm/ng-packs/packages/oauth/src/lib/utils/init-factory.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { ABP, AuthService, ConfigStateService, CORE_OPTIONS } from '@abp/ng.core'; -import { Injector } from '@angular/core'; -import { OAuthService } from 'angular-oauth2-oidc'; -import { clearOAuthStorage } from './clear-o-auth-storage'; -import { lastValueFrom } from 'rxjs'; -import { tap } from 'rxjs/operators'; - -export function initFactory(injector: Injector): () => Promise { - return async () => { - const authService = injector.get(AuthService); - await authService.init(); - const configState = injector.get(ConfigStateService); - - const options = injector.get(CORE_OPTIONS) as ABP.Root; - - if (options.skipGetAppConfiguration) { - return; - } - - const result$ = configState.refreshAppState().pipe(tap(() => checkAccessToken(injector))); - await lastValueFrom(result$); - }; -} - -export function checkAccessToken(injector: Injector) { - const configState = injector.get(ConfigStateService); - const oAuth = injector.get(OAuthService); - if (oAuth.hasValidAccessToken() && !configState.getDeep('currentUser.id')) { - clearOAuthStorage(); - } -} From 13a09fb7eaf25b34823dc24d4866a81ffa73d1da Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Tue, 27 Dec 2022 11:26:15 +0300 Subject: [PATCH 14/20] IAuthGuard interface had override. it removed. --- npm/ng-packs/packages/core/src/lib/abstracts/auth.guard.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/abstracts/auth.guard.ts b/npm/ng-packs/packages/core/src/lib/abstracts/auth.guard.ts index 023b8110dc..fff0bdb8f5 100644 --- a/npm/ng-packs/packages/core/src/lib/abstracts/auth.guard.ts +++ b/npm/ng-packs/packages/core/src/lib/abstracts/auth.guard.ts @@ -4,12 +4,10 @@ import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root', }) -export class AuthGuard implements CanActivate, IAuthGuard { +export class AuthGuard implements IAuthGuard { canActivate(): Observable | boolean | UrlTree { console.error('You should add @abp/ng-oauth packages or create your own auth packages.'); return false; } } -export interface IAuthGuard { - canActivate(): Observable | boolean | UrlTree; -} +export interface IAuthGuard extends CanActivate {} From 18e57272f2175e31b41ef3068e097d49a9e8ddd7 Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Tue, 27 Dec 2022 15:10:02 +0300 Subject: [PATCH 15/20] Re-add wait service on core api.interceptor.ts --- .../src/lib/interceptors/api.interceptor.ts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/interceptors/api.interceptor.ts b/npm/ng-packs/packages/core/src/lib/interceptors/api.interceptor.ts index f7dd03267e..1e85cc4716 100644 --- a/npm/ng-packs/packages/core/src/lib/interceptors/api.interceptor.ts +++ b/npm/ng-packs/packages/core/src/lib/interceptors/api.interceptor.ts @@ -1,23 +1,21 @@ -import { - HttpEvent, - HttpHandler, - HttpHeaders, - HttpInterceptor, - HttpRequest, -} from '@angular/common/http'; +import { HttpHandler, HttpHeaders, HttpInterceptor, HttpRequest } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs'; +import { finalize } from 'rxjs/operators'; +import { HttpWaitService } from '../services'; @Injectable({ providedIn: 'root', }) export class ApiInterceptor implements IApiInterceptor { + constructor(private httpWaitService: HttpWaitService) {} + getAdditionalHeaders(existingHeaders?: HttpHeaders) { return existingHeaders; } - intercept(req: HttpRequest, next: HttpHandler): Observable> { - return next.handle(req); + intercept(request: HttpRequest, next: HttpHandler) { + this.httpWaitService.addRequest(request); + return next.handle(request).pipe(finalize(() => this.httpWaitService.deleteRequest(request))); } } From 0093386332639d471dd113a7625a860d3a380719 Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Tue, 27 Dec 2022 15:25:40 +0300 Subject: [PATCH 16/20] add @abp/ng.oauth on package.json --- npm/ng-packs/package.json | 1 + .../oauth/src/lib/services/oauth.service.ts | 2 +- ...flow-strategy.ts => auth-flow-strategy.ts} | 0 .../packages/oauth/src/lib/tokens/index.ts | 2 +- .../packages/schematics/collection.json | 40 ------------------- 5 files changed, 3 insertions(+), 42 deletions(-) rename npm/ng-packs/packages/oauth/src/lib/tokens/{ auth-flow-strategy.ts => auth-flow-strategy.ts} (100%) delete mode 100644 npm/ng-packs/packages/schematics/collection.json diff --git a/npm/ng-packs/package.json b/npm/ng-packs/package.json index ce78984581..de3a111552 100644 --- a/npm/ng-packs/package.json +++ b/npm/ng-packs/package.json @@ -64,6 +64,7 @@ "@abp/ng.account": "~7.0.0-rc.5", "@abp/ng.account.core": "~7.0.0-rc.5", "@abp/ng.core": "~7.0.0-rc.5", + "@abp/ng.oauth":"~7.0.0-rc.5", "@abp/ng.feature-management": "~7.0.0-rc.5", "@abp/ng.identity": "~7.0.0-rc.5", "@abp/ng.permission-management": "~7.0.0-rc.5", diff --git a/npm/ng-packs/packages/oauth/src/lib/services/oauth.service.ts b/npm/ng-packs/packages/oauth/src/lib/services/oauth.service.ts index 9588fef305..e28eebacc4 100644 --- a/npm/ng-packs/packages/oauth/src/lib/services/oauth.service.ts +++ b/npm/ng-packs/packages/oauth/src/lib/services/oauth.service.ts @@ -5,7 +5,7 @@ import { filter, map, switchMap, take, tap } from 'rxjs/operators'; import { IAuthService, LoginParams } from '@abp/ng.core'; import { AuthFlowStrategy } from '../strategies'; import { EnvironmentService } from '@abp/ng.core'; -import { AUTH_FLOW_STRATEGY } from '../tokens/ auth-flow-strategy'; +import { AUTH_FLOW_STRATEGY } from '../tokens/auth-flow-strategy'; import { OAuthService } from 'angular-oauth2-oidc'; @Injectable({ diff --git a/npm/ng-packs/packages/oauth/src/lib/tokens/ auth-flow-strategy.ts b/npm/ng-packs/packages/oauth/src/lib/tokens/auth-flow-strategy.ts similarity index 100% rename from npm/ng-packs/packages/oauth/src/lib/tokens/ auth-flow-strategy.ts rename to npm/ng-packs/packages/oauth/src/lib/tokens/auth-flow-strategy.ts diff --git a/npm/ng-packs/packages/oauth/src/lib/tokens/index.ts b/npm/ng-packs/packages/oauth/src/lib/tokens/index.ts index 2ee0bf7fde..1ec58f54b3 100644 --- a/npm/ng-packs/packages/oauth/src/lib/tokens/index.ts +++ b/npm/ng-packs/packages/oauth/src/lib/tokens/index.ts @@ -1 +1 @@ -export * from './ auth-flow-strategy'; +export * from './auth-flow-strategy'; diff --git a/npm/ng-packs/packages/schematics/collection.json b/npm/ng-packs/packages/schematics/collection.json deleted file mode 100644 index 16f148d78a..0000000000 --- a/npm/ng-packs/packages/schematics/collection.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "schematics": { - "proxy-add": { - "description": "ABP Proxy Generator Add Schematics", - "factory": "./commands/proxy-add", - "schema": "./commands/proxy-add/schema.json" - }, - "proxy-index": { - "description": "ABP Proxy Generator Index Schematics", - "factory": "./commands/proxy-index", - "schema": "./commands/proxy-index/schema.json" - }, - "proxy-refresh": { - "description": "ABP Proxy Generator Refresh Schematics", - "factory": "./commands/proxy-refresh", - "schema": "./commands/proxy-refresh/schema.json" - }, - "proxy-remove": { - "description": "ABP Proxy Generator Remove Schematics", - "factory": "./commands/proxy-remove", - "schema": "./commands/proxy-remove/schema.json" - }, - "api": { - "description": "ABP API Generator Schematics", - "factory": "./commands/api", - "schema": "./commands/api/schema.json" - }, - "create-lib": { - "description": "ABP Create Library Schematics", - "factory": "./commands/create-lib", - "schema": "./commands/create-lib/schema.json" - }, - "change-theme": { - "description": "ABP Change Styles of Theme Schematics", - "factory": "./commands/change-theme", - "schema": "./commands/change-theme/schema.json" - - } - } -} From 52a1d26b9dcd4a684e265e29f8e814679926ec08 Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Tue, 27 Dec 2022 15:28:45 +0300 Subject: [PATCH 17/20] remove oauth depedency --- npm/ng-packs/packages/theme-shared/ng-package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/npm/ng-packs/packages/theme-shared/ng-package.json b/npm/ng-packs/packages/theme-shared/ng-package.json index 8082dca0c9..bc84e15a22 100644 --- a/npm/ng-packs/packages/theme-shared/ng-package.json +++ b/npm/ng-packs/packages/theme-shared/ng-package.json @@ -11,7 +11,6 @@ "@ngx-validate/core", "@swimlane/ngx-datatable", "bootstrap", - "@popperjs/core", - "@abp/ng.oauth" + "@popperjs/core" ] } From 3a3a823fa9c866498d0c5612875b392a6e11e555 Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Tue, 27 Dec 2022 15:34:51 +0300 Subject: [PATCH 18/20] add "@abp/ng.oauth" on package.json --- npm/ng-packs/package.json | 1 - templates/app-nolayers/angular/package.json | 1 + templates/app/angular/package.json | 1 + templates/module/angular/package.json | 1 + 4 files changed, 3 insertions(+), 1 deletion(-) diff --git a/npm/ng-packs/package.json b/npm/ng-packs/package.json index de3a111552..ce78984581 100644 --- a/npm/ng-packs/package.json +++ b/npm/ng-packs/package.json @@ -64,7 +64,6 @@ "@abp/ng.account": "~7.0.0-rc.5", "@abp/ng.account.core": "~7.0.0-rc.5", "@abp/ng.core": "~7.0.0-rc.5", - "@abp/ng.oauth":"~7.0.0-rc.5", "@abp/ng.feature-management": "~7.0.0-rc.5", "@abp/ng.identity": "~7.0.0-rc.5", "@abp/ng.permission-management": "~7.0.0-rc.5", diff --git a/templates/app-nolayers/angular/package.json b/templates/app-nolayers/angular/package.json index 552e2bbf2b..089c0b8285 100644 --- a/templates/app-nolayers/angular/package.json +++ b/templates/app-nolayers/angular/package.json @@ -15,6 +15,7 @@ "@abp/ng.account": "~7.0.0-rc.5", "@abp/ng.components": "~7.0.0-rc.5", "@abp/ng.core": "~7.0.0-rc.5", + "@abp/ng.oauth":"~7.0.0-rc.5", "@abp/ng.identity": "~7.0.0-rc.5", "@abp/ng.setting-management": "~7.0.0-rc.5", "@abp/ng.tenant-management": "~7.0.0-rc.5", diff --git a/templates/app/angular/package.json b/templates/app/angular/package.json index dd3682f13b..f30b1bab75 100644 --- a/templates/app/angular/package.json +++ b/templates/app/angular/package.json @@ -15,6 +15,7 @@ "@abp/ng.account": "~7.0.0-rc.5", "@abp/ng.components": "~7.0.0-rc.5", "@abp/ng.core": "~7.0.0-rc.5", + "@abp/ng.oauth":"~7.0.0-rc.5", "@abp/ng.identity": "~7.0.0-rc.5", "@abp/ng.setting-management": "~7.0.0-rc.5", "@abp/ng.tenant-management": "~7.0.0-rc.5", diff --git a/templates/module/angular/package.json b/templates/module/angular/package.json index fd9c924367..d4c1a0d2bc 100644 --- a/templates/module/angular/package.json +++ b/templates/module/angular/package.json @@ -18,6 +18,7 @@ "@abp/ng.account": "~7.0.0-rc.5", "@abp/ng.components": "~7.0.0-rc.5", "@abp/ng.core": "~7.0.0-rc.5", + "@abp/ng.oauth":"~7.0.0-rc.5", "@abp/ng.identity": "~7.0.0-rc.5", "@abp/ng.setting-management": "~7.0.0-rc.5", "@abp/ng.tenant-management": "~7.0.0-rc.5", From 2aa56f1f4b85f477809fc833be32ab36073aa23b Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Tue, 27 Dec 2022 15:47:39 +0300 Subject: [PATCH 19/20] Fix has a circular dependency on @abp/ng.core --- npm/ng-packs/packages/core/locale/src/utils/register-locale.ts | 2 +- .../packages/core/src/lib/tokens/check-authentication-state.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/npm/ng-packs/packages/core/locale/src/utils/register-locale.ts b/npm/ng-packs/packages/core/locale/src/utils/register-locale.ts index db5d5c6452..8309836ecc 100644 --- a/npm/ng-packs/packages/core/locale/src/utils/register-locale.ts +++ b/npm/ng-packs/packages/core/locale/src/utils/register-locale.ts @@ -1,4 +1,4 @@ -import { differentLocales } from '@abp/ng.core'; +import { differentLocales } from '../../../src/lib/constants'; import { isDevMode } from '@angular/core'; export interface LocaleErrorHandlerData { diff --git a/npm/ng-packs/packages/core/src/lib/tokens/check-authentication-state.ts b/npm/ng-packs/packages/core/src/lib/tokens/check-authentication-state.ts index 0c68b18b52..48dd836e4b 100644 --- a/npm/ng-packs/packages/core/src/lib/tokens/check-authentication-state.ts +++ b/npm/ng-packs/packages/core/src/lib/tokens/check-authentication-state.ts @@ -1,5 +1,5 @@ import { InjectionToken } from '@angular/core'; -import { CheckAuthenticationStateFn } from '@abp/ng.core'; +import { CheckAuthenticationStateFn } from '../models/auth'; export const CHECK_AUTHENTICATION_STATE_FN_KEY = new InjectionToken( 'CHECK_AUTHENTICATION_STATE_FN_KEY', From b07bd195f542515cb3acc883fcf64e50f333d819 Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Tue, 27 Dec 2022 16:48:23 +0300 Subject: [PATCH 20/20] Add default value to SetTokenResponseToStorageFn type --- npm/ng-packs/apps/dev-app/src/app/app.module.ts | 4 ++-- .../packages/core/locale/src/utils/register-locale.ts | 2 +- npm/ng-packs/packages/core/src/lib/models/auth.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/npm/ng-packs/apps/dev-app/src/app/app.module.ts b/npm/ng-packs/apps/dev-app/src/app/app.module.ts index 52ebd38362..c3a0cd68c5 100644 --- a/npm/ng-packs/apps/dev-app/src/app/app.module.ts +++ b/npm/ng-packs/apps/dev-app/src/app/app.module.ts @@ -14,7 +14,7 @@ import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { APP_ROUTE_PROVIDER } from './route.provider'; import { FeatureManagementModule } from '@abp/ng.feature-management'; -import { AbpOAuthModule } from '@abp/ng.oauth'; +// import { AbpOAuthModule } from '@abp/ng.oauth'; @NgModule({ imports: [ @@ -27,7 +27,7 @@ import { AbpOAuthModule } from '@abp/ng.oauth'; sendNullsAsQueryParam: false, skipGetAppConfiguration: false, }), - AbpOAuthModule.forRoot(), + // AbpOAuthModule.forRoot(), ThemeSharedModule.forRoot(), AccountConfigModule.forRoot(), IdentityConfigModule.forRoot(), diff --git a/npm/ng-packs/packages/core/locale/src/utils/register-locale.ts b/npm/ng-packs/packages/core/locale/src/utils/register-locale.ts index 8309836ecc..db5d5c6452 100644 --- a/npm/ng-packs/packages/core/locale/src/utils/register-locale.ts +++ b/npm/ng-packs/packages/core/locale/src/utils/register-locale.ts @@ -1,4 +1,4 @@ -import { differentLocales } from '../../../src/lib/constants'; +import { differentLocales } from '@abp/ng.core'; import { isDevMode } from '@angular/core'; export interface LocaleErrorHandlerData { diff --git a/npm/ng-packs/packages/core/src/lib/models/auth.ts b/npm/ng-packs/packages/core/src/lib/models/auth.ts index f5d532454b..9c0250d316 100644 --- a/npm/ng-packs/packages/core/src/lib/models/auth.ts +++ b/npm/ng-packs/packages/core/src/lib/models/auth.ts @@ -13,5 +13,5 @@ export type PipeToLoginFn = ( injector: Injector, ) => UnaryFunction; -export type SetTokenResponseToStorageFn = (injector: Injector, tokenRes: T) => void; +export type SetTokenResponseToStorageFn = (injector: Injector, tokenRes: T) => void; export type CheckAuthenticationStateFn = (injector: Injector) => void;