Skip to content

fix(server): decode Basic auth as UTF-8 and split on the first colon - #3290

Open
arshilkxwork wants to merge 2 commits into
apache:masterfrom
arshilkxwork:fix/basic-auth-utf8-colon
Open

arshilkxwork wants to merge 2 commits into
apache:masterfrom
arshilkxwork:fix/basic-auth-utf8-colon

Conversation

@arshilkxwork

@arshilkxwork arshilkxwork commented Oct 7, 2026 •

Copy link
Copy Markdown

Purpose of the PR

AuthenticationFilter decoded the Basic credential as US-ASCII and split it on every colon. The user API accepts passwords with non-ASCII characters or a colon, so once an account had such a password it could no longer use Basic auth: the non-ASCII case got 401 and the colon case got 400. The admin account is affected too.

Main Changes

The filter now decodes the credential as UTF-8 (RFC 7617 section 2.1) and splits it at the first colon. Everything after that colon is the password. A credential with no colon, an empty user-id or an empty password still gets 400. The grizzly Charsets import is replaced with StandardCharsets, and the TODO that pointed at this issue is gone.

CypherAPI.toUserPass parses the header again for the Cypher endpoint. It already decoded UTF-8 but split on every colon, so a colon password would have passed the filter and then failed there. It now splits once as well, which matches Gremlin's WsAndHttpBasicAuthHandler (split(":", 2)).

One edge case changes: a credential with a trailing colon used to lose it, because String.split drops trailing empty strings. user:pass: authenticated with the password pass and now tries pass:, which gets 401 unless that is the real password. user:: goes from 400 to 401.

Non-ASCII passwords work for clients that encode the header as UTF-8, such as curl and browsers. Clients that encode ISO-8859-1 still get 401 for them, as before. That includes Jersey's HttpAuthenticationFeature and OkHttp's Credentials.basic default, which OkHttpBasicAuthInterceptor in hugegraph-commons uses. Changing those clients is left for a separate PR.

Following the discussion on the issue, two things stay out of this PR:

Verifying these changes

  • Need tests and can be verified as follows:
    • New LoginApiTest#testBasicAuthWithNonAsciiOrColonPassword creates users with the passwords ädminpass1 and new:pass1234, sends GET graphspaces/DEFAULT/graphs with a UTF-8 Basic header, and expects 200 for the right password and 401 for a wrong one.
    • Run locally against a RocksDB server configured as in run-api-test.sh: LoginApiTest, UserApiTest and CypherApiTest pass (20 tests). With the old filter the new test fails with 401 on ädminpass1.
    • The Cypher endpoint was checked by hand: a user with the password a:b:c gets 200 from GET .../graphs/hugegraph/cypher.
    • Checked by hand with curl after the change: admin:pa gets 200, admin:wrong 401, and admin:, admin and :pa 400, the same as before.
    • The full api-test suite was not run locally.

Does this PR potentially affect the following parts?

Documentation Status

  • Doc - TODO: required documentation is pending; complete it before merging.
  • Doc - Done: documentation is included here or linked below.
  • Doc - No Need: no user-visible documentation is affected.

The ASCII-decoding text in the chart (values.schema.json and the README Validation section) describes the published image the chart deploys. It should change together with the guard once a release ships this fix.

- Decode the Basic credential as UTF-8 (RFC 7617 section 2.1). US-ASCII
  turned every non-ASCII byte into U+FFFD, so a password such as
  "ädminpass1" never matched the stored hash and the request got 401.
- Split at the first colon. RFC 7617 bars a colon from the user-id only,
  and split(":") answered 400 for a password such as "new:pass1234".
- A credential with no colon, an empty user-id or an empty password
  still gets 400.
- Add a LoginApiTest case covering both passwords.
- CypherAPI.toUserPass still split the decoded credential on every
  colon and returned null for anything but two parts, so a password
  with ':' passed AuthenticationFilter and then failed on the cypher
  endpoint. It now splits once, like the filter and Gremlin's handler.
- Reword the filter comment: RFC 7617 allows UTF-8 as the only declared
  charset and does not define a default.
- Point the Helm wrapper comment at apache#3284; the TODO it named is gone.
- Parse the created user with TypeReference like the other tests in
  LoginApiTest, and say why the Basic header is built by hand.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Basic auth decodes the credential as ASCII and splits on every colon: a non-ASCII password answers 401, a password with ':' answers 400

1 participant