@@ -203,6 +203,7 @@ export interface AuthClientOptions {
203203 enableAccessTokenEndpoint ?: boolean ;
204204 noContentProfileResponseWhenUnauthenticated ?: boolean ;
205205 enableConnectAccountEndpoint ?: boolean ;
206+ tokenRefreshLeeway ?: number ;
206207
207208 useDPoP ?: boolean ;
208209 dpopKeyPair ?: DpopKeyPair ;
@@ -258,6 +259,7 @@ export class AuthClient {
258259 private readonly enableAccessTokenEndpoint : boolean ;
259260 private readonly noContentProfileResponseWhenUnauthenticated : boolean ;
260261 private readonly enableConnectAccountEndpoint : boolean ;
262+ private readonly tokenRefreshLeeway : number ;
261263
262264 private dpopOptions ?: DpopOptions ;
263265
@@ -379,6 +381,7 @@ export class AuthClient {
379381 options . noContentProfileResponseWhenUnauthenticated ?? false ;
380382 this . enableConnectAccountEndpoint =
381383 options . enableConnectAccountEndpoint ?? false ;
384+ this . tokenRefreshLeeway = options . tokenRefreshLeeway ?? 0 ;
382385
383386 this . useDPoP = options . useDPoP ?? false ;
384387
@@ -1200,6 +1203,13 @@ export class AuthClient {
12001203 audience : options . audience ?? this . authorizationParameters . audience
12011204 }
12021205 ) ;
1206+ const now = Date . now ( ) / 1000 ;
1207+ const expiresAt =
1208+ typeof tokenSet . expiresAt === "number" ? tokenSet . expiresAt : undefined ;
1209+ const isExpired = typeof expiresAt === "number" && expiresAt <= now ;
1210+ const isWithinRefreshWindow =
1211+ typeof expiresAt === "number" &&
1212+ expiresAt <= now + this . tokenRefreshLeeway ;
12031213
12041214 // no access token was found that matches the, optional, provided audience and scope
12051215 if ( ! tokenSet . refreshToken && ! tokenSet . accessToken ) {
@@ -1213,12 +1223,7 @@ export class AuthClient {
12131223 }
12141224
12151225 // the access token was found, but it has expired and we do not have a refresh token
1216- if (
1217- ! tokenSet . refreshToken &&
1218- tokenSet . accessToken &&
1219- tokenSet . expiresAt &&
1220- tokenSet . expiresAt <= Date . now ( ) / 1000
1221- ) {
1226+ if ( ! tokenSet . refreshToken && tokenSet . accessToken && isExpired ) {
12221227 return [
12231228 new AccessTokenError (
12241229 AccessTokenErrorCode . MISSING_REFRESH_TOKEN ,
@@ -1230,11 +1235,7 @@ export class AuthClient {
12301235
12311236 if ( tokenSet . refreshToken ) {
12321237 // either the access token has expired or we are forcing a refresh
1233- if (
1234- options . refresh ||
1235- ! tokenSet . expiresAt ||
1236- tokenSet . expiresAt <= Date . now ( ) / 1000
1237- ) {
1238+ if ( options . refresh || ! expiresAt || isWithinRefreshWindow ) {
12381239 const [ error , response ] = await this . #refreshTokenSet( tokenSet , {
12391240 audience : options . audience ,
12401241 scope : options . scope ? scope : undefined ,
0 commit comments