Created
September 18, 2021 17:02
-
-
Save paulrobello/7fe1c88492b175563f7c660d6e3d4309 to your computer and use it in GitHub Desktop.
angular-oauth2-oidc auth interceptor with id token support
This file contains 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 characters
import { Injectable, ModuleWithProviders, NgModule, Optional } from '@angular/core'; | |
import { | |
HTTP_INTERCEPTORS, | |
HttpEvent, | |
HttpHandler, | |
HttpInterceptor, | |
HttpRequest | |
} from '@angular/common/http'; | |
import { | |
OAuthNoopResourceServerErrorHandler, | |
OAuthResourceServerErrorHandler, | |
OAuthService, UrlHelperService | |
} from 'angular-oauth2-oidc'; | |
import { Observable, of, merge } from 'rxjs'; | |
import { | |
catchError, | |
filter, | |
map, | |
take, | |
mergeMap, | |
timeout | |
} from 'rxjs/operators'; | |
export abstract class AppOAuthModuleConfig { | |
resourceServer?: AppOAuthResourceServerConfig; | |
} | |
export type SendTokenType = 'NONE' | 'AUTH' | 'ID'; | |
export abstract class AppOAuthResourceServerConfig { | |
/** | |
* Urls for which calls should be intercepted. | |
* If there is an ResourceServerErrorHandler registered, it is used for them. | |
* If sendAccessToken is set to true, the access_token is send to them too. | |
*/ | |
allowedUrls?: Array<string>; | |
sendToken: SendTokenType = 'NONE'; | |
customUrlValidation?: (url: string) => boolean; | |
} | |
@Injectable({ | |
providedIn: 'root' | |
}) | |
export class AppOAuthInterceptor implements HttpInterceptor { | |
constructor( | |
private oAuthService: OAuthService, | |
private errorHandler: OAuthResourceServerErrorHandler, | |
@Optional() private moduleConfig: AppOAuthModuleConfig | |
) { | |
} | |
private checkUrl(url: string): boolean { | |
if (this.moduleConfig?.resourceServer?.customUrlValidation) { | |
return this.moduleConfig.resourceServer.customUrlValidation(url); | |
} | |
if (this.moduleConfig?.resourceServer?.allowedUrls) { | |
return !!this.moduleConfig.resourceServer.allowedUrls.find((u) => | |
url.toLowerCase().startsWith(u.toLowerCase()) | |
); | |
} | |
return true; | |
} | |
public intercept( | |
req: HttpRequest<any>, | |
next: HttpHandler | |
): Observable<HttpEvent<any>> { | |
const url = req.url.toLowerCase(); | |
if ( | |
!this.moduleConfig || | |
!this.moduleConfig.resourceServer || | |
!this.checkUrl(url) | |
) { | |
return next.handle(req); | |
} | |
const sendToken = this.moduleConfig.resourceServer.sendToken; | |
if (!sendToken || sendToken == 'NONE') { | |
return next | |
.handle(req) | |
.pipe(catchError((err) => this.errorHandler.handleError(err))); | |
} | |
const token = sendToken == 'AUTH' ? this.oAuthService.getAccessToken() : this.oAuthService.getIdToken(); | |
return merge( | |
of(token).pipe(filter((token) => !!token)), | |
this.oAuthService.events.pipe( | |
filter((e) => e.type === 'token_received'), | |
timeout(this.oAuthService.waitForTokenInMsec || 0), | |
catchError((_) => of(null)), // timeout is not an error | |
map((_) => this.oAuthService.getAccessToken()) | |
) | |
).pipe( | |
take(1), | |
mergeMap((token) => { | |
if (token) { | |
const header = 'Bearer ' + token; | |
const headers = req.headers.set('Authorization', header); | |
req = req.clone({headers}); | |
} | |
return next | |
.handle(req) | |
.pipe(catchError((err) => this.errorHandler.handleError(err))); | |
}) | |
); | |
} | |
} | |
@NgModule({}) | |
export class AuthInterceptModule { | |
static forRoot( | |
config?: AppOAuthModuleConfig | |
): ModuleWithProviders<AuthInterceptModule> { | |
return { | |
ngModule: AuthInterceptModule, | |
providers: [ | |
OAuthService, | |
UrlHelperService, | |
{provide: AppOAuthModuleConfig, useValue: config}, | |
{ | |
provide: OAuthResourceServerErrorHandler, | |
useClass: OAuthNoopResourceServerErrorHandler | |
}, | |
{ | |
provide: HTTP_INTERCEPTORS, | |
useClass: AppOAuthInterceptor, | |
multi: true | |
}] | |
}; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment