Skip to content

Commit

Permalink
stores front cookie and anti-csrf in website cookie - fixes #9. Allow…
Browse files Browse the repository at this point in the history
…s for reading of userId and JWT payload on the frontend, securely.
  • Loading branch information
rishabhpoddar committed Sep 14, 2020
1 parent 7639779 commit 1d74c39
Show file tree
Hide file tree
Showing 8 changed files with 339 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Stores Anti CSRF token in cookie that can be shared across sub domains. This value is then read and added to the request header separately.
- Compatible with FDI 1.2
- Adds ability to get userID and JWT payload (securely) from the frontend

## [4.3.0] - 2020-08-20
### Changed
Expand Down
2 changes: 1 addition & 1 deletion bundle/bundle.js

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions lib/build/axios.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export default class AuthHttpRequest {
sessionExpiredStatusCode?: number;
autoAddCredentials?: boolean;
}): void;
static getUserId(): string;
static getJWTPayloadSecurely(): Promise<any>;
/**
* @description sends the actual http request and returns a response if successful/
* If not successful due to session expiry reasons, it
Expand Down
58 changes: 57 additions & 1 deletion lib/build/axios.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ function responseInterceptor(axiosInstance) {
var _this = this;
return function(response) {
return __awaiter(_this, void 0, void 0, function() {
var url, idRefreshToken, config, antiCsrfToken;
var url, idRefreshToken, config, antiCsrfToken, frontToken;
return __generator(this, function(_a) {
try {
if (!AuthHttpRequest.initCalled) {
Expand Down Expand Up @@ -265,11 +265,16 @@ function responseInterceptor(axiosInstance) {
if (antiCsrfToken !== undefined) {
_1.AntiCsrfToken.setItem(_1.getIDFromCookie(), antiCsrfToken);
}
frontToken = response.headers["front-token"];
if (frontToken !== undefined) {
_1.FrontToken.setItem(frontToken);
}
return [2 /*return*/, response];
}
} finally {
if (_1.getIDFromCookie() === undefined) {
_1.AntiCsrfToken.removeToken();
_1.FrontToken.removeToken();
}
}
return [2 /*return*/];
Expand Down Expand Up @@ -307,6 +312,49 @@ var AuthHttpRequest = /** @class */ (function() {
AuthHttpRequest.apiDomain = _1.getDomainFromUrl(refreshTokenUrl);
AuthHttpRequest.initCalled = true;
};
AuthHttpRequest.getUserId = function() {
var tokenInfo = _1.FrontToken.getTokenInfo();
if (tokenInfo === undefined) {
throw new Error("No session exists");
}
return tokenInfo.uid;
};
AuthHttpRequest.getJWTPayloadSecurely = function() {
return __awaiter(this, void 0, void 0, function() {
var tokenInfo, preRequestIdToken, retry;
return __generator(this, function(_a) {
switch (_a.label) {
case 0:
tokenInfo = _1.FrontToken.getTokenInfo();
if (tokenInfo === undefined) {
throw new Error("No session exists");
}
if (!(tokenInfo.ate < Date.now())) return [3 /*break*/, 4];
preRequestIdToken = _1.getIDFromCookie();
return [
4 /*yield*/,
_1.handleUnauthorised(
AuthHttpRequest.refreshTokenUrl,
preRequestIdToken,
AuthHttpRequest.websiteRootDomain,
AuthHttpRequest.refreshAPICustomHeaders,
AuthHttpRequest.sessionExpiredStatusCode
)
];
case 1:
retry = _a.sent();
if (!retry) return [3 /*break*/, 3];
return [4 /*yield*/, AuthHttpRequest.getJWTPayloadSecurely()];
case 2:
return [2 /*return*/, _a.sent()];
case 3:
throw new Error("Could not refresh session");
case 4:
return [2 /*return*/, tokenInfo.up];
}
});
});
};
AuthHttpRequest.sessionExpiredStatusCode = 401;
AuthHttpRequest.initCalled = false;
AuthHttpRequest.apiDomain = "";
Expand Down Expand Up @@ -334,6 +382,7 @@ var AuthHttpRequest = /** @class */ (function() {
idRefreshToken,
retry,
antiCsrfToken_1,
frontToken,
err_1,
retry;
return __generator(this, function(_b) {
Expand Down Expand Up @@ -442,6 +491,10 @@ var AuthHttpRequest = /** @class */ (function() {
if (antiCsrfToken_1 !== undefined) {
_1.AntiCsrfToken.setItem(_1.getIDFromCookie(), antiCsrfToken_1);
}
frontToken = response.headers["front-token"];
if (frontToken !== undefined) {
_1.FrontToken.setItem(frontToken);
}
return [2 /*return*/, response];
case 10:
return [3 /*break*/, 15];
Expand Down Expand Up @@ -489,6 +542,7 @@ var AuthHttpRequest = /** @class */ (function() {
case 17:
if (_1.getIDFromCookie() === undefined) {
_1.AntiCsrfToken.removeToken();
_1.FrontToken.removeToken();
}
return [7 /*endfinally*/];
case 18:
Expand Down Expand Up @@ -530,6 +584,7 @@ var AuthHttpRequest = /** @class */ (function() {
case 3:
if (_1.getIDFromCookie() === undefined) {
_1.AntiCsrfToken.removeToken();
_1.FrontToken.removeToken();
}
return [7 /*endfinally*/];
case 4:
Expand Down Expand Up @@ -681,6 +736,7 @@ var AuthHttpRequest = /** @class */ (function() {
} finally {
if (_1.getIDFromCookie() === undefined) {
_1.AntiCsrfToken.removeToken();
_1.FrontToken.removeToken();
}
}
return [2 /*return*/];
Expand Down
14 changes: 14 additions & 0 deletions lib/build/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ export declare class AntiCsrfToken {
static removeToken(): void;
static setItem(associatedIdRefreshToken: string | undefined, antiCsrf: string): undefined;
}
export declare class FrontToken {
private constructor();
static getTokenInfo(): {
uid: string;
ate: number;
up: any;
} | undefined;
static removeToken(): void;
static setItem(frontToken: string): void;
}
/**
* @description returns true if retry, else false is session has expired completely.
*/
Expand Down Expand Up @@ -38,6 +48,8 @@ export default class AuthHttpRequest {
autoAddCredentials?: boolean;
}): void;
static getRefreshURLDomain: () => string | undefined;
static getUserId(): string;
static getJWTPayloadSecurely(): Promise<any>;
/**
* @description sends the actual http request and returns a response if successful/
* If not successful due to session expiry reasons, it
Expand Down Expand Up @@ -74,3 +86,5 @@ export declare function getIDFromCookie(): string | undefined;
export declare function setIDToCookie(idRefreshToken: string, domain: string): void;
export declare function getAntiCSRFromCookie(domain: string): string | null;
export declare function setAntiCSRFToCookie(antiCSRFToken: string | undefined, domain: string): void;
export declare function getFrontTokenFromCookie(): string | null;
export declare function setFrontTokenToCookie(frontToken: string | undefined, domain: string): void;
112 changes: 111 additions & 1 deletion lib/build/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,26 @@ var AntiCsrfToken = /** @class */ (function() {
return AntiCsrfToken;
})();
exports.AntiCsrfToken = AntiCsrfToken;
// Note: We do not store this in memory because another tab may have
// modified this value, and if so, we may not know about it in this tab
var FrontToken = /** @class */ (function() {
function FrontToken() {}
FrontToken.getTokenInfo = function() {
var frontToken = getFrontTokenFromCookie();
if (frontToken === null) {
return undefined;
}
return JSON.parse(atob(frontToken));
};
FrontToken.removeToken = function() {
setFrontTokenToCookie(undefined, AuthHttpRequest.websiteRootDomain);
};
FrontToken.setItem = function(frontToken) {
setFrontTokenToCookie(frontToken, AuthHttpRequest.websiteRootDomain);
};
return FrontToken;
})();
exports.FrontToken = FrontToken;
/**
* @description returns true if retry, else false is session has expired completely.
*/
Expand Down Expand Up @@ -309,6 +329,49 @@ var AuthHttpRequest = /** @class */ (function() {
AuthHttpRequest.apiDomain = getDomainFromUrl(refreshTokenUrl);
AuthHttpRequest.initCalled = true;
};
AuthHttpRequest.getUserId = function() {
var tokenInfo = FrontToken.getTokenInfo();
if (tokenInfo === undefined) {
throw new Error("No session exists");
}
return tokenInfo.uid;
};
AuthHttpRequest.getJWTPayloadSecurely = function() {
return __awaiter(this, void 0, void 0, function() {
var tokenInfo, preRequestIdToken, retry;
return __generator(this, function(_a) {
switch (_a.label) {
case 0:
tokenInfo = FrontToken.getTokenInfo();
if (tokenInfo === undefined) {
throw new Error("No session exists");
}
if (!(tokenInfo.ate < Date.now())) return [3 /*break*/, 4];
preRequestIdToken = getIDFromCookie();
return [
4 /*yield*/,
handleUnauthorised(
AuthHttpRequest.refreshTokenUrl,
preRequestIdToken,
AuthHttpRequest.websiteRootDomain,
AuthHttpRequest.refreshAPICustomHeaders,
AuthHttpRequest.sessionExpiredStatusCode
)
];
case 1:
retry = _a.sent();
if (!retry) return [3 /*break*/, 3];
return [4 /*yield*/, AuthHttpRequest.getJWTPayloadSecurely()];
case 2:
return [2 /*return*/, _a.sent()];
case 3:
throw new Error("Could not refresh session");
case 4:
return [2 /*return*/, tokenInfo.up];
}
});
});
};
AuthHttpRequest.sessionExpiredStatusCode = 401;
AuthHttpRequest.initCalled = false;
AuthHttpRequest.apiDomain = "";
Expand Down Expand Up @@ -435,6 +498,8 @@ var AuthHttpRequest = /** @class */ (function() {
response.headers.forEach(function(value, key) {
if (key.toString() === "anti-csrf") {
AntiCsrfToken.setItem(getIDFromCookie(), value);
} else if (key.toString() === "front-token") {
FrontToken.setItem(value);
}
});
return [2 /*return*/, response];
Expand Down Expand Up @@ -478,6 +543,7 @@ var AuthHttpRequest = /** @class */ (function() {
case 16:
if (getIDFromCookie() === undefined) {
AntiCsrfToken.removeToken();
FrontToken.removeToken();
}
return [7 /*endfinally*/];
case 17:
Expand Down Expand Up @@ -519,6 +585,7 @@ var AuthHttpRequest = /** @class */ (function() {
case 3:
if (getIDFromCookie() === undefined) {
AntiCsrfToken.removeToken();
FrontToken.removeToken();
}
return [7 /*endfinally*/];
case 4:
Expand Down Expand Up @@ -604,6 +671,7 @@ var AuthHttpRequest = /** @class */ (function() {
exports.default = AuthHttpRequest;
var ID_COOKIE_NAME = "sIRTFrontend";
var ANTI_CSRF_COOKIE_NAME = "sAntiCsrf";
var FRONT_TOKEN_COOKIE_NAME = "sFrontToken";
/**
* @description attempts to call the refresh token API each time we are sure the session has expired, or it throws an error or,
* or the ID_COOKIE_NAME has changed value -> which may mean that we have a new set of tokens.
Expand Down Expand Up @@ -685,6 +753,8 @@ function onUnauthorisedResponse(
response.headers.forEach(function(value, key) {
if (key.toString() === "anti-csrf") {
AntiCsrfToken.setItem(getIDFromCookie(), value);
} else if (key.toString() === "front-token") {
FrontToken.setItem(value);
}
});
return [2 /*return*/, { value: { result: "RETRY" } }];
Expand Down Expand Up @@ -755,7 +825,6 @@ function setIDToCookie(idRefreshToken, domain) {
}
}
exports.setIDToCookie = setIDToCookie;
// NOTE: we do not store this in memory and always read as to synchronize events across tabs
function getAntiCSRFromCookie(domain) {
var value = "; " + document.cookie;
var parts = value.split("; " + ANTI_CSRF_COOKIE_NAME + "=");
Expand Down Expand Up @@ -808,3 +877,44 @@ function setAntiCSRFToCookie(antiCSRFToken, domain) {
}
}
exports.setAntiCSRFToCookie = setAntiCSRFToCookie;
function getFrontTokenFromCookie() {
var value = "; " + document.cookie;
var parts = value.split("; " + FRONT_TOKEN_COOKIE_NAME + "=");
if (parts.length >= 2) {
var last = parts.pop();
if (last !== undefined) {
var temp = last.split(";").shift();
if (temp === undefined) {
return null;
}
return temp;
}
}
return null;
}
exports.getFrontTokenFromCookie = getFrontTokenFromCookie;
// give frontToken as undefined to remove it.
function setFrontTokenToCookie(frontToken, domain) {
var expires = "Thu, 01 Jan 1970 00:00:01 GMT";
var cookieVal = "";
if (frontToken !== undefined) {
cookieVal = frontToken;
expires = undefined; // set cookie without expiry
}
if (domain === "localhost") {
// since some browsers ignore cookies with domain set to localhost
if (expires !== undefined) {
document.cookie = FRONT_TOKEN_COOKIE_NAME + "=" + cookieVal + ";expires=" + expires + ";path=/";
} else {
document.cookie = FRONT_TOKEN_COOKIE_NAME + "=" + cookieVal + ";path=/";
}
} else {
if (expires !== undefined) {
document.cookie =
FRONT_TOKEN_COOKIE_NAME + "=" + cookieVal + ";expires=" + expires + ";domain=" + domain + ";path=/";
} else {
document.cookie = FRONT_TOKEN_COOKIE_NAME + "=" + cookieVal + ";domain=" + domain + ";path=/";
}
}
}
exports.setFrontTokenToCookie = setFrontTokenToCookie;
Loading

0 comments on commit 1d74c39

Please sign in to comment.