{"id":"ff712a03e0b3fa90","repo":"aio-libs/aiohttp","slug":"a-is-not-allowed-in-username-rfc-1945-section","errorCode":null,"errorMessage":"A \":\" is not allowed in username (RFC 1945#section-11.1)","messagePattern":"A \":\" is not allowed in username \\(RFC 1945#section-11\\.1\\)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"aiohttp/client_middleware_digest_auth.py","lineNumber":209,"sourceCode":"    The core digest calculation is inspired by the implementation in\n    https://github.com/requests/requests/blob/v2.18.4/requests/auth.py\n    with added support for modern digest auth features and error handling.\n    \"\"\"\n\n    def __init__(\n        self,\n        login: str,\n        password: str,\n        preemptive: bool = True,\n    ) -> None:\n        if login is None:\n            raise ValueError(\"None is not allowed as login value\")\n\n        if password is None:\n            raise ValueError(\"None is not allowed as password value\")\n\n        if \":\" in login:\n            raise ValueError('A \":\" is not allowed in username (RFC 1945#section-11.1)')\n\n        self._login_str: Final[str] = login\n        self._login_bytes: Final[bytes] = login.encode(\"utf-8\")\n        self._password_bytes: Final[bytes] = password.encode(\"utf-8\")\n\n        self._last_nonce_bytes = b\"\"\n        self._nonce_count = 0\n        self._challenge: DigestAuthChallenge = {}\n        self._preemptive: bool = preemptive\n        # Set of URLs defining the protection space\n        self._protection_space: list[str] = []\n        # Origin the credentials are scoped to; set on the first request.\n        self._origin: URL | None = None\n\n    async def _encode(self, method: str, url: URL, body: Payload | Literal[b\"\"]) -> str:\n        \"\"\"\n        Build digest authorization header for the current challenge.\n","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/client_middleware_digest_auth.py#L191-L227","documentation":"Raised in `DigestAuthMiddleware.__init__` (client_middleware_digest_auth.py:208-209) when `login` contains a colon (`:`). RFC 1945 §11.1 (referenced in the message) defines the `userid` grammar as `*<any CHAR except CTL and \":\">`, so a colon in the username is syntactically illegal in Basic/Digest auth and would corrupt the `username=\"...\"` field of the Authorization header. The check fails fast at construction.","triggerScenarios":"Passing an email-style or `domain\\user`-style login that contains a colon; usernames from external identity providers that include colons; copy-paste including a `user:pass` blob into the login field.","commonSituations":"Confusing `login` with a combined `user:password` string; email-as-username where the local-part has a colon; non-RFC-compliant identity systems.","solutions":["Strip the password portion if it was accidentally included: split on the first colon and pass only the username.","URL-encode or otherwise sanitize the username if the upstream truly uses colons (note: this may break server-side Digest validation).","Use a username without a colon, per RFC 1945."],"exampleFix":"// before\nmw = DigestAuthMiddleware(login='alice:s3cret', password='x')  # colon in login\n// after\nmw = DigestAuthMiddleware(login='alice', password='s3cret')","handlingStrategy":"validation","validationCode":"def sanitize_login(login: str) -> str:\n    if ':' in login:\n        raise ValueError('login must not contain \":\" (RFC 1945)')\n    return login","typeGuard":"def is_rfc1945_login(v) -> bool:\n    return isinstance(v, str) and ':' not in v","tryCatchPattern":null,"preventionTips":["Don't paste `user:pass` strings into the login field.","Validate usernames against the RFC 1945 grammar at the config boundary.","If upstream usernames contain colons, coordinate with the auth server on an encoding scheme."],"tags":["client","digest-auth","authentication","validation","rfc"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}