From d53ef3906c1c7518975d7ab5a98a6c8eeb4acf0d Mon Sep 17 00:00:00 2001 From: Ricardo Santos Date: Fri, 27 Feb 2026 10:12:38 +0000 Subject: [PATCH 1/2] Enhance access token handling and display sensitive claims in tables --- .../SPA/src/app/app.component.css | 4 ++ .../SPA/src/app/home/home.component.css | 26 ++++++- .../SPA/src/app/home/home.component.html | 45 +++++++++++- .../SPA/src/app/home/home.component.ts | 70 ++++++++++++++++++- 4 files changed, 141 insertions(+), 4 deletions(-) diff --git a/1-Authentication/2-sign-in-angular/SPA/src/app/app.component.css b/1-Authentication/2-sign-in-angular/SPA/src/app/app.component.css index ecdc1e2c2..10b116c54 100644 --- a/1-Authentication/2-sign-in-angular/SPA/src/app/app.component.css +++ b/1-Authentication/2-sign-in-angular/SPA/src/app/app.component.css @@ -20,3 +20,7 @@ footer { text-align: center; flex: 1 1 auto; } + +.container { + padding-bottom: 72px; /* prevent fixed footer from overlapping page content */ +} diff --git a/1-Authentication/2-sign-in-angular/SPA/src/app/home/home.component.css b/1-Authentication/2-sign-in-angular/SPA/src/app/home/home.component.css index 8f4848485..3c5b2eb80 100644 --- a/1-Authentication/2-sign-in-angular/SPA/src/app/home/home.component.css +++ b/1-Authentication/2-sign-in-angular/SPA/src/app/home/home.component.css @@ -1,4 +1,5 @@ -#table-container { +#table-container, +#access-table-container { height: '100vh'; overflow: auto; } @@ -16,6 +17,29 @@ table { padding: 8px 8px 8px 0; } +.mat-column-value { + word-break: break-all; + overflow-wrap: break-word; + white-space: normal; +} + +/* Sensitive claim redaction */ +.redacted { + background-color: #111; + color: #111; + border-radius: 3px; + cursor: pointer; + user-select: none; + text-decoration: line-through; + transition: background-color 0.2s, color 0.2s; +} + +.redacted:hover { + background-color: transparent; + color: inherit; + text-decoration: none; +} + p { text-align: center; } diff --git a/1-Authentication/2-sign-in-angular/SPA/src/app/home/home.component.html b/1-Authentication/2-sign-in-angular/SPA/src/app/home/home.component.html index fe23f91f0..d4155439d 100644 --- a/1-Authentication/2-sign-in-angular/SPA/src/app/home/home.component.html +++ b/1-Authentication/2-sign-in-angular/SPA/src/app/home/home.component.html @@ -21,14 +21,57 @@ Value - {{element.value}} + {{formatValue(element.value)}} + + + Description + {{element.description}} + + + + + + + +
+

+ See below the claims in your Access token (if available). +

