@@ -56,13 +56,15 @@ function requestToken(
56
56
plugin : MongoDBOIDCPlugin ,
57
57
oidcParams : IdPServerInfo ,
58
58
abortSignal ?: OIDCAbortSignal ,
59
- username ?: string
59
+ username ?: string ,
60
+ refreshToken ?: string
60
61
) : ReturnType < OIDCCallbackFunction > {
61
62
return plugin . mongoClientOptions . authMechanismProperties . OIDC_HUMAN_CALLBACK ( {
62
63
timeoutContext : abortSignal ,
63
64
version : 1 ,
64
65
idpInfo : oidcParams ,
65
66
username,
67
+ refreshToken,
66
68
} ) ;
67
69
}
68
70
@@ -390,6 +392,7 @@ describe('OIDC plugin (local OIDC provider)', function () {
390
392
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
391
393
expect ( Object . keys ( serializedData . state [ 0 ] [ 1 ] ) . sort ( ) ) . to . deep . equal ( [
392
394
'currentTokenSet' ,
395
+ 'discardingTokenSets' ,
393
396
'lastIdTokenClaims' ,
394
397
'serverOIDCMetadata' ,
395
398
] ) ;
@@ -905,24 +908,77 @@ describe('OIDC plugin (local OIDC provider)', function () {
905
908
906
909
describe ( 'automaticRefreshTimeoutMS' , function ( ) {
907
910
it ( 'returns the correct automatic refresh timeout' , function ( ) {
911
+ const nowS = Date . now ( ) / 1000 ;
912
+ const nowMS = nowS * 1000 ;
908
913
expect ( automaticRefreshTimeoutMS ( { } ) ) . to . equal ( undefined ) ;
909
- expect ( automaticRefreshTimeoutMS ( { expires_in : 10000 } ) ) . to . equal (
914
+ expect ( automaticRefreshTimeoutMS ( { expires_at : nowS + 10000 } ) ) . to . equal (
910
915
undefined
911
916
) ;
912
917
expect (
913
- automaticRefreshTimeoutMS ( { refresh_token : 'asdf' , expires_in : 10000 } )
918
+ automaticRefreshTimeoutMS (
919
+ {
920
+ refresh_token : 'asdf' ,
921
+ expires_at : nowS + 10000 ,
922
+ } ,
923
+ undefined ,
924
+ nowMS
925
+ )
914
926
) . to . equal ( 9700000 ) ;
915
927
expect (
916
- automaticRefreshTimeoutMS ( { refresh_token : 'asdf' , expires_in : 100 } )
928
+ automaticRefreshTimeoutMS (
929
+ {
930
+ refresh_token : 'asdf' ,
931
+ expires_at : nowS + 100 ,
932
+ } ,
933
+ undefined ,
934
+ nowMS
935
+ )
917
936
) . to . equal ( 50000 ) ;
918
937
expect (
919
- automaticRefreshTimeoutMS ( { refresh_token : 'asdf' , expires_in : 10 } )
938
+ automaticRefreshTimeoutMS (
939
+ {
940
+ refresh_token : 'asdf' ,
941
+ expires_at : nowS + 100 ,
942
+ id_token : '...' ,
943
+ claims ( ) {
944
+ return { exp : nowS + 500 } ;
945
+ } ,
946
+ } ,
947
+ true ,
948
+ nowMS
949
+ )
950
+ ) . to . equal ( 250000 ) ;
951
+ expect (
952
+ automaticRefreshTimeoutMS (
953
+ {
954
+ refresh_token : 'asdf' ,
955
+ expires_at : nowS + 100 ,
956
+ id_token : '...' ,
957
+ claims ( ) {
958
+ return { exp : nowS + 500 } ;
959
+ } ,
960
+ } ,
961
+ false ,
962
+ nowMS
963
+ )
964
+ ) . to . equal ( 50000 ) ;
965
+ expect (
966
+ automaticRefreshTimeoutMS ( {
967
+ refresh_token : 'asdf' ,
968
+ expires_at : nowS + 10 ,
969
+ } )
920
970
) . to . equal ( undefined ) ;
921
971
expect (
922
- automaticRefreshTimeoutMS ( { refresh_token : 'asdf' , expires_in : 0 } )
972
+ automaticRefreshTimeoutMS ( {
973
+ refresh_token : 'asdf' ,
974
+ expires_at : nowS + 0 ,
975
+ } )
923
976
) . to . equal ( undefined ) ;
924
977
expect (
925
- automaticRefreshTimeoutMS ( { refresh_token : 'asdf' , expires_in : - 10 } )
978
+ automaticRefreshTimeoutMS ( {
979
+ refresh_token : 'asdf' ,
980
+ expires_at : nowS + - 10 ,
981
+ } )
926
982
) . to . equal ( undefined ) ;
927
983
} ) ;
928
984
} ) ;
@@ -1235,6 +1291,93 @@ describe('OIDC plugin (mock OIDC provider)', function () {
1235
1291
} ) ;
1236
1292
} ) ;
1237
1293
1294
+ context ( 'when drivers re-request tokens early' , function ( ) {
1295
+ let plugin : MongoDBOIDCPlugin ;
1296
+
1297
+ beforeEach ( function ( ) {
1298
+ plugin = createMongoDBOIDCPlugin ( {
1299
+ openBrowserTimeout : 60_000 ,
1300
+ openBrowser : fetchBrowser ,
1301
+ allowedFlows : [ 'auth-code' ] ,
1302
+ redirectURI : 'http://localhost:0/callback' ,
1303
+ } ) ;
1304
+ } ) ;
1305
+
1306
+ it ( 'will return a different token even if the existing one is not yet expired' , async function ( ) {
1307
+ const result1 = await requestToken ( plugin , {
1308
+ issuer : provider . issuer ,
1309
+ clientId : 'mockclientid' ,
1310
+ requestScopes : [ ] ,
1311
+ } ) ;
1312
+ const result2 = await requestToken ( plugin , {
1313
+ issuer : provider . issuer ,
1314
+ clientId : 'mockclientid' ,
1315
+ requestScopes : [ ] ,
1316
+ } ) ;
1317
+ const result3 = await requestToken (
1318
+ plugin ,
1319
+ {
1320
+ issuer : provider . issuer ,
1321
+ clientId : 'mockclientid' ,
1322
+ requestScopes : [ ] ,
1323
+ } ,
1324
+ undefined ,
1325
+ undefined ,
1326
+ result2 . refreshToken
1327
+ ) ;
1328
+ const result4 = await requestToken (
1329
+ plugin ,
1330
+ {
1331
+ issuer : provider . issuer ,
1332
+ clientId : 'mockclientid' ,
1333
+ requestScopes : [ ] ,
1334
+ } ,
1335
+ undefined ,
1336
+ undefined ,
1337
+ result2 . refreshToken
1338
+ ) ;
1339
+ expect ( result1 ) . to . deep . equal ( result2 ) ;
1340
+ expect ( result2 . accessToken ) . not . to . equal ( result3 . accessToken ) ;
1341
+ expect ( result2 . refreshToken ) . not . to . equal ( result3 . refreshToken ) ;
1342
+ expect ( result3 ) . to . deep . equal ( result4 ) ;
1343
+ } ) ;
1344
+
1345
+ it ( 'will return only one new token per expired token even when called in parallel' , async function ( ) {
1346
+ const result1 = await requestToken ( plugin , {
1347
+ issuer : provider . issuer ,
1348
+ clientId : 'mockclientid' ,
1349
+ requestScopes : [ ] ,
1350
+ } ) ;
1351
+ const [ result2 , result3 ] = await Promise . all ( [
1352
+ requestToken (
1353
+ plugin ,
1354
+ {
1355
+ issuer : provider . issuer ,
1356
+ clientId : 'mockclientid' ,
1357
+ requestScopes : [ ] ,
1358
+ } ,
1359
+ undefined ,
1360
+ undefined ,
1361
+ result1 . refreshToken
1362
+ ) ,
1363
+ requestToken (
1364
+ plugin ,
1365
+ {
1366
+ issuer : provider . issuer ,
1367
+ clientId : 'mockclientid' ,
1368
+ requestScopes : [ ] ,
1369
+ } ,
1370
+ undefined ,
1371
+ undefined ,
1372
+ result1 . refreshToken
1373
+ ) ,
1374
+ ] ) ;
1375
+ expect ( result1 . accessToken ) . not . to . equal ( result2 . accessToken ) ;
1376
+ expect ( result1 . refreshToken ) . not . to . equal ( result2 . refreshToken ) ;
1377
+ expect ( result2 ) . to . deep . equal ( result3 ) ;
1378
+ } ) ;
1379
+ } ) ;
1380
+
1238
1381
context ( 'HTTP request tracking' , function ( ) {
1239
1382
it ( 'will log all outgoing HTTP requests' , async function ( ) {
1240
1383
const pluginHttpRequests : string [ ] = [ ] ;
0 commit comments