Merge pull request #15201 from abpframework/issue/10019-move-oauth-seperated-package

Issue/10019 move oauth seperated package
pull/15239/head
Muhammed Altuğ 3 years ago committed by GitHub
commit 40029b08f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,14 @@
# ABP OAuth Package
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(...)".
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.

@ -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"]
}
}
}

@ -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(),

@ -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();

@ -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 "
]
}

@ -0,0 +1,13 @@
import { CanActivate, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class AuthGuard implements IAuthGuard {
canActivate(): Observable<boolean> | boolean | UrlTree {
console.error('You should add @abp/ng-oauth packages or create your own auth packages.');
return false;
}
}
export interface IAuthGuard extends CanActivate {}

@ -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<any> {
this.warningMessage();
return Promise.resolve(undefined);
}
login(params: LoginParams): Observable<any> {
this.warningMessage();
return of(undefined);
}
logout(queryParams?: Params): Observable<any> {
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<any>;
logout(queryParams?: Params): Observable<any>;
navigateToLogin(queryParams?: Params): void;
login(params: LoginParams): Observable<any>;
}

@ -1 +1,3 @@
export * from './ng-model.component';
export * from './auth.guard';
export * from './auth.service';

@ -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,
],
};
}

@ -1,2 +1 @@
export * from './auth.guard';
export * from './permission.guard';

@ -1,2 +1 @@
export * from './oauth-configuration.handler';
export * from './routes.handler';

@ -1,53 +1,24 @@
import { HttpHandler, HttpHeaders, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Inject, Injectable } from '@angular/core';
import { OAuthService } from 'angular-oauth2-oidc';
import { Injectable } from '@angular/core';
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 { HttpWaitService } from '../services';
@Injectable({
providedIn: 'root',
})
export class ApiInterceptor implements HttpInterceptor {
constructor(
private oAuthService: OAuthService,
private sessionState: SessionStateService,
private httpWaitService: HttpWaitService,
@Inject(TENANT_KEY) private tenantKey: string,
) {}
export class ApiInterceptor implements IApiInterceptor {
constructor(private httpWaitService: HttpWaitService) {}
getAdditionalHeaders(existingHeaders?: HttpHeaders) {
return existingHeaders;
}
intercept(request: HttpRequest<any>, next: HttpHandler) {
this.httpWaitService.addRequest(request);
return next
.handle(
request.clone({
setHeaders: this.getAdditionalHeaders(request.headers),
}),
)
.pipe(finalize(() => this.httpWaitService.deleteRequest(request)));
return next.handle(request).pipe(finalize(() => this.httpWaitService.deleteRequest(request)));
}
}
getAdditionalHeaders(existingHeaders?: HttpHeaders) {
const headers = {} as any;
const token = this.oAuthService.getAccessToken();
if (!existingHeaders?.has('Authorization') && token) {
headers['Authorization'] = `Bearer ${token}`;
}
const lang = this.sessionState.getLanguage();
if (!existingHeaders?.has('Accept-Language') && lang) {
headers['Accept-Language'] = lang;
}
const tenant = this.sessionState.getTenant();
if (!existingHeaders?.has(this.tenantKey) && tenant?.id) {
headers[this.tenantKey] = tenant.id;
}
headers['X-Requested-With'] = 'XMLHttpRequest';
return headers;
}
export interface IApiInterceptor extends HttpInterceptor {
getAdditionalHeaders(existingHeaders?: HttpHeaders): HttpHeaders;
}

@ -1,6 +1,17 @@
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<LoginParams, 'redirectUrl' | 'rememberMe'>,
injector: Injector,
) => UnaryFunction<any, any>;
export type SetTokenResponseToStorageFn<T = any> = (injector: Injector, tokenRes: T) => void;
export type CheckAuthenticationStateFn = (injector: Injector) => void;

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

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

@ -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<any> {
return this.strategy.logout(queryParams);
}
navigateToLogin(queryParams?: Params) {
this.strategy.navigateToLogin(queryParams);
}
login(params: LoginParams) {
return this.strategy.login(params);
}
}

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

