authvisage-sdk
Version:
authvisage client sdk
91 lines • 3.56 kB
JavaScript
import { safeAwait } from "@/utils/safe-await";
import { decodeJwt } from "@/utils/decode-jwt";
import { ListenerManager } from "@/helpers/listenerManager";
export class TokenManager {
constructor(backendUrl) {
this.backendUrl = backendUrl;
this.listenerManager = new ListenerManager();
this.expirationTimer = null;
}
/**
* Handle token expiration by setting a timer.
* @param expiresIn - The time in seconds until the token expires.
*/
_handleTokenExpiration(expiresIn) {
if (this.expirationTimer) {
clearTimeout(this.expirationTimer);
}
if (expiresIn) {
this.expirationTimer = setTimeout(() => {
this.listenerManager.notify(null);
}, expiresIn * 1000);
}
}
/**
* Initializes the user session by validating and decoding the access token, notifying registered listeners,
* and scheduling automatic token expiration handling.
*
* @param session - The token response containing the `access_token` and `expires_in` values.
* @throws {Error} If the session does not include an `access_token`.
* @returns A promise that resolves once the session is set and expiration handling is in place.
*/
async setSession(session) {
if (!session.access_token) {
throw new Error("Session must contain an access token.");
}
const decodedToken = decodeJwt(session.access_token);
this.listenerManager.notify(decodedToken);
// Handle token expiration
this._handleTokenExpiration(session.expires_in);
}
/**
* Sends a refresh request to get a new access token.
* Assumes the refresh token is stored in cookies.
*/
async getAccessToken() {
const response = await fetch(`${this.backendUrl}/refresh-token`, {
method: "POST",
credentials: "include",
});
if (!response.ok) {
throw new Error(`Failed to refresh access token: ${response.statusText}`);
}
const [data, error] = await safeAwait(response.json());
if (error) {
throw new Error("Failed to parse token response.");
}
this.setSession(data);
return data.access_token;
}
/**
* Logs the current user out by sending a POST request to the backend logout endpoint.
*
* This method includes credentials with the request and throws an error if the response
* status is not in the 200–299 range. On a successful logout, it notifies all registered
* listeners with `null`.
*
* @returns A promise that resolves when the logout operation completes successfully.
* @throws {Error} If the logout request fails or the response is not OK.
*/
async logout() {
const response = await fetch(`${this.backendUrl}/logout`, {
method: "POST",
credentials: "include",
});
if (!response.ok) {
throw new Error(`Failed to sign out: ${response.statusText}`);
}
this.listenerManager.notify(null);
}
/**
* Subscribes to authentication state changes.
*
* @param callback - Function to be invoked whenever the authentication state updates.
* @returns A Promise that resolves to an unsubscribe function which,
* when called, removes the listener.
*/
onAuthStateChange(callback) {
return this.listenerManager.subscribe(callback);
}
}
//# sourceMappingURL=tokenManager.js.map