]>
Commit | Line | Data |
---|---|---|
ab398a05 | 1 | import { Observable, of, throwError as observableThrowError } from 'rxjs' |
db400f44 | 2 | import { catchError, switchMap } from 'rxjs/operators' |
c0e8b12e | 3 | import { HTTP_INTERCEPTORS, HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http' |
67ed6552 | 4 | import { Injectable, Injector } from '@angular/core' |
ab398a05 | 5 | import { Router } from '@angular/router' |
c0e8b12e C |
6 | import { AuthService } from '@app/core/auth/auth.service' |
7 | import { HttpStatusCode } from '@shared/models' | |
8 | import { OAuth2ErrorCode, PeerTubeProblemDocument } from '@shared/models/server' | |
d592e0a9 C |
9 | |
10 | @Injectable() | |
11 | export class AuthInterceptor implements HttpInterceptor { | |
12 | private authService: AuthService | |
13 | ||
14 | // https://github.com/angular/angular/issues/18224#issuecomment-316957213 | |
ab398a05 | 15 | constructor (private injector: Injector, private router: Router) {} |
d592e0a9 C |
16 | |
17 | intercept (req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { | |
18 | if (this.authService === undefined) { | |
19 | this.authService = this.injector.get(AuthService) | |
20 | } | |
21 | ||
22 | const authReq = this.cloneRequestWithAuth(req) | |
23 | ||
24 | // Pass on the cloned request instead of the original request | |
25 | // Catch 401 errors (refresh token expired) | |
26 | return next.handle(authReq) | |
db400f44 | 27 | .pipe( |
ab398a05 | 28 | catchError((err: HttpErrorResponse) => { |
e030bfb5 | 29 | const error = err.error as PeerTubeProblemDocument |
d12b40fb | 30 | const isOTPMissingError = this.authService.isOTPMissingError(err) |
e030bfb5 | 31 | |
d12b40fb C |
32 | if (!isOTPMissingError) { |
33 | if (err.status === HttpStatusCode.UNAUTHORIZED_401 && error && error.code === OAuth2ErrorCode.INVALID_TOKEN) { | |
34 | return this.handleTokenExpired(req, next) | |
35 | } | |
f43db2f4 | 36 | |
d12b40fb C |
37 | if (err.status === HttpStatusCode.UNAUTHORIZED_401) { |
38 | return this.handleNotAuthenticated(err) | |
39 | } | |
db400f44 C |
40 | } |
41 | ||
1378c0d3 | 42 | return observableThrowError(() => err) |
db400f44 C |
43 | }) |
44 | ) | |
d592e0a9 C |
45 | } |
46 | ||
47 | private handleTokenExpired (req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { | |
48 | return this.authService.refreshAccessToken() | |
db400f44 C |
49 | .pipe( |
50 | switchMap(() => { | |
51 | const authReq = this.cloneRequestWithAuth(req) | |
d592e0a9 | 52 | |
db400f44 C |
53 | return next.handle(authReq) |
54 | }) | |
55 | ) | |
d592e0a9 C |
56 | } |
57 | ||
58 | private cloneRequestWithAuth (req: HttpRequest<any>) { | |
59 | const authHeaderValue = this.authService.getRequestHeaderValue() | |
60 | ||
61 | if (authHeaderValue === null) return req | |
62 | ||
63 | // Clone the request to add the new header | |
64 | return req.clone({ headers: req.headers.set('Authorization', authHeaderValue) }) | |
65 | } | |
ab398a05 | 66 | |
9469783d | 67 | private handleNotAuthenticated (err: HttpErrorResponse): Observable<any> { |
c0e3d9ff | 68 | this.router.navigate([ '/401' ], { state: { obj: err }, skipLocationChange: true }) |
ab398a05 RK |
69 | return of(err.message) |
70 | } | |
d592e0a9 C |
71 | } |
72 | ||
73 | export const AUTH_INTERCEPTOR_PROVIDER = { | |
74 | provide: HTTP_INTERCEPTORS, | |
75 | useClass: AuthInterceptor, | |
76 | multi: true | |
77 | } |