Fixes #28694: UTF-8-safe base64 encoding for the login password - #33162
Conversation
`btoa` maps every JS character to a single Latin-1 byte, so a password containing non-ASCII characters was base64-encoded as Latin-1 while the server decodes those bytes as UTF-8 — the reconstructed password did not match and basic/LDAP login was rejected. Characters above U+00FF (emoji, CJK) made `btoa` throw outright. Encode the UTF-8 bytes first. The fix lands in the shared `getBase64EncodedString` helper (previously an unused `btoa` wrapper with the same defect) and the login call site now routes through it, so any future base64 path gets the correct implementation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
❌ PR checklist incompleteThis PR cannot be merged until the following are addressed on its linked issue:
The fields live on the linked issue in the Shipping project (open the issue → right sidebar → Projects). After you set them, re-run this check (or push a commit) — issue/project changes do not re-trigger it automatically. Maintainers can bypass this check by adding the |
🔄 Playwright impact map auto-refreshedThis PR touched specs or UI source that changed the source→spec routing map. I regenerated What is this file? It is the auto-generated half of Playwright's PR planner. It routes "if source X changes, run specs Y" by walking spec imports and cross-referencing What if I want to regenerate locally instead? Run this before pushing your next change to skip the bot commit: python3 .github/scripts/generate_playwright_impact_map.py
git add .github/playwright/impact-map.generated.json
git commit --amend --no-edit # or a separate commit |
Code Review ✅ ApprovedFixes UTF-8 handling in the login password encoding by implementing a UTF-8-safe base64 encoder in the shared OptionsDisplay: compact → Counting what did not apply, without listing it. Comment with these commands to change the behavior for this request:
Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source |
|
🚦 Removed from the merge queue —
|



Describe your changes:
Fixes #28694
The UI base64-encodes the password before POSTing it to
/api/v1/auth/login, usingbtoa(password).btoamaps every JS character to a single Latin-1 byte, while the serverbase64-decodes and interprets the bytes as UTF-8 — so any non-ASCII character arrived as a
different byte sequence (
£→a3instead ofc2 a3), the reconstructed password did notmatch, and login was rejected. For LDAP the bind failed with error 49 /
data 52e. Charactersabove U+00FF (emoji, CJK) made
btoathrow outright, breaking login entirely.I encoded the UTF-8 bytes before base64. The fix lands in the shared
getBase64EncodedStringhelper inStringUtils.ts— previously an unusedbtoawrappercarrying the same defect — and the login call site now routes through it, so any future
client-side base64 path gets the correct implementation rather than raw
btoa.Scope notes from the investigation:
btoa(password)inBasicAuthProvider.tsxwas the only password path that base64-encodes.Registration, password reset, and change-password send the password as plaintext JSON (already
UTF-8 via axios), so they were never affected and need no change.
AuthProvider.tsxroutescase LDAP: case Basic:tothe same
BasicAuthProvider.new String(decodedBytes)on Java 21 is UTF-8 perJEP 400 (
BasicAuthServletHandler,LdapAuthServletHandler,UserResource#loginUserWithPassword).PasswordUtil(passay)and the UI
passwordRegex.I used
Array.from(...).join('')rather than theString.fromCharCode(...spread)form suggestedin the issue, because the spread blows the argument limit on long input and this is a shared util.
Type of change:
High-level design:
N/A — small change.
Tests:
Use cases covered
Test§123£,Pässwörd1!)can log in with the
basicprovider.密码2024,pw🔒key) can log in —previously
btoathrew and the login never left the browser.ldapprovider, which shares the authenticator.Unit tests
openmetadata-ui/src/main/resources/ui/src/utils/StringUtils.test.ts— byte-exact expectedbase64 for ASCII, Latin-1-range, CJK, emoji and empty input; a UTF-8 round-trip through
atob+TextDecoder; and an explicit assertion that the output is not whatbtoawouldproduce.
openmetadata-ui/src/main/resources/ui/src/components/Auth/AuthProviders/BasicAuthProvider.test.tsx(new) — asserts the exact
passwordvalue handed tobasicAuthSignIndecodes back to what theuser typed. This is the guard against someone reintroducing
btoaat the call site, which autil-level test alone would not catch.
btoa(text)fails 11 of them.
Backend integration tests
Ingestion integration tests
Playwright (UI) tests
openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Login.spec.ts— creates a userwith the password
Tëst§123£aA!via the API, then signs in through the UI and asserts thesession lands off
/signinwith the correct profile name. This is the only test that provesthe round trip end to end against a real server.
Manual testing performed
Checks run locally, on the changed files only:
The new Playwright spec has not been executed locally — I did not have a local stack up. It is
left for CI to run.
UI screen recording / screenshots:
Not applicable — no visual change; the fix is to the encoding of the login request payload. The
observable behaviour is verified by the Playwright spec and the payload assertion in the unit test.
Checklist:
I have read the CONTRIBUTING document.
My PR title is
Fixes <issue-number>: <short explanation>My PR is linked to a GitHub issue via
Fixes #<issue-number>above.I have commented on my code, particularly in hard-to-understand areas.
For JSON Schema changes: not applicable.
For UI changes: no screen recording — no visual change (explained above).
I have added tests (unit / integration / Playwright as applicable) and listed them above.
I have added a test that covers the exact scenario we are fixing. The issue number (Basic/LDAP login fails for passwords containing non-ASCII characters (
btoaLatin-1 encoding corrupts the password) #28694) iscommented in each of the three test files for future reference.
🤖 Generated with Claude Code