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: 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