Skip to content

Commit 1c34adb

Browse files
feat: remove token storage, use acquireTokenSilent (#40)
1 parent 1ddf662 commit 1c34adb

File tree

4 files changed

+73
-44
lines changed

4 files changed

+73
-44
lines changed

apps/api/src/modules/summary/summary.resolver.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { QueryOptionsDto } from 'src/common/graphql/dtos/query-options.dto';
66
import { SummaryResponse } from './dto/summary-response.dto';
77
import { UseGuards } from '@nestjs/common';
88
import { AzureADGuard } from 'src/auth/azure-ad.guard';
9-
import { CurrentUser } from 'src/Decorator/user.decorator';
9+
import { CurrentUser } from 'src/common/decorators/user.decorator';
1010

1111
@Resolver(() => Summary)
1212
export class SummaryResolver {

apps/portal/src/app/app.component.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ export class AppComponent implements OnInit, OnDestroy {
5656
.subscribe((result: EventMessage) => {
5757
const payload = result.payload as AuthenticationResult;
5858
this.authService.instance.setActiveAccount(payload.account);
59-
sessionStorage.setItem('accessToken', payload.accessToken);
6059
this.setLoginDisplay();
6160
});
6261

@@ -89,9 +88,6 @@ export class AppComponent implements OnInit, OnDestroy {
8988

9089
if (account) {
9190
this.authService.acquireTokenSilent({ scopes, account }).subscribe({
92-
next: (tokenResponse: AuthenticationResult) => {
93-
sessionStorage.setItem('accessToken', tokenResponse.accessToken);
94-
},
9591
error: () => {
9692
// Fallback to interactive method if silent fails
9793
this.authService.acquireTokenRedirect({ scopes });

apps/portal/src/app/graphql/graphql.service.ts

Lines changed: 72 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ import { DocumentNode } from 'graphql';
33
import { QueryRef, Apollo, MutationResult } from 'apollo-angular';
44
import { Observable, throwError } from 'rxjs';
55
import { ApolloQueryResult } from '@apollo/client';
6-
import { catchError } from 'rxjs/operators';
6+
import { catchError, switchMap } from 'rxjs/operators';
77
import { MsalService } from '@azure/msal-angular';
8+
import { AuthenticationResult, InteractionRequiredAuthError } from '@azure/msal-browser';
9+
import { environment } from '../../environments/environment';
810

911
@Injectable({
1012
providedIn: 'root',
@@ -15,8 +17,37 @@ export class GraphqlService {
1517
private authService: MsalService,
1618
) {}
1719

18-
private getToken(): string | null {
19-
return sessionStorage.getItem('accessToken');
20+
private getToken(): Observable<string> {
21+
const account =
22+
this.authService.instance.getActiveAccount() || this.authService.instance.getAllAccounts()[0];
23+
24+
if (!account) {
25+
// eslint-disable-next-line prefer-promise-reject-errors
26+
return throwError('No active account found');
27+
}
28+
29+
const request = {
30+
scopes: [environment.apiScope],
31+
account,
32+
};
33+
const token = this.authService.acquireTokenSilent(request).pipe(
34+
switchMap((response: AuthenticationResult) => {
35+
if (response && response.accessToken) {
36+
return [response.accessToken];
37+
}
38+
39+
return throwError('Failed to acquire token');
40+
}),
41+
catchError((error) => {
42+
if (error instanceof InteractionRequiredAuthError) {
43+
// fallback to interaction when silent call fails
44+
this.authService.acquireTokenRedirect(request);
45+
}
46+
47+
return throwError(error);
48+
}),
49+
);
50+
return token;
2051
}
2152

2253
private handleUnauthorizedError(): void {
@@ -26,19 +57,21 @@ export class GraphqlService {
2657
}
2758

2859
query<T>(query: DocumentNode): Observable<ApolloQueryResult<T>> {
29-
const token = this.getToken();
30-
const queryRef: QueryRef<T> = this.apollo.use('default').watchQuery({
31-
query,
32-
errorPolicy: 'all',
33-
fetchPolicy: 'network-only',
34-
context: {
35-
headers: {
36-
Authorization: `Bearer ${token}`,
37-
},
38-
},
39-
});
60+
return this.getToken().pipe(
61+
switchMap((token: string) => {
62+
const queryRef: QueryRef<T> = this.apollo.use('default').watchQuery({
63+
query,
64+
errorPolicy: 'all',
65+
fetchPolicy: 'network-only',
66+
context: {
67+
headers: {
68+
Authorization: `Bearer ${token}`,
69+
},
70+
},
71+
});
4072

41-
return queryRef.valueChanges.pipe(
73+
return queryRef.valueChanges;
74+
}),
4275
catchError((error) => {
4376
if (this.isUnauthorizedError(error)) {
4477
this.handleUnauthorizedError();
@@ -51,32 +84,32 @@ export class GraphqlService {
5184

5285
// eslint-disable-next-line @typescript-eslint/no-explicit-any
5386
mutate<T>(mutation: DocumentNode, variables: any): Observable<MutationResult<T>> {
54-
const token = this.getToken();
55-
return this.apollo
56-
.mutate<T>({
57-
mutation,
58-
variables: { file: variables.inputFile },
59-
context: {
60-
hasUpload: true,
61-
useMultipart: true,
62-
headers: {
63-
'content-type': 'application/json',
64-
'x-apollo-operation-name': 'createSummary',
65-
Authorization: `Bearer ${token}`,
87+
return this.getToken().pipe(
88+
switchMap((token: string) =>
89+
this.apollo.mutate<T>({
90+
mutation,
91+
variables: { file: variables.inputFile },
92+
context: {
93+
hasUpload: true,
94+
useMultipart: true,
95+
headers: {
96+
'content-type': 'application/json',
97+
'x-apollo-operation-name': 'createSummary',
98+
Authorization: `Bearer ${token}`,
99+
},
66100
},
67-
},
68-
errorPolicy: 'all',
69-
fetchPolicy: 'network-only',
70-
})
71-
.pipe(
72-
catchError((error) => {
73-
if (this.isUnauthorizedError(error)) {
74-
this.handleUnauthorizedError();
75-
}
76-
77-
return throwError(error);
101+
errorPolicy: 'all',
102+
fetchPolicy: 'network-only',
78103
}),
79-
);
104+
),
105+
catchError((error) => {
106+
if (this.isUnauthorizedError(error)) {
107+
this.handleUnauthorizedError();
108+
}
109+
110+
return throwError(error);
111+
}),
112+
);
80113
}
81114

82115
// eslint-disable-next-line @typescript-eslint/no-explicit-any

0 commit comments

Comments
 (0)