Skip to content

Created backend token manager for automatic access token refresh#50

Open
Mehulantony wants to merge 1 commit intomainfrom
token
Open

Created backend token manager for automatic access token refresh#50
Mehulantony wants to merge 1 commit intomainfrom
token

Conversation

@Mehulantony
Copy link
Copy Markdown
Collaborator

Created a backend token manager that handles automatic refresh of Auth0 access tokens using the existing refresh-token workflow. The implementation follows the approach used in TinyNode and preserves the current Auth0 registration/login flow.

The token manager checks whether the current access token is expired and, if necessary, requests a new access token using the stored refresh token. This moves token lifecycle management to the backend while keeping Auth0 as the source of truth for authentication.

It must be noted that the initial refresh token must still be obtained through the existing Auth0 UX registration/login flow.


try {
const payload = JSON.parse(
Buffer.from(token.split('.')[1], 'base64').toString()
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Guard against malformed tokens
It may be safer to add a check to ensure the token has the expected JWT structure before attempting to decode it.

if (parts.length !== 3) return true;

This prevents errors if the token is malformed or not actually a JWT.

Also, consider using jsonwebtoken.decode() instead of manually decoding with Buffer.from(...), since it handles Base64URL encoding and parsing more robustly.

"express": "^5.2.1",
"express-oauth2-jwt-bearer": "~1.7.1",
"express-urlrewrite": "~2.0.3",
"jsonwebtoken": "^9.0.3",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We add "jsonwebtoken "as a dependency but don’t appear to use it in "token-manager". Do we plan to use it for decoding/verifying tokens here? If not, maybe we can either (
a) use it for "isTokenExpired" instead of manual parsing, or
(b) drop the dependency for now to keep the surface area smaller.

import config from '../config/index.js'
import fs from 'node:fs/promises'

const sourcePath = '.env'
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"sourcePath" is hard-coded to .env relative to the process CWD. In some deployment setups (docker) this might not point to the right file or might silently fail. Should we either
(a) make this path configurable
(e.g. process.env.ENV_FILE_PATH ?? '.env'`), or
(b) clearly document that this is intended as a local-dev convenience only?

body: JSON.stringify({ refresh_token: refreshToken })
})

const tokenObject = await response.json()
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the token endpoint responds with non‑JSON (e.g. an HTML error page), response.json() will throw before we can check response.ok. Consider wrapping the json() call in a try/catch and surfacing a clearer error message (including HTTP status) when parsing fails.

)
}

process.env.ACCESS_TOKEN = tokenObject.access_token
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This module mutates process.env at runtime, which is convenient but can be surprising if other parts of the app read env vars only at startup. Could we document this behavior prominently, and/or consider keeping the token in a module‑level variable and having callers always use getValidAccessToken() instead of reading from process.env directly?

* using the stored refresh token
*/

async function checkAndRefreshAccessToken() {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With multiple concurrent requests, it’s possible that several callers all detect an expired token and call generateNewAccessToken() at the same time. Not a correctness bug, but it could result in unnecessary token refresh calls. Consider adding a simple in‑flight promise lock so that only one refresh happens at a time and others await it.

Buffer.from(token.split('.')[1], 'base64').toString()
)

return !payload.exp || Date.now() >= payload.exp * 1000
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We treat the token as expired exactly at exp. To be more robust to small clock skew between our server and the issuer, we might want to treat it as expired slightly before, e.g. subtracting 30 seconds from exp before comparing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants