From 745c28a006b850b464d80f0f30304bd38a10079e Mon Sep 17 00:00:00 2001 From: mauricioguim Date: Fri, 2 May 2025 14:44:10 -0300 Subject: [PATCH 1/2] Add username and password authentication to the Redis connection --- src/Io/Factory.php | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/Io/Factory.php b/src/Io/Factory.php index 5a3d692..6602dca 100644 --- a/src/Io/Factory.php +++ b/src/Io/Factory.php @@ -96,9 +96,23 @@ public function createClient(string $uri): PromiseInterface // use `?password=secret` query or `user:secret@host` password form URL if (isset($args['password']) || isset($parts['pass'])) { $pass = $args['password'] ?? rawurldecode($parts['pass']); // @phpstan-ignore-line + + $user = null; + if (isset($args['username']) || isset($parts['user'])) { + $user = $args['username'] ?? rawurldecode($parts['user']); // @phpstan-ignore-line + } + \assert(\is_string($pass)); - $promise = $promise->then(function (StreamingClient $redis) use ($pass, $uri) { - return $redis->callAsync('auth', $pass)->then( + \assert($user === null || \is_string($user)); + + $promise = $promise->then(function (StreamingClient $redis) use ($user, $pass, $uri) { + if ($user !== null) { + $authPromise = $redis->callAsync('auth', $user, $pass); + } else { + $authPromise = $redis->callAsync('auth', $pass); + } + + return $authPromise->then( function () use ($redis) { return $redis; }, @@ -211,4 +225,4 @@ function (\Throwable $e) use ($redis, $uri) { // variable assignment needed for legacy PHPStan on PHP 7.1 only return $ret; } -} +} \ No newline at end of file From e219502b168c3cc24901f0e249c581838bb4ed05 Mon Sep 17 00:00:00 2001 From: mauricioguim Date: Fri, 2 May 2025 15:03:17 -0300 Subject: [PATCH 2/2] Update README to support username+password authentication for Redis connections (Redis 6+ ACL) --- README.md | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 94416ff..45ce55c 100644 --- a/README.md +++ b/README.md @@ -336,9 +336,17 @@ $redis = new Clue\React\Redis\RedisClient('localhost'); $redis = new Clue\React\Redis\RedisClient('redis://localhost:6379'); ``` -Redis supports password-based authentication (`AUTH` command). Note that Redis' -authentication mechanism does not employ a username, so you can pass the -password `h@llo` URL-encoded (percent-encoded) as part of the URI like this: +Starting with Redis 6, you can use ACLs and authenticate with both a +username and a password. Any URI containing user:pass@ will now invoke +the AUTH command under the hood. For example: + +```php +// Authenticate with username and password (Redis 6+ ACL) +$redis = new Clue\React\Redis\RedisClient('redis://mauricio:mypass@localhost:6379'); +``` + +If you omit the username (or use an empty userinfo part), the client will fall back +to the legacy AUTH behavior: ```php // all forms are equivalent @@ -489,7 +497,15 @@ that eventually *fulfills* with its *results* on success or *rejects* with an #### callAsync() The `callAsync(string $command, string|int|float ...$args): PromiseInterface` method can be used to -invoke a Redis command. +invoke a Redis command. Note that the `AUTH` command now supports **two** arguments for Redis 6+ ACL mode: + +```php +// authenticate with username + password (Redis 6+) +$redis->callAsync('AUTH', 'alice', 'hunter2'); + +// legacy password-only mode +$redis->callAsync('AUTH', 'hunter2'); +``` For example, the [`GET` command](https://redis.io/commands/get) can be invoked like this: