supertokens-website
Advanced tools
Comparing version 3.2.0 to 3.2.2
618
axios.js
@@ -1,29 +0,48 @@ | ||
var __awaiter = | ||
(this && this.__awaiter) || | ||
function(thisArg, _arguments, P, generator) { | ||
return new (P || (P = Promise))(function(resolve, reject) { | ||
function fulfilled(value) { | ||
try { | ||
step(generator.next(value)); | ||
} catch (e) { | ||
reject(e); | ||
} | ||
var __assign = (this && this.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __generator = (this && this.__generator) || function (thisArg, body) { | ||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; | ||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
function verb(n) { return function (v) { return step([n, v]); }; } | ||
function step(op) { | ||
if (f) throw new TypeError("Generator is already executing."); | ||
while (_) try { | ||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [op[0] & 2, t.value]; | ||
switch (op[0]) { | ||
case 0: case 1: t = op; break; | ||
case 4: _.label++; return { value: op[1], done: false }; | ||
case 5: _.label++; y = op[1]; op = [0]; continue; | ||
case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
default: | ||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
if (t[2]) _.ops.pop(); | ||
_.trys.pop(); continue; | ||
} | ||
function rejected(value) { | ||
try { | ||
step(generator["throw"](value)); | ||
} catch (e) { | ||
reject(e); | ||
} | ||
} | ||
function step(result) { | ||
result.done | ||
? resolve(result.value) | ||
: new P(function(resolve) { | ||
resolve(result.value); | ||
}).then(fulfilled, rejected); | ||
} | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
op = body.call(thisArg, _); | ||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
} | ||
}; | ||
var _this = this; | ||
import axios from "axios"; | ||
@@ -33,23 +52,23 @@ import FetchAuthRequest, { AntiCsrfToken, getDomainFromUrl, handleUnauthorised } from "."; | ||
function interceptorFunctionRequestFulfilled(config) { | ||
return __awaiter(this, void 0, void 0, function*() { | ||
let url = config.url; | ||
if (typeof url === "string" && getDomainFromUrl(url) !== AuthHttpRequest.apiDomain) { | ||
// this check means that if you are using fetch via inteceptor, then we only do the refresh steps if you are calling your APIs. | ||
return config; | ||
} | ||
const preRequestIdToken = getIDFromCookie(); | ||
const antiCsrfToken = AntiCsrfToken.getToken(preRequestIdToken); | ||
config = Object.assign({}, config, { withCredentials: true }); | ||
let configWithAntiCsrf = config; | ||
if (antiCsrfToken !== undefined) { | ||
configWithAntiCsrf = Object.assign({}, configWithAntiCsrf, { | ||
headers: | ||
configWithAntiCsrf === undefined | ||
return __awaiter(this, void 0, void 0, function () { | ||
var url, preRequestIdToken, antiCsrfToken, configWithAntiCsrf; | ||
return __generator(this, function (_a) { | ||
url = config.url; | ||
if (typeof url === "string" && getDomainFromUrl(url) !== AuthHttpRequest.apiDomain) { | ||
// this check means that if you are using fetch via inteceptor, then we only do the refresh steps if you are calling your APIs. | ||
return [2 /*return*/, config]; | ||
} | ||
preRequestIdToken = getIDFromCookie(); | ||
antiCsrfToken = AntiCsrfToken.getToken(preRequestIdToken); | ||
config = __assign({}, config, { withCredentials: true }); | ||
configWithAntiCsrf = config; | ||
if (antiCsrfToken !== undefined) { | ||
configWithAntiCsrf = __assign({}, configWithAntiCsrf, { headers: configWithAntiCsrf === undefined | ||
? { | ||
"anti-csrf": antiCsrfToken | ||
} | ||
: Object.assign({}, configWithAntiCsrf.headers, { "anti-csrf": antiCsrfToken }) | ||
}); | ||
} | ||
return configWithAntiCsrf; | ||
"anti-csrf": antiCsrfToken | ||
} | ||
: __assign({}, configWithAntiCsrf.headers, { "anti-csrf": antiCsrfToken }) }); | ||
} | ||
return [2 /*return*/, configWithAntiCsrf]; | ||
}); | ||
}); | ||
@@ -61,4 +80,6 @@ } | ||
*/ | ||
export default class AuthHttpRequest { | ||
static init(refreshTokenUrl, sessionExpiredStatusCode) { | ||
var AuthHttpRequest = /** @class */ (function () { | ||
function AuthHttpRequest() { | ||
} | ||
AuthHttpRequest.init = function (refreshTokenUrl, sessionExpiredStatusCode) { | ||
FetchAuthRequest.init(refreshTokenUrl, sessionExpiredStatusCode); | ||
@@ -71,245 +92,300 @@ AuthHttpRequest.refreshTokenUrl = refreshTokenUrl; | ||
AuthHttpRequest.initCalled = true; | ||
} | ||
} | ||
AuthHttpRequest.sessionExpiredStatusCode = 440; | ||
AuthHttpRequest.initCalled = false; | ||
AuthHttpRequest.apiDomain = ""; | ||
/** | ||
* @description sends the actual http request and returns a response if successful/ | ||
* If not successful due to session expiry reasons, it | ||
* attempts to call the refresh token API and if that is successful, calls this API again. | ||
* @throws Error | ||
*/ | ||
AuthHttpRequest.doRequest = (httpCall, config, url, prevResponse, prevError, viaInterceptor = false) => | ||
__awaiter(this, void 0, void 0, function*() { | ||
if (!AuthHttpRequest.initCalled) { | ||
throw Error("init function not called"); | ||
} | ||
if (typeof url === "string" && getDomainFromUrl(url) !== AuthHttpRequest.apiDomain && viaInterceptor) { | ||
if (prevError !== undefined) { | ||
throw prevError; | ||
} else if (prevResponse !== undefined) { | ||
return prevResponse; | ||
} | ||
// this check means that if you are using fetch via inteceptor, then we only do the refresh steps if you are calling your APIs. | ||
return yield httpCall(config); | ||
} | ||
try { | ||
let throwError = false; | ||
let returnObj = undefined; | ||
while (true) { | ||
// we read this here so that if there is a session expiry error, then we can compare this value (that caused the error) with the value after the request is sent. | ||
// to avoid race conditions | ||
const preRequestIdToken = getIDFromCookie(); | ||
const antiCsrfToken = AntiCsrfToken.getToken(preRequestIdToken); | ||
config = Object.assign({}, config, { withCredentials: true }); | ||
let configWithAntiCsrf = config; | ||
if (antiCsrfToken !== undefined) { | ||
configWithAntiCsrf = Object.assign({}, configWithAntiCsrf, { | ||
headers: | ||
configWithAntiCsrf === undefined | ||
? { | ||
"anti-csrf": antiCsrfToken | ||
} | ||
: Object.assign({}, configWithAntiCsrf.headers, { "anti-csrf": antiCsrfToken }) | ||
}); | ||
} | ||
try { | ||
let localPrevError = prevError; | ||
let localPrevResponse = prevResponse; | ||
prevError = undefined; | ||
prevResponse = undefined; | ||
if (localPrevError !== undefined) { | ||
throw localPrevError; | ||
} | ||
let response = | ||
localPrevResponse === undefined ? yield httpCall(configWithAntiCsrf) : localPrevResponse; | ||
if (response.status === AuthHttpRequest.sessionExpiredStatusCode) { | ||
let retry = yield handleUnauthorised(AuthHttpRequest.refreshTokenUrl, preRequestIdToken); | ||
}; | ||
AuthHttpRequest.sessionExpiredStatusCode = 440; | ||
AuthHttpRequest.initCalled = false; | ||
AuthHttpRequest.apiDomain = ""; | ||
/** | ||
* @description sends the actual http request and returns a response if successful/ | ||
* If not successful due to session expiry reasons, it | ||
* attempts to call the refresh token API and if that is successful, calls this API again. | ||
* @throws Error | ||
*/ | ||
AuthHttpRequest.doRequest = function (httpCall, config, url, prevResponse, prevError, viaInterceptor) { | ||
if (viaInterceptor === void 0) { viaInterceptor = false; } | ||
return __awaiter(_this, void 0, void 0, function () { | ||
var throwError, returnObj, preRequestIdToken, antiCsrfToken, configWithAntiCsrf, localPrevError, localPrevResponse, response, _a, retry, antiCsrfToken_1, err_1, retry; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
if (!AuthHttpRequest.initCalled) { | ||
throw Error("init function not called"); | ||
} | ||
if (!(typeof url === "string" && getDomainFromUrl(url) !== AuthHttpRequest.apiDomain && viaInterceptor)) return [3 /*break*/, 2]; | ||
if (prevError !== undefined) { | ||
throw prevError; | ||
} | ||
else if (prevResponse !== undefined) { | ||
return [2 /*return*/, prevResponse]; | ||
} | ||
return [4 /*yield*/, httpCall(config)]; | ||
case 1: | ||
// this check means that if you are using fetch via inteceptor, then we only do the refresh steps if you are calling your APIs. | ||
return [2 /*return*/, _b.sent()]; | ||
case 2: | ||
_b.trys.push([2, , 17, 18]); | ||
throwError = false; | ||
returnObj = undefined; | ||
_b.label = 3; | ||
case 3: | ||
if (!true) return [3 /*break*/, 16]; | ||
preRequestIdToken = getIDFromCookie(); | ||
antiCsrfToken = AntiCsrfToken.getToken(preRequestIdToken); | ||
config = __assign({}, config, { withCredentials: true }); | ||
configWithAntiCsrf = config; | ||
if (antiCsrfToken !== undefined) { | ||
configWithAntiCsrf = __assign({}, configWithAntiCsrf, { headers: configWithAntiCsrf === undefined | ||
? { | ||
"anti-csrf": antiCsrfToken | ||
} | ||
: __assign({}, configWithAntiCsrf.headers, { "anti-csrf": antiCsrfToken }) }); | ||
} | ||
_b.label = 4; | ||
case 4: | ||
_b.trys.push([4, 11, , 15]); | ||
localPrevError = prevError; | ||
localPrevResponse = prevResponse; | ||
prevError = undefined; | ||
prevResponse = undefined; | ||
if (localPrevError !== undefined) { | ||
throw localPrevError; | ||
} | ||
if (!(localPrevResponse === undefined)) return [3 /*break*/, 6]; | ||
return [4 /*yield*/, httpCall(configWithAntiCsrf)]; | ||
case 5: | ||
_a = _b.sent(); | ||
return [3 /*break*/, 7]; | ||
case 6: | ||
_a = localPrevResponse; | ||
_b.label = 7; | ||
case 7: | ||
response = _a; | ||
if (!(response.status === AuthHttpRequest.sessionExpiredStatusCode)) return [3 /*break*/, 9]; | ||
return [4 /*yield*/, handleUnauthorised(AuthHttpRequest.refreshTokenUrl, preRequestIdToken)]; | ||
case 8: | ||
retry = _b.sent(); | ||
if (!retry) { | ||
returnObj = response; | ||
break; | ||
return [3 /*break*/, 16]; | ||
} | ||
} else { | ||
let antiCsrfToken = response.headers["anti-csrf"]; | ||
if (antiCsrfToken !== undefined) { | ||
AntiCsrfToken.setItem(getIDFromCookie(), antiCsrfToken); | ||
return [3 /*break*/, 10]; | ||
case 9: | ||
antiCsrfToken_1 = response.headers["anti-csrf"]; | ||
if (antiCsrfToken_1 !== undefined) { | ||
AntiCsrfToken.setItem(getIDFromCookie(), antiCsrfToken_1); | ||
} | ||
return response; | ||
} | ||
} catch (err) { | ||
if ( | ||
err.response !== undefined && | ||
err.response.status === AuthHttpRequest.sessionExpiredStatusCode | ||
) { | ||
let retry = yield handleUnauthorised(AuthHttpRequest.refreshTokenUrl, preRequestIdToken); | ||
return [2 /*return*/, response]; | ||
case 10: return [3 /*break*/, 15]; | ||
case 11: | ||
err_1 = _b.sent(); | ||
if (!(err_1.response !== undefined && | ||
err_1.response.status === AuthHttpRequest.sessionExpiredStatusCode)) return [3 /*break*/, 13]; | ||
return [4 /*yield*/, handleUnauthorised(AuthHttpRequest.refreshTokenUrl, preRequestIdToken)]; | ||
case 12: | ||
retry = _b.sent(); | ||
if (!retry) { | ||
throwError = true; | ||
returnObj = err; | ||
break; | ||
returnObj = err_1; | ||
return [3 /*break*/, 16]; | ||
} | ||
} else { | ||
throw err; | ||
return [3 /*break*/, 14]; | ||
case 13: throw err_1; | ||
case 14: return [3 /*break*/, 15]; | ||
case 15: return [3 /*break*/, 3]; | ||
case 16: | ||
// if it comes here, means we called break. which happens only if we have logged out. | ||
if (throwError) { | ||
throw returnObj; | ||
} | ||
else { | ||
return [2 /*return*/, returnObj]; | ||
} | ||
return [3 /*break*/, 18]; | ||
case 17: | ||
if (getIDFromCookie() === undefined) { | ||
AntiCsrfToken.removeToken(); | ||
} | ||
return [7 /*endfinally*/]; | ||
case 18: return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
}; | ||
/** | ||
* @description attempts to refresh session regardless of expiry | ||
* @returns true if successful, else false if session has expired. Wrapped in a Promise | ||
* @throws error if anything goes wrong | ||
*/ | ||
AuthHttpRequest.attemptRefreshingSession = function () { return __awaiter(_this, void 0, void 0, function () { | ||
var preRequestIdToken; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
if (!AuthHttpRequest.initCalled) { | ||
throw Error("init function not called"); | ||
} | ||
} | ||
_a.label = 1; | ||
case 1: | ||
_a.trys.push([1, , 3, 4]); | ||
preRequestIdToken = getIDFromCookie(); | ||
return [4 /*yield*/, handleUnauthorised(AuthHttpRequest.refreshTokenUrl, preRequestIdToken)]; | ||
case 2: return [2 /*return*/, _a.sent()]; | ||
case 3: | ||
if (getIDFromCookie() === undefined) { | ||
AntiCsrfToken.removeToken(); | ||
} | ||
return [7 /*endfinally*/]; | ||
case 4: return [2 /*return*/]; | ||
} | ||
// if it comes here, means we called break. which happens only if we have logged out. | ||
if (throwError) { | ||
throw returnObj; | ||
} else { | ||
return returnObj; | ||
}); | ||
}); }; | ||
AuthHttpRequest.get = function (url, config) { return __awaiter(_this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, AuthHttpRequest.axios(__assign({ method: "get", url: url }, config))]; | ||
case 1: return [2 /*return*/, _a.sent()]; | ||
} | ||
} finally { | ||
if (getIDFromCookie() === undefined) { | ||
AntiCsrfToken.removeToken(); | ||
}); | ||
}); }; | ||
AuthHttpRequest.post = function (url, data, config) { return __awaiter(_this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, AuthHttpRequest.axios(__assign({ method: "post", url: url, | ||
data: data }, config))]; | ||
case 1: return [2 /*return*/, _a.sent()]; | ||
} | ||
} | ||
}); | ||
/** | ||
* @description attempts to refresh session regardless of expiry | ||
* @returns true if successful, else false if session has expired. Wrapped in a Promise | ||
* @throws error if anything goes wrong | ||
*/ | ||
AuthHttpRequest.attemptRefreshingSession = () => | ||
__awaiter(this, void 0, void 0, function*() { | ||
if (!AuthHttpRequest.initCalled) { | ||
throw Error("init function not called"); | ||
} | ||
try { | ||
const preRequestIdToken = getIDFromCookie(); | ||
return yield handleUnauthorised(AuthHttpRequest.refreshTokenUrl, preRequestIdToken); | ||
} finally { | ||
if (getIDFromCookie() === undefined) { | ||
AntiCsrfToken.removeToken(); | ||
}); | ||
}); }; | ||
AuthHttpRequest.delete = function (url, config) { return __awaiter(_this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, AuthHttpRequest.axios(__assign({ method: "delete", url: url }, config))]; | ||
case 1: return [2 /*return*/, _a.sent()]; | ||
} | ||
} | ||
}); | ||
AuthHttpRequest.get = (url, config) => | ||
__awaiter(this, void 0, void 0, function*() { | ||
return yield AuthHttpRequest.axios(Object.assign({ method: "get", url }, config)); | ||
}); | ||
AuthHttpRequest.post = (url, data, config) => | ||
__awaiter(this, void 0, void 0, function*() { | ||
return yield AuthHttpRequest.axios(Object.assign({ method: "post", url, data }, config)); | ||
}); | ||
AuthHttpRequest.delete = (url, config) => | ||
__awaiter(this, void 0, void 0, function*() { | ||
return yield AuthHttpRequest.axios(Object.assign({ method: "delete", url }, config)); | ||
}); | ||
AuthHttpRequest.put = (url, data, config) => | ||
__awaiter(this, void 0, void 0, function*() { | ||
return yield AuthHttpRequest.axios(Object.assign({ method: "put", url, data }, config)); | ||
}); | ||
AuthHttpRequest.axios = (anything, maybeConfig) => | ||
__awaiter(this, void 0, void 0, function*() { | ||
let config = {}; | ||
if (typeof anything === "string") { | ||
if (maybeConfig === undefined) { | ||
config = { | ||
url: anything, | ||
method: "get" | ||
}; | ||
} else { | ||
config = Object.assign({ url: anything }, maybeConfig); | ||
}); | ||
}); }; | ||
AuthHttpRequest.put = function (url, data, config) { return __awaiter(_this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, AuthHttpRequest.axios(__assign({ method: "put", url: url, | ||
data: data }, config))]; | ||
case 1: return [2 /*return*/, _a.sent()]; | ||
} | ||
} else { | ||
config = anything; | ||
}); | ||
}); }; | ||
AuthHttpRequest.axios = function (anything, maybeConfig) { return __awaiter(_this, void 0, void 0, function () { | ||
var config; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
config = {}; | ||
if (typeof anything === "string") { | ||
if (maybeConfig === undefined) { | ||
config = { | ||
url: anything, | ||
method: "get" | ||
}; | ||
} | ||
else { | ||
config = __assign({ url: anything }, maybeConfig); | ||
} | ||
} | ||
else { | ||
config = anything; | ||
} | ||
return [4 /*yield*/, AuthHttpRequest.doRequest(function (config) { | ||
// we create an instance since we don't want to intercept this. | ||
var instance = axios.create(); | ||
return instance(config); | ||
}, config, config.url)]; | ||
case 1: return [2 /*return*/, _a.sent()]; | ||
} | ||
}); | ||
}); }; | ||
AuthHttpRequest.makeSuper = function (axiosInstance) { | ||
// we first check if this axiosInstance already has our interceptors. | ||
var requestInterceptors = axiosInstance.interceptors.request; | ||
for (var i = 0; i < requestInterceptors.handlers.length; i++) { | ||
if (requestInterceptors.handlers[i].fulfilled === interceptorFunctionRequestFulfilled) { | ||
return; | ||
} | ||
} | ||
return yield AuthHttpRequest.doRequest( | ||
config => { | ||
// we create an instance since we don't want to intercept this. | ||
const instance = axios.create(); | ||
return instance(config); | ||
}, | ||
config, | ||
config.url | ||
); | ||
}); | ||
AuthHttpRequest.makeSuper = axiosInstance => { | ||
// we first check if this axiosInstance already has our interceptors. | ||
let requestInterceptors = axiosInstance.interceptors.request; | ||
for (let i = 0; i < requestInterceptors.handlers.length; i++) { | ||
if (requestInterceptors.handlers[i].fulfilled === interceptorFunctionRequestFulfilled) { | ||
return; | ||
} | ||
} | ||
// Add a request interceptor | ||
axiosInstance.interceptors.request.use(interceptorFunctionRequestFulfilled, function(error) { | ||
return __awaiter(this, void 0, void 0, function*() { | ||
return Promise.reject(error); | ||
// Add a request interceptor | ||
axiosInstance.interceptors.request.use(interceptorFunctionRequestFulfilled, function (error) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
return [2 /*return*/, Promise.reject(error)]; | ||
}); | ||
}); | ||
}); | ||
}); | ||
// Add a response interceptor | ||
axiosInstance.interceptors.response.use( | ||
function(response) { | ||
return __awaiter(this, void 0, void 0, function*() { | ||
try { | ||
// Add a response interceptor | ||
axiosInstance.interceptors.response.use(function (response) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var config, antiCsrfToken; | ||
return __generator(this, function (_a) { | ||
try { | ||
if (!AuthHttpRequest.initCalled) { | ||
Promise.reject(new Error("init function not called")); | ||
} | ||
if (response.status === AuthHttpRequest.sessionExpiredStatusCode) { | ||
config = response.config; | ||
return [2 /*return*/, AuthHttpRequest.doRequest(function (config) { | ||
// we create an instance since we don't want to intercept this. | ||
var instance = axios.create(); | ||
return instance(config); | ||
}, config, config.url, response, true)]; | ||
} | ||
else { | ||
antiCsrfToken = response.headers["anti-csrf"]; | ||
if (antiCsrfToken !== undefined) { | ||
AntiCsrfToken.setItem(getIDFromCookie(), antiCsrfToken); | ||
} | ||
return [2 /*return*/, response]; | ||
} | ||
} | ||
finally { | ||
if (getIDFromCookie() === undefined) { | ||
AntiCsrfToken.removeToken(); | ||
} | ||
} | ||
return [2 /*return*/]; | ||
}); | ||
}); | ||
}, function (error) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var config; | ||
return __generator(this, function (_a) { | ||
if (!AuthHttpRequest.initCalled) { | ||
Promise.reject(new Error("init function not called")); | ||
} | ||
if (response.status === AuthHttpRequest.sessionExpiredStatusCode) { | ||
let config = response.config; | ||
return AuthHttpRequest.doRequest( | ||
config => { | ||
// we create an instance since we don't want to intercept this. | ||
const instance = axios.create(); | ||
return instance(config); | ||
}, | ||
config, | ||
config.url, | ||
response, | ||
true | ||
); | ||
} else { | ||
let antiCsrfToken = response.headers["anti-csrf"]; | ||
if (antiCsrfToken !== undefined) { | ||
AntiCsrfToken.setItem(getIDFromCookie(), antiCsrfToken); | ||
try { | ||
if (error.response !== undefined && | ||
error.response.status === AuthHttpRequest.sessionExpiredStatusCode) { | ||
config = error.config; | ||
return [2 /*return*/, AuthHttpRequest.doRequest(function (config) { | ||
// we create an instance since we don't want to intercept this. | ||
var instance = axios.create(); | ||
return instance(config); | ||
}, config, config.url, undefined, error, true)]; | ||
} | ||
return response; | ||
else { | ||
return [2 /*return*/, Promise.reject(error)]; | ||
} | ||
} | ||
} finally { | ||
if (getIDFromCookie() === undefined) { | ||
AntiCsrfToken.removeToken(); | ||
finally { | ||
if (getIDFromCookie() === undefined) { | ||
AntiCsrfToken.removeToken(); | ||
} | ||
} | ||
} | ||
return [2 /*return*/]; | ||
}); | ||
}); | ||
}, | ||
function(error) { | ||
return __awaiter(this, void 0, void 0, function*() { | ||
if (!AuthHttpRequest.initCalled) { | ||
Promise.reject(new Error("init function not called")); | ||
} | ||
try { | ||
if ( | ||
error.response !== undefined && | ||
error.response.status === AuthHttpRequest.sessionExpiredStatusCode | ||
) { | ||
let config = error.config; | ||
return AuthHttpRequest.doRequest( | ||
config => { | ||
// we create an instance since we don't want to intercept this. | ||
const instance = axios.create(); | ||
return instance(config); | ||
}, | ||
config, | ||
config.url, | ||
undefined, | ||
error, | ||
true | ||
); | ||
} else { | ||
return Promise.reject(error); | ||
} | ||
} finally { | ||
if (getIDFromCookie() === undefined) { | ||
AntiCsrfToken.removeToken(); | ||
} | ||
} | ||
}); | ||
} | ||
); | ||
}; | ||
AuthHttpRequest.sessionPossiblyExists = () => | ||
__awaiter(this, void 0, void 0, function*() { | ||
return getIDFromCookie() !== undefined; | ||
}); | ||
}); | ||
}; | ||
AuthHttpRequest.sessionPossiblyExists = function () { return __awaiter(_this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
return [2 /*return*/, getIDFromCookie() !== undefined]; | ||
}); | ||
}); }; | ||
return AuthHttpRequest; | ||
}()); | ||
export default AuthHttpRequest; |
@@ -6,3 +6,3 @@ # Contributing to SuperTokens | ||
# Questions | ||
We are most accessible via team@supertokens.io, via the GitHub issues feature and our [Discord server](https://discord.gg/zVcVeev). | ||
We are most accessible via team@supertokens.io, via the GitHub issues feature and our [Discord server](https://supertokens.io/discord). | ||
@@ -9,0 +9,0 @@ ## Pull Requests |
@@ -1,32 +0,39 @@ | ||
var __awaiter = | ||
(this && this.__awaiter) || | ||
function(thisArg, _arguments, P, generator) { | ||
return new (P || (P = Promise))(function(resolve, reject) { | ||
function fulfilled(value) { | ||
try { | ||
step(generator.next(value)); | ||
} catch (e) { | ||
reject(e); | ||
} | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __generator = (this && this.__generator) || function (thisArg, body) { | ||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; | ||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
function verb(n) { return function (v) { return step([n, v]); }; } | ||
function step(op) { | ||
if (f) throw new TypeError("Generator is already executing."); | ||
while (_) try { | ||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [op[0] & 2, t.value]; | ||
switch (op[0]) { | ||
case 0: case 1: t = op; break; | ||
case 4: _.label++; return { value: op[1], done: false }; | ||
case 5: _.label++; y = op[1]; op = [0]; continue; | ||
case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
default: | ||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
if (t[2]) _.ops.pop(); | ||
_.trys.pop(); continue; | ||
} | ||
function rejected(value) { | ||
try { | ||
step(generator["throw"](value)); | ||
} catch (e) { | ||
reject(e); | ||
} | ||
} | ||
function step(result) { | ||
result.done | ||
? resolve(result.value) | ||
: new P(function(resolve) { | ||
resolve(result.value); | ||
}).then(fulfilled, rejected); | ||
} | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
op = body.call(thisArg, _); | ||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
} | ||
}; | ||
import Lock from "browser-tabs-lock"; | ||
import AuthHttpRequest, { AntiCsrfToken } from "./"; | ||
const ID_COOKIE_NAME = "sIdRefreshToken"; | ||
var ID_COOKIE_NAME = "sIdRefreshToken"; | ||
/** | ||
@@ -37,20 +44,31 @@ * @description attempts to call the refresh token API each time we are sure the session has expired, or it throws an error or, | ||
export function onUnauthorisedResponse(refreshTokenUrl, preRequestIdToken) { | ||
return __awaiter(this, void 0, void 0, function*() { | ||
let lock = new Lock(); | ||
while (true) { | ||
if (yield lock.acquireLock("REFRESH_TOKEN_USE", 1000)) { | ||
// to sync across tabs. the 1000 ms wait is for how much time to try and azquire the lock. | ||
try { | ||
let postLockID = getIDFromCookie(); | ||
return __awaiter(this, void 0, void 0, function () { | ||
var lock, postLockID, response, error_1, idCookieValue; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
lock = new Lock(); | ||
_a.label = 1; | ||
case 1: | ||
if (!true) return [3 /*break*/, 8]; | ||
return [4 /*yield*/, lock.acquireLock("REFRESH_TOKEN_USE", 1000)]; | ||
case 2: | ||
if (!_a.sent()) return [3 /*break*/, 7]; | ||
_a.label = 3; | ||
case 3: | ||
_a.trys.push([3, 5, 6, 7]); | ||
postLockID = getIDFromCookie(); | ||
if (postLockID === undefined) { | ||
return { result: "SESSION_EXPIRED" }; | ||
return [2 /*return*/, { result: "SESSION_EXPIRED" }]; | ||
} | ||
if (postLockID !== preRequestIdToken) { | ||
// means that some other process has already called this API and succeeded. so we need to call it again | ||
return { result: "RETRY" }; | ||
return [2 /*return*/, { result: "RETRY" }]; | ||
} | ||
let response = yield AuthHttpRequest.originalFetch(refreshTokenUrl, { | ||
method: "post", | ||
credentials: "include" | ||
}); | ||
return [4 /*yield*/, AuthHttpRequest.originalFetch(refreshTokenUrl, { | ||
method: "post", | ||
credentials: "include" | ||
})]; | ||
case 4: | ||
response = _a.sent(); | ||
if (response.status !== 200) { | ||
@@ -61,5 +79,5 @@ throw response; | ||
// removed by server. So we logout | ||
return { result: "SESSION_EXPIRED" }; | ||
return [2 /*return*/, { result: "SESSION_EXPIRED" }]; | ||
} | ||
response.headers.forEach((value, key) => { | ||
response.headers.forEach(function (value, key) { | ||
if (key.toString() === "anti-csrf") { | ||
@@ -69,31 +87,36 @@ AntiCsrfToken.setItem(getIDFromCookie(), value); | ||
}); | ||
return { result: "RETRY" }; | ||
} catch (error) { | ||
return [2 /*return*/, { result: "RETRY" }]; | ||
case 5: | ||
error_1 = _a.sent(); | ||
if (getIDFromCookie() === undefined) { | ||
// removed by server. | ||
return { result: "SESSION_EXPIRED" }; | ||
return [2 /*return*/, { result: "SESSION_EXPIRED" }]; | ||
} | ||
return { result: "API_ERROR", error }; | ||
} finally { | ||
return [2 /*return*/, { result: "API_ERROR", error: error_1 }]; | ||
case 6: | ||
lock.releaseLock("REFRESH_TOKEN_USE"); | ||
} | ||
return [7 /*endfinally*/]; | ||
case 7: | ||
idCookieValue = getIDFromCookie(); | ||
if (idCookieValue === undefined) { | ||
// removed by server. So we logout | ||
return [2 /*return*/, { result: "SESSION_EXPIRED" }]; | ||
} | ||
else { | ||
if (idCookieValue !== preRequestIdToken) { | ||
return [2 /*return*/, { result: "RETRY" }]; | ||
} | ||
// here we try to call the API again since we probably failed to acquire lock and nothing has changed. | ||
} | ||
return [3 /*break*/, 1]; | ||
case 8: return [2 /*return*/]; | ||
} | ||
let idCookieValue = getIDFromCookie(); | ||
if (idCookieValue === undefined) { | ||
// removed by server. So we logout | ||
return { result: "SESSION_EXPIRED" }; | ||
} else { | ||
if (idCookieValue !== preRequestIdToken) { | ||
return { result: "RETRY" }; | ||
} | ||
// here we try to call the API again since we probably failed to acquire lock and nothing has changed. | ||
} | ||
} | ||
}); | ||
}); | ||
} | ||
export function getIDFromCookie() { | ||
let value = "; " + document.cookie; | ||
let parts = value.split("; " + ID_COOKIE_NAME + "="); | ||
var value = "; " + document.cookie; | ||
var parts = value.split("; " + ID_COOKIE_NAME + "="); | ||
if (parts.length === 2) { | ||
let last = parts.pop(); | ||
var last = parts.pop(); | ||
if (last !== undefined) { | ||
@@ -100,0 +123,0 @@ return last.split(";").shift(); |
457
index.js
@@ -1,33 +0,53 @@ | ||
var __awaiter = | ||
(this && this.__awaiter) || | ||
function(thisArg, _arguments, P, generator) { | ||
return new (P || (P = Promise))(function(resolve, reject) { | ||
function fulfilled(value) { | ||
try { | ||
step(generator.next(value)); | ||
} catch (e) { | ||
reject(e); | ||
} | ||
var __assign = (this && this.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __generator = (this && this.__generator) || function (thisArg, body) { | ||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; | ||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
function verb(n) { return function (v) { return step([n, v]); }; } | ||
function step(op) { | ||
if (f) throw new TypeError("Generator is already executing."); | ||
while (_) try { | ||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [op[0] & 2, t.value]; | ||
switch (op[0]) { | ||
case 0: case 1: t = op; break; | ||
case 4: _.label++; return { value: op[1], done: false }; | ||
case 5: _.label++; y = op[1]; op = [0]; continue; | ||
case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
default: | ||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
if (t[2]) _.ops.pop(); | ||
_.trys.pop(); continue; | ||
} | ||
function rejected(value) { | ||
try { | ||
step(generator["throw"](value)); | ||
} catch (e) { | ||
reject(e); | ||
} | ||
} | ||
function step(result) { | ||
result.done | ||
? resolve(result.value) | ||
: new P(function(resolve) { | ||
resolve(result.value); | ||
}).then(fulfilled, rejected); | ||
} | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
op = body.call(thisArg, _); | ||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
} | ||
}; | ||
var _this = this; | ||
import { getIDFromCookie, onUnauthorisedResponse } from "./handleSessionExp"; | ||
export class AntiCsrfToken { | ||
constructor() {} | ||
static getToken(associatedIdRefreshToken) { | ||
var AntiCsrfToken = /** @class */ (function () { | ||
function AntiCsrfToken() { | ||
} | ||
AntiCsrfToken.getToken = function (associatedIdRefreshToken) { | ||
if (associatedIdRefreshToken === undefined) { | ||
@@ -38,3 +58,3 @@ AntiCsrfToken.tokenInfo = undefined; | ||
if (AntiCsrfToken.tokenInfo === undefined) { | ||
let antiCsrf = window.localStorage.getItem("anti-csrf-localstorage"); | ||
var antiCsrf = window.localStorage.getItem("anti-csrf-localstorage"); | ||
if (antiCsrf === null) { | ||
@@ -44,6 +64,7 @@ return undefined; | ||
AntiCsrfToken.tokenInfo = { | ||
antiCsrf, | ||
associatedIdRefreshToken | ||
antiCsrf: antiCsrf, | ||
associatedIdRefreshToken: associatedIdRefreshToken | ||
}; | ||
} else if (AntiCsrfToken.tokenInfo.associatedIdRefreshToken !== associatedIdRefreshToken) { | ||
} | ||
else if (AntiCsrfToken.tokenInfo.associatedIdRefreshToken !== associatedIdRefreshToken) { | ||
// csrf token has changed. | ||
@@ -54,8 +75,8 @@ AntiCsrfToken.tokenInfo = undefined; | ||
return AntiCsrfToken.tokenInfo.antiCsrf; | ||
} | ||
static removeToken() { | ||
}; | ||
AntiCsrfToken.removeToken = function () { | ||
AntiCsrfToken.tokenInfo = undefined; | ||
window.localStorage.removeItem("anti-csrf-localstorage"); | ||
} | ||
static setItem(associatedIdRefreshToken, antiCsrf) { | ||
}; | ||
AntiCsrfToken.setItem = function (associatedIdRefreshToken, antiCsrf) { | ||
if (associatedIdRefreshToken === undefined) { | ||
@@ -67,7 +88,9 @@ AntiCsrfToken.tokenInfo = undefined; | ||
AntiCsrfToken.tokenInfo = { | ||
antiCsrf, | ||
associatedIdRefreshToken | ||
antiCsrf: antiCsrf, | ||
associatedIdRefreshToken: associatedIdRefreshToken | ||
}; | ||
} | ||
} | ||
}; | ||
return AntiCsrfToken; | ||
}()); | ||
export { AntiCsrfToken }; | ||
/** | ||
@@ -77,16 +100,25 @@ * @description returns true if retry, else false is session has expired completely. | ||
export function handleUnauthorised(refreshAPI, preRequestIdToken) { | ||
return __awaiter(this, void 0, void 0, function*() { | ||
if (refreshAPI === undefined) { | ||
throw Error("Please define refresh token API: AuthHttpRequest.init(<PATH HERE>, unauthorised status code)"); | ||
} | ||
if (preRequestIdToken === undefined) { | ||
return getIDFromCookie() !== undefined; | ||
} | ||
let result = yield onUnauthorisedResponse(refreshAPI, preRequestIdToken); | ||
if (result.result === "SESSION_EXPIRED") { | ||
return false; | ||
} else if (result.result === "API_ERROR") { | ||
throw result.error; | ||
} | ||
return true; | ||
return __awaiter(this, void 0, void 0, function () { | ||
var result; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
if (refreshAPI === undefined) { | ||
throw Error("Please define refresh token API: AuthHttpRequest.init(<PATH HERE>, unauthorised status code)"); | ||
} | ||
if (preRequestIdToken === undefined) { | ||
return [2 /*return*/, getIDFromCookie() !== undefined]; | ||
} | ||
return [4 /*yield*/, onUnauthorisedResponse(refreshAPI, preRequestIdToken)]; | ||
case 1: | ||
result = _a.sent(); | ||
if (result.result === "SESSION_EXPIRED") { | ||
return [2 /*return*/, false]; | ||
} | ||
else if (result.result === "API_ERROR") { | ||
throw result.error; | ||
} | ||
return [2 /*return*/, true]; | ||
} | ||
}); | ||
}); | ||
@@ -102,5 +134,6 @@ } | ||
.split("/") | ||
.filter((_, i) => i <= 2) | ||
.filter(function (_, i) { return i <= 2; }) | ||
.join("/"); | ||
} else { | ||
} | ||
else { | ||
return window.location.origin; | ||
@@ -113,8 +146,11 @@ } | ||
*/ | ||
export default class AuthHttpRequest { | ||
static init(refreshTokenUrl, sessionExpiredStatusCode, viaInterceptor) { | ||
var AuthHttpRequest = /** @class */ (function () { | ||
function AuthHttpRequest() { | ||
} | ||
AuthHttpRequest.init = function (refreshTokenUrl, sessionExpiredStatusCode, viaInterceptor) { | ||
if (viaInterceptor === undefined) { | ||
if (AuthHttpRequest.viaInterceptor === undefined) { | ||
viaInterceptor = false; | ||
} else { | ||
} | ||
else { | ||
viaInterceptor = AuthHttpRequest.viaInterceptor; | ||
@@ -127,3 +163,3 @@ } | ||
} | ||
let env = window.fetch === undefined ? global : window; | ||
var env = window.fetch === undefined ? global : window; | ||
if (AuthHttpRequest.originalFetch === undefined) { | ||
@@ -133,3 +169,3 @@ AuthHttpRequest.originalFetch = env.fetch.bind(env); | ||
if (viaInterceptor) { | ||
env.fetch = (url, config) => { | ||
env.fetch = function (url, config) { | ||
return AuthHttpRequest.fetch(url, config); | ||
@@ -141,135 +177,178 @@ }; | ||
AuthHttpRequest.initCalled = true; | ||
} | ||
} | ||
AuthHttpRequest.sessionExpiredStatusCode = 440; | ||
AuthHttpRequest.initCalled = false; | ||
AuthHttpRequest.apiDomain = ""; | ||
/** | ||
* @description sends the actual http request and returns a response if successful/ | ||
* If not successful due to session expiry reasons, it | ||
* attempts to call the refresh token API and if that is successful, calls this API again. | ||
* @throws Error | ||
*/ | ||
AuthHttpRequest.doRequest = (httpCall, config, url) => | ||
__awaiter(this, void 0, void 0, function*() { | ||
if (!AuthHttpRequest.initCalled) { | ||
throw Error("init function not called"); | ||
} | ||
if ( | ||
typeof url === "string" && | ||
getDomainFromUrl(url) !== AuthHttpRequest.apiDomain && | ||
AuthHttpRequest.viaInterceptor | ||
) { | ||
// this check means that if you are using fetch via inteceptor, then we only do the refresh steps if you are calling your APIs. | ||
return yield httpCall(config); | ||
} | ||
try { | ||
let throwError = false; | ||
let returnObj = undefined; | ||
while (true) { | ||
// we read this here so that if there is a session expiry error, then we can compare this value (that caused the error) with the value after the request is sent. | ||
// to avoid race conditions | ||
const preRequestIdToken = getIDFromCookie(); | ||
const antiCsrfToken = AntiCsrfToken.getToken(preRequestIdToken); | ||
config = Object.assign({}, config, { credentials: "include" }); | ||
let configWithAntiCsrf = config; | ||
if (antiCsrfToken !== undefined) { | ||
configWithAntiCsrf = Object.assign({}, configWithAntiCsrf, { | ||
headers: | ||
configWithAntiCsrf === undefined | ||
}; | ||
AuthHttpRequest.sessionExpiredStatusCode = 440; | ||
AuthHttpRequest.initCalled = false; | ||
AuthHttpRequest.apiDomain = ""; | ||
/** | ||
* @description sends the actual http request and returns a response if successful/ | ||
* If not successful due to session expiry reasons, it | ||
* attempts to call the refresh token API and if that is successful, calls this API again. | ||
* @throws Error | ||
*/ | ||
AuthHttpRequest.doRequest = function (httpCall, config, url) { return __awaiter(_this, void 0, void 0, function () { | ||
var throwError, returnObj, preRequestIdToken, antiCsrfToken, configWithAntiCsrf, response, retry, err_1, retry; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
if (!AuthHttpRequest.initCalled) { | ||
throw Error("init function not called"); | ||
} | ||
if (!(typeof url === "string" && | ||
getDomainFromUrl(url) !== AuthHttpRequest.apiDomain && | ||
AuthHttpRequest.viaInterceptor)) return [3 /*break*/, 2]; | ||
return [4 /*yield*/, httpCall(config)]; | ||
case 1: | ||
// this check means that if you are using fetch via inteceptor, then we only do the refresh steps if you are calling your APIs. | ||
return [2 /*return*/, _a.sent()]; | ||
case 2: | ||
_a.trys.push([2, , 15, 16]); | ||
throwError = false; | ||
returnObj = undefined; | ||
_a.label = 3; | ||
case 3: | ||
if (!true) return [3 /*break*/, 14]; | ||
preRequestIdToken = getIDFromCookie(); | ||
antiCsrfToken = AntiCsrfToken.getToken(preRequestIdToken); | ||
config = __assign({}, config, { credentials: "include" }); | ||
configWithAntiCsrf = config; | ||
if (antiCsrfToken !== undefined) { | ||
configWithAntiCsrf = __assign({}, configWithAntiCsrf, { headers: configWithAntiCsrf === undefined | ||
? { | ||
"anti-csrf": antiCsrfToken | ||
} | ||
: Object.assign({}, configWithAntiCsrf.headers, { "anti-csrf": antiCsrfToken }) | ||
}); | ||
} | ||
try { | ||
let response = yield httpCall(configWithAntiCsrf); | ||
if (response.status === AuthHttpRequest.sessionExpiredStatusCode) { | ||
let retry = yield handleUnauthorised(AuthHttpRequest.refreshTokenUrl, preRequestIdToken); | ||
if (!retry) { | ||
returnObj = response; | ||
break; | ||
} | ||
} else { | ||
response.headers.forEach((value, key) => { | ||
if (key.toString() === "anti-csrf") { | ||
AntiCsrfToken.setItem(getIDFromCookie(), value); | ||
} | ||
}); | ||
return response; | ||
"anti-csrf": antiCsrfToken | ||
} | ||
: __assign({}, configWithAntiCsrf.headers, { "anti-csrf": antiCsrfToken }) }); | ||
} | ||
} catch (err) { | ||
if (err.status === AuthHttpRequest.sessionExpiredStatusCode) { | ||
let retry = yield handleUnauthorised(AuthHttpRequest.refreshTokenUrl, preRequestIdToken); | ||
if (!retry) { | ||
throwError = true; | ||
returnObj = err; | ||
break; | ||
_a.label = 4; | ||
case 4: | ||
_a.trys.push([4, 9, , 13]); | ||
return [4 /*yield*/, httpCall(configWithAntiCsrf)]; | ||
case 5: | ||
response = _a.sent(); | ||
if (!(response.status === AuthHttpRequest.sessionExpiredStatusCode)) return [3 /*break*/, 7]; | ||
return [4 /*yield*/, handleUnauthorised(AuthHttpRequest.refreshTokenUrl, preRequestIdToken)]; | ||
case 6: | ||
retry = _a.sent(); | ||
if (!retry) { | ||
returnObj = response; | ||
return [3 /*break*/, 14]; | ||
} | ||
return [3 /*break*/, 8]; | ||
case 7: | ||
response.headers.forEach(function (value, key) { | ||
if (key.toString() === "anti-csrf") { | ||
AntiCsrfToken.setItem(getIDFromCookie(), value); | ||
} | ||
} else { | ||
throw err; | ||
}); | ||
return [2 /*return*/, response]; | ||
case 8: return [3 /*break*/, 13]; | ||
case 9: | ||
err_1 = _a.sent(); | ||
if (!(err_1.status === AuthHttpRequest.sessionExpiredStatusCode)) return [3 /*break*/, 11]; | ||
return [4 /*yield*/, handleUnauthorised(AuthHttpRequest.refreshTokenUrl, preRequestIdToken)]; | ||
case 10: | ||
retry = _a.sent(); | ||
if (!retry) { | ||
throwError = true; | ||
returnObj = err_1; | ||
return [3 /*break*/, 14]; | ||
} | ||
} | ||
return [3 /*break*/, 12]; | ||
case 11: throw err_1; | ||
case 12: return [3 /*break*/, 13]; | ||
case 13: return [3 /*break*/, 3]; | ||
case 14: | ||
// if it comes here, means we breaked. which happens only if we have logged out. | ||
if (throwError) { | ||
throw returnObj; | ||
} | ||
else { | ||
return [2 /*return*/, returnObj]; | ||
} | ||
return [3 /*break*/, 16]; | ||
case 15: | ||
if (getIDFromCookie() === undefined) { | ||
AntiCsrfToken.removeToken(); | ||
} | ||
return [7 /*endfinally*/]; | ||
case 16: return [2 /*return*/]; | ||
} | ||
// if it comes here, means we breaked. which happens only if we have logged out. | ||
if (throwError) { | ||
throw returnObj; | ||
} else { | ||
return returnObj; | ||
}); | ||
}); }; | ||
/** | ||
* @description attempts to refresh session regardless of expiry | ||
* @returns true if successful, else false if session has expired. Wrapped in a Promise | ||
* @throws error if anything goes wrong | ||
*/ | ||
AuthHttpRequest.attemptRefreshingSession = function () { return __awaiter(_this, void 0, void 0, function () { | ||
var preRequestIdToken; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
if (!AuthHttpRequest.initCalled) { | ||
throw Error("init function not called"); | ||
} | ||
_a.label = 1; | ||
case 1: | ||
_a.trys.push([1, , 3, 4]); | ||
preRequestIdToken = getIDFromCookie(); | ||
return [4 /*yield*/, handleUnauthorised(AuthHttpRequest.refreshTokenUrl, preRequestIdToken)]; | ||
case 2: return [2 /*return*/, _a.sent()]; | ||
case 3: | ||
if (getIDFromCookie() === undefined) { | ||
AntiCsrfToken.removeToken(); | ||
} | ||
return [7 /*endfinally*/]; | ||
case 4: return [2 /*return*/]; | ||
} | ||
} finally { | ||
if (getIDFromCookie() === undefined) { | ||
AntiCsrfToken.removeToken(); | ||
}); | ||
}); }; | ||
AuthHttpRequest.get = function (url, config) { return __awaiter(_this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, AuthHttpRequest.fetch(url, __assign({ method: "GET" }, config))]; | ||
case 1: return [2 /*return*/, _a.sent()]; | ||
} | ||
} | ||
}); | ||
/** | ||
* @description attempts to refresh session regardless of expiry | ||
* @returns true if successful, else false if session has expired. Wrapped in a Promise | ||
* @throws error if anything goes wrong | ||
*/ | ||
AuthHttpRequest.attemptRefreshingSession = () => | ||
__awaiter(this, void 0, void 0, function*() { | ||
if (!AuthHttpRequest.initCalled) { | ||
throw Error("init function not called"); | ||
} | ||
try { | ||
const preRequestIdToken = getIDFromCookie(); | ||
return yield handleUnauthorised(AuthHttpRequest.refreshTokenUrl, preRequestIdToken); | ||
} finally { | ||
if (getIDFromCookie() === undefined) { | ||
AntiCsrfToken.removeToken(); | ||
}); | ||
}); }; | ||
AuthHttpRequest.post = function (url, config) { return __awaiter(_this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, AuthHttpRequest.fetch(url, __assign({ method: "POST" }, config))]; | ||
case 1: return [2 /*return*/, _a.sent()]; | ||
} | ||
} | ||
}); | ||
AuthHttpRequest.get = (url, config) => | ||
__awaiter(this, void 0, void 0, function*() { | ||
return yield AuthHttpRequest.fetch(url, Object.assign({ method: "GET" }, config)); | ||
}); | ||
AuthHttpRequest.post = (url, config) => | ||
__awaiter(this, void 0, void 0, function*() { | ||
return yield AuthHttpRequest.fetch(url, Object.assign({ method: "POST" }, config)); | ||
}); | ||
AuthHttpRequest.delete = (url, config) => | ||
__awaiter(this, void 0, void 0, function*() { | ||
return yield AuthHttpRequest.fetch(url, Object.assign({ method: "DELETE" }, config)); | ||
}); | ||
AuthHttpRequest.put = (url, config) => | ||
__awaiter(this, void 0, void 0, function*() { | ||
return yield AuthHttpRequest.fetch(url, Object.assign({ method: "PUT" }, config)); | ||
}); | ||
AuthHttpRequest.fetch = (url, config) => | ||
__awaiter(this, void 0, void 0, function*() { | ||
return yield AuthHttpRequest.doRequest( | ||
config => { | ||
return AuthHttpRequest.originalFetch(url, Object.assign({}, config)); | ||
}, | ||
config, | ||
url | ||
); | ||
}); | ||
AuthHttpRequest.sessionPossiblyExists = () => | ||
__awaiter(this, void 0, void 0, function*() { | ||
return getIDFromCookie() !== undefined; | ||
}); | ||
}); | ||
}); }; | ||
AuthHttpRequest.delete = function (url, config) { return __awaiter(_this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, AuthHttpRequest.fetch(url, __assign({ method: "DELETE" }, config))]; | ||
case 1: return [2 /*return*/, _a.sent()]; | ||
} | ||
}); | ||
}); }; | ||
AuthHttpRequest.put = function (url, config) { return __awaiter(_this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, AuthHttpRequest.fetch(url, __assign({ method: "PUT" }, config))]; | ||
case 1: return [2 /*return*/, _a.sent()]; | ||
} | ||
}); | ||
}); }; | ||
AuthHttpRequest.fetch = function (url, config) { return __awaiter(_this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, AuthHttpRequest.doRequest(function (config) { | ||
return AuthHttpRequest.originalFetch(url, __assign({}, config)); | ||
}, config, url)]; | ||
case 1: return [2 /*return*/, _a.sent()]; | ||
} | ||
}); | ||
}); }; | ||
AuthHttpRequest.sessionPossiblyExists = function () { return __awaiter(_this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
return [2 /*return*/, getIDFromCookie() !== undefined]; | ||
}); | ||
}); }; | ||
return AuthHttpRequest; | ||
}()); | ||
export default AuthHttpRequest; |
{ | ||
"name": "supertokens-website", | ||
"version": "3.2.0", | ||
"version": "3.2.2", | ||
"description": "frontend sdk for website to be used for auth solution.", | ||
@@ -61,2 +61,2 @@ "main": "index.js", | ||
"homepage": "https://github.com/supertokens/supertokens-website#readme" | ||
} | ||
} |
@@ -19,5 +19,5 @@ ![SuperTokens banner](https://raw.githubusercontent.com/supertokens/supertokens-logo/master/images/Artboard%20%E2%80%93%2027%402x.png) | ||
## Support, questions and bugs | ||
For now, we are most reachable via team@supertokens.io, via the GitHub issues feature and our [Discord server](https://discord.gg/zVcVeev). | ||
For now, we are most reachable via team@supertokens.io, via the GitHub issues feature and our [Discord server](https://supertokens.io/discord). | ||
## Authors | ||
Created with :heart: by the folks at SuperTokens. We are a startup passionate about security and solving software challenges in a way that's helpful for everyone! Please feel free to give us feedback at team@supertokens.io, until our website is ready :grinning: |
@@ -9,3 +9,3 @@ import axios from "axios"; | ||
const BASE_URL = "http://localhost:8888"; | ||
let { delay } = require("./utils"); | ||
let { delay, checkIfIdRefreshIsCleared } = require("./utils"); | ||
@@ -223,4 +223,3 @@ AuthHttpRequest.makeSuper(axios); | ||
"Content-Type": "application/json" | ||
}, | ||
credentials: "include" | ||
} | ||
}); | ||
@@ -268,4 +267,3 @@ let userIdFromResponse = loginResponse.data; | ||
"Content-Type": "application/json" | ||
}, | ||
credentials: "include" | ||
} | ||
}); | ||
@@ -314,4 +312,3 @@ let userIdFromResponse = await loginResponse.data; | ||
"Content-Type": "application/json" | ||
}, | ||
credentials: "include" | ||
} | ||
}); | ||
@@ -363,2 +360,87 @@ let userIdFromResponse = await loginResponse.data; | ||
}); | ||
it("refresh is not called when calling user info before access token expiry", async function() { | ||
let httpServer = await Server.createNew(10); | ||
try { | ||
AuthHttpRequest.init(`${BASE_URL}/refresh`, 440); | ||
let userId = "testing-supertokens-website"; | ||
let loginResponse = await axios.post(`${BASE_URL}/login`, JSON.stringify({ userId }), { | ||
headers: { | ||
Accept: "application/json", | ||
"Content-Type": "application/json" | ||
} | ||
}); | ||
let userIdFromResponse = await loginResponse.data; | ||
let cookies = loginResponse.headers["set-cookie"]; | ||
let sAccessTokenCookieFound = false; | ||
let sRefreshTokenCookieFound = false; | ||
let sIdRefreshTokenCookieFound = false; | ||
assert.strictEqual(Array.isArray(cookies), true); | ||
for (let i = 0; i < cookies.length; i++) { | ||
if (cookies[i].includes("sAccessToken=")) { | ||
sAccessTokenCookieFound = true; | ||
} else if (cookies[i].includes("sRefreshToken")) { | ||
sRefreshTokenCookieFound = true; | ||
} else if (cookies[i].includes("sIdRefreshToken")) { | ||
sIdRefreshTokenCookieFound = true; | ||
} | ||
} | ||
if (!sAccessTokenCookieFound || !sRefreshTokenCookieFound || !sIdRefreshTokenCookieFound) { | ||
throw Error(""); | ||
} | ||
assert.strictEqual(userId, userIdFromResponse); | ||
let response = await axios.get(`${BASE_URL}/`); | ||
let responseData = await response.data; | ||
assert.strictEqual(responseData, "success"); | ||
assert.strictEqual(refreshCalled, false); | ||
assert.strictEqual(noOfTimesRefreshCalledDuringTest, 0); | ||
} finally { | ||
httpServer.close(); | ||
} | ||
}); | ||
it("session should not exist after logout is called", async function() { | ||
let httpServer = await Server.createNew(10); | ||
try { | ||
AuthHttpRequest.init(`${BASE_URL}/refresh`, 440); | ||
let userId = "testing-supertokens-website"; | ||
let loginResponse = await axios.post(`${BASE_URL}/login`, JSON.stringify({ userId }), { | ||
headers: { | ||
Accept: "application/json", | ||
"Content-Type": "application/json" | ||
} | ||
}); | ||
let userIdFromResponse = await loginResponse.data; | ||
let cookies = loginResponse.headers["set-cookie"]; | ||
let sAccessTokenCookieFound = false; | ||
let sRefreshTokenCookieFound = false; | ||
let sIdRefreshTokenCookieFound = false; | ||
assert.strictEqual(Array.isArray(cookies), true); | ||
for (let i = 0; i < cookies.length; i++) { | ||
if (cookies[i].includes("sAccessToken=")) { | ||
sAccessTokenCookieFound = true; | ||
} else if (cookies[i].includes("sRefreshToken")) { | ||
sRefreshTokenCookieFound = true; | ||
} else if (cookies[i].includes("sIdRefreshToken")) { | ||
sIdRefreshTokenCookieFound = true; | ||
} | ||
} | ||
if (!sAccessTokenCookieFound || !sRefreshTokenCookieFound || !sIdRefreshTokenCookieFound) { | ||
throw Error(""); | ||
} | ||
assert.strictEqual(userId, userIdFromResponse); | ||
let logoutResponse = await axios.post(`${BASE_URL}/logout`, "", { | ||
headers: { | ||
Accept: "application/json", | ||
"Content-Type": "application/json" | ||
} | ||
}); | ||
let logoutData = await logoutResponse.data; | ||
assert.strictEqual(logoutData, "success"); | ||
assert.strictEqual(checkIfIdRefreshIsCleared(), true); | ||
} finally { | ||
httpServer.close(); | ||
} | ||
}); | ||
}); |
@@ -6,3 +6,3 @@ let jsdom = require("mocha-jsdom"); | ||
const BASE_URL = "http://localhost:8888"; | ||
let { delay } = require("./utils"); | ||
let { delay, checkIfIdRefreshIsCleared } = require("./utils"); | ||
@@ -177,4 +177,3 @@ describe("Fetch AuthHttpRequest class tests", function() { | ||
}, | ||
body: JSON.stringify({ userId }), | ||
credentials: "include" | ||
body: JSON.stringify({ userId }) | ||
}); | ||
@@ -223,4 +222,3 @@ let userIdFromResponse = await loginResponse.text(); | ||
}, | ||
body: JSON.stringify({ userId }), | ||
credentials: "include" | ||
body: JSON.stringify({ userId }) | ||
}); | ||
@@ -271,4 +269,3 @@ let userIdFromResponse = await loginResponse.text(); | ||
}, | ||
body: JSON.stringify({ userId }), | ||
credentials: "include" | ||
body: JSON.stringify({ userId }) | ||
}); | ||
@@ -320,2 +317,92 @@ let userIdFromResponse = await loginResponse.text(); | ||
}); | ||
it("refresh is not called when calling user info before access token expiry", async function() { | ||
let httpServer = await Server.createNew(10); | ||
try { | ||
AuthHttpRequest.init(`${BASE_URL}/refresh`, 440, true); | ||
let userId = "testing-supertokens-website"; | ||
let loginResponse = await fetch(`${BASE_URL}/login`, { | ||
method: "post", | ||
headers: { | ||
Accept: "application/json", | ||
"Content-Type": "application/json" | ||
}, | ||
body: JSON.stringify({ userId }) | ||
}); | ||
let userIdFromResponse = await loginResponse.text(); | ||
let cookies = loginResponse.headers._headers["set-cookie"]; | ||
let sAccessTokenCookieFound = false; | ||
let sRefreshTokenCookieFound = false; | ||
let sIdRefreshTokenCookieFound = false; | ||
assert.strictEqual(Array.isArray(cookies), true); | ||
for (let i = 0; i < cookies.length; i++) { | ||
if (cookies[i].includes("sAccessToken=")) { | ||
sAccessTokenCookieFound = true; | ||
} else if (cookies[i].includes("sRefreshToken")) { | ||
sRefreshTokenCookieFound = true; | ||
} else if (cookies[i].includes("sIdRefreshToken")) { | ||
sIdRefreshTokenCookieFound = true; | ||
} | ||
} | ||
if (!sAccessTokenCookieFound || !sRefreshTokenCookieFound || !sIdRefreshTokenCookieFound) { | ||
throw Error(""); | ||
} | ||
assert.strictEqual(userId, userIdFromResponse); | ||
let response = await fetch(`${BASE_URL}/`); | ||
let responseText = await response.text(); | ||
assert.strictEqual(responseText, "success"); | ||
assert.strictEqual(refreshCalled, false); | ||
assert.strictEqual(noOfTimesRefreshCalledDuringTest, 0); | ||
} finally { | ||
httpServer.close(); | ||
} | ||
}); | ||
it("session should not exist after logout is called", async function() { | ||
let httpServer = await Server.createNew(10); | ||
try { | ||
AuthHttpRequest.init(`${BASE_URL}/refresh`, 440, true); | ||
let userId = "testing-supertokens-website"; | ||
let loginResponse = await fetch(`${BASE_URL}/login`, { | ||
method: "post", | ||
headers: { | ||
Accept: "application/json", | ||
"Content-Type": "application/json" | ||
}, | ||
body: JSON.stringify({ userId }) | ||
}); | ||
let userIdFromResponse = await loginResponse.text(); | ||
let cookies = loginResponse.headers._headers["set-cookie"]; | ||
let sAccessTokenCookieFound = false; | ||
let sRefreshTokenCookieFound = false; | ||
let sIdRefreshTokenCookieFound = false; | ||
assert.strictEqual(Array.isArray(cookies), true); | ||
for (let i = 0; i < cookies.length; i++) { | ||
if (cookies[i].includes("sAccessToken=")) { | ||
sAccessTokenCookieFound = true; | ||
} else if (cookies[i].includes("sRefreshToken")) { | ||
sRefreshTokenCookieFound = true; | ||
} else if (cookies[i].includes("sIdRefreshToken")) { | ||
sIdRefreshTokenCookieFound = true; | ||
} | ||
} | ||
if (!sAccessTokenCookieFound || !sRefreshTokenCookieFound || !sIdRefreshTokenCookieFound) { | ||
throw Error(""); | ||
} | ||
assert.strictEqual(userId, userIdFromResponse); | ||
let logoutResponse = await fetch(`${BASE_URL}/logout`, { | ||
method: "POST", | ||
headers: { | ||
Accept: "application/json", | ||
"Content-Type": "application/json" | ||
} | ||
}); | ||
let logoutText = await logoutResponse.text(); | ||
assert.strictEqual(logoutText, "success"); | ||
assert.strictEqual(checkIfIdRefreshIsCleared(), true); | ||
} finally { | ||
httpServer.close(); | ||
} | ||
}); | ||
}); |
module.exports.delay = function(sec) { | ||
return new Promise(res => setTimeout(res, sec * 1000)); | ||
}; | ||
module.exports.checkIfIdRefreshIsCleared = function() { | ||
const ID_COOKIE_NAME = "sIdRefreshToken"; | ||
let value = "; " + document.cookie; | ||
let parts = value.split("; " + ID_COOKIE_NAME + "="); | ||
if (parts.length === 2) { | ||
let last = parts.pop(); | ||
if (last !== undefined) { | ||
let properties = last.split(";"); | ||
for (let i = 0; i < properties.length; i++) { | ||
let current = properties[i].replace("'", ""); | ||
if (current.indexOf("Expires=") != -1) { | ||
let expiryDateString = current.split("Expires=")[1]; | ||
let expiryDate = new Date(expiryDateString); | ||
let currentDate = new Date(); | ||
return expiryDate < currentDate; | ||
} | ||
} | ||
} | ||
} | ||
}; |
{ | ||
"compilerOptions": { | ||
"target": "ES2016", | ||
"target": "es5", | ||
"noImplicitAny": true, | ||
@@ -5,0 +5,0 @@ "allowSyntheticDefaultImports": true, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
131404
29
2463
10