Skip to content

Commit

Permalink
feat: maintain reference to refresh-promise to avoid repetitive calls (
Browse files Browse the repository at this point in the history
…#7)

* feat: maintain reference to refresh-promise to avoid repetitive calls when a promise exists
note: refresh calls were made even if the tokens were updated recently and lock was released recently
* update: use prod-env for releases on `main` branch
* update: mark `refreshTokenPromise` as private
* chore: break out of wait-loop @ getAccessToken

---------

Co-authored-by: paras-verma <paras.19508@sscbs.du.ac.in>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Oct 22, 2024
1 parent 30835f9 commit 62b4e58
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 37 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ jobs:

publish-npmjs:
needs: release
environment: prod
environment: ${{ github.ref_name == 'main' && 'prod' || '' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
Expand All @@ -85,7 +85,7 @@ jobs:

publish-gitlab:
needs: release
environment: prod
environment: ${{ github.ref_name == 'main' && 'prod' || '' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@fundwave/oidc-client",
"version": "2.0.0",
"version": "2.0.1-retain-refresh-promise-reference.2",
"description": "JS client for making server calls for an OIDC flow",
"license": "MIT",
"author": "The Fundwave Authors",
Expand Down
62 changes: 30 additions & 32 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import jwt_decode from "jwt-decode";

export class OIDCClient {
#refreshTokenPromise;

constructor(options) {
this.refreshTokenLock = false;
this.refreshPath = options?.refreshPath || "token/refresh";
Expand Down Expand Up @@ -40,13 +42,13 @@ export class OIDCClient {
this.refreshTokenLock = false;
}

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

const token = await this.getAccessToken();

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

async getAccessToken() {
Expand All @@ -55,13 +57,13 @@ export class OIDCClient {
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;
for (let count = 0; this.refreshTokenLock && count < 15; count++) {
if (this.#refreshTokenPromise) break;
await this._wait((count) ? (200 * count) : undefined); //delays the next check of refreshTokenLock
}

if (!this.verifyTokenValidity()) await this._refreshToken();
if (this.#refreshTokenPromise) await this.#refreshTokenPromise;
else if (!this.verifyTokenValidity()) await this._refreshToken();
} catch (err) {
console.log(err);
sessionStorage.removeItem("token");
Expand Down Expand Up @@ -92,11 +94,9 @@ export class OIDCClient {
}

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

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

const token = sessionStorage.getItem("token");
const refreshToken = localStorage.getItem("refreshToken");
const headers = { ...this.BASE_HEADERS };

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

Expand All @@ -105,35 +105,33 @@ export class OIDCClient {
if (token) headers["Authorization"] = `Bearer ${token}`;
headers["Refresh-Token"] = refreshToken;

let base = this.getBaseUrl();
const 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}`;
const refreshPath = this.getRefreshPath();
const serviceUrl = base.replace(/\/?$/, "/").concat(refreshPath.replace(/^\/?/, ""));

return fetch(serviceUrl, { method: "GET", headers: headers })
this.#refreshTokenPromise = 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);
}
if (response.status === 403) throw 403;

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);
})
.catch((err) => {
console.log(err);
console.log("Failed to refresh tokens", err);
throw err;
})
.finally(() => {
this.releaseRefreshTokenLock();
this.#refreshTokenPromise = null;
});

return this.#refreshTokenPromise;
}
}

0 comments on commit 62b4e58

Please sign in to comment.