Last active
February 21, 2023 10:30
-
-
Save Toilal/8849bd63d53bd2df2dd4df92d3b12f26 to your computer and use it in GitHub Desktop.
Revisions
-
Toilal revised this gist
Jun 19, 2018 . No changes.There are no files selected for viewing
-
Toilal created this gist
Jun 19, 2018 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,47 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { JWT_OPTIONS, JwtInterceptor, JwtModule } from '@auth0/angular-jwt'; import { AuthorizationService } from './authorization.service'; import { environment } from '../../environments/environment'; import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http'; import { RefreshTokenInterceptor } from './refresh-token-interceptor'; function jwtOptionsFactory (authorizationService: AuthorizationService) { return { tokenGetter: () => { return authorizationService.getAccessToken(); }, blacklistedRoutes: [`${environment.apiBaseUrl}/login-check`] }; } @NgModule({ imports: [ CommonModule, HttpClientModule, JwtModule.forRoot({ jwtOptionsProvider: { provide: JWT_OPTIONS, useFactory: jwtOptionsFactory, deps: [AuthorizationService] } }) ], providers: [ AuthorizationService, JwtInterceptor, // Providing JwtInterceptor allow to inject JwtInterceptor manually into RefreshTokenInterceptor { provide: HTTP_INTERCEPTORS, useExisting: JwtInterceptor, multi: true }, { provide: HTTP_INTERCEPTORS, useClass: RefreshTokenInterceptor, multi: true } ], declarations: [] }) export class ApiModule { } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,95 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { environment } from '../../environments/environment'; import { Observable, ReplaySubject } from 'rxjs'; import { LoginResponse } from './login-response'; @Injectable({ providedIn: 'root' }) export class AuthorizationService { constructor (private httpClient: HttpClient) { } loginCheckUrl = `${environment.apiBaseUrl}/login-check`; refreshTokenUrl = `${environment.apiBaseUrl}/refresh-token`; login (username: string, password: string): Observable<LoginResponse> { const body = new HttpParams() .set('_username', username) .set('_password', password); const headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded'); const postObservable = this.httpClient.post<LoginResponse>(this.loginCheckUrl, body.toString(), { headers }); const subject = new ReplaySubject<LoginResponse>(1); subject.subscribe((r: LoginResponse) => { this.setAccessToken(r.token); this.setRefreshToken(r.refresh_token); }, (err) => { this.handleAuthenticationError(err); }); postObservable.subscribe(subject); return subject; } refresh (): Observable<LoginResponse> { const body = new HttpParams().set('refresh_token', this.getRefreshToken()); const headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded'); const refreshObservable = this.httpClient.post<LoginResponse>(this.refreshTokenUrl, body.toString(), { headers }); const refreshSubject = new ReplaySubject<LoginResponse>(1); refreshSubject.subscribe((r: LoginResponse) => { this.setAccessToken(r.token); this.setRefreshToken(r.refresh_token); }, (err) => { this.handleAuthenticationError(err); }); refreshObservable.subscribe(refreshSubject); return refreshSubject; } logout () { this.setAccessToken(null); this.setRefreshToken(null); } isAuthenticated (): boolean { return !!this.getAccessToken(); } private handleAuthenticationError (err: any) { // TODO: Only for authentication error codes this.setAccessToken(null); this.setRefreshToken(null); } private setAccessToken (accessToken: string) { if (!accessToken) { localStorage.removeItem('access_token'); } else { localStorage.setItem('access_token', accessToken); } } private setRefreshToken (refreshToken: string) { if (!refreshToken) { localStorage.removeItem('refresh_token'); } else { localStorage.setItem('refresh_token', refreshToken); } } getAccessToken () { return localStorage.getItem('access_token'); } getRefreshToken () { return localStorage.getItem('refresh_token'); } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,29 @@ import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable, throwError } from 'rxjs'; import { catchError, mergeMap } from 'rxjs/operators'; import { AuthorizationService } from './authorization.service'; import { JwtInterceptor } from '@auth0/angular-jwt'; @Injectable() export class RefreshTokenInterceptor implements HttpInterceptor { constructor (private authorizationService: AuthorizationService, private jwtInterceptor: JwtInterceptor) { } intercept (req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { if (this.jwtInterceptor.isWhitelistedDomain(req) && !this.jwtInterceptor.isBlacklistedRoute(req)) { return next.handle(req).pipe( catchError((err) => { const errorResponse = err as HttpErrorResponse; if (errorResponse.status === 401 && errorResponse.error.message === 'Expired JWT Token') { return this.authorizationService.refresh().pipe(mergeMap(() => { return this.jwtInterceptor.intercept(req, next); })); } return throwError(err); })); } else { return next.handle(req); } } }