Skip to content

Commit 507f570

Browse files
authored
Use access token as auth header instead of idToken (#19)
* Use access token as auth header instead of idToken * Remove unused import * Refactor token interceptor to not nest too many levels * Stop sending unnecessary fields on component deletion
1 parent e510b0d commit 507f570

18 files changed

Lines changed: 342 additions & 273 deletions

src/app/app.component.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ describe('AppComponent', () => {
4141
azureLoggedUser$ = new Subject<AppUser>();
4242
natsLiveMessage$ = new Subject<NatsMessage | null>();
4343
natsMessageCount$ = new Subject<number>();
44-
mockAzureService = jasmine.createSpyObj('AzureService', ['initialize', 'login', 'logout', 'refreshToken'], { loggedUser$: azureLoggedUser$.asObservable() });
44+
mockAzureService = jasmine.createSpyObj('AzureService', ['initialize', 'login', 'logout', 'getAccessToken'], { loggedUser$: azureLoggedUser$.asObservable() });
4545
mockNatsService = jasmine.createSpyObj('NatsService', ['initialize', 'initializeUser', 'readMessages', 'isValidMessage'], { liveMessage$: natsLiveMessage$.asObservable(), unreadMessagesCount$: natsMessageCount$.asObservable() });
4646
mockToastService = jasmine.createSpyObj('AppShellToastService', ['showToast'], { toasts$: of([]) });
4747
mockCatalogService = jasmine.createSpyObj(
@@ -77,7 +77,7 @@ describe('AppComponent', () => {
7777
mockCatalogService.getSelectedCatalogSlug.and.returnValue(null);
7878
mockCatalogService.getSelectedCatalogDescriptor.and.returnValue({ slug: 'test-catalog', id: '1' });
7979
mockAppConfigService.getConfig.and.returnValue({ natsUrl: 'nats://localhost:4222' });
80-
mockAzureService.refreshToken.and.returnValue(Promise.resolve({ accessToken: 'new-token' } as any));
80+
mockAzureService.getAccessToken.and.returnValue(Promise.resolve('new-token'));
8181
mockProjectService.getUserProjects.and.returnValue(of([]));
8282
mockDialogRef.afterClosed.and.returnValue(dialogSubject.asObservable());
8383
mockMatDialog.open.and.returnValue(mockDialogRef);

src/app/app.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,8 @@ export class AppComponent implements OnInit, OnDestroy {
139139
// Apply optimistic UI, start with it and later apply validations after fetching projects to avoid empty parts
140140
this.projectPicker = {...this.projectPicker, label: 'Project: ', selected: currentProjectForUi.projectKey};
141141
}
142-
this.azureService.refreshToken().then((azureData) => {
143-
this.projectService.getUserProjects(azureData.accessToken).subscribe((projects: string[]) => {
142+
this.azureService.getAccessToken().then((accessToken: string) => {
143+
this.projectService.getUserProjects(accessToken).subscribe((projects: string[]) => {
144144
user.projects = projects;
145145
this.initializeNats(user);
146146
if (projects.length > 0) {

src/app/app.config.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { AppConfigService } from './services/app-config.service';
22
import { msalProviders } from './azure.config';
3-
import { ApplicationConfig, provideZoneChangeDetection, provideAppInitializer, inject } from '@angular/core';
3+
import { ApplicationConfig, provideZoneChangeDetection, provideAppInitializer, inject, Injector } from '@angular/core';
4+
import { MsalService } from '@azure/msal-angular';
5+
import { firstValueFrom } from 'rxjs';
46
import { provideRouter } from '@angular/router';
57
import { routes } from './app.routes';
68
import { provideMarkdown } from 'ngx-markdown';
@@ -30,9 +32,15 @@ export const appConfig: ApplicationConfig = {
3032
provideAppInitializer(() => {
3133
const appConfigService = inject(AppConfigService);
3234
const iconService = inject(IconRegistryService);
33-
return appConfigService.loadConfig().then(() =>
34-
iconService.registerIconsFromManifest()
35-
);
35+
const injector = inject(Injector);
36+
return appConfigService.loadConfig()
37+
.then(() => iconService.registerIconsFromManifest())
38+
.then(() => {
39+
const msalService = injector.get(MsalService);
40+
return firstValueFrom(msalService.initialize()).then(() =>
41+
firstValueFrom(msalService.handleRedirectObservable())
42+
);
43+
});
3644
}),
3745
{
3846
provide: MAT_FORM_FIELD_DEFAULT_OPTIONS,

src/app/azure.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export function MSALGuardConfigFactory(config: AppConfigService): MsalGuardConfi
6161
return {
6262
interactionType: InteractionType.Redirect,
6363
authRequest: {
64-
scopes: [...config.getConfig()?.apiConfig?.scopes || []],
64+
scopes: [...config.getConfig()?.apiConfig?.scopes || [], 'User.Read'],
6565
},
6666
loginFailedRoute: '/login-failed',
6767
};

src/app/components/request-deletion-dialog/request-deletion-dialog.component.spec.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,7 @@ describe('RequestDeletionDialogComponent', () => {
7878
changeNumber: '-',
7979
reason: 'Test reason',
8080
projectKey: 'test-project',
81-
componentName: 'test-component',
82-
location: 'test-location'
81+
componentName: 'test-component'
8382
});
8483
});
8584

@@ -95,8 +94,7 @@ describe('RequestDeletionDialogComponent', () => {
9594
changeNumber: 'CHG1234567',
9695
reason: 'Test reason',
9796
projectKey: 'test-project',
98-
componentName: 'test-component',
99-
location: 'test-location'
97+
componentName: 'test-component'
10098
});
10199
});
102100

src/app/components/request-deletion-dialog/request-deletion-dialog.component.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,7 @@ export class RequestDeletionDialogComponent {
6060
changeNumber: this.deploymentStatus ? this.changeNumber : '-',
6161
reason: this.reason,
6262
projectKey: this.data.projectKey,
63-
componentName: this.data.componentName,
64-
location: this.data.location
63+
componentName: this.data.componentName
6564
};
6665
this.dialogRef.close(result);
6766
}

src/app/models/request-deletion-dialog-data.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,4 @@ export interface RequestDeletionDialogResult {
1010
deploymentStatus: boolean;
1111
changeNumber: string;
1212
reason: string;
13-
location: string;
1413
}

src/app/screens/product-action-screen/product-action-screen.component.spec.ts

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -256,8 +256,7 @@ describe('ProductActionScreenComponent', () => {
256256
{ name: 'param_4', type: 'string', value: '' },
257257
{ name: 'param_5', type: 'string', value: 'value_location_1' },
258258
{ name: 'param_6', type: 'singlelist', value: '' },
259-
{ name: 'catalog_item_id', type: 'string', value: 'fakeId' },
260-
{ name: 'access_token', type: 'string', value: 'fakeAccessToken' }
259+
{ name: 'catalog_item_id', type: 'string', value: 'fakeId' }
261260
]
262261
})
263262
req.flush({});
@@ -292,8 +291,7 @@ describe('ProductActionScreenComponent', () => {
292291
expect(req.request.body).toEqual({
293292
id: 'fakeAction',
294293
parameters: [
295-
{ name: 'catalog_item_id', type: 'string', value: 'fakeId' },
296-
{ name: 'access_token', type: 'string', value: 'fakeAccessToken' }
294+
{ name: 'catalog_item_id', type: 'string', value: 'fakeId' }
297295
],
298296
})
299297
req.flush({});
@@ -325,8 +323,7 @@ describe('ProductActionScreenComponent', () => {
325323
expect(req.request.body).toEqual({
326324
id: 'fakeAction',
327325
parameters: [
328-
{ name: 'catalog_item_id', type: 'string', value: 'fakeId' },
329-
{ name: 'access_token', type: 'string', value: 'fakeAccessToken' }
326+
{ name: 'catalog_item_id', type: 'string', value: 'fakeId' }
330327
],
331328
})
332329
req.flush({});
@@ -366,8 +363,7 @@ describe('ProductActionScreenComponent', () => {
366363
parameters: [
367364
{ name: 'param_1', type: 'string', value: 'value1' },
368365
{ name: 'param_2', type: 'string', value: 'value2' },
369-
{ name: 'catalog_item_id', type: 'string', value: 'fakeId' },
370-
{ name: 'access_token', type: 'string', value: 'fakeAccessToken' }
366+
{ name: 'catalog_item_id', type: 'string', value: 'fakeId' }
371367
],
372368
});
373369

@@ -413,8 +409,7 @@ describe('ProductActionScreenComponent', () => {
413409
id: 'fakeAction',
414410
parameters: [
415411
{ name: 'param_1', type: 'string', value: 'value1' },
416-
{ name: 'catalog_item_id', type: 'string', value: 'fakeId' },
417-
{ name: 'access_token', type: 'string', value: 'fakeAccessToken' },
412+
{ name: 'catalog_item_id', type: 'string', value: 'fakeId' }
418413
],
419414
});
420415

src/app/screens/product-action-screen/product-action-screen.component.ts

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { ProductActionParameterValidation } from '../../models/product-action-pa
1717
import { MatOptionModule } from '@angular/material/core';
1818
import { MatSelectModule } from '@angular/material/select';
1919
import { ProjectService } from '../../services/project.service';
20-
import { Subject, switchMap, takeUntil } from 'rxjs';
20+
import { Subject, takeUntil } from 'rxjs';
2121
import { AppProject } from '../../models/project';
2222
import { AzureService } from '../../services/azure.service';
2323
import { AppUser } from '../../models/app-user';
@@ -358,32 +358,23 @@ export class ProductActionScreenComponent implements OnInit, OnDestroy {
358358
if (this.formGroup.valid && this.action.url && this.selectedProject) {
359359
this.isExecutingAction = true;
360360
const actionUrl = this.action.url;
361-
this.azureService.getRefreshedAccessToken().pipe(
362-
switchMap((refreshedAccessToken: string) => {
363-
const actionBody = {
364-
id: this.action.id,
365-
parameters: this.actionParams.map(param => ({
366-
name: param.name,
367-
type: param.type,
368-
value: this.formGroup.getRawValue()[param.name] || this.getParamDefaultValue(param) || ''
369-
})),
370-
};
371-
actionBody.parameters.push(
372-
{
373-
name: 'catalog_item_id',
374-
type: 'string',
375-
value: this.product.id
376-
},
377-
{
378-
name: 'access_token', // ToDo: when using access token in headers, this parameter is not needed and the provisioner should retrieve it and add the param.
379-
type: 'string',
380-
value: refreshedAccessToken
381-
}
382-
);
383-
this.formGroup.disable();
384-
return this.http.post(actionUrl, actionBody);
385-
})
386-
).subscribe({
361+
const actionBody = {
362+
id: this.action.id,
363+
parameters: this.actionParams.map(param => ({
364+
name: param.name,
365+
type: param.type,
366+
value: this.formGroup.getRawValue()[param.name] || this.getParamDefaultValue(param) || ''
367+
})),
368+
};
369+
actionBody.parameters.push(
370+
{
371+
name: 'catalog_item_id',
372+
type: 'string',
373+
value: this.product.id
374+
}
375+
);
376+
this.formGroup.disable();
377+
this.http.post(actionUrl, actionBody).subscribe({
387378
next: () => {
388379
if(this.action.triggerMessage && this.action.triggerMessage !== '') {
389380
this.toastService.showToast({

src/app/screens/project-components-screen/project-components-screen.component.spec.ts

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,7 @@ describe('ProjectComponentsScreenComponent', () => {
179179
autoFocus: false,
180180
data: {
181181
componentName: 'test-component',
182-
projectKey: 'PROJECT_1',
183-
location: 'LOC_1'
182+
projectKey: 'PROJECT_1'
184183
}
185184
});
186185
});
@@ -202,8 +201,7 @@ describe('ProjectComponentsScreenComponent', () => {
202201
autoFocus: false,
203202
data: {
204203
componentName: 'another-test-component',
205-
projectKey: 'PROJECT_2',
206-
location: 'LOC_2'
204+
projectKey: 'PROJECT_2'
207205
}
208206
});
209207
});
@@ -229,8 +227,7 @@ describe('ProjectComponentsScreenComponent', () => {
229227
changeNumber: 'CHG1234567',
230228
reason: 'Test reason',
231229
projectKey: 'PROJECT_1',
232-
componentName: 'test-component',
233-
location: 'LOC_1'
230+
componentName: 'test-component'
234231
};
235232
spyOn(component.dialog, 'open').and.returnValue({
236233
afterClosed: () => of(mockResult)
@@ -239,12 +236,9 @@ describe('ProjectComponentsScreenComponent', () => {
239236
component.onRequestDeletionClicked(testComponent);
240237
expect(component.projectComponents[0].status).toBe('DELETING');
241238
const incidentParams: CreateIncidentParameter[] = [
242-
{ name: 'cluster_location', type: 'string', value: 'LOC_1' as String },
243-
{ name: 'caller', type: 'string', value: 'test-user' as String },
244239
{ name: 'is_deployed', type: 'boolean', value: true as Boolean },
245240
{ name: 'change_number', type: 'string', value: 'CHG1234567' as String },
246-
{ name: 'reason', type: 'string', value: 'Test reason' as String },
247-
{ name: 'access_token', type: 'string', value: 'test-access-token' as String }
241+
{ name: 'reason', type: 'string', value: 'Test reason' as String }
248242
]
249243
expect(provisionerServiceSpy.requestComponentDeletion).toHaveBeenCalledWith(
250244
'PROJECT_1',
@@ -280,21 +274,17 @@ describe('ProjectComponentsScreenComponent', () => {
280274
changeNumber: 'CHG1234567',
281275
reason: 'Test reason',
282276
projectKey: 'PROJECT_1',
283-
componentName: 'test-component',
284-
location: 'LOC_1'
277+
componentName: 'test-component'
285278
};
286279
spyOn(component.dialog, 'open').and.returnValue({
287280
afterClosed: () => of(mockResult)
288281
} as any);
289282
provisionerServiceSpy.requestComponentDeletion.and.returnValue(throwError(() => new Error('Deletion failed')));
290283
component.onRequestDeletionClicked(testComponent);
291284
const incidentParams: CreateIncidentParameter[] = [
292-
{ name: 'cluster_location', type: 'string', value: 'LOC_1' as String },
293-
{ name: 'caller', type: 'string', value: 'test-user' as String },
294285
{ name: 'is_deployed', type: 'boolean', value: true as Boolean },
295286
{ name: 'change_number', type: 'string', value: 'CHG1234567' as String },
296-
{ name: 'reason', type: 'string', value: 'Test reason' as String },
297-
{ name: 'access_token', type: 'string', value: 'test-access-token' as String }
287+
{ name: 'reason', type: 'string', value: 'Test reason' as String }
298288
]
299289
expect(provisionerServiceSpy.requestComponentDeletion).toHaveBeenCalledWith(
300290
'PROJECT_1',
@@ -330,21 +320,17 @@ describe('ProjectComponentsScreenComponent', () => {
330320
changeNumber: 'CHG1234567',
331321
reason: 'Test reason',
332322
projectKey: 'PROJECT_1',
333-
componentName: 'test-component',
334-
location: 'LOC_1'
323+
componentName: 'test-component'
335324
};
336325
spyOn(component.dialog, 'open').and.returnValue({
337326
afterClosed: () => of(mockResult)
338327
} as any);
339328
component.loggedUser = null;
340329
component.onRequestDeletionClicked(testComponent);
341330
const incidentParams: CreateIncidentParameter[] = [
342-
{ name: 'cluster_location', type: 'string', value: 'LOC_1' as String },
343-
{ name: 'caller', type: 'string', value: 'unknown' as String },
344331
{ name: 'is_deployed', type: 'boolean', value: true as Boolean },
345332
{ name: 'change_number', type: 'string', value: 'CHG1234567' as String },
346-
{ name: 'reason', type: 'string', value: 'Test reason' as String },
347-
{ name: 'access_token', type: 'string', value: 'test-access-token' as String }
333+
{ name: 'reason', type: 'string', value: 'Test reason' as String }
348334
]
349335
expect(provisionerServiceSpy.requestComponentDeletion).toHaveBeenCalledWith(
350336
'PROJECT_1',

0 commit comments

Comments
 (0)