|
| 1 | +import { |
| 2 | + HttpEvent, |
| 3 | + HttpHandler, |
| 4 | + HttpInterceptor, |
| 5 | + HttpRequest, |
| 6 | +} from '@angular/common/http' |
| 7 | +import { Injectable } from '@angular/core' |
| 8 | +import { Observable } from 'rxjs' |
| 9 | +import { CookieService } from 'ngx-cookie-service' |
| 10 | + |
| 11 | +declare const runtimeEnvironment: any |
| 12 | + |
| 13 | +/** |
| 14 | + * XsrfFallbackInterceptor |
| 15 | + * |
| 16 | + * Fallback XSRF interceptor to cover cases where Angular's built-in XSRF |
| 17 | + * support (configured via withXsrfConfiguration) does not attach the header, |
| 18 | + * especially when using the local proxy setup. |
| 19 | + * |
| 20 | + * Behaviour: |
| 21 | + * - For mutating backend calls (POST/PUT/PATCH/DELETE) to ORCID web APIs: |
| 22 | + * - If an XSRF header is already present, do nothing. |
| 23 | + * - Otherwise, read the appropriate cookie and set `x-xsrf-token`: |
| 24 | + * - For requests to AUTH_SERVER origin → `AUTH-XSRF-TOKEN` |
| 25 | + * - For API_WEB / BASE_URL / relative (e.g. /signin/auth.json) → `XSRF-TOKEN` |
| 26 | + */ |
| 27 | +@Injectable() |
| 28 | +export class XsrfFallbackInterceptor implements HttpInterceptor { |
| 29 | + constructor(private _cookie: CookieService) {} |
| 30 | + |
| 31 | + intercept( |
| 32 | + req: HttpRequest<any>, |
| 33 | + next: HttpHandler |
| 34 | + ): Observable<HttpEvent<any>> { |
| 35 | + const method = req.method.toUpperCase() |
| 36 | + |
| 37 | + // Only care about mutating requests |
| 38 | + if (!['POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) { |
| 39 | + return next.handle(req) |
| 40 | + } |
| 41 | + |
| 42 | + // If header already present (either manually or by Angular), leave as-is |
| 43 | + if (req.headers.has('x-xsrf-token')) { |
| 44 | + return next.handle(req) |
| 45 | + } |
| 46 | + |
| 47 | + const apiBase = runtimeEnvironment.API_WEB |
| 48 | + const baseUrl = runtimeEnvironment.BASE_URL |
| 49 | + const authBase = runtimeEnvironment.AUTH_SERVER |
| 50 | + |
| 51 | + const isBackendHost = |
| 52 | + req.url.startsWith(apiBase) || |
| 53 | + req.url.startsWith(baseUrl) || |
| 54 | + req.url.startsWith('/') || |
| 55 | + req.url.startsWith(authBase) |
| 56 | + |
| 57 | + if (!isBackendHost) { |
| 58 | + return next.handle(req) |
| 59 | + } |
| 60 | + |
| 61 | + // Decide which cookie to use based on target *host*, not path. |
| 62 | + // Only the auth server (AUTH_SERVER origin) sets/expects AUTH-XSRF-TOKEN. |
| 63 | + // API_WEB / BASE_URL / relative URLs (e.g. /signin/auth.json) use XSRF-TOKEN. |
| 64 | + const isAuthServerCall = req.url.startsWith(authBase) |
| 65 | + |
| 66 | + const cookieName = isAuthServerCall ? 'AUTH-XSRF-TOKEN' : 'XSRF-TOKEN' |
| 67 | + const token = this._cookie.get(cookieName) |
| 68 | + |
| 69 | + if (!token) { |
| 70 | + return next.handle(req) |
| 71 | + } |
| 72 | + |
| 73 | + const cloned = req.clone({ |
| 74 | + withCredentials: true, |
| 75 | + headers: req.headers.set('x-xsrf-token', token), |
| 76 | + }) |
| 77 | + |
| 78 | + return next.handle(cloned) |
| 79 | + } |
| 80 | +} |
0 commit comments