Skip to content

Commit 30b91ac

Browse files
committed
PD-0000 Fix CSRF token issues
1 parent dc45009 commit 30b91ac

3 files changed

Lines changed: 89 additions & 0 deletions

File tree

src/app/app.module.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { BidiModule } from '@angular/cdk/bidi'
1111
import { PseudoModule } from 'src/locale/i18n.pseudo.component'
1212
import { TitleService } from './core/title-service/title.service'
1313
import { HttpContentTypeHeaderInterceptor } from './core/http-content-type-header-interceptor/http-content-type-header-interceptor'
14+
import { XsrfFallbackInterceptor } from './core/xsrf/xsrf-fallback.interceptor'
1415
import { FirefoxXsrfPreloadInterceptor } from './core/lang-preload/firefox-xsrf-preload.interceptor'
1516
import {
1617
HTTP_INTERCEPTORS,
@@ -56,6 +57,13 @@ import { FormsModule } from '@angular/forms'
5657
useClass: HttpContentTypeHeaderInterceptor,
5758
multi: true,
5859
},
60+
// Fallback XSRF interceptor to ensure x-xsrf-token is present
61+
// when using local proxy / same-origin dev setups.
62+
{
63+
provide: HTTP_INTERCEPTORS,
64+
useClass: XsrfFallbackInterceptor,
65+
multi: true,
66+
},
5967
provideHttpClient(
6068
withInterceptorsFromDi(),
6169
withXsrfConfiguration({
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
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+
}

src/app/types/config.endpoint.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export const TogglzFlag = {
1414
MAINTENANCE_MESSAGE: 'MAINTENANCE_MESSAGE',
1515
FEATURED_AFFILIATIONS: 'FEATURED_AFFILIATIONS',
1616
PERMISSION_NOTIFICATIONS: 'PERMISSION_NOTIFICATIONS',
17+
SEARCH_AND_LINK_WIZARD_WITH_CERTIFIED_AND_FEATURED_LINKS: 'SEARCH_AND_LINK_WIZARD_WITH_CERTIFIED_AND_FEATURED_LINKS',
1718
} as const
1819

1920
export type TogglzFlag = (typeof TogglzFlag)[keyof typeof TogglzFlag]

0 commit comments

Comments
 (0)