Skip to content

Commit 1f4175d

Browse files
authored
fix: working on auth flow and storage (#100)
1 parent 30ea627 commit 1f4175d

File tree

9 files changed

+121
-172
lines changed

9 files changed

+121
-172
lines changed

projects/ngx-resgrid-apps-shared/src/lib/interceptors/http.auth.interceptor.ts

Lines changed: 31 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
HttpErrorResponse,
88
} from '@angular/common/http';
99
import { HTTP_INTERCEPTORS } from '@angular/common/http';
10-
import { BehaviorSubject, Observable, of, throwError, timer } from 'rxjs';
10+
import { BehaviorSubject, Observable, from, of, throwError, timer } from 'rxjs';
1111
import { AuthService } from '../services/v4/auth.service';
1212
import { ResgridConfig } from '../resgrid-config';
1313
import {
@@ -24,14 +24,9 @@ import { LoggerService } from '../services/logger.service';
2424
export const retryCount = 3;
2525

2626
@Injectable({
27-
providedIn: 'root'
27+
providedIn: 'root',
2828
})
2929
export class HttpsRequestInterceptor implements HttpInterceptor {
30-
private isRefreshing = false;
31-
private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(
32-
null
33-
);
34-
3530
constructor(
3631
private authService: AuthService,
3732
private config: ResgridConfig,
@@ -43,61 +38,43 @@ export class HttpsRequestInterceptor implements HttpInterceptor {
4338
next: HttpHandler
4439
): Observable<HttpEvent<any>> {
4540
if (this.shouldAddTokenToRequest(req.url)) {
46-
const dupReq = this.addAuthHeader(req);
47-
48-
return next.handle(dupReq).pipe(
49-
catchError((error) => {
50-
if (error instanceof HttpErrorResponse && error.status === 401) {
51-
return this.handle401Error(req, next);
52-
}
53-
54-
return throwError(error);
41+
return from(this.addAuthHeader(req)).pipe(
42+
switchMap((dupReq: any) => {
43+
return next.handle(dupReq).pipe(
44+
catchError((error) => {
45+
if (error instanceof HttpErrorResponse && error.status === 401) {
46+
return this.handle401Error(req, next);
47+
}
48+
49+
return throwError(() => error);
50+
})
51+
);
5552
})
5653
);
5754
}
5855
return next.handle(req);
5956
}
6057

61-
private handle401Error(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
62-
if (!this.isRefreshing) {
63-
this.isRefreshing = true;
64-
this.refreshTokenSubject.next(null);
65-
66-
return this.authService.refreshTokens().pipe(
67-
switchMap((token: any) => {
68-
this.isRefreshing = false;
69-
70-
const dupReq = this.addAuthHeader(req);
71-
const tokens = this.authService.retrieveTokens();
72-
73-
if (tokens) {
74-
this.refreshTokenSubject.next(tokens.access_token);
75-
}// else {
76-
// this.logger.logError('No tokens found after refresh');
77-
// this.authService.logout();
78-
// return throwError('');
79-
//}
80-
81-
return next.handle(dupReq);
82-
}),
83-
catchError((err) => {
84-
this.isRefreshing = false;
85-
86-
this.authService.logout();
87-
return throwError(err);
88-
})
89-
);
90-
}
91-
92-
return this.refreshTokenSubject.pipe(
93-
filter(token => token !== null),
94-
take(1),
95-
switchMap((token) => next.handle(this.addAuthHeader(req)))
58+
private handle401Error(
59+
req: HttpRequest<any>,
60+
next: HttpHandler
61+
): Observable<HttpEvent<any>> {
62+
return this.authService.refreshTokens().pipe(
63+
switchMap((response) => this.addAuthHeader(req)),
64+
switchMap((dupReq: any) => {
65+
return next.handle(dupReq);
66+
}),
67+
catchError((err) => {
68+
this.authService.logout();
69+
return throwError(() => err);
70+
})
9671
);
9772
}
9873

99-
private addAuthHeader(request: HttpRequest<any>): HttpRequest<any> {
100-
const tokens = this.authService.retrieveTokens();
74+
private async addAuthHeader(
75+
request: HttpRequest<any>
76+
): Promise<HttpRequest<any>> {
77+
const tokens = await this.authService.retrieveTokens();
10178
if (tokens) {
10279
const dupReq = request.clone({
10380
headers: request.headers.set(
@@ -112,51 +89,6 @@ export class HttpsRequestInterceptor implements HttpInterceptor {
11289
return request;
11390
}
11491

115-
private handleResponseError(
116-
error: HttpErrorResponse,
117-
request: HttpRequest<any>,
118-
next: HttpHandler
119-
): Observable<HttpEvent<any>> {
120-
if (error.status === 400) {
121-
// Show message
122-
}
123-
124-
// Unauthorized
125-
else if (error.status === 401) {
126-
this.logger.logDebug(
127-
`In handleResponseError got 401 response: ${request.url}`
128-
);
129-
130-
return timer(10000).pipe(
131-
switchMap(() => {
132-
const dupReq = this.addAuthHeader(request);
133-
134-
return next.handle(dupReq); //.pipe(delay(1500));
135-
})
136-
);
137-
}
138-
139-
// Access denied error
140-
else if (error.status === 403) {
141-
// Show message
142-
// Logout
143-
//this.logout();
144-
}
145-
146-
// Server error
147-
else if (error.status === 500) {
148-
// Show message
149-
}
150-
151-
// Maintenance error
152-
else if (error.status === 503) {
153-
// Show message
154-
// Redirect to the maintenance page
155-
}
156-
157-
return throwError(error);
158-
}
159-
16092
private shouldAddTokenToRequest(requestUrl: string): boolean {
16193
if (!requestUrl.startsWith(this.config.baseApiUrl())) {
16294
return false;
@@ -168,4 +100,4 @@ export class HttpsRequestInterceptor implements HttpInterceptor {
168100

169101
return true;
170102
}
171-
}
103+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export interface IStorageProvider {
2+
read(key: string): Promise<string | null>;
3+
write(key: string, value: string): Promise<void>;
4+
remove(key: string): Promise<void>;
5+
clear(): Promise<void>;
6+
}

projects/ngx-resgrid-apps-shared/src/lib/ngx-resgridlib.module.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ export class NgxResgridLibModule {
6666
provide: 'RG_CACHE_PROVIDER',
6767
useValue: configuration.cacheProvider
6868
},
69+
{
70+
provide: 'RG_STORAGE_PROVIDER',
71+
useValue: configuration.storageProvider
72+
},
6973
],
7074
};
7175
}

projects/ngx-resgrid-apps-shared/src/lib/resgrid-config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export interface ResgridASConfiguration {
99
logLevel: number;
1010
isMobileApp: boolean;
1111
cacheProvider: any;
12+
storageProvider: any;
1213
}
1314

1415
export class ResgridConfig implements ResgridASConfiguration {
@@ -22,6 +23,7 @@ export class ResgridConfig implements ResgridASConfiguration {
2223
public logLevel: number = 0;
2324
public isMobileApp: boolean = false;
2425
public cacheProvider: any = null;
26+
public storageProvider: any = null;
2527

2628
get apiUrl(): string {
2729
return `${this.baseApiUrl()}/api/${this.apiVersion}`;

projects/ngx-resgrid-apps-shared/src/lib/services/realtime-geolocation.service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,14 @@ export class RealtimeGeolocationService {
4141
//this.connectionStateObserver.next(ConnectionState.Connecting);
4242
}
4343

44-
public start(): void {
44+
public async start(): Promise<void> {
4545
console.log('SignalR Channel Start()');
4646
this.retryCount = 0;
4747

4848
if (!this.started) {
4949
try {
5050
this.connectionStateObserver?.next(ConnectionState.Connecting);
51-
const tokens = this.authService.retrieveTokens();
51+
const tokens = await this.authService.retrieveTokens();
5252

5353
if (tokens) {
5454
this.hubConnection = new signalR.HubConnectionBuilder()
Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
1-
import { Injectable } from '@angular/core';
1+
import { Inject, Injectable } from '@angular/core';
22
import { ResgridConfig } from '../resgrid-config';
33
import { LoggerService } from './logger.service';
4+
import { IStorageProvider } from '../models/storageProvider';
45

56
@Injectable({
67
providedIn: 'root',
78
})
89
export class StorageService {
9-
constructor(private logger: LoggerService, private config: ResgridConfig) {}
10+
constructor(@Inject('RG_STORAGE_PROVIDER') private storageProvider: IStorageProvider, private logger: LoggerService, private config: ResgridConfig) {}
11+
12+
async read(key: string): Promise<string | null> {
13+
if (this.storageProvider) {
14+
return await this.storageProvider.read(key);
15+
}
1016

11-
read(key: string): any {
1217
const combinedKey = `${this.config.clientId}.${key}`;
1318
const storedValue = localStorage.getItem(combinedKey);
1419

@@ -17,7 +22,7 @@ export class StorageService {
1722
`readKey ${combinedKey} length: ${storedValue.length}`
1823
);
1924

20-
return JSON.parse(storedValue);
25+
return storedValue;
2126
}
2227

2328
this.logger.logDebug(
@@ -27,24 +32,27 @@ export class StorageService {
2732
return null;
2833
}
2934

30-
write(key: string, value: any): boolean {
31-
localStorage.setItem(
32-
`${this.config.clientId}.${key}`,
33-
JSON.stringify(value)
34-
);
35+
async write(key: string, value: string): Promise<void> {
36+
if (this.storageProvider) {
37+
return await this.storageProvider.write(key, value);
38+
}
3539

36-
return true;
40+
localStorage.setItem(`${this.config.clientId}.${key}`, value);
3741
}
3842

39-
remove(key: string): boolean {
40-
localStorage.removeItem(`${this.config.clientId}.${key}`);
43+
async remove(key: string): Promise<void> {
44+
if (this.storageProvider) {
45+
return await this.storageProvider.remove(key);
46+
}
4147

42-
return true;
48+
localStorage.removeItem(`${this.config.clientId}.${key}`);
4349
}
4450

45-
clear(): boolean {
46-
localStorage.clear();
51+
async clear(): Promise<void> {
52+
if (this.storageProvider) {
53+
return await this.storageProvider.clear();
54+
}
4755

48-
return true;
56+
localStorage.clear();
4957
}
5058
}

0 commit comments

Comments
 (0)