@@ -11,12 +11,15 @@ import { Command, CommandSignature, TypeMapping, CommanderConfig, RedisFunction,
1111import RedisClientMultiCommand , { RedisClientMultiCommandType } from './multi-command' ;
1212import { RedisMultiQueuedCommand } from '../multi-command' ;
1313import HELLO , { HelloOptions } from '../commands/HELLO' ;
14+ import { AuthOptions } from '../commands/AUTH' ;
1415import { ScanOptions , ScanCommonOptions } from '../commands/SCAN' ;
1516import { RedisLegacyClient , RedisLegacyClientType } from './legacy-mode' ;
1617import { RedisPoolOptions , RedisClientPool } from './pool' ;
1718import { RedisVariadicArgument , parseArgs , pushVariadicArguments } from '../commands/generic-transformers' ;
1819import { BasicCommandParser , CommandParser } from './parser' ;
1920
21+ export type RedisCredentialSupplier = ( ) => Promise < AuthOptions > ;
22+
2023export interface RedisClientOptions <
2124 M extends RedisModules = RedisModules ,
2225 F extends RedisFunctions = RedisFunctions ,
@@ -34,6 +37,10 @@ export interface RedisClientOptions<
3437 * Socket connection properties
3538 */
3639 socket ?: SocketOptions ;
40+ /**
41+ * Credential supplier callback function
42+ */
43+ credentialSupplier ?: RedisCredentialSupplier ;
3744 /**
3845 * ACL username ([see ACL guide](https://redis.io/topics/acl))
3946 */
@@ -276,6 +283,7 @@ export default class RedisClient<
276283 readonly #options?: RedisClientOptions < M , F , S , RESP , TYPE_MAPPING > ;
277284 readonly #socket: RedisSocket ;
278285 readonly #queue: RedisCommandsQueue ;
286+ #credentialSupplier?: RedisCredentialSupplier ;
279287 #selectedDB = 0 ;
280288 #monitorCallback?: MonitorCallback < TYPE_MAPPING > ;
281289 private _self = this ;
@@ -334,6 +342,13 @@ export default class RedisClient<
334342 this . _commandOptions = options . commandOptions ;
335343 }
336344
345+ // if a credential supplier has been provided, use it, otherwise create a provider from the
346+ // supplier username and password (if provided), otherwise leave it undefined
347+ this . #credentialSupplier = options ?. credentialSupplier ?? ( ( options ?. username || options ?. password ) ? ( ) => Promise . resolve ( {
348+ username : options ?. username ,
349+ password : options ?. password ?? '' ,
350+ } ) : undefined ) ;
351+
337352 return options ;
338353 }
339354
@@ -345,16 +360,16 @@ export default class RedisClient<
345360 ) ;
346361 }
347362
348- #handshake( selectedDB : number ) {
363+ #handshake( selectedDB : number , credential ?: AuthOptions ) {
349364 const commands = [ ] ;
350365
351366 if ( this . #options?. RESP ) {
352367 const hello : HelloOptions = { } ;
353368
354- if ( this . #options . password ) {
369+ if ( credential ? .password ) {
355370 hello . AUTH = {
356- username : this . #options . username ?? 'default' ,
357- password : this . #options . password
371+ username : credential ? .username ?? 'default' ,
372+ password : credential ? .password
358373 } ;
359374 }
360375
@@ -366,11 +381,11 @@ export default class RedisClient<
366381 parseArgs ( HELLO , this . #options. RESP , hello )
367382 ) ;
368383 } else {
369- if ( this . #options ?. username || this . #options ?. password ) {
384+ if ( credential ) {
370385 commands . push (
371386 parseArgs ( COMMANDS . AUTH , {
372- username : this . #options . username ,
373- password : this . #options . password ?? ''
387+ username : credential . username ,
388+ password : credential . password ?? ''
374389 } )
375390 ) ;
376391 }
@@ -396,7 +411,11 @@ export default class RedisClient<
396411 }
397412
398413 #initiateSocket( ) : RedisSocket {
399- const socketInitiator = ( ) => {
414+ const socketInitiator = async ( ) => {
415+ // we have to call the credential fetch before pushing any commands into the queue,
416+ // so fetch the credentials before doing anything else.
417+ const credential : AuthOptions | undefined = await this . #credentialSupplier?.( ) ;
418+
400419 const promises = [ ] ,
401420 chainId = Symbol ( 'Socket Initiator' ) ;
402421
@@ -418,7 +437,7 @@ export default class RedisClient<
418437 ) ;
419438 }
420439
421- const commands = this . #handshake( this . #selectedDB) ;
440+ const commands = this . #handshake( this . #selectedDB, credential ) ;
422441 for ( let i = commands . length - 1 ; i >= 0 ; -- i ) {
423442 promises . push (
424443 this . #queue. addCommand ( commands [ i ] , {
@@ -997,10 +1016,11 @@ export default class RedisClient<
9971016 * Reset the client to its default state (i.e. stop PubSub, stop monitoring, select default DB, etc.)
9981017 */
9991018 async reset ( ) {
1019+ const credential : AuthOptions | undefined = await this . #credentialSupplier?.( ) ;
10001020 const chainId = Symbol ( 'Reset Chain' ) ,
10011021 promises = [ this . _self . #queue. reset ( chainId ) ] ,
10021022 selectedDB = this . _self . #options?. database ?? 0 ;
1003- for ( const command of this . _self . #handshake( selectedDB ) ) {
1023+ for ( const command of this . _self . #handshake( selectedDB , credential ) ) {
10041024 promises . push (
10051025 this . _self . #queue. addCommand ( command , {
10061026 chainId
0 commit comments