@ -1,11 +0,0 @@
import { Injectable, NgZone, Optional } from '@angular/core';
import { OAuthService } from '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;
}
}

@ -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<any>;
abstract login(params?: LoginParams | Params): Observable<any>;
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<any> {
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<any> {
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<LoginParams, 'redirectUrl' | 'rememberMe'>) {
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] : '';
}

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

@ -0,0 +1,6 @@
import { InjectionToken } from '@angular/core';
import { CheckAuthenticationStateFn } from '../models/auth';
export const CHECK_AUTHENTICATION_STATE_FN_KEY = new InjectionToken<CheckAuthenticationStateFn>(
'CHECK_AUTHENTICATION_STATE_FN_KEY',
);

@ -6,4 +6,7 @@ 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';
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';

@ -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',
);
};
},
},
);

@ -0,0 +1,4 @@
import { InjectionToken } from '@angular/core';
import { PipeToLoginFn } from '../models/auth';
export const PIPE_TO_LOGIN_FN_KEY = new InjectionToken<PipeToLoginFn>('PIPE_TO_LOGIN_FN_KEY');

@ -0,0 +1,6 @@
import { InjectionToken } from '@angular/core';
import { SetTokenResponseToStorageFn } from '../models';
export const SET_TOKEN_RESPONSE_TO_STORAGE_FN_KEY = new InjectionToken<SetTokenResponseToStorageFn>(
'SET_TOKEN_RESPONSE_TO_STORAGE_FN_KEY',
);

@ -1,5 +1,4 @@
export * from './array-utils';
export * from './auth-utils';
export * from './common-utils';
export * from './date-utils';
export * from './environment-utils';

@ -1,20 +1,20 @@
import { registerLocaleData } from '@angular/common';
import { Injector } from '@angular/core';
import { OAuthService } from 'angular-oauth2-oidc';
import { InjectFlags, Injector } from '@angular/core';
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';
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 () => {
@ -25,41 +25,36 @@ export function getInitialData(injector: Injector) {
environmentService.setState(options.environment as Environment);
await getRemoteEnv(injector, options.environment);
await parseTenantFromUrl(injector);
await injector.get(AuthService).init();
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;
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(() => checkAuthenticationState(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));
}
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);

@ -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';
@ -23,3 +22,4 @@ export * from './lib/strategies';
export * from './lib/tokens';
export * from './lib/utils';
export * from './lib/validators';
export * from './lib/interceptors';

@ -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": {}
}
]
}

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

@ -0,0 +1,22 @@
/* eslint-disable */
export default {
displayName: 'oauth',
preset: '../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
globals: {
'ts-jest': {
tsconfig: '<rootDir>/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',
],
};

@ -0,0 +1,14 @@
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/packages/oauth",
"lib": {
"entryFile": "src/public-api.ts"
},
"allowedNonPeerDependencies": [
"@abp/utils",
"@abp/ng.core",
"angular-oauth2-oidc",
"just-clone",
"just-compare"
]
}

@ -0,0 +1,20 @@
{
"name": "@abp/ng.oauth",
"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",
"tslib": "^2.0.0"
},
"publishConfig": {
"access": "public"
}
}

@ -0,0 +1 @@
export * from './oauth.guard';