+
+ + + + + + + + + + + + + +
Claim {{element.claim}} Value {{formatValue(element.value)}} Description {{element.description}}
+ + + + Raw Access Token + +
{{ accessToken }}
+
+
+
diff --git a/1-Authentication/2-sign-in-angular/SPA/src/app/home/home.component.ts b/1-Authentication/2-sign-in-angular/SPA/src/app/home/home.component.ts index dcde91137..613f72519 100644 --- a/1-Authentication/2-sign-in-angular/SPA/src/app/home/home.component.ts +++ b/1-Authentication/2-sign-in-angular/SPA/src/app/home/home.component.ts @@ -6,6 +6,7 @@ import { MsalBroadcastService, MsalGuardConfiguration, MsalService, MSAL_GUARD_C import { AuthenticationResult, InteractionStatus, InteractionType } from '@azure/msal-browser'; import { createClaimsTable } from '../claim-utils'; +import { loginRequest } from '../auth-config'; @Component({ selector: 'app-home', @@ -16,6 +17,30 @@ export class HomeComponent implements OnInit { loginDisplay = false; dataSource: any = []; displayedColumns: string[] = ['claim', 'value', 'description']; + // Access token data + accessToken: string | null = null; + accessDataSource: any = []; + + /** Claims whose values are considered sensitive (PII / timestamps) */ + private readonly sensitiveClaims = new Set([ + 'ipaddr', + 'upn', + 'sub', + 'oid', + 'sid', + 'nonce' + ]); + + isSensitive(claim: string): boolean { + return this.sensitiveClaims.has(claim); + } + + formatValue(value: any): string { + if (Array.isArray(value)) { + return '[ ' + value.join(', ') + ' ]'; + } + return value; + } private readonly _destroying$ = new Subject(); @@ -37,6 +62,10 @@ export class HomeComponent implements OnInit { this.getClaims( this.authService.instance.getActiveAccount()?.idTokenClaims ); + // Try to acquire and display an access token (if available) + if (this.loginDisplay) { + this.getAccessToken(); + } }); } @@ -51,10 +80,47 @@ export class HomeComponent implements OnInit { } } + /** + * Acquire an access token silently and populate access token claims table + */ + getAccessToken(): void { + const account = this.authService.instance.getActiveAccount(); + if (!account) return; + + this.authService.acquireTokenSilent({ + scopes: loginRequest.scopes, + account, + }).subscribe((result: AuthenticationResult) => { + if (result && result.accessToken) { + this.accessToken = result.accessToken; + const claims = this.decodeJwt(result.accessToken); + if (claims) { + this.accessDataSource = [...createClaimsTable(claims)]; + } + } + }, (error) => { + // silent acquire may fail; ignore here — token will be acquired on-demand by guarded flows + console.error('Failed to acquire access token silently', error); + }); + } + + private decodeJwt(token: string): any | null { + try { + const payload = token.split('.')[1]; + const base64 = payload.replace(/-/g, '+').replace(/_/g, '/'); + const json = decodeURIComponent(atob(base64).split('').map((c) => { + return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); + }).join('')); + return JSON.parse(json); + } catch (e) { + return null; + } + } + signUp() { if (this.msalGuardConfig.interactionType === InteractionType.Popup) { this.authService.loginPopup({ - scopes: [], + scopes: loginRequest.scopes, prompt: 'create', }) .subscribe((response: AuthenticationResult) => { @@ -62,7 +128,7 @@ export class HomeComponent implements OnInit { }); } else { this.authService.loginRedirect({ - scopes: [], + scopes: loginRequest.scopes, prompt: 'create', }); } From 44eb3be094454575f2712322c6bcf9f5b5f6645b Mon Sep 17 00:00:00 2001 From: Ricardo Santos Date: Fri, 27 Feb 2026 13:53:27 +0000 Subject: [PATCH 2/2] Update Angular app configuration for MSAL integration and set specific client ID and authority --- 1-Authentication/2-sign-in-angular/SPA/package.json | 2 +- .../SPA/src/app/app-routing.module.ts | 1 + .../2-sign-in-angular/SPA/src/app/app.component.ts | 1 + .../2-sign-in-angular/SPA/src/app/app.module.ts | 1 + .../2-sign-in-angular/SPA/src/app/auth-config.ts | 13 ++++++++++--- 5 files changed, 14 insertions(+), 4 deletions(-) diff --git a/1-Authentication/2-sign-in-angular/SPA/package.json b/1-Authentication/2-sign-in-angular/SPA/package.json index 8f74dd637..b7c050395 100644 --- a/1-Authentication/2-sign-in-angular/SPA/package.json +++ b/1-Authentication/2-sign-in-angular/SPA/package.json @@ -3,7 +3,7 @@ "version": "0.0.0", "scripts": { "ng": "ng", - "start": "ng serve", + "start": "ng serve --port 4002", "build": "ng build", "watch": "ng build --watch --configuration development", "test": "ng test --watch=false --no-progress --browsers=ChromeHeadlessCI", diff --git a/1-Authentication/2-sign-in-angular/SPA/src/app/app-routing.module.ts b/1-Authentication/2-sign-in-angular/SPA/src/app/app-routing.module.ts index dcd054b5e..36fd7e4a6 100644 --- a/1-Authentication/2-sign-in-angular/SPA/src/app/app-routing.module.ts +++ b/1-Authentication/2-sign-in-angular/SPA/src/app/app-routing.module.ts @@ -16,6 +16,7 @@ const routes: Routes = [ path: 'guarded', component: GuardedComponent, canActivate: [ + //here Msal Guard MsalGuard ] }, diff --git a/1-Authentication/2-sign-in-angular/SPA/src/app/app.component.ts b/1-Authentication/2-sign-in-angular/SPA/src/app/app.component.ts index 6322cd51c..429775618 100644 --- a/1-Authentication/2-sign-in-angular/SPA/src/app/app.component.ts +++ b/1-Authentication/2-sign-in-angular/SPA/src/app/app.component.ts @@ -30,6 +30,7 @@ export class AppComponent implements OnInit, OnDestroy { private readonly _destroying$ = new Subject(); constructor( + //here @Inject(MSAL_GUARD_CONFIG) private msalGuardConfig: MsalGuardConfiguration, private authService: MsalService, private msalBroadcastService: MsalBroadcastService, diff --git a/1-Authentication/2-sign-in-angular/SPA/src/app/app.module.ts b/1-Authentication/2-sign-in-angular/SPA/src/app/app.module.ts index 799ca2eb1..54a4c6ed6 100644 --- a/1-Authentication/2-sign-in-angular/SPA/src/app/app.module.ts +++ b/1-Authentication/2-sign-in-angular/SPA/src/app/app.module.ts @@ -77,6 +77,7 @@ export function MsalGuardConfigurationFactory(): MsalGuardConfiguration { MsalModule, ], providers: [ + // MSAL Interceptor to add scopes to outgoing requests { provide: HTTP_INTERCEPTORS, useClass: MsalInterceptor, diff --git a/1-Authentication/2-sign-in-angular/SPA/src/app/auth-config.ts b/1-Authentication/2-sign-in-angular/SPA/src/app/auth-config.ts index 1be3fc1e7..abcc17fbc 100644 --- a/1-Authentication/2-sign-in-angular/SPA/src/app/auth-config.ts +++ b/1-Authentication/2-sign-in-angular/SPA/src/app/auth-config.ts @@ -18,8 +18,8 @@ import { */ export const msalConfig: Configuration = { auth: { - clientId: 'Enter_the_Application_Id_Here', // This is the ONLY mandatory field that you need to supply. - authority: 'https://Enter_the_Tenant_Subdomain_Here.ciamlogin.com/', // Replace the placeholder with your tenant subdomain + clientId: '675bdfbe-4b0b-480d-802d-d3d6405dda47', // This is the ONLY mandatory field that you need to supply. + authority: 'https://login.microsoftonline.com/b6078821-12c7-4949-9827-52da66c836c7', // Replace the placeholder with your tenant subdomain redirectUri: '/', // Points to window.location.origin by default. You must register this URI on Microsoft Entra admin center/App Registration. postLogoutRedirectUri: '/', // Points to window.location.origin by default. }, @@ -44,5 +44,12 @@ export const msalConfig: Configuration = { * https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#openid-connect-scopes */ export const loginRequest = { - scopes: [], + scopes: [ + // 'openid', + // 'offline_access', + 'api://675bdfbe-4b0b-480d-802d-d3d6405dda47/.default', + ], }; + +// Print configured scopes for debugging/verification +console.log('loginRequest scopes:', loginRequest.scopes);