Skip to content

Fixes #28694: UTF-8-safe base64 encoding for the login password - #33162

Merged
ShaileshParmar11 merged 3 commits into
mainfrom
fix/28694-non-ascii-password-login
Sep 11, 2026
Merged

Fixes #28694: UTF-8-safe base64 encoding for the login password#33162
ShaileshParmar11 merged 3 commits into
mainfrom
fix/28694-non-ascii-password-login

Conversation

@mohityadav766

Copy link
Copy Markdown
Member

Describe your changes:

Fixes #28694

The UI base64-encodes the password before POSTing it to /api/v1/auth/login, using
btoa(password). btoa maps every JS character to a single Latin-1 byte, while the server
base64-decodes and interprets the bytes as UTF-8 — so any non-ASCII character arrived as a
different byte sequence (£a3 instead of c2 a3), the reconstructed password did not
match, and login was rejected. For LDAP the bind failed with error 49 / data 52e. Characters
above U+00FF (emoji, CJK) made btoa throw outright, breaking login entirely.

I encoded the UTF-8 bytes before base64. The fix lands in the shared
getBase64EncodedString helper in StringUtils.ts — previously an unused btoa wrapper
carrying 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) in BasicAuthProvider.tsx was 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.
  • One frontend fix covers both providers: AuthProvider.tsx routes case LDAP: case Basic: to
    the same BasicAuthProvider.
  • The server side is correct and unchanged — new String(decodedBytes) on Java 21 is UTF-8 per
    JEP 400 (BasicAuthServletHandler, LdapAuthServletHandler, UserResource#loginUserWithPassword).
  • Non-ASCII passwords are settable: they satisfy both the server rules in PasswordUtil (passay)
    and the UI passwordRegex.

I used Array.from(...).join('') rather than the String.fromCharCode(...spread) form suggested
in the issue, because the spread blows the argument limit on long input and this is a shared util.

Type of change:

  • Bug fix

High-level design:

N/A — small change.

Tests:

Use cases covered

  • A user whose password contains Latin-1-range non-ASCII characters (Test§123£, Pässwörd1!)
    can log in with the basic provider.
  • A user whose password contains characters above U+00FF (密码2024, pw🔒key) can log in —
    previously btoa threw and the login never left the browser.
  • The same fix covers the ldap provider, which shares the authenticator.
  • Plain-ASCII passwords keep encoding byte-for-byte identically (no regression for existing users).

Unit tests

  • I added unit tests for the new/changed logic.
  • Files added/updated:
    • openmetadata-ui/src/main/resources/ui/src/utils/StringUtils.test.ts — byte-exact expected
      base64 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 what btoa would
      produce.
    • openmetadata-ui/src/main/resources/ui/src/components/Auth/AuthProviders/BasicAuthProvider.test.tsx
      (new) — asserts the exact password value handed to basicAuthSignIn decodes back to what the
      user typed. This is the guard against someone reintroducing btoa at the call site, which a
      util-level test alone would not catch.
  • Verified as a real RED→GREEN: 106 tests pass with the fix; reverting the helper to btoa(text)
    fails 11 of them.

Backend integration tests

  • Not applicable (no backend API changes).

Ingestion integration tests

  • Not applicable (no ingestion changes).

Playwright (UI) tests

  • I added Playwright E2E tests for UI changes.
  • Files added/updated:
    • openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Login.spec.ts — creates a user
      with the password Tëst§123£aA! via the API, then signs in through the UI and asserts the
      session lands off /signin with the correct profile name. This is the only test that proves
      the round trip end to end against a real server.

Manual testing performed

Checks run locally, on the changed files only:

yarn test src/utils/StringUtils.test.ts src/components/Auth/AuthProviders/BasicAuthProvider.test.tsx
  → 2 suites, 106 tests passed
yarn test src/components/Auth
  → 9 suites, 79 tests passed
yarn license-header-check     → All files have licenses
yarn lint:playwright          → 0 errors (no new warnings in Login.spec.ts)
prettier --check + organize-imports + eslint --fix on all 5 changed files → clean
tsc --noEmit -p playwright/tsconfig.json → clean

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 (btoa Latin-1 encoding corrupts the password) #28694) is
    commented in each of the three test files for future reference.

🤖 Generated with Claude Code

`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>
@github-actions

Copy link
Copy Markdown
Contributor

❌ PR checklist incomplete

This 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 skip-pr-checks label.

@github-actions github-actions Bot added backend safe to test Add this label to run secure Github workflows on PRs labels Sep 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🔄 Playwright impact map auto-refreshed

This PR touched specs or UI source that changed the source→spec routing map. I regenerated .github/playwright/impact-map.generated.json and pushed the diff to this branch.

- source entries: 759 → 759
- 0 added, 0 removed, 6 changed spec-list

Entries whose spec list changed:
  openmetadata-ui/src/main/resources/ui/playwright/support/entity/TableClass.ts
  openmetadata-ui/src/main/resources/ui/playwright/support/fixtures/base.ts
  openmetadata-ui/src/main/resources/ui/playwright/utils/common.ts
  openmetadata-ui/src/main/resources/ui/playwright/utils/domain.ts
  openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts
  openmetadata-ui/src/main/resources/ui/playwright/utils/tier.ts

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 getByTestId strings. Hand-authored routing in impact-map.json always wins on conflict.

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

@github-actions

github-actions Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 70%
70.93% (96333/135813) 55.6% (57209/102888) 56.95% (19069/33480)

@gitar-bot

gitar-bot Bot commented Sep 11, 2026

Copy link
Copy Markdown
Code Review ✅ Approved

Fixes UTF-8 handling in the login password encoding by implementing a UTF-8-safe base64 encoder in the shared getBase64EncodedString helper. Non-ASCII characters (Latin-1 range, CJK, emoji) now encode correctly, and the fix covers both basic and LDAP authentication. Comprehensive unit tests verify byte-exact encoding for ASCII, Latin-1, CJK, and emoji inputs, plus an end-to-end Playwright spec confirms the round trip against a real server. No issues found.

Options

Display: compact → Counting what did not apply, without listing it.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source

@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown
Contributor

🚦 Removed from the merge queue — failed_checks (2026-09-11T07:42:55Z)

Blocked the queue: playwright-summary

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend safe to test Add this label to run secure Github workflows on PRs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Basic/LDAP login fails for passwords containing non-ASCII characters (btoa Latin-1 encoding corrupts the password)

3 participants