@ -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> | boolean | UrlTree {

@ -0,0 +1 @@
export * from './oauth-configuration.handler';

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

@ -0,0 +1,51 @@
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, IApiInterceptor } from '@abp/ng.core';
@Injectable({
providedIn: 'root',
})
export class OAuthApiInterceptor implements IApiInterceptor {
constructor(
private oAuthService: OAuthService,
private sessionState: SessionStateService,
private httpWaitService: HttpWaitService,
@Inject(TENANT_KEY) private tenantKey: string,
) {}
intercept(request: HttpRequest<any>, next: HttpHandler) {
this.httpWaitService.addRequest(request);
return next
.handle(
request.clone({
setHeaders: this.getAdditionalHeaders(request.headers),
}),
)
.pipe(finalize(() => this.httpWaitService.deleteRequest(request)));
}
getAdditionalHeaders(existingHeaders?: HttpHeaders) {
const headers = {} as any;
const token = this.oAuthService.getAccessToken();
if (!existingHeaders?.has('Authorization') && token) {
headers['Authorization'] = `Bearer ${token}`;
}
const lang = this.sessionState.getLanguage();
if (!existingHeaders?.has('Accept-Language') && lang) {
headers['Accept-Language'] = lang;
}
const tenant = this.sessionState.getTenant();
if (!existingHeaders?.has(this.tenantKey) && tenant?.id) {
headers[this.tenantKey] = tenant.id;
}
headers['X-Requested-With'] = 'XMLHttpRequest';
return headers;
}
}

@ -0,0 +1 @@
export * from './api.interceptor';

@ -0,0 +1,71 @@
import { APP_INITIALIZER, Injector, ModuleWithProviders, NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { OAuthModule, OAuthStorage } from 'angular-oauth2-oidc';
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 } from './services';
import { OAuthConfigurationHandler } from './handlers/oauth-configuration.handler';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { OAuthApiInterceptor } from './interceptors/api.interceptor';
import { AbpOAuthGuard } from './guards/oauth.guard';
import { NavigateToManageProfileProvider } from './providers';
import { checkAccessToken, pipeToLogin, setTokenResponseToStorage } from './utils';
@NgModule({
imports: [CommonModule, OAuthModule],
})
export class AbpOAuthModule {
static forRoot(): ModuleWithProviders<AbpOAuthModule> {
return {
ngModule: AbpOAuthModule,
providers: [
{
provide: AuthService,
useClass: AbpOAuthService,
},
{
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: CHECK_AUTHENTICATION_STATE_FN_KEY,
useValue: checkAccessToken,
},
{
provide: HTTP_INTERCEPTORS,
useExisting: ApiInterceptor,
multi: true,
},
NavigateToManageProfileProvider,
{
provide: APP_INITIALIZER,
multi: true,
deps: [OAuthConfigurationHandler],
useFactory: noop,
},
OAuthModule.forRoot().providers,
{ provide: OAuthStorage, useFactory: storageFactory },
],
};
}
}

@ -0,0 +1 @@
export * from './navigate-to-manage-profile.provider';

@ -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',
);
};
},
};

@ -0,0 +1 @@
export * from './oauth.service';

@ -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<any> {
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();
}
}

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

@ -0,0 +1,108 @@
import { Injector } from '@angular/core';
import { Params } from '@angular/router';
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,
ConfigStateService,
EnvironmentService,
HttpErrorReporterService,
SessionStateService,
TENANT_KEY,
} from '@abp/ng.core';
import { clearOAuthStorage } from '../utils/clear-o-auth-storage';
import { oAuthStorage } from '../utils/oauth-storage';
export abstract class AuthFlowStrategy {
abstract readonly isInternalAuth: boolean;
protected httpErrorReporter: HttpErrorReporterService;
protected environment: EnvironmentService;
protected configState: ConfigStateService;
protected oAuthService: OAuthService2;
protected oAuthConfig: AuthConfig;
protected sessionState: SessionStateService;
protected tenantKey: string;
abstract checkIfInternalAuth(queryParams?: Params): boolean;
abstract navigateToLogin(queryParams?: Params): void;
abstract logout(queryParams?: Params): Observable<any>;
abstract login(params?: LoginParams | Params): Observable<any>;
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(OAuthService2);
this.sessionState = injector.get(SessionStateService);
this.oAuthConfig = this.environment.getEnvironment().oAuthConfig;
this.tenantKey = injector.get(TENANT_KEY);
this.listenToOauthErrors();
}
async init(): Promise<any> {
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;
}

