Skip to content

Commit

Permalink
feat: ts
Browse files Browse the repository at this point in the history
  • Loading branch information
paras-verma committed Jul 28, 2023
1 parent 30835f9 commit 2baa115
Show file tree
Hide file tree
Showing 5 changed files with 446 additions and 141 deletions.
34 changes: 29 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"eslint": "^8.37.0",
"eslint-plugin-ternary": "^2.0.0",
"jest": "^29.5.0",
"jsonwebtoken": "^9.0.0"
"jsonwebtoken": "^9.0.0",
"typescript": "^5.1.6"
},
"repository": {
"type": "git",
Expand Down
283 changes: 148 additions & 135 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,139 +1,152 @@
import jwt_decode from "jwt-decode";

export class OIDCClient {
constructor(options) {
this.refreshTokenLock = false;
this.refreshPath = options?.refreshPath || "token/refresh";
this.baseUrl = options?.baseUrl;
this.BASE_HEADERS = options?.headers || {
"Content-Type": "application/json; charset=UTF-8",
Accept: "application/json, text/javascript, */*; q=0.01",
};
}

setBaseUrl(url) {
this.baseUrl = url;
}

getBaseUrl() {
return this.baseUrl;
}

setRefreshPath(path) {
if (path.startsWith("/")) path = path.slice(1);
this.refreshPath = path;
}

getRefreshPath() {
return this.refreshPath;
}

getBaseHeaders() {
return this.BASE_HEADERS;
}

lockRefreshTokenLock() {
this.refreshTokenLock = true;
}

releaseRefreshTokenLock() {
this.refreshTokenLock = false;
}

async prepareHeaders(HEADERS) {
if (!HEADERS) HEADERS = this.BASE_HEADERS;

const token = await this.getAccessToken();

if (token) return { ...HEADERS, Authorization: `Bearer ${token}` };
else return HEADERS;
}

async getAccessToken() {
if (typeof localStorage === "undefined" || !localStorage.getItem("refreshToken"))
// Either we're in a non-browser environment, or session security is used
return;

try {
let count = 0;
while (this.refreshTokenLock && count < 15) {
await this._wait((count > 0) ? (200 * count) : undefined); //delays the next check of refreshTokenLock
count += 1;
}

if (!this.verifyTokenValidity()) await this._refreshToken();
} catch (err) {
console.log(err);
sessionStorage.removeItem("token");
localStorage.removeItem("refreshToken");
document.dispatchEvent(new CustomEvent("logged-out", { bubbles: true, composed: true }));
}

return sessionStorage.getItem("token");
}

async _wait(time = 1200) {
return new Promise((resolve) => {
setTimeout(function () {
resolve();
}, time);
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {

Check failure on line 2 in src/index.js

View workflow job for this annotation

GitHub Actions / build

Unexpected var, use let or const instead
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }

Check failure on line 3 in src/index.js

View workflow job for this annotation

GitHub Actions / build

Ternary clause expression requires enclosing ( .. )

Check failure on line 3 in src/index.js

View workflow job for this annotation

GitHub Actions / build

Ternary clause expression requires enclosing ( .. )
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); }

Check failure on line 7 in src/index.js

View workflow job for this annotation

GitHub Actions / build

Ternary clause expression requires enclosing ( .. )

Check failure on line 7 in src/index.js

View workflow job for this annotation

GitHub Actions / build

Ternary clause expression requires enclosing ( .. )
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}

verifyTokenValidity() {
const token = sessionStorage.getItem("token");
if (!token) return false;
try {
const exp = jwt_decode(token);
return exp && exp.exp >= (new Date().getTime() + 10000) / 1000;
} catch (err) {
return false;
};
var __importDefault = (this && this.__importDefault) || function (mod) {

Check failure on line 11 in src/index.js

View workflow job for this annotation

GitHub Actions / build

Unexpected var, use let or const instead
return (mod && mod.__esModule) ? mod : { "default": mod };

Check failure on line 12 in src/index.js

View workflow job for this annotation

GitHub Actions / build

Ternary clause expression requires enclosing ( .. )
};
Object.defineProperty(exports, "__esModule", { value: true });

Check failure on line 14 in src/index.js

View workflow job for this annotation

GitHub Actions / build

'exports' is not defined
exports.OIDCClient = void 0;

Check failure on line 15 in src/index.js

View workflow job for this annotation

GitHub Actions / build

'exports' is not defined
const jwt_decode_1 = __importDefault(require("jwt-decode"));

Check failure on line 16 in src/index.js

View workflow job for this annotation

GitHub Actions / build

'require' is not defined
class OIDCClient {
constructor(options) {
this.refreshTokenLock = false;
this.refreshPath = (options === null || options === void 0 ? void 0 : options.refreshPath) || "token/refresh";
this.baseUrl = options === null || options === void 0 ? void 0 : options.baseUrl;
this.BASE_HEADERS = (options === null || options === void 0 ? void 0 : options.headers) || {
"Content-Type": "application/json; charset=UTF-8",
Accept: "application/json, text/javascript, */*; q=0.01",
};
}
setBaseUrl(url) {
this.baseUrl = url;
}
getBaseUrl() {
return this.baseUrl;
}
setRefreshPath(path) {
if (path.startsWith("/"))
path = path.slice(1);
this.refreshPath = path;
}
getRefreshPath() {
return this.refreshPath;
}
getBaseHeaders() {
return this.BASE_HEADERS;
}
lockRefreshTokenLock() {
this.refreshTokenLock = true;
}
}

async _refreshToken() {
const headers = { ...this.BASE_HEADERS };

const refreshToken = localStorage.getItem("refreshToken");

const token = sessionStorage.getItem("token");

if (!refreshToken) throw "No refresh token";

this.lockRefreshTokenLock();

if (token) headers["Authorization"] = `Bearer ${token}`;
headers["Refresh-Token"] = refreshToken;

let base = this.getBaseUrl();
if (!base) throw new Error("Missing `baseUrl` argument for OIDCClient");
if (!base.endsWith("/")) base = `${base}/`;

let refreshPath = this.getRefreshPath();
if (refreshPath.startsWith("/")) refreshPath = refreshPath.slice(1);

const serviceUrl = `${base}${refreshPath}`;

return fetch(serviceUrl, { method: "GET", headers: headers })
.then(async (response) => {
if (response.status === 403) {
throw 403;
} else {
const data = await response.json();
const token = data?.["token"] || response.headers.get("token");
const refreshToken = data?.["refreshToken"] || response.headers.get("refreshToken");

if (!token && !refreshToken) throw new Error("Couldn't fetch `access-token` or `refresh-token`");
if (token) sessionStorage.setItem("token", token);
if (refreshToken) localStorage.setItem("refreshToken", refreshToken);
releaseRefreshTokenLock() {
this.refreshTokenLock = false;
}
prepareHeaders(HEADERS) {
return __awaiter(this, void 0, void 0, function* () {
if (!HEADERS)
HEADERS = this.BASE_HEADERS;
const token = yield this.getAccessToken();
if (token)
return Object.assign(Object.assign({}, HEADERS), { Authorization: `Bearer ${token}` });
else
return HEADERS;
});
}
getAccessToken() {
return __awaiter(this, void 0, void 0, function* () {
if (typeof localStorage === "undefined" || !localStorage.getItem("refreshToken"))
// Either we're in a non-browser environment, or session security is used
return;
try {
let count = 0;
while (this.refreshTokenLock && count < 15) {
yield this._wait(count > 0 ? 200 * count : undefined); //delays the next check of refreshTokenLock
count += 1;
}
if (!this.verifyTokenValidity())
yield this._refreshToken();
}
catch (err) {
console.log(err);
sessionStorage.removeItem("token");
localStorage.removeItem("refreshToken");
document.dispatchEvent(new CustomEvent("logged-out", { bubbles: true, composed: true }));
}
return sessionStorage.getItem("token");
});
}
_wait(time = 1200) {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve) => {
setTimeout(function () {
resolve();
}, time);
});
});
}
verifyTokenValidity() {
const token = sessionStorage.getItem("token");
if (!token)
return false;
try {
const exp = (0, jwt_decode_1.default)(token);
return (exp === null || exp === void 0 ? void 0 : exp.exp) && exp.exp >= (new Date().getTime() + 10000) / 1000;
}
})
.catch((err) => {
console.log(err);
throw err;
})
.finally(() => {
this.releaseRefreshTokenLock();
});
}
catch (err) {
return false;
}
}
_refreshToken() {
return __awaiter(this, void 0, void 0, function* () {
const headers = Object.assign({}, this.BASE_HEADERS);
const refreshToken = localStorage.getItem("refreshToken");
const token = sessionStorage.getItem("token");
if (!refreshToken)
throw "No refresh token";
this.lockRefreshTokenLock();
if (token)
headers["Authorization"] = `Bearer ${token}`;
headers["Refresh-Token"] = refreshToken;
let base = this.getBaseUrl();
if (!base)
throw new Error("Missing `baseUrl` argument for OIDCClient");
if (!base.endsWith("/"))
base = `${base}/`;
let refreshPath = this.getRefreshPath();
if (refreshPath.startsWith("/"))
refreshPath = refreshPath.slice(1);
const serviceUrl = `${base}${refreshPath}`;
return fetch(serviceUrl, { method: "GET", headers: headers })
.then((response) => __awaiter(this, void 0, void 0, function* () {
if (response.status === 403) {
throw 403;
}
else {
const data = yield response.json();
const token = (data === null || data === void 0 ? void 0 : data["token"]) || response.headers.get("token");
const refreshToken = (data === null || data === void 0 ? void 0 : data["refreshToken"]) || response.headers.get("refreshToken");
if (!token && !refreshToken)
throw new Error("Couldn't fetch `access-token` or `refresh-token`");
if (token)
sessionStorage.setItem("token", token);
if (refreshToken)
localStorage.setItem("refreshToken", refreshToken);
}
}))
.catch((err) => {
console.log(err);
throw err;
})
.finally(() => {
this.releaseRefreshTokenLock();
});
});
}
}
exports.OIDCClient = OIDCClient;
Loading

0 comments on commit 2baa115

Please sign in to comment.