diff --git a/frontend/app/src/app/modules/auth/auth-helper.service.ts b/frontend/app/src/app/modules/auth/auth-helper.service.ts index a0684aad..a99df65d 100644 --- a/frontend/app/src/app/modules/auth/auth-helper.service.ts +++ b/frontend/app/src/app/modules/auth/auth-helper.service.ts @@ -90,12 +90,18 @@ export class AuthHelperService { key as keyof SCUserConfiguration; user[userKey] = JSONPath({ - path: this.userConfigurationMap[userKey] as string, + path: + this.userConfigurationMap[ + userKey + ] as string, json: userInfo, })[0]; } - if (user.givenName && user.familyName) { + if ( + user.givenName && + user.familyName + ) { user.name = `${user.givenName} ${user.familyName}`; } @@ -103,7 +109,8 @@ export class AuthHelperService { return user; } - public async deleteOriginPath(): Promise { + public async deleteOriginPath(): + Promise { await this.storageProvider.delete( AUTH_ORIGIN_PATH, ); @@ -129,9 +136,11 @@ export class AuthHelperService { } } - public async endBrowserSession(): Promise { + public async endBrowserSession(): + Promise { const endSessionEndpoint = - await this.authService.getEndSessionEndpoint(); + await this.authService + .getEndSessionEndpoint(); if (!endSessionEndpoint) { return; @@ -139,25 +148,41 @@ export class AuthHelperService { const alert = await this.alertController.create({ - header: this.translateService.instant( - `${AUTH_MESSAGE_PREFIX}.log_out_alert.header`, - ), - message: this.translateService.instant( - `${AUTH_MESSAGE_PREFIX}.log_out_alert.message`, - ), + header: + this.translateService.instant( + `${AUTH_MESSAGE_PREFIX}.log_out_alert.header`, + ), + + message: + this.translateService.instant( + `${AUTH_MESSAGE_PREFIX}.log_out_alert.message`, + ), + buttons: [ { text: - this.translateService.instant('no'), + this.translateService.instant( + 'no', + ), + + role: 'cancel', cssClass: 'default', }, { text: - this.translateService.instant('yes'), + this.translateService.instant( + 'yes', + ), + role: 'confirm', cssClass: 'preferred', - handler: () => { - this.browser.open( + + handler: async () => { + /* + * Only now, after explicit confirmation, + * open the IdP end-session endpoint. + */ + await this.browser.open( endSessionEndpoint, ); }, diff --git a/frontend/app/src/app/modules/auth/auth.service.ts b/frontend/app/src/app/modules/auth/auth.service.ts index ab5c4432..a661392b 100644 --- a/frontend/app/src/app/modules/auth/auth.service.ts +++ b/frontend/app/src/app/modules/auth/auth.service.ts @@ -53,7 +53,6 @@ import { } from './auth.provider.methods'; import {requestorFactory} from './factories/requestor.factory'; import {storageFactory} from './factories/storage.factory'; -import {SimpleBrowser} from '../../util/browser.factory'; const TOKEN_RESPONSE_KEY = 'token_response'; const AUTH_EXPIRY_BUFFER = -5 * 60; @@ -184,7 +183,6 @@ export class AuthService { constructor( private readonly platform: Platform, private readonly configProvider: ConfigProvider, - private readonly browser: SimpleBrowser, ) { this.storage = storageFactory(platform); @@ -420,12 +418,9 @@ export class AuthService { } public async signOut( - state?: string, + _state?: string, revokeTokens = false, ): Promise { - const currentToken = - this.tokenSubject.value; - try { if (revokeTokens) { await this.revokeTokens(); @@ -435,23 +430,17 @@ export class AuthService { TOKEN_RESPONSE_KEY, ); - const endSessionEndpoint = - await this.getEndSessionEndpoint(); - - if ( - endSessionEndpoint && - currentToken - ) { - await this.performEndSessionRequest( - endSessionEndpoint, - currentToken, - state, - ); - - return; - } - - await this.internalEndSessionCallback(); + /* + * Local logout only. + * + * The optional logout from the identity provider is + * deliberately handled by AuthHelperService after the + * confirmation dialog. + */ + this.notify({ + action: + AuthActions.SignOutSuccess, + }); } catch (error) { this.notify({ action: @@ -886,43 +875,6 @@ export class AuthService { .completeAuthorizationRequestIfPossible(); } - private async performEndSessionRequest( - endpoint: string, - token: TokenResponse, - state?: string, - ): Promise { - const url = - new URL(endpoint); - - if (token.idToken) { - url.searchParams.set( - 'id_token_hint', - token.idToken, - ); - } - - if ( - this.authConfig - .end_session_redirect_url - ) { - url.searchParams.set( - 'post_logout_redirect_uri', - this.authConfig - .end_session_redirect_url, - ); - } - - if (state) { - url.searchParams.set( - 'state', - state, - ); - } - - await this.browser.open( - url.toString(), - ); - } private async internalEndSessionCallback(): Promise { diff --git a/frontend/app/src/app/modules/profile/page/profile-page.component.ts b/frontend/app/src/app/modules/profile/page/profile-page.component.ts index d8acb09d..e2458aee 100644 --- a/frontend/app/src/app/modules/profile/page/profile-page.component.ts +++ b/frontend/app/src/app/modules/profile/page/profile-page.component.ts @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 StApps + * Copyright (C) 2022 StApps * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation, version 3. @@ -12,12 +12,14 @@ * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ + import {Component} from '@angular/core'; -import {AuthHelperService} from '../../auth/auth-helper.service'; import {ActivatedRoute} from '@angular/router'; + +import {AuthHelperService} from '../../auth/auth-helper.service'; +import {AuthService} from '../../auth/auth.service'; import {ScheduleProvider} from '../../calendar/schedule.provider'; import {profilePageSections} from '../../../../config/profile-page-sections'; -import {AuthService} from '../../auth/auth.service'; @Component({ selector: 'app-home', @@ -34,23 +36,40 @@ export class ProfilePageComponent { readonly scheduleProvider: ScheduleProvider, ) {} - async signIn() { - const originPath = this.activatedRoute.snapshot.queryParamMap.get('origin_path'); + async signIn(): Promise { + const originPath = + this.activatedRoute.snapshot + .queryParamMap + .get('origin_path'); await ( originPath - ? this.authHelper.setOriginPath(originPath) - : this.authHelper.deleteOriginPath() + ? this.authHelper + .setOriginPath(originPath) + : this.authHelper + .deleteOriginPath() ); await this.authService.signIn(); } - async signOut() { + async signOut(): Promise { + /* + * First log out locally. + */ await this.authService.signOut(); + + /* + * Then ask whether the user also wants to terminate + * the session at the identity provider. + * + * The IdP end-session endpoint is opened only if + * the user confirms with "Yes". + */ + await this.authHelper.endBrowserSession(); } - ionViewWillEnter() { + ionViewWillEnter(): void { void this.authService.loadUserInfo(); } }