implement client side xsrf protection

This commit is contained in:
Sebastian Sdorra
2019-11-18 11:45:48 +01:00
parent 234d98aee7
commit 2c85a137a5
2 changed files with 54 additions and 3 deletions

View File

@@ -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");
});
});

View File

@@ -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;
}; };