Comparing version 3.0.2 to 4.0.0
@@ -55,25 +55,4 @@ "use strict"; | ||
var expireFudge = 10; | ||
var isRefreshing = false; | ||
var queue = []; | ||
var currentlyRequestingPromise = undefined; | ||
/** | ||
* Function that resolves all items in the queue with the provided token | ||
* @param token New access token | ||
*/ | ||
var resolveQueue = function (token) { | ||
queue.forEach(function (p) { | ||
p.resolve(token); | ||
}); | ||
queue = []; | ||
}; | ||
/** | ||
* Function that declines all items in the queue with the provided error | ||
* @param error Error | ||
*/ | ||
var declineQueue = function (error) { | ||
queue.forEach(function (p) { | ||
p.reject(error); | ||
}); | ||
queue = []; | ||
}; | ||
/** | ||
* Gets the unix timestamp from an access token | ||
@@ -114,2 +93,3 @@ * | ||
* Refreshes the access token using the provided function | ||
* Note: NOT to be called externally. Only accessible through an interceptor | ||
* | ||
@@ -124,36 +104,37 @@ * @param {requestRefresh} requestRefresh - Function that is used to get a new access token | ||
switch (_c.label) { | ||
case 0: | ||
refreshToken = (0, tokensUtils_1.getRefreshToken)(); | ||
case 0: return [4 /*yield*/, (0, tokensUtils_1.getRefreshToken)()]; | ||
case 1: | ||
refreshToken = _c.sent(); | ||
if (!refreshToken) | ||
throw new Error('No refresh token available'); | ||
_c.label = 1; | ||
case 1: | ||
_c.trys.push([1, 7, 8, 9]); | ||
isRefreshing = true; | ||
_c.label = 2; | ||
case 2: | ||
_c.trys.push([2, 8, , 11]); | ||
return [4 /*yield*/, requestRefresh(refreshToken)]; | ||
case 2: | ||
case 3: | ||
newTokens = _c.sent(); | ||
if (!(typeof newTokens === 'object' && (newTokens === null || newTokens === void 0 ? void 0 : newTokens.accessToken))) return [3 /*break*/, 4]; | ||
if (!(typeof newTokens === 'object' && (newTokens === null || newTokens === void 0 ? void 0 : newTokens.accessToken))) return [3 /*break*/, 5]; | ||
return [4 /*yield*/, (0, setAuthTokens_1.setAuthTokens)(newTokens)]; | ||
case 3: | ||
case 4: | ||
_c.sent(); | ||
return [2 /*return*/, newTokens.accessToken]; | ||
case 4: | ||
if (!(typeof newTokens === 'string')) return [3 /*break*/, 6]; | ||
case 5: | ||
if (!(typeof newTokens === 'string')) return [3 /*break*/, 7]; | ||
return [4 /*yield*/, (0, tokensUtils_1.setAccessToken)(newTokens)]; | ||
case 5: | ||
case 6: | ||
_c.sent(); | ||
return [2 /*return*/, newTokens]; | ||
case 6: throw new Error('requestRefresh must either return a string or an object with an accessToken'); | ||
case 7: | ||
case 7: throw new Error('requestRefresh must either return a string or an object with an accessToken'); | ||
case 8: | ||
error_1 = _c.sent(); | ||
// Failed to refresh token | ||
if (axios_1.default.isAxiosError(error_1)) { | ||
status_1 = (_a = error_1.response) === null || _a === void 0 ? void 0 : _a.status; | ||
if (status_1 === 401 || status_1 === 422) { | ||
// The refresh token is invalid so remove the stored tokens | ||
(_b = StorageProxy_1.StorageProxy.Storage) === null || _b === void 0 ? void 0 : _b.remove(StorageKey_1.STORAGE_KEY); | ||
throw new Error("Got ".concat(status_1, " on token refresh; clearing both auth tokens")); | ||
} | ||
} | ||
if (!axios_1.default.isAxiosError(error_1)) return [3 /*break*/, 10]; | ||
status_1 = (_a = error_1.response) === null || _a === void 0 ? void 0 : _a.status; | ||
if (!(status_1 === 401 || status_1 === 422)) return [3 /*break*/, 10]; | ||
// The refresh token is invalid so remove the stored tokens | ||
return [4 /*yield*/, ((_b = StorageProxy_1.StorageProxy.Storage) === null || _b === void 0 ? void 0 : _b.remove(StorageKey_1.STORAGE_KEY))]; | ||
case 9: | ||
// The refresh token is invalid so remove the stored tokens | ||
_c.sent(); | ||
throw new Error("Got ".concat(status_1, " on token refresh; clearing both auth tokens")); | ||
case 10: | ||
// A different error, probably network error | ||
@@ -166,7 +147,4 @@ if (error_1 instanceof Error) { | ||
} | ||
return [3 /*break*/, 9]; | ||
case 8: | ||
isRefreshing = false; | ||
return [7 /*endfinally*/]; | ||
case 9: return [2 /*return*/]; | ||
return [3 /*break*/, 11]; | ||
case 11: return [2 /*return*/]; | ||
} | ||
@@ -189,11 +167,14 @@ }); | ||
switch (_a.label) { | ||
case 0: | ||
accessToken = (0, tokensUtils_1.getAccessToken)(); | ||
if (!(!accessToken || isTokenExpired(accessToken))) return [3 /*break*/, 2]; | ||
case 0: return [4 /*yield*/, (0, tokensUtils_1.getAccessToken)() | ||
// check if access token is expired | ||
]; | ||
case 1: | ||
accessToken = _a.sent(); | ||
if (!(!accessToken || isTokenExpired(accessToken))) return [3 /*break*/, 3]; | ||
return [4 /*yield*/, refreshToken(requestRefresh)]; | ||
case 1: | ||
case 2: | ||
// do refresh | ||
accessToken = _a.sent(); | ||
_a.label = 2; | ||
case 2: return [2 /*return*/, accessToken]; | ||
_a.label = 3; | ||
case 3: return [2 /*return*/, accessToken]; | ||
} | ||
@@ -222,38 +203,38 @@ }); | ||
switch (_a.label) { | ||
case 0: | ||
case 0: return [4 /*yield*/, (0, tokensUtils_1.getRefreshToken)()]; | ||
case 1: | ||
// Waiting for a fix in axios types | ||
// We need refresh token to do any authenticated requests | ||
if (!(0, tokensUtils_1.getRefreshToken)()) | ||
return [2 /*return*/, requestConfig | ||
// Queue the request if another refresh request is currently happening | ||
]; | ||
// Queue the request if another refresh request is currently happening | ||
if (isRefreshing) { | ||
return [2 /*return*/, new Promise(function (resolve, reject) { | ||
queue.push({ resolve: resolve, reject: reject }); | ||
}) | ||
.then(function (token) { | ||
if (requestConfig.headers) { | ||
requestConfig.headers[header] = "".concat(headerPrefix).concat(token); | ||
} | ||
return requestConfig; | ||
}) | ||
.catch(Promise.reject)]; | ||
} | ||
_a.label = 1; | ||
case 1: | ||
_a.trys.push([1, 3, , 4]); | ||
return [4 /*yield*/, (0, exports.refreshTokenIfNeeded)(requestRefresh)]; | ||
if (!(_a.sent())) | ||
return [2 /*return*/, requestConfig]; | ||
accessToken = undefined; | ||
if (!currentlyRequestingPromise) return [3 /*break*/, 3]; | ||
return [4 /*yield*/, currentlyRequestingPromise]; | ||
case 2: | ||
accessToken = _a.sent(); | ||
resolveQueue(accessToken); | ||
return [3 /*break*/, 4]; | ||
_a.label = 3; | ||
case 3: | ||
if (!!accessToken) return [3 /*break*/, 7]; | ||
_a.label = 4; | ||
case 4: | ||
_a.trys.push([4, 6, , 7]); | ||
// Sets the promise so everyone else will wait - then get the value | ||
currentlyRequestingPromise = (0, exports.refreshTokenIfNeeded)(requestRefresh); | ||
return [4 /*yield*/, currentlyRequestingPromise | ||
// Reset the promise | ||
]; | ||
case 5: | ||
accessToken = _a.sent(); | ||
// Reset the promise | ||
currentlyRequestingPromise = undefined; | ||
return [3 /*break*/, 7]; | ||
case 6: | ||
error_2 = _a.sent(); | ||
// Reset the promise | ||
currentlyRequestingPromise = undefined; | ||
if (error_2 instanceof Error) { | ||
declineQueue(error_2); | ||
throw new Error("Unable to refresh access token for request due to token refresh error: ".concat(error_2.message)); | ||
} | ||
return [3 /*break*/, 4]; | ||
case 4: | ||
return [3 /*break*/, 7]; | ||
case 7: | ||
// add token to headers | ||
@@ -260,0 +241,0 @@ if (accessToken && requestConfig.headers) { |
export declare class BrowserStorageService { | ||
private _storage; | ||
constructor(storage: Storage); | ||
remove(key: string): void; | ||
get(key: string): string | null; | ||
set(key: string, value: string): void; | ||
remove(key: string): Promise<void>; | ||
get(key: string): Promise<string | null>; | ||
set(key: string, value: string): Promise<void>; | ||
} | ||
//# sourceMappingURL=BrowserStorageService.d.ts.map |
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
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) : adopt(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 (g && (g = 0, op[0] && (_ = 0)), _) 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; | ||
} | ||
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 }; | ||
} | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -9,9 +45,23 @@ exports.BrowserStorageService = void 0; | ||
BrowserStorageService.prototype.remove = function (key) { | ||
this._storage.removeItem(key); | ||
return __awaiter(this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
this._storage.removeItem(key); | ||
return [2 /*return*/]; | ||
}); | ||
}); | ||
}; | ||
BrowserStorageService.prototype.get = function (key) { | ||
return this._storage.getItem(key); | ||
return __awaiter(this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
return [2 /*return*/, this._storage.getItem(key)]; | ||
}); | ||
}); | ||
}; | ||
BrowserStorageService.prototype.set = function (key, value) { | ||
this._storage.setItem(key, value); | ||
return __awaiter(this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
this._storage.setItem(key, value); | ||
return [2 /*return*/]; | ||
}); | ||
}); | ||
}; | ||
@@ -18,0 +68,0 @@ return BrowserStorageService; |
@@ -6,3 +6,3 @@ import { IAuthTokens } from './IAuthTokens'; | ||
*/ | ||
export declare const setAuthTokens: (tokens: IAuthTokens) => void; | ||
export declare const setAuthTokens: (tokens: IAuthTokens) => Promise<void>; | ||
//# sourceMappingURL=setAuthTokens.d.ts.map |
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
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) : adopt(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 (g && (g = 0, op[0] && (_ = 0)), _) 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; | ||
} | ||
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 }; | ||
} | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -10,4 +46,9 @@ exports.setAuthTokens = void 0; | ||
*/ | ||
var setAuthTokens = function (tokens) { var _a; return (_a = StorageProxy_1.StorageProxy.Storage) === null || _a === void 0 ? void 0 : _a.set(StorageKey_1.STORAGE_KEY, JSON.stringify(tokens)); }; | ||
var setAuthTokens = function (tokens) { return __awaiter(void 0, void 0, void 0, function () { var _a; return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: return [4 /*yield*/, ((_a = StorageProxy_1.StorageProxy.Storage) === null || _a === void 0 ? void 0 : _a.set(StorageKey_1.STORAGE_KEY, JSON.stringify(tokens)))]; | ||
case 1: return [2 /*return*/, _b.sent()]; | ||
} | ||
}); }); }; | ||
exports.setAuthTokens = setAuthTokens; | ||
//# sourceMappingURL=setAuthTokens.js.map |
export type StorageType = { | ||
remove(key: string): void; | ||
set(key: string, value: string): void; | ||
get(value: string): string | null; | ||
remove(key: string): Promise<void>; | ||
set(key: string, value: string): Promise<void>; | ||
get(value: string): Promise<string | null>; | ||
}; | ||
//# sourceMappingURL=StorageType.d.ts.map |
@@ -6,3 +6,3 @@ import { Token } from './Token'; | ||
*/ | ||
export declare const setAccessToken: (token: Token) => void; | ||
export declare const setAccessToken: (token: Token) => Promise<void>; | ||
/** | ||
@@ -12,3 +12,3 @@ * Returns the stored refresh token | ||
*/ | ||
export declare const getRefreshToken: () => Token | undefined; | ||
export declare const getRefreshToken: () => Promise<Token | undefined>; | ||
/** | ||
@@ -18,7 +18,7 @@ * Returns the stored access token | ||
*/ | ||
export declare const getAccessToken: () => Token | undefined; | ||
export declare const getAccessToken: () => Promise<Token | undefined>; | ||
/** | ||
* Clears both tokens | ||
*/ | ||
export declare const clearAuthTokens: () => void; | ||
export declare const clearAuthTokens: () => Promise<void>; | ||
/** | ||
@@ -28,3 +28,3 @@ * Checks if refresh tokens are stored | ||
*/ | ||
export declare const isLoggedIn: () => boolean; | ||
export declare const isLoggedIn: () => Promise<boolean>; | ||
//# sourceMappingURL=tokensUtils.d.ts.map |
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
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) : adopt(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 (g && (g = 0, op[0] && (_ = 0)), _) 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; | ||
} | ||
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 }; | ||
} | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -12,18 +48,26 @@ exports.isLoggedIn = exports.clearAuthTokens = exports.getAccessToken = exports.getRefreshToken = exports.setAccessToken = void 0; | ||
*/ | ||
var getAuthTokens = function () { | ||
var getAuthTokens = function () { return __awaiter(void 0, void 0, void 0, function () { | ||
var rawTokens; | ||
var _a; | ||
var rawTokens = (_a = StorageProxy_1.StorageProxy.Storage) === null || _a === void 0 ? void 0 : _a.get(StorageKey_1.STORAGE_KEY); | ||
if (!rawTokens) | ||
return; | ||
try { | ||
// parse stored tokens JSON | ||
return JSON.parse(rawTokens); | ||
} | ||
catch (error) { | ||
if (error instanceof SyntaxError) { | ||
error.message = "Failed to parse auth tokens: ".concat(rawTokens); | ||
throw error; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: return [4 /*yield*/, ((_a = StorageProxy_1.StorageProxy.Storage) === null || _a === void 0 ? void 0 : _a.get(StorageKey_1.STORAGE_KEY))]; | ||
case 1: | ||
rawTokens = _b.sent(); | ||
if (!rawTokens) | ||
return [2 /*return*/]; | ||
try { | ||
// parse stored tokens JSON | ||
return [2 /*return*/, JSON.parse(rawTokens)]; | ||
} | ||
catch (error) { | ||
if (error instanceof SyntaxError) { | ||
error.message = "Failed to parse auth tokens: ".concat(rawTokens); | ||
throw error; | ||
} | ||
} | ||
return [2 /*return*/]; | ||
} | ||
} | ||
}; | ||
}); | ||
}); }; | ||
/** | ||
@@ -33,10 +77,20 @@ * Sets the access token | ||
*/ | ||
var setAccessToken = function (token) { | ||
var tokens = getAuthTokens(); | ||
if (!tokens) { | ||
throw new Error('Unable to update access token since there are not tokens currently stored'); | ||
} | ||
tokens.accessToken = token; | ||
(0, setAuthTokens_1.setAuthTokens)(tokens); | ||
}; | ||
var setAccessToken = function (token) { return __awaiter(void 0, void 0, void 0, function () { | ||
var tokens; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, getAuthTokens()]; | ||
case 1: | ||
tokens = _a.sent(); | ||
if (!tokens) { | ||
throw new Error('Unable to update access token since there are not tokens currently stored'); | ||
} | ||
tokens.accessToken = token; | ||
return [4 /*yield*/, (0, setAuthTokens_1.setAuthTokens)(tokens)]; | ||
case 2: | ||
_a.sent(); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); }; | ||
exports.setAccessToken = setAccessToken; | ||
@@ -47,6 +101,13 @@ /** | ||
*/ | ||
var getRefreshToken = function () { | ||
var tokens = getAuthTokens(); | ||
return tokens ? tokens.refreshToken : undefined; | ||
}; | ||
var getRefreshToken = function () { return __awaiter(void 0, void 0, void 0, function () { | ||
var tokens; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, getAuthTokens()]; | ||
case 1: | ||
tokens = _a.sent(); | ||
return [2 /*return*/, tokens ? tokens.refreshToken : undefined]; | ||
} | ||
}); | ||
}); }; | ||
exports.getRefreshToken = getRefreshToken; | ||
@@ -57,6 +118,13 @@ /** | ||
*/ | ||
var getAccessToken = function () { | ||
var tokens = getAuthTokens(); | ||
return tokens ? tokens.accessToken : undefined; | ||
}; | ||
var getAccessToken = function () { return __awaiter(void 0, void 0, void 0, function () { | ||
var tokens; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, getAuthTokens()]; | ||
case 1: | ||
tokens = _a.sent(); | ||
return [2 /*return*/, tokens ? tokens.accessToken : undefined]; | ||
} | ||
}); | ||
}); }; | ||
exports.getAccessToken = getAccessToken; | ||
@@ -66,3 +134,18 @@ /** | ||
*/ | ||
var clearAuthTokens = function () { var _a; return (_a = StorageProxy_1.StorageProxy.Storage) === null || _a === void 0 ? void 0 : _a.remove(StorageKey_1.STORAGE_KEY); }; | ||
var clearAuthTokens = function () { return __awaiter(void 0, void 0, void 0, function () { var _a; return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: return [4 /*yield*/, ((_a = StorageProxy_1.StorageProxy.Storage) === null || _a === void 0 ? void 0 : _a.remove(StorageKey_1.STORAGE_KEY)) | ||
/** | ||
* Checks if refresh tokens are stored | ||
* @returns Whether the user is logged in or not | ||
*/ | ||
]; | ||
case 1: return [2 /*return*/, _b.sent() | ||
/** | ||
* Checks if refresh tokens are stored | ||
* @returns Whether the user is logged in or not | ||
*/ | ||
]; | ||
} | ||
}); }); }; | ||
exports.clearAuthTokens = clearAuthTokens; | ||
@@ -73,7 +156,14 @@ /** | ||
*/ | ||
var isLoggedIn = function () { | ||
var token = (0, exports.getRefreshToken)(); | ||
return !!token; | ||
}; | ||
var isLoggedIn = function () { return __awaiter(void 0, void 0, void 0, function () { | ||
var token; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, (0, exports.getRefreshToken)()]; | ||
case 1: | ||
token = _a.sent(); | ||
return [2 /*return*/, !!token]; | ||
} | ||
}); | ||
}); }; | ||
exports.isLoggedIn = isLoggedIn; | ||
//# sourceMappingURL=tokensUtils.js.map |
{ | ||
"name": "axios-jwt", | ||
"version": "3.0.2", | ||
"version": "4.0.0", | ||
"description": "Axios interceptor to store, use, and refresh tokens for authentication.", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -149,3 +149,3 @@ # axios-jwt | ||
return axios.post(`${BASE_URL}/auth/refresh_token`, { refresh }) | ||
.then(response => resolve(response.data.access_token)) | ||
.then(response => response.data.access_token) | ||
}; | ||
@@ -152,0 +152,0 @@ |
@@ -18,35 +18,5 @@ import { getAccessToken, getRefreshToken, setAccessToken } from './tokensUtils' | ||
type RequestsQueue = { | ||
resolve: (value?: unknown) => void | ||
reject: (reason?: unknown) => void | ||
}[] | ||
let currentlyRequestingPromise: Promise<Token | undefined> | undefined = undefined | ||
let isRefreshing = false | ||
let queue: RequestsQueue = [] | ||
/** | ||
* Function that resolves all items in the queue with the provided token | ||
* @param token New access token | ||
*/ | ||
const resolveQueue = (token?: Token) => { | ||
queue.forEach((p) => { | ||
p.resolve(token) | ||
}) | ||
queue = [] | ||
} | ||
/** | ||
* Function that declines all items in the queue with the provided error | ||
* @param error Error | ||
*/ | ||
const declineQueue = (error: Error) => { | ||
queue.forEach((p) => { | ||
p.reject(error) | ||
}) | ||
queue = [] | ||
} | ||
/** | ||
* Gets the unix timestamp from an access token | ||
@@ -91,2 +61,3 @@ * | ||
* Refreshes the access token using the provided function | ||
* Note: NOT to be called externally. Only accessible through an interceptor | ||
* | ||
@@ -97,8 +68,6 @@ * @param {requestRefresh} requestRefresh - Function that is used to get a new access token | ||
const refreshToken = async (requestRefresh: TokenRefreshRequest): Promise<Token> => { | ||
const refreshToken = getRefreshToken() | ||
const refreshToken = await getRefreshToken() | ||
if (!refreshToken) throw new Error('No refresh token available') | ||
try { | ||
isRefreshing = true | ||
// Refresh and store access token using the supplied refresh function | ||
@@ -121,3 +90,3 @@ const newTokens = await requestRefresh(refreshToken) | ||
// The refresh token is invalid so remove the stored tokens | ||
StorageProxy.Storage?.remove(STORAGE_KEY) | ||
await StorageProxy.Storage?.remove(STORAGE_KEY) | ||
throw new Error(`Got ${status} on token refresh; clearing both auth tokens`) | ||
@@ -133,4 +102,2 @@ } | ||
} | ||
} finally { | ||
isRefreshing = false | ||
} | ||
@@ -154,3 +121,3 @@ } | ||
// use access token (if we have it) | ||
let accessToken = getAccessToken() | ||
let accessToken = await getAccessToken() | ||
@@ -160,3 +127,2 @@ // check if access token is expired | ||
// do refresh | ||
accessToken = await refreshToken(requestRefresh) | ||
@@ -192,29 +158,26 @@ } | ||
// We need refresh token to do any authenticated requests | ||
if (!getRefreshToken()) return requestConfig | ||
if (!(await getRefreshToken())) return requestConfig | ||
// Queue the request if another refresh request is currently happening | ||
if (isRefreshing) { | ||
return new Promise((resolve, reject) => { | ||
queue.push({ resolve, reject }) | ||
}) | ||
.then((token) => { | ||
if (requestConfig.headers) { | ||
requestConfig.headers[header] = `${headerPrefix}${token}` | ||
} | ||
return requestConfig | ||
}) | ||
.catch(Promise.reject) | ||
} | ||
let accessToken = undefined | ||
// Do refresh if needed | ||
let accessToken | ||
try { | ||
accessToken = await refreshTokenIfNeeded(requestRefresh) | ||
resolveQueue(accessToken) | ||
} catch (error: unknown) { | ||
if (error instanceof Error) { | ||
declineQueue(error) | ||
throw new Error( | ||
`Unable to refresh access token for request due to token refresh error: ${error.message}` | ||
) | ||
// Try to await a current request | ||
if (currentlyRequestingPromise) accessToken = await currentlyRequestingPromise | ||
if (!accessToken) { | ||
try { | ||
// Sets the promise so everyone else will wait - then get the value | ||
currentlyRequestingPromise = refreshTokenIfNeeded(requestRefresh) | ||
accessToken = await currentlyRequestingPromise | ||
// Reset the promise | ||
currentlyRequestingPromise = undefined | ||
} catch (error: unknown) { | ||
// Reset the promise | ||
currentlyRequestingPromise = undefined | ||
if (error instanceof Error) { | ||
throw new Error( | ||
`Unable to refresh access token for request due to token refresh error: ${error.message}` | ||
) | ||
} | ||
} | ||
@@ -221,0 +184,0 @@ } |
@@ -8,13 +8,13 @@ export class BrowserStorageService { | ||
remove(key: string) { | ||
async remove(key: string) { | ||
this._storage.removeItem(key) | ||
} | ||
get(key: string) { | ||
async get(key: string) { | ||
return this._storage.getItem(key) | ||
} | ||
set(key: string, value: string) { | ||
async set(key: string, value: string) { | ||
this._storage.setItem(key, value) | ||
} | ||
} |
@@ -9,3 +9,3 @@ import { StorageProxy } from './StorageProxy' | ||
*/ | ||
export const setAuthTokens = (tokens: IAuthTokens): void => | ||
StorageProxy.Storage?.set(STORAGE_KEY, JSON.stringify(tokens)) | ||
export const setAuthTokens = async (tokens: IAuthTokens): Promise<void> => | ||
await StorageProxy.Storage?.set(STORAGE_KEY, JSON.stringify(tokens)) |
export type StorageType = { | ||
remove(key: string): void | ||
set(key: string, value: string): void | ||
get(value: string): string | null | ||
remove(key: string): Promise<void> | ||
set(key: string, value: string): Promise<void> | ||
get(value: string): Promise<string | null> | ||
} |
@@ -13,4 +13,4 @@ import { setAuthTokens } from './setAuthTokens' | ||
*/ | ||
const getAuthTokens = (): IAuthTokens | undefined => { | ||
const rawTokens = StorageProxy.Storage?.get(STORAGE_KEY) | ||
const getAuthTokens = async (): Promise<IAuthTokens | undefined> => { | ||
const rawTokens = await StorageProxy.Storage?.get(STORAGE_KEY) | ||
if (!rawTokens) return | ||
@@ -33,4 +33,4 @@ | ||
*/ | ||
export const setAccessToken = (token: Token): void => { | ||
const tokens = getAuthTokens() | ||
export const setAccessToken = async (token: Token): Promise<void> => { | ||
const tokens = await getAuthTokens() | ||
if (!tokens) { | ||
@@ -41,3 +41,3 @@ throw new Error('Unable to update access token since there are not tokens currently stored') | ||
tokens.accessToken = token | ||
setAuthTokens(tokens) | ||
await setAuthTokens(tokens) | ||
} | ||
@@ -49,4 +49,4 @@ | ||
*/ | ||
export const getRefreshToken = (): Token | undefined => { | ||
const tokens = getAuthTokens() | ||
export const getRefreshToken = async (): Promise<Token | undefined> => { | ||
const tokens = await getAuthTokens() | ||
return tokens ? tokens.refreshToken : undefined | ||
@@ -59,4 +59,4 @@ } | ||
*/ | ||
export const getAccessToken = (): Token | undefined => { | ||
const tokens = getAuthTokens() | ||
export const getAccessToken = async (): Promise<Token | undefined> => { | ||
const tokens = await getAuthTokens() | ||
return tokens ? tokens.accessToken : undefined | ||
@@ -68,3 +68,4 @@ } | ||
*/ | ||
export const clearAuthTokens = (): void => StorageProxy.Storage?.remove(STORAGE_KEY) | ||
export const clearAuthTokens = async (): Promise<void> => | ||
await StorageProxy.Storage?.remove(STORAGE_KEY) | ||
@@ -75,5 +76,5 @@ /** | ||
*/ | ||
export const isLoggedIn = (): boolean => { | ||
const token = getRefreshToken() | ||
export const isLoggedIn = async (): Promise<boolean> => { | ||
const token = await getRefreshToken() | ||
return !!token | ||
} |
@@ -1,4 +0,4 @@ | ||
import { AxiosRequestConfig } from 'axios'; | ||
import jwt from 'jsonwebtoken'; | ||
import { authTokenInterceptor } from '../index'; | ||
import { AxiosRequestConfig } from 'axios' | ||
import jwt from 'jsonwebtoken' | ||
import { authTokenInterceptor } from '../index' | ||
@@ -5,0 +5,0 @@ describe('authTokenInterceptor', () => { |
@@ -1,7 +0,6 @@ | ||
import { STORAGE_KEY } from '../src/StorageKey'; | ||
import { clearAuthTokens } from '../index'; | ||
import { STORAGE_KEY } from '../src/StorageKey' | ||
import { clearAuthTokens } from '../index' | ||
describe('clearAuthTokens', () => { | ||
it('removes the tokens from localstorage', () => { | ||
it('removes the tokens from localstorage', async () => { | ||
// GIVEN | ||
@@ -14,3 +13,3 @@ // Tokens are stored in localStorage | ||
// I call clearAuthTokens | ||
clearAuthTokens() | ||
await clearAuthTokens() | ||
@@ -17,0 +16,0 @@ // THEN |
@@ -1,3 +0,3 @@ | ||
import { getAccessToken, authTokenInterceptor, getBrowserSessionStorage } from '../index'; | ||
import { STORAGE_KEY } from '../src/StorageKey'; | ||
import { getAccessToken, authTokenInterceptor, getBrowserSessionStorage } from '../index' | ||
import { STORAGE_KEY } from '../src/StorageKey' | ||
@@ -11,3 +11,3 @@ describe('getAccessToken', () => { | ||
describe('for localStorage', function () { | ||
it('returns undefined if tokens are not set', () => { | ||
it('returns undefined if tokens are not set', async () => { | ||
// GIVEN | ||
@@ -19,3 +19,3 @@ // localStorage is empty | ||
// I call getAccessToken | ||
const result = getAccessToken() | ||
const result = await getAccessToken() | ||
@@ -27,3 +27,3 @@ // THEN | ||
it('returns the access token is it is set', () => { | ||
it('returns the access token is it is set', async () => { | ||
// GIVEN | ||
@@ -36,3 +36,3 @@ // Both tokens are stored in localstorage | ||
// I call getAccessToken | ||
const result = getAccessToken() | ||
const result = await getAccessToken() | ||
@@ -43,13 +43,13 @@ // THEN | ||
}) | ||
}); | ||
}) | ||
describe('for sessionStorage', function () { | ||
beforeEach( () => { | ||
beforeEach(() => { | ||
const getStorage = getBrowserSessionStorage | ||
const requestRefresh = jest.fn() | ||
authTokenInterceptor({getStorage, requestRefresh }) | ||
authTokenInterceptor({ getStorage, requestRefresh }) | ||
}) | ||
it('returns undefined if tokens are not set', () => { | ||
it('returns undefined if tokens are not set', async () => { | ||
// GIVEN | ||
@@ -61,3 +61,3 @@ // localStorage is empty | ||
// I call getAccessToken | ||
const result = getAccessToken() | ||
const result = await getAccessToken() | ||
@@ -69,3 +69,3 @@ // THEN | ||
it('returns the access token is it is set', () => { | ||
it('returns the access token is it is set', async () => { | ||
// GIVEN | ||
@@ -78,3 +78,3 @@ // Both tokens are stored in localstorage | ||
// I call getAccessToken | ||
const result = getAccessToken() | ||
const result = await getAccessToken() | ||
@@ -81,0 +81,0 @@ // THEN |
@@ -1,6 +0,6 @@ | ||
import { getRefreshToken } from '../index'; | ||
import { STORAGE_KEY } from '../src/StorageKey'; | ||
import { getRefreshToken } from '../index' | ||
import { STORAGE_KEY } from '../src/StorageKey' | ||
describe('getRefreshToken', () => { | ||
it('returns undefined if tokens are not set', () => { | ||
it('returns undefined if tokens are not set', async () => { | ||
// GIVEN | ||
@@ -12,3 +12,3 @@ // localStorage is empty | ||
// I call getRefreshToken | ||
const result = getRefreshToken() | ||
const result = await getRefreshToken() | ||
@@ -20,3 +20,3 @@ // THEN | ||
it('returns the access token is it is set', () => { | ||
it('returns the access token is it is set', async () => { | ||
// GIVEN | ||
@@ -29,3 +29,3 @@ // Both tokens are stored in localstorage | ||
// I call getRefreshToken | ||
const result = getRefreshToken() | ||
const result = await getRefreshToken() | ||
@@ -32,0 +32,0 @@ // THEN |
@@ -1,6 +0,6 @@ | ||
import { STORAGE_KEY } from '../src/StorageKey'; | ||
import { isLoggedIn } from '../index'; | ||
import { STORAGE_KEY } from '../src/StorageKey' | ||
import { isLoggedIn } from '../index' | ||
describe('isLoggedIn', () => { | ||
it('returns false if tokens are not set', () => { | ||
it('returns false if tokens are not set', async () => { | ||
// GIVEN | ||
@@ -12,3 +12,3 @@ // localStorage is empty | ||
// I call isLoggedIn | ||
const result = isLoggedIn() | ||
const result = await isLoggedIn() | ||
@@ -20,3 +20,3 @@ // THEN | ||
it('returns true if refresh token is set', () => { | ||
it('returns true if refresh token is set', async () => { | ||
// GIVEN | ||
@@ -29,3 +29,3 @@ // Both tokens are stored in localstorage | ||
// I call isLoggedIn | ||
const result = isLoggedIn() | ||
const result = await isLoggedIn() | ||
@@ -32,0 +32,0 @@ // THEN |
@@ -1,3 +0,3 @@ | ||
import { setAccessToken } from '../index'; | ||
import { STORAGE_KEY } from '../src/StorageKey'; | ||
import { setAccessToken } from '../index' | ||
import { STORAGE_KEY } from '../src/StorageKey' | ||
@@ -14,5 +14,5 @@ describe('setAccessToken', () => { | ||
// I expect an error to have been thrown | ||
expect(() => { | ||
setAccessToken('accesstoken') | ||
}).toThrow('Unable to update access token since there are not tokens currently stored') | ||
expect(async () => { | ||
await setAccessToken('accesstoken') | ||
}).rejects.toThrow('Unable to update access token since there are not tokens currently stored') | ||
}) | ||
@@ -29,8 +29,8 @@ | ||
// I expect an error to be thrown | ||
expect(() => { | ||
setAccessToken('accesstoken') | ||
}).toThrow('Failed to parse auth tokens: totallynotjson') | ||
expect(async () => { | ||
await setAccessToken('accesstoken') | ||
}).rejects.toThrow('Failed to parse auth tokens: totallynotjson') | ||
}) | ||
it('stores the tokens in localstorage', () => { | ||
it('stores the tokens in localstorage', async () => { | ||
// GIVEN | ||
@@ -43,3 +43,3 @@ // localStorage is empty | ||
// I call setAccessToken | ||
setAccessToken('newaccesstoken') | ||
await setAccessToken('newaccesstoken') | ||
@@ -49,4 +49,7 @@ // THEN | ||
const storedTokens = localStorage.getItem(STORAGE_KEY) as string | ||
expect(JSON.parse(storedTokens)).toEqual({ accessToken: 'newaccesstoken', refreshToken: 'refreshtoken' }) | ||
expect(JSON.parse(storedTokens)).toEqual({ | ||
accessToken: 'newaccesstoken', | ||
refreshToken: 'refreshtoken', | ||
}) | ||
}) | ||
}) |
@@ -1,6 +0,6 @@ | ||
import { setAuthTokens } from '../index'; | ||
import { STORAGE_KEY } from '../src/StorageKey'; | ||
import { setAuthTokens } from '../index' | ||
import { STORAGE_KEY } from '../src/StorageKey' | ||
describe('setAuthTokens', () => { | ||
it('stores the tokens in localstorage', () => { | ||
it('stores the tokens in localstorage', async () => { | ||
// GIVEN | ||
@@ -13,3 +13,3 @@ // localStorage is empty | ||
const tokens = { accessToken: 'accesstoken', refreshToken: 'refreshtoken' } | ||
setAuthTokens(tokens) | ||
await setAuthTokens(tokens) | ||
@@ -16,0 +16,0 @@ // THEN |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
102876
1862