@@ -11,11 +11,14 @@ 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 , pushVariadicArguments } from '../commands/generic-transformers' ;
1819
20+ export type RedisCredentialSupplier = ( ) => Promise < AuthOptions > ;
21+
1922export interface RedisClientOptions <
2023 M extends RedisModules = RedisModules ,
2124 F extends RedisFunctions = RedisFunctions ,
@@ -33,6 +36,10 @@ export interface RedisClientOptions<
3336 * Socket connection properties
3437 */
3538 socket ?: SocketOptions ;
39+ /**
40+ * Credential supplier callback function
41+ */
42+ credentialSupplier ?: RedisCredentialSupplier ;
3643 /**
3744 * ACL username ([see ACL guide](https://redis.io/topics/acl))
3845 */
@@ -289,6 +296,7 @@ export default class RedisClient<
289296 readonly #options?: RedisClientOptions < M , F , S , RESP , TYPE_MAPPING > ;
290297 readonly #socket: RedisSocket ;
291298 readonly #queue: RedisCommandsQueue ;
299+ #credentialSupplier?: RedisCredentialSupplier ;
292300 #selectedDB = 0 ;
293301 #monitorCallback?: MonitorCallback < TYPE_MAPPING > ;
294302 private _self = this ;
@@ -347,6 +355,13 @@ export default class RedisClient<
347355 this . _commandOptions = options . commandOptions ;
348356 }
349357
358+ // if a credential supplier has been provided, use it, otherwise create a provider from the
359+ // supplier username and password (if provided), otherwise leave it undefined
360+ this . #credentialSupplier = options ?. credentialSupplier ?? ( ( options ?. username || options ?. password ) ? ( ) => Promise . resolve ( {
361+ username : options ?. username ,
362+ password : options ?. password ?? '' ,
363+ } ) : undefined ) ;
364+
350365 return options ;
351366 }
352367
@@ -358,16 +373,16 @@ export default class RedisClient<
358373 ) ;
359374 }
360375
361- #handshake( selectedDB : number ) {
376+ #handshake( selectedDB : number , credential ?: AuthOptions ) {
362377 const commands = [ ] ;
363378
364379 if ( this . #options?. RESP ) {
365380 const hello : HelloOptions = { } ;
366381
367- if ( this . #options . password ) {
382+ if ( credential ? .password ) {
368383 hello . AUTH = {
369- username : this . #options . username ?? 'default' ,
370- password : this . #options . password
384+ username : credential ? .username ?? 'default' ,
385+ password : credential ? .password
371386 } ;
372387 }
373388
@@ -379,11 +394,11 @@ export default class RedisClient<
379394 HELLO . transformArguments ( this . #options. RESP , hello )
380395 ) ;
381396 } else {
382- if ( this . #options ?. username || this . #options ?. password ) {
397+ if ( credential ) {
383398 commands . push (
384399 COMMANDS . AUTH . transformArguments ( {
385- username : this . #options . username ,
386- password : this . #options . password ?? ''
400+ username : credential . username ,
401+ password : credential . password ?? ''
387402 } )
388403 ) ;
389404 }
@@ -409,7 +424,11 @@ export default class RedisClient<
409424 }
410425
411426 #initiateSocket( ) : RedisSocket {
412- const socketInitiator = ( ) => {
427+ const socketInitiator = async ( ) => {
428+ // we have to call the credential fetch before pushing any commands into the queue,
429+ // so fetch the credentials before doing anything else.
430+ const credential : AuthOptions | undefined = await this . #credentialSupplier?.( ) ;
431+
413432 const promises = [ ] ,
414433 chainId = Symbol ( 'Socket Initiator' ) ;
415434
@@ -431,7 +450,7 @@ export default class RedisClient<
431450 ) ;
432451 }
433452
434- const commands = this . #handshake( this . #selectedDB) ;
453+ const commands = this . #handshake( this . #selectedDB, credential ) ;
435454 for ( let i = commands . length - 1 ; i >= 0 ; -- i ) {
436455 promises . push (
437456 this . #queue. addCommand ( commands [ i ] , {
@@ -981,10 +1000,11 @@ export default class RedisClient<
9811000 * Reset the client to its default state (i.e. stop PubSub, stop monitoring, select default DB, etc.)
9821001 */
9831002 async reset ( ) {
1003+ const credential : AuthOptions | undefined = await this . #credentialSupplier?.( ) ;
9841004 const chainId = Symbol ( 'Reset Chain' ) ,
9851005 promises = [ this . _self . #queue. reset ( chainId ) ] ,
9861006 selectedDB = this . _self . #options?. database ?? 0 ;
987- for ( const command of this . _self . #handshake( selectedDB ) ) {
1007+ for ( const command of this . _self . #handshake( selectedDB , credential ) ) {
9881008 promises . push (
9891009 this . _self . #queue. addCommand ( command , {
9901010 chainId
0 commit comments