UNPKG

@wristband/nextjs-auth

Version:

SDK for integrating your Next.js application with Wristband. Handles user authentication, session management, and token management.

73 lines (72 loc) 3.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.refreshExpiredToken = refreshExpiredToken; const error_1 = require("../../error"); /** * Refreshes an access token if it has expired. * * @param refreshToken - The refresh token to use * @param expiresAt - When the current access token expires (milliseconds since epoch) * @param wristbandService - Service instance to make the token refresh request * @param tokenExpirationBuffer - Optional buffer time in seconds * @returns New token data if refreshed, null if not expired yet * @throws {WristbandError} if refresh fails */ async function refreshExpiredToken(refreshToken, expiresAt, wristbandService, tokenExpirationBuffer) { // Safety checks if (!refreshToken) { throw new TypeError('Refresh token must be a valid string'); } if (!expiresAt || expiresAt < 0) { throw new TypeError('The expiresAt field must be an integer greater than 0'); } if (Date.now().valueOf() <= expiresAt) { return null; } // Try up to 3 times to perform a token refresh let tokenResponse = null; for (let attempt = 1; attempt <= 3; attempt += 1) { try { // eslint-disable-next-line no-await-in-loop tokenResponse = await wristbandService.refreshToken(refreshToken); break; } catch (error) { if (error instanceof error_1.InvalidGrantError) { // Specifically handle invalid_grant errors throw new error_1.WristbandError('invalid_refresh_token', error.errorDescription, error); } if (error instanceof error_1.FetchError && error.response && error.response.status >= 400 && error.response.status < 500) { const errorDescription = error.body && error.body.error_description ? error.body.error_description : 'Invalid Refresh Token'; // Only 4xx errors should short-circuit the retry loop early. throw new error_1.WristbandError('invalid_refresh_token', errorDescription, error); } // Final attempt failed if (attempt === 3) { throw new error_1.WristbandError('unexpected_error', 'Unexpected Error', error); } // Wait before retrying (100ms delay) // eslint-disable-next-line no-await-in-loop await new Promise((resolve) => { setTimeout(resolve, 100); }); } } if (!tokenResponse) { // This is merely a safety check, but this should never happen. throw new error_1.WristbandError('unexpected_error', 'Unexpected Error'); } const { access_token: accessToken, id_token: idToken, expires_in: expiresIn, refresh_token: responseRefreshToken, } = tokenResponse; const resolvedExpiresIn = expiresIn - (tokenExpirationBuffer || 0); const resolvedExpiresAt = Date.now() + resolvedExpiresIn * 1000; return { accessToken, expiresAt: resolvedExpiresAt, expiresIn: resolvedExpiresIn, idToken, refreshToken: responseRefreshToken, }; }