@@ -123,27 +123,47 @@ async function getDefaultOpenBrowser(): Promise<
123
123
} ;
124
124
}
125
125
126
+ // Trimmed-down type for easier testing
127
+ type TokenSetExpiryInfo = Pick <
128
+ TokenSet ,
129
+ 'refresh_token' | 'id_token' | 'expires_at'
130
+ > & {
131
+ claims ?: ( ) => { exp : number } ;
132
+ } ;
133
+
134
+ function tokenExpiryInSeconds (
135
+ tokenSet : TokenSetExpiryInfo = { } ,
136
+ passIdTokenAsAccessToken = false
137
+ ) : number {
138
+ // If we have an ID token and are supposed to use it, its `exp` claim
139
+ // specifies the token expiry. Otherwise, we assume that the `expires_at`
140
+ // value presented by the OIDC provider is correct, since OIDC clients are
141
+ // not supposed to inspect access tokens and treat them as opaque.
142
+ const expiresAt =
143
+ ( tokenSet . id_token &&
144
+ passIdTokenAsAccessToken &&
145
+ tokenSet . claims ?.( ) . exp ) ||
146
+ tokenSet . expires_at ||
147
+ 0 ;
148
+ return Math . max ( 0 , ( expiresAt ?? 0 ) - Date . now ( ) / 1000 ) ;
149
+ }
150
+
126
151
/** @internal Exported for testing only */
127
152
export function automaticRefreshTimeoutMS (
128
- tokenSet : Pick < TokenSet , 'refresh_token' | 'expires_in' >
153
+ tokenSet : TokenSetExpiryInfo ,
154
+ passIdTokenAsAccessToken = false
129
155
) : number | undefined {
156
+ const expiresIn = tokenExpiryInSeconds ( tokenSet , passIdTokenAsAccessToken ) ;
157
+ if ( ! tokenSet . refresh_token || ! expiresIn ) return ;
158
+
130
159
// If the tokens expire in more than 1 minute, automatically register
131
160
// a refresh handler. (They should not expire in less; however,
132
161
// if we didn't handle that case, we'd run the risk of refreshing very
133
162
// frequently.) Refresh the token 5 minutes before expiration or
134
163
// halfway between now and the expiration time, whichever comes later
135
164
// (expires in 1 hour -> refresh in 55 min, expires in 5 min -> refresh in 2.5 min).
136
- if (
137
- tokenSet . refresh_token &&
138
- tokenSet . expires_in &&
139
- tokenSet . expires_in >= 60 /* 1 minute */
140
- ) {
141
- return (
142
- Math . max (
143
- tokenSet . expires_in - 300 /* 5 minutes */ ,
144
- tokenSet . expires_in / 2
145
- ) * 1000
146
- ) ;
165
+ if ( expiresIn >= 60 /* 1 minute */ ) {
166
+ return Math . max ( expiresIn - 300 /* 5 minutes */ , expiresIn / 2 ) * 1000 ;
147
167
}
148
168
}
149
169
@@ -565,7 +585,10 @@ export class MongoDBOIDCPluginImpl implements MongoDBOIDCPlugin {
565
585
const refreshTokenId = getRefreshTokenId ( tokenSet . refresh_token ) ;
566
586
const updateId = updateIdCounter ++ ;
567
587
568
- const timerDuration = automaticRefreshTimeoutMS ( tokenSet ) ;
588
+ const timerDuration = automaticRefreshTimeoutMS (
589
+ tokenSet ,
590
+ this . options . passIdTokenAsAccessToken
591
+ ) ;
569
592
// Use `.call()` because in browsers, `setTimeout()` requires that it is called
570
593
// without a `this` value. `.unref()` is not available in browsers either.
571
594
if ( state . timer ) this . timers . clearTimeout . call ( null , state . timer ) ;
@@ -819,7 +842,13 @@ export class MongoDBOIDCPluginImpl implements MongoDBOIDCPlugin {
819
842
820
843
try {
821
844
get_tokens: {
822
- if ( ( state . currentTokenSet ?. set ?. expires_in ?? 0 ) > 5 * 60 ) {
845
+ if (
846
+ tokenExpiryInSeconds (
847
+ state . currentTokenSet ?. set ,
848
+ passIdTokenAsAccessToken
849
+ ) >
850
+ 5 * 60
851
+ ) {
823
852
this . logger . emit ( 'mongodb-oidc-plugin:skip-auth-attempt' , {
824
853
reason : 'not-expired' ,
825
854
} ) ;
0 commit comments