@ -0,0 +1,88 @@
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 { pipeToLogin, 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 + '=([^;]+)'));
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<any> {
const tenant = this.sessionState.getTenant();
return from(
this.oAuthService.fetchTokenUsingPasswordFlow(
params.username,
params.password,
new HttpHeaders({ ...(tenant && tenant.id && { [this.tenantKey]: tenant.id }) }),
),
).pipe(pipeToLogin(params, this.injector));
}
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();
});
}
}

@ -0,0 +1,3 @@
export * from './auth-flow-strategy';
export * from './auth-code-flow-strategy';
export * from './auth-password-flow-strategy';

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

@ -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<AuthGuard>;
let guard: AuthGuard;
let spectator: SpectatorService<AbpOAuthGuard>;
let guard: AbpOAuthGuard;
const createService = createServiceFactory({
service: AuthGuard,
service: AbpOAuthGuard,
mocks: [OAuthService, AuthService],
});

@ -0,0 +1,111 @@
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 { of } from 'rxjs';
import { checkAccessToken } from '../utils/check-access-token';
const environment = { oAuthConfig: { issuer: 'test' } };
@Component({
selector: 'abp-dummy',
template: '',
})
export class DummyComponent {}
describe('InitialUtils', () => {
let spectator: Spectator<DummyComponent>;
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));
// Todo: refactor it
// 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();
});
});
});

@ -0,0 +1,5 @@
describe('Test', () => {
it('should be passed', () => {
expect(true).toBe(true);
});
});

@ -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);
},
};

@ -0,0 +1 @@
export * from './auth-flow-strategy';

@ -3,13 +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 { LoginParams } from '../models/auth';
import { ConfigStateService } from '../services/config-state.service';
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<LoginParams, 'redirectUrl' | 'rememberMe'>,
injector: Injector,
) {
@ -23,9 +27,12 @@ export function pipeToLogin(
if (params.redirectUrl) router.navigate([params.redirectUrl]);
}),
);
}
};
export function setTokenResponseToStorage(injector: Injector, tokenRes: TokenResponse) {
export const setTokenResponseToStorage: SetTokenResponseToStorageFn<TokenResponse> = function (
injector: Injector,
tokenRes: TokenResponse,
) {
const { access_token, refresh_token, scope: grantedScopes, expires_in } = tokenRes;
const storage = injector.get(OAuthStorage);
@ -43,7 +50,7 @@ export function setTokenResponseToStorage(injector: Injector, tokenRes: TokenRes
const expiresAt = now.getTime() + expiresInMilliSeconds;
storage.setItem('expires_at', '' + expiresAt);
}
}
};
export function setRememberMe(remember: boolean) {
removeRememberMe();

@ -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();
}
};

@ -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));
}

@ -0,0 +1,5 @@
export * from './oauth-storage';
export * from './storage.factory';
export * from './auth-utils';
export * from './clear-o-auth-storage';
export * from './check-access-token';

@ -0,0 +1 @@
export const oAuthStorage = localStorage;

@ -0,0 +1,6 @@
import { OAuthStorage } from 'angular-oauth2-oidc';
import { oAuthStorage } from './oauth-storage';
export function storageFactory(): OAuthStorage {
return oAuthStorage;
}

@ -0,0 +1,9 @@
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';
export * from './lib/providers';

@ -0,0 +1 @@
import 'jest-preset-angular/setup-jest';

@ -0,0 +1,17 @@
{
"extends": "../../tsconfig.base.json",
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
],
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"target": "es2020"
}
}

@ -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"]
}

@ -0,0 +1,11 @@
{
"extends": "./tsconfig.lib.json",
"compilerOptions": {
"declarationMap": false,
"target": "ES2022",
"useDefineForClassFields": false
},
"angularCompilerOptions": {
"compilationMode": "partial"
}
}

@ -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"]
}

@ -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<HttpErrorWrapperComponent>) {

@ -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 <templates>', 'template dirs', false);
program.option('-p, --template-path <templatePath>', '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();

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

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

@ -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(),

@ -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"
]
}

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

@ -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(),

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

@ -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(),

Loading…
Cancel
Save