Skip to content

Support Redis ACL users: connection.username #48

Closed

Description

@AlexeyShalaev

Problem

RedisConnectionSettings has password but no username, so a client built by redis-client-kit always authenticates as the default user. That rules out Redis ACL users (Redis 6+), which are the standard way to share one Redis between several applications safely.

Our case: several products share one Redis. Each product should get its own ACL user restricted to its key prefix and channels, for example:

ACL SETUSER bertie on >*** resetkeys ~bertie:* resetchannels &bertie:* -@all +@read +@write ...

With only password, every product has to log in as default with the shared password. Then any of them can read, overwrite or FLUSHDB the others' keys.

redis-py already supports this: Redis(...), ConnectionPool(...), the asyncio client and RedisCluster(...) all accept username= next to password=. Only the settings and the kwargs builder are missing.

What we would like to see

  1. RedisConnectionSettings.username: str | None = None (description: "Redis ACL user name (optional; default when unset)"). It is set via the usual nested env var, e.g. REDIS__CONNECTION__USERNAME.
  2. build_base_redis_kwargs passes "username": settings.connection.username next to "password". Both the sync and the asyncio paths and cluster mode get it.
  3. Logging and masking stay as they are: password is still masked; username is not secret, so it may appear in masked kwargs or connection info.
  4. Tests:
    • kwargs contain the username when it is set, and None or no key when it is not (same behaviour as today for the default user);
    • the settings read REDIS__CONNECTION__USERNAME from the environment;
    • an integration test, if the suite has a real Redis: ACL SETUSER a user limited to ~app:*, connect with username/password, then SET app:x succeeds and GET other:x raises NoPermissionError (NOPERM).
  5. Docs (README / settings reference) and a CHANGELOG entry. This is additive and backwards compatible, so a patch or minor release fits (0.3.1 or 0.4.0).

Out of scope

Credential providers and rotating passwords. A plain username + password pair is enough.

Activity

  1. AlexeyShalaev commented on Oct 9, 2026

    @AlexeyShalaev
    MemberAuthor

    Taking this one. I reproduced it on master first against redis:7-alpine: with an ACL user's password in connection.password, RESP2 fails with "AUTH called without any password configured for the default user", and RESP3 is worse — redis-py sends HELLO 3 AUTH default <password>, a nopass default user accepts it, and the client gets full access, FLUSHDB included.

    Plan:

    • RedisConnectionSettings.username: str | None = None, described as in the issue.
    • build_base_redis_kwargs passes username next to password, so single, cluster, sync and asyncio all get it. redis-py's RedisCluster takes it through **kwargs (it is in REDIS_ALLOWED_KEYS).
    • One deviation from the letter of point 2: the kwargs builder reads it as getattr(settings.connection, "username", None) and RedisConnectionProtocol does not gain a required member, only a docstring line. The configuration guide shows hand-written settings objects implementing the protocols, and the docs promise that every attribute the protocols name is read; adding username there would make those objects fail with AttributeError (and mypy) on upgrade, which is not the backwards-compatible change this asks for.
    • Masking stays as it is; a test pins that username survives mask_redis_kwargs.
    • Tests: kwargs with and without a username, a protocol object with no username attribute, cluster kwargs on both sides, settings read from REDIS__CONNECTION__USERNAME, and integration tests on a real Redis: an ACL user limited to ~app:* can SET app:x, gets NoPermissionError on GET other:x and on FLUSHDB, and passes the kit's own health check and write probe.
    • Docs: the configuration guide gets an "ACL users" section with a rule that works with the kit. The rule in the issue is not enough for it: the health check needs +ping, client_name needs +client|setname, db != 0 needs +select, and +@write still allows FLUSHDB until -@dangerous takes it away. I checked each of those on a live server. docs/agents.md gets the new field.
    • CHANGELOG: release-please writes it from the feat: squash title, so no hand edit; this lands as 0.4.0.
  2. AlexeyShalaev commented on Oct 9, 2026

    @AlexeyShalaev
    MemberAuthor

    PR is up: #49. It follows the plan above. One thing to decide when you review: username is read with getattr and is not a member of RedisConnectionProtocol, so hand-written settings objects keep working. If you would rather have it in the protocol and ship this as a breaking change, it's two lines. CI is green, including the ACL integration tests on RESP2 and RESP3.

  3. added a commit that references this issue on Oct 9, 2026
    53b09e5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions