reverted to keycloak from angular-oidc due to unfixable infinite loops

This commit is contained in:
Timo Bejan 2020-10-20 10:37:22 +03:00
parent 5a6ece7daa
commit 0e686008c6
8 changed files with 103 additions and 134 deletions

View File

@ -39,14 +39,12 @@ import { ServiceWorkerModule } from '@angular/service-worker';
import { environment } from '../environments/environment';
import { ProjectDetailsDialogComponent } from './screens/project-overview-screen/project-details-dialog/project-details-dialog.component';
import { AuthModule } from './auth/auth.module';
import { AuthGuard } from './auth/auth.guard';
import { FileUploadModule } from './upload/file-upload.module';
import { FullPageLoadingIndicatorComponent } from './utils/full-page-loading-indicator/full-page-loading-indicator.component';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { InitialsAvatarComponent } from './common/initials-avatar/initials-avatar.component';
import { StatusBarComponent } from './components/status-bar/status-bar.component';
import { LogoComponent } from './logo/logo.component';
import { AuthInterceptorService } from './interceptor/auth-interceptor.service';
import { CompositeRouteGuard } from './utils/composite-route.guard';
import { AppStateGuard } from './state/app-state.guard';
import { SimpleDoughnutChartComponent } from './components/simple-doughnut-chart/simple-doughnut-chart.component';
@ -54,6 +52,7 @@ import { ManualRedactionDialogComponent } from './screens/file/manual-redaction-
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { AnnotationIconComponent } from './components/annotation-icon/annotation-icon.component';
import { AuthGuard } from "./auth/auth.guard";
export function HttpLoaderFactory(httpClient: HttpClient) {
return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json');
@ -157,10 +156,6 @@ export function HttpLoaderFactory(httpClient: HttpClient) {
provide: HTTP_INTERCEPTORS,
multi: true,
useClass: ApiPathInterceptorService
}, {
provide: HTTP_INTERCEPTORS,
multi: true,
useClass: AuthInterceptorService
}, {
provide: APP_INITIALIZER,
multi: true,

View File

@ -1,78 +1,30 @@
import {Injectable} from "@angular/core";
import {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot, UrlTree} from "@angular/router";
import {Observable} from "rxjs";
import {AuthConfig, OAuthService} from "angular-oauth2-oidc";
import {AppConfigKey, AppConfigService} from "../app-config/app-config.service";
import {map} from "rxjs/operators";
import {ActivatedRouteSnapshot, Router, RouterStateSnapshot} from "@angular/router";
import {KeycloakAuthGuard, KeycloakService} from "keycloak-angular";
import {UserService} from "../user/user.service";
@Injectable({
providedIn: 'root'
providedIn: 'root',
})
export class AuthGuard implements CanActivate {
private _configured = false;
constructor(private readonly _oauthService: OAuthService,
private readonly _userService: UserService,
private readonly _appConfigService: AppConfigService) {
export class AuthGuard extends KeycloakAuthGuard {
constructor(
protected readonly _router: Router,
protected readonly _keycloak: KeycloakService,
private readonly _userService: UserService
) {
super(_router, _keycloak);
}
private async _configure() {
this._configured = true;
const authConfig = await this._createConfiguration().toPromise();
this._oauthService.configure(authConfig);
this._oauthService.setupAutomaticSilentRefresh();
public async isAccessAllowed(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
window['silentRefresh'] =() =>{
this
._oauthService
.silentRefresh()
.then(info => console.log('refresh ok', info))
.catch(err => console.log('refresh error', err));
};
return this._oauthService.loadDiscoveryDocumentAndTryLogin();
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
if (!this._configured) {
return this._configure().then(() => this._checkToken());
if (!this.authenticated) {
await this._keycloak.login({
redirectUri: window.location.origin + state.url,
});
}
return this._checkToken();
}
await this._userService.loadCurrentUser();
private async _checkToken() {
const timeLeft= ((this._oauthService.getAccessTokenExpiration() - new Date().getTime()) / 1000);
const expired = timeLeft < 60;
if (!this._oauthService.getAccessToken() || expired) {
this._oauthService.initLoginFlow();
return false;
}
if (!this._userService.user) {
await this._userService.loadCurrentUser();
return true;
}
return true;
}
private _createConfiguration(): Observable<AuthConfig> {
return this._appConfigService.loadAppConfig().pipe(map(config => {
return {
issuer: config[AppConfigKey.OAUTH_URL],
redirectUri: window.location.origin,
clientId: config[AppConfigKey.OAUTH_CLIENT_ID],
scope: 'openid profile email offline_access',
responseType: 'code',
showDebugInformation: true,
silentRefreshRedirectUri: window.location.origin + '/assets/oauth/silent-refresh.html',
useSilentRefresh: true,
}
}));
}
}

View File

@ -1,28 +1,55 @@
import {NgModule} from "@angular/core";
import {APP_INITIALIZER, NgModule} from "@angular/core";
import {CommonModule} from "@angular/common";
import {HttpClientModule} from "@angular/common/http";
import {OAuthModule} from "angular-oauth2-oidc";
import {AuthGuard} from "./auth.guard";
import {AppConfigModule} from "../app-config/app-config.module";
import {KeycloakService} from "keycloak-angular";
import {AppConfigKey, AppConfigService} from "../app-config/app-config.service";
export function keycloakInitializer(keycloak: KeycloakService, appConfigService: AppConfigService) {
return () => {
return appConfigService.loadAppConfig().toPromise().then(() => {
let url = appConfigService.getConfig(AppConfigKey.OAUTH_URL);
url = url.replace(/\/$/, ""); // remove trailing slash
const realm = url.substring(url.lastIndexOf("/") + 1, url.length);
url = url.substr(0, url.lastIndexOf("/"));
return keycloak.init({
config: {
url: url,
realm: realm,
clientId: appConfigService.getConfig(AppConfigKey.OAUTH_CLIENT_ID)
},
initOptions: {
checkLoginIframe: false,
onLoad: 'check-sso',
silentCheckSsoRedirectUri:
window.location.origin + '/assets/keycloak/silent-check-sso.html',
flow: 'standard'
},
enableBearerInterceptor: true,
})
});
}
}
@NgModule({
declarations: [],
imports: [
CommonModule,
HttpClientModule,
AppConfigModule,
OAuthModule.forRoot(
{
resourceServer: {
allowedUrls: ['/'],
sendAccessToken: true
}
}
)
],
providers: [
AuthGuard
{
provide: APP_INITIALIZER,
useFactory: keycloakInitializer,
multi: true,
deps: [KeycloakService, AppConfigService],
},
],
})
export class AuthModule {

View File

@ -1,24 +0,0 @@
import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
import {Injectable} from '@angular/core';
import {Observable, of} from 'rxjs';
import {AppConfigService} from "../app-config/app-config.service";
import {catchError} from "rxjs/operators";
import {OAuthService} from "angular-oauth2-oidc";
@Injectable()
export class AuthInterceptorService implements HttpInterceptor {
constructor(private readonly _appConfigService: AppConfigService, private readonly _oauthService: OAuthService) {
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).pipe(catchError((err: any) => {
if (err instanceof HttpErrorResponse) {
if (err.status === 401) {
this._oauthService.initLoginFlow();
}
}
return of(err);
}));
}
}

View File

@ -1,22 +1,37 @@
import {Injectable} from "@angular/core";
import {OAuthService, UserInfo} from "angular-oauth2-oidc";
import {KeycloakService} from "keycloak-angular";
import {KeycloakProfile} from "keycloak-js";
export class UserWrapper {
constructor(private _currentUser: KeycloakProfile, public roles: string[]) {
}
get name() {
return this._currentUser.firstName + " " + this._currentUser.lastName;
}
}
@Injectable({
providedIn: 'root'
})
export class UserService {
private _currentUser: UserInfo;
private _currentUser: UserWrapper;
constructor(private _oauthService: OAuthService) {
constructor(private _keycloakService: KeycloakService) {
}
logout() {
this._oauthService.logOut();
this._keycloakService.logout();
}
async loadCurrentUser() {
this._currentUser = await this._oauthService.loadUserProfile();
this._currentUser = new UserWrapper(await this._keycloakService.loadUserProfile(false),this._keycloakService.getUserRoles(true));
console.log(this._currentUser);
}
get user() {

View File

@ -43,9 +43,9 @@
"@ngx-translate/http-loader": "^6.0.0",
"@nrwl/angular": "^10.2.0",
"@pdftron/webviewer": "^7.0.1",
"angular-oauth2-oidc": "^10.0.3",
"angular-oauth2-oidc-jwks": "^9.0.0",
"file-saver": "^2.0.2",
"keycloak-angular": "^8.0.1",
"keycloak-js": "^11.0.2",
"ng2-file-upload": "^1.4.0",
"ngp-sort-pipe": "^0.0.4",
"ngx-dropzone": "^2.2.2",

View File

@ -2,7 +2,10 @@
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/dist" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>

View File

@ -2263,20 +2263,6 @@ alphanum-sort@^1.0.0:
resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=
angular-oauth2-oidc-jwks@^9.0.0:
version "9.0.0"
resolved "https://registry.yarnpkg.com/angular-oauth2-oidc-jwks/-/angular-oauth2-oidc-jwks-9.0.0.tgz#f11e4e561ff423928ab63ca2cca84703a00ff85d"
integrity sha512-3hTJc7vEI/ka/nnliMcCQuDnszzL3AhGInBBbn96BO+ZOdvP/4PbEumUsDto2WRpPMPxD6HAmExwYeQWljcc5A==
dependencies:
jsrsasign "^8.0.12"
angular-oauth2-oidc@^10.0.3:
version "10.0.3"
resolved "https://registry.yarnpkg.com/angular-oauth2-oidc/-/angular-oauth2-oidc-10.0.3.tgz#612ef75c2e07b56592d2506f9618ee6a61857ad9"
integrity sha512-9wC8I3e3cN6rMBOlo5JB2y3Fd2erp8pJ67t4vEVzyPbnRG6BJ4rreSOznSL9zw/2SjhC9kRV2OfFie29CUCzEg==
dependencies:
tslib "^2.0.0"
ansi-colors@4.1.1, ansi-colors@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
@ -2694,7 +2680,7 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
base64-js@^1.0.2:
base64-js@1.3.1, base64-js@^1.0.2:
version "1.3.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
@ -6787,6 +6773,11 @@ jest@26.2.2:
import-local "^3.0.2"
jest-cli "^26.2.2"
js-sha256@0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966"
integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
@ -6937,11 +6928,6 @@ jsprim@^1.2.2:
json-schema "0.2.3"
verror "1.10.0"
jsrsasign@^8.0.12:
version "8.0.24"
resolved "https://registry.yarnpkg.com/jsrsasign/-/jsrsasign-8.0.24.tgz#fc26bac45494caac3dd8f69c1f95847c4bda6c83"
integrity sha512-u45jAyusqUpyGbFc2IbHoeE4rSkoBWQgLe/w99temHenX+GyCz4nflU5sjK7ajU1ffZTezl6le7u43Yjr/lkQg==
karma-source-map-support@1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz#58526ceccf7e8730e56effd97a4de8d712ac0d6b"
@ -6949,6 +6935,21 @@ karma-source-map-support@1.4.0:
dependencies:
source-map-support "^0.5.5"
keycloak-angular@^8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/keycloak-angular/-/keycloak-angular-8.0.1.tgz#29851e7aded21925faa051c69dfa5872bda6661f"
integrity sha512-q68vcaFiSYNjbzPM1v+6LohMpWUyus9hcQBi2rNz06xOtWuRU4U6t5vQgoim6bDhtkhWpR5+a3SYl0lzUJKyrw==
dependencies:
tslib "^2.0.0"
keycloak-js@^11.0.2:
version "11.0.2"
resolved "https://registry.yarnpkg.com/keycloak-js/-/keycloak-js-11.0.2.tgz#e981c5270e72066e38b2a1bd98f1138d6cd560c1"
integrity sha512-dnvzgTetovu3eTjJtvBQQhxRN4jqvd/DaA2wFaE4aWIFXhwRcoPpZT8ZJ7MwlICDPdCgzbCsOsBjpL8CbYOZsg==
dependencies:
base64-js "1.3.1"
js-sha256 "0.9.0"
killable@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892"