mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-08 22:45:45 +01:00
implement client side xsrf protection
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { apiClient, createUrl } from "./apiclient";
|
import { apiClient, createUrl, extractXsrfToken, extractXsrfTokenFromCookie } from "./apiclient";
|
||||||
import fetchMock from "fetch-mock";
|
import fetchMock from "fetch-mock";
|
||||||
import { BackendError } from "./errors";
|
import { BackendError } from "./errors";
|
||||||
|
|
||||||
@@ -70,3 +70,22 @@ describe("error handling tests", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("extract xsrf token", () => {
|
||||||
|
it("should return undefined if no cookie exists", () => {
|
||||||
|
const token = extractXsrfTokenFromCookie(undefined);
|
||||||
|
expect(token).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return undefined without X-Bearer-Token exists", () => {
|
||||||
|
const token = extractXsrfTokenFromCookie("a=b; c=d; e=f");
|
||||||
|
expect(token).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return xsrf token", () => {
|
||||||
|
const cookie =
|
||||||
|
"a=b; X-Bearer-Token=eyJhbGciOiJIUzI1NiJ9.eyJ4c3JmIjoiYjE0NDRmNWEtOWI5Mi00ZDA0LWFkMzMtMTAxYjY3MWQ1YTc0Iiwic3ViIjoic2NtYWRtaW4iLCJqdGkiOiI2RFJpQVphNWwxIiwiaWF0IjoxNTc0MDcyNDQ4LCJleHAiOjE1NzQwNzYwNDgsInNjbS1tYW5hZ2VyLnJlZnJlc2hFeHBpcmF0aW9uIjoxNTc0MTE1NjQ4OTU5LCJzY20tbWFuYWdlci5wYXJlbnRUb2tlbklkIjoiNkRSaUFaYTVsMSJ9.VUJtKeWUn3xtHCEbG51r7ceXZ8CF3cmN8J-eb9EDY_U; c=d";
|
||||||
|
const token = extractXsrfTokenFromCookie(cookie);
|
||||||
|
expect(token).toBe("b1444f5a-9b92-4d04-ad33-101b671d5a74");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -2,14 +2,46 @@ import { contextPath } from "./urls";
|
|||||||
import { createBackendError, ForbiddenError, isBackendError, UnauthorizedError } from "./errors";
|
import { createBackendError, ForbiddenError, isBackendError, UnauthorizedError } from "./errors";
|
||||||
import { BackendErrorContent } from "./errors";
|
import { BackendErrorContent } from "./errors";
|
||||||
|
|
||||||
|
const extractXsrfTokenFromJwt = (jwt: string) => {
|
||||||
|
const parts = jwt.split(".");
|
||||||
|
if (parts.length === 3) {
|
||||||
|
return JSON.parse(atob(parts[1])).xsrf;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// @VisibleForTesting
|
||||||
|
export const extractXsrfTokenFromCookie = (cookieString?: string) => {
|
||||||
|
if (cookieString) {
|
||||||
|
const cookies = cookieString.split(";");
|
||||||
|
for (const c of cookies) {
|
||||||
|
const parts = c.trim().split("=");
|
||||||
|
if (parts[0] === "X-Bearer-Token") {
|
||||||
|
return extractXsrfTokenFromJwt(parts[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const extractXsrfToken = () => {
|
||||||
|
return extractXsrfTokenFromCookie(document.cookie);
|
||||||
|
};
|
||||||
|
|
||||||
const applyFetchOptions: (p: RequestInit) => RequestInit = o => {
|
const applyFetchOptions: (p: RequestInit) => RequestInit = o => {
|
||||||
o.credentials = "same-origin";
|
const headers: { [key: string]: string } = {
|
||||||
o.headers = {
|
|
||||||
Cache: "no-cache",
|
Cache: "no-cache",
|
||||||
// identify the request as ajax request
|
// identify the request as ajax request
|
||||||
"X-Requested-With": "XMLHttpRequest",
|
"X-Requested-With": "XMLHttpRequest",
|
||||||
|
// identify the web interface
|
||||||
"X-SCM-Client": "WUI"
|
"X-SCM-Client": "WUI"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const xsrf = extractXsrfToken();
|
||||||
|
if (xsrf) {
|
||||||
|
headers["X-XSRF-Token"] = xsrf;
|
||||||
|
}
|
||||||
|
|
||||||
|
o.credentials = "same-origin";
|
||||||
|
o.headers = headers;
|
||||||
return o;
|
return o;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user