From 38faa475bae024e3ef2f376888ff09fb1665b4af Mon Sep 17 00:00:00 2001 From: Shubham Kumar Date: Mon, 10 Nov 2025 21:22:16 +0530 Subject: [PATCH] fix(oauth2): replace localStorage with sessionStorage in callback component (#2790) --- src/app/zitadel/auth.service.ts | 85 +++++++++---------- .../zitadel/callback/callback.component.ts | 26 ++++-- src/index.html | 2 +- 3 files changed, 60 insertions(+), 53 deletions(-) diff --git a/src/app/zitadel/auth.service.ts b/src/app/zitadel/auth.service.ts index c94f9fc23..75af2d531 100644 --- a/src/app/zitadel/auth.service.ts +++ b/src/app/zitadel/auth.service.ts @@ -33,7 +33,7 @@ export class AuthService { async login() { const codeVerifier = this.generateRandomString(); const codeChallenge = await this.generateCodeChallenge(codeVerifier); - localStorage.setItem('code_verifier', codeVerifier); + sessionStorage.setItem('code_verifier', codeVerifier); const url = `${this.authUrl}` + `?client_id=${encodeURIComponent(this.clientId)}` + @@ -105,56 +105,51 @@ export class AuthService { return base64; } - exchangeCodeForTokens(code: string, codeVerifier: string | null) { + async exchangeCodeForTokens(code: string, codeVerifier: string | null): Promise { const payload = { code: code, code_verifier: codeVerifier || '' }; - fetch(this.api + 'authentication/token', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(payload) - }) - .then((res) => { - if (!res.ok) { - throw new Error(`Error exchanging code: ${res.status} ${res.statusText}`); - } - return res.json(); - }) - .then( - (tokens: { - access_token: string; - id_token: string; - refresh_token: string; - expires_in: number; - token_type: string; - }) => { - const token: OAuth2Token = { - access_token: tokens.access_token, - token_type: tokens.token_type, - refresh_token: tokens.refresh_token, - expires_in: tokens.expires_in, - scope: 'Bearer' - }; - - localStorage.setItem('id_token', tokens.id_token); - localStorage.setItem('mifosXZitadel', 'true'); - sessionStorage.setItem('mifosXZitadelTokenDetails', JSON.stringify(token)); - localStorage.setItem('refresh_token', tokens.refresh_token); - this.scheduleRefresh(tokens.expires_in); - localStorage.removeItem('auth_code'); - localStorage.removeItem('code_verifier'); - this.userdetails(); - } - ) - .catch((error) => { - localStorage.removeItem('auth_code'); - localStorage.removeItem('code_verifier'); - window.location.href = '/#/login'; + try { + const response = await fetch(this.api + 'authentication/token', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(payload) }); + + if (!response.ok) { + throw new Error(`Error exchanging code: ${response.status} ${response.statusText}`); + } + + const tokens: { + access_token: string; + id_token: string; + refresh_token: string; + expires_in: number; + token_type: string; + } = await response.json(); + + const token: OAuth2Token = { + access_token: tokens.access_token, + token_type: tokens.token_type, + refresh_token: tokens.refresh_token, + expires_in: tokens.expires_in, + scope: 'Bearer' + }; + + sessionStorage.setItem('mifosXZitadelTokenDetails', JSON.stringify(token)); + localStorage.setItem('id_token', tokens.id_token); + localStorage.setItem('refresh_token', tokens.refresh_token); + localStorage.setItem('mifosXZitadel', 'true'); + this.scheduleRefresh(tokens.expires_in); + await this.userdetails(); + } catch (error) { + window.location.href = '/#/login'; + throw error; + } } userdetails() { diff --git a/src/app/zitadel/callback/callback.component.ts b/src/app/zitadel/callback/callback.component.ts index 97697e11a..e94feb7ca 100644 --- a/src/app/zitadel/callback/callback.component.ts +++ b/src/app/zitadel/callback/callback.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { AuthService } from '../auth.service'; @Component({ @@ -9,15 +9,27 @@ import { AuthService } from '../auth.service'; export class CallbackComponent implements OnInit { constructor( private route: ActivatedRoute, + private router: Router, private authService: AuthService ) {} - ngOnInit(): void { - let code = localStorage.getItem('auth_code'); - - if (code) { - const codeVerifier = localStorage.getItem('code_verifier'); - this.authService.exchangeCodeForTokens(code, codeVerifier); + async ngOnInit(): Promise { + try { + const code = sessionStorage.getItem('auth_code'); + if (code) { + const codeVerifier = sessionStorage.getItem('code_verifier'); + try { + await this.authService.exchangeCodeForTokens(code, codeVerifier); + } finally { + // Clean up sensitive data immediately after use + sessionStorage.removeItem('auth_code'); + sessionStorage.removeItem('code_verifier'); + } + } + } catch (error) { + console.error('Authentication failed:', error); + // Navigate to login page + this.router.navigate(['/login']); } } } diff --git a/src/index.html b/src/index.html index a168e7b28..870489224 100644 --- a/src/index.html +++ b/src/index.html @@ -25,7 +25,7 @@ } const code = getQueryParam('code'); if (code) { - localStorage.setItem('auth_code', code); + sessionStorage.setItem('auth_code', code); window.location.href = '/#/callback'; }