Skip to content

UI auth: refine session token handling - #36250

Merged
chirag-madlani merged 10 commits into
mainfrom
restrict-ui-to-user-login-tokens
Oct 28, 2026
Merged

chirag-madlani merged 10 commits into
mainfrom
restrict-ui-to-user-login-tokens

Conversation

@chirag-madlani

@chirag-madlani chirag-madlani commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

Describe your changes:

Refines how the UI handles session tokens. Interactive UI sessions now use tokens from the login flow, and API tokens (bot and personal access tokens) are meant for API clients only. REST API behaviour is unchanged.

  • SwTokenStorageUtils: isApiOnlyToken() classifies stored tokens. getOidcToken() clears the session for API-only tokens and notifies AuthProvider, which runs the standard sign-out.
  • New i18n key server.api-only-token-ui-access-denied, translated in all locales.
  • Decision record: ADR:2026-10-27-ui-runs-only-on-user-login-tokens.

Test updates

  • playwright/e2e/Flow/IngestionBot.spec.ts is replaced by DomainIsolationIT.test_ingestionBot_readsDomainAssetAndService, which checks the same domain access through the API.
  • The msal-mock SSO fixture signs in with a token from the mock IdP (new test-only /test/id-token endpoint in mock-oidc-provider), and its CI leg now starts that container.
  • Updated the eslint suppression baseline, the Playwright impact map and the generated docs for the removed spec.

Follow-ups: #36318, #36319

Test plan

  • SwTokenStorageUtils.test.ts and AuthProvider.test.tsx; auth suites pass (390)
  • DomainIsolationIT#test_ingestionBot_readsDomainAssetAndService passes locally
  • eslint, check-i18n, yarn build, bundle:check, bundle:runtime
  • SSO matrix (msal-mock, auth0-mock, basic, Keycloak legs)

🤖 Generated with Claude Code

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge, with no new actionable findings.

Summary

The UI rejects bot tokens and personal access tokens stored as session tokens. Rejection clears the stored session and asks AuthProvider to sign out.

  • The UI signs out sessions that use bot or personal access tokens.
  • The ingestion bot's domain access is checked through the API.
  • The MSAL mock login uses an ID token signed by the mock provider.

Previous-thread outcomes: translations are fixed, and rejection now calls the explicit sign-out path. chirag-madlani accepted the remaining in-flight refresh race and its real-coordinator test as follow-up work in #36319. Greptile withdrew the related test finding.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A["Read stored token"] --> B{"API-only token?"}
  B -->|No| C["Return token"]
  B -->|Yes| D["Clear stored session"]
  D --> E["Notify AuthProvider"]
  E --> F["Show translated message"]
  F --> G["Run standard sign-out"]
Loading

Reviews (9) · Last reviewed commit: "Tidy wording in the session-token decisi..." · Reviewed by Greptile

Bot, bot-impersonation and personal access tokens are meant for API
clients. getOidcToken now drops such a token (clearing the session and
showing a single toast), so a token injected into storage - on load or
mid-session - never reaches a request and the user lands on sign-in.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@chirag-madlani
chirag-madlani requested a review from a team as a code owner October 26, 2026 12:41
@chirag-madlani
chirag-madlani requested review from shah-harshit and a balanced review from Copilot October 26, 2026 12:41
@github-actions

Copy link
Copy Markdown
Contributor

❌ PR checklist incomplete

This PR cannot be merged until the following are addressed on its linked issue:

  • No GitHub issue is linked. Link an issue in the Development section of the PR (or add Fixes #12345 to the description). For a same-org cross-repo issue, add Fixes open-metadata/<repo>#123 to the description.

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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@github-actions github-actions Bot added safe to test Add this label to run secure Github workflows on PRs UI UI specific issues labels Oct 26, 2026
Comment thread openmetadata-ui/src/main/resources/ui/src/locale/languages/ar-sa.json Outdated
Comment thread openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json Outdated
- getOidcToken notifies a listener instead of toasting itself, keeping
  ToastUtils/i18n off the boot bundle (fixes bundle budget/runtime check)
- AuthProvider registers the listener: toast + full sign-out, which pauses
  the AuthCoordinator so a silent SSO renewal cannot restore the session
- Translate api-only-token-ui-access-denied in all locales
- Fix no-use-before-define on clearOidcToken

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@github-actions

github-actions Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 79%
79.75% (131291/164618) 66.27% (46313/69882) 69.22% (26280/37964)

IngestionBot.spec.ts drove the UI with the ingestion bot's JWT, which the
UI now rejects. DomainIsolationIT now checks the same guarantee at the
API, using the bot's stored token so isBot/tokenType claims are exercised:
the bot can read a domain-assigned table and its domain-assigned service.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 26, 2026 13:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@github-actions

github-actions Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

✅ SSO Login Nightly — PASSED

View workflow run · Commit c3b925e

Provider Status Pass Fail Flaky Skip Duration
basic ✅ pass 28 0 0 0 6m 39s
ldap ✅ pass 7 0 0 0 0m 49s
keycloak-oidc-confidential ✅ pass 12 0 0 0 1m 43s
keycloak-oidc-public ✅ pass 11 0 0 0 1m 29s
msal-mock ✅ pass 7 0 0 0 0m 43s
auth0-mock ✅ pass 6 0 0 0 0m 50s
okta ✅ pass 24 0 0 0 6m 27s
keycloak-azure-saml ✅ pass 23 0 0 0 6m 27s
keycloak-azure-saml-crosssite ⏩ skipped - - - - -

Totals: 118 passed · 0 failed · 0 flaky · 0 skipped · duration 25m 11s

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@chirag-madlani
chirag-madlani added this pull request to the merge queue Oct 27, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🚦 Removed from the merge queue — merge_conflict (2026-10-27T14:06:07Z)

The entry left the queue before it was built, so no checks ran against it.

Conflicts with the latest main — rebase, then re-queue.

Resolve the positional-locator baseline: main removed two, this branch
removes one (IngestionBot spec), so the count is 535.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ UI Checkstyle passed — lint findings in changed files

🔍 ESLint findings in this PR's files — 0 error(s), 8 warning(s)

Errors block the build. Warnings do not yet — they are rules whose backlog is still
being worked down, listed so this PR does not add to it. See docs/ui-code-quality-gate.md.

0 error(s), 8 warning(s) across 1 changed file(s).

Count Rule
8 react-hooks/exhaustive-deps
All findings
Location Rule Message
🟡 src/components/Auth/AuthProviders/AuthProvider.tsx:347:9 react-hooks/exhaustive-deps The 'onLoginHandler' function makes the dependencies of useMemo Hook (at line 1231) change on every render. Move it inside the useMemo callback. Alternatively,
🟡 src/components/Auth/AuthProviders/AuthProvider.tsx:470:6 react-hooks/exhaustive-deps React Hook useCallback has missing dependencies: 'navigate', 'setApplicationLoading', 'setCurrentUser', and 'setIsAuthenticated'. Either include them or remove
🟡 src/components/Auth/AuthProviders/AuthProvider.tsx:567:9 react-hooks/exhaustive-deps The 'resetUserDetails' function makes the dependencies of useMemo Hook (at line 1231) change on every render. To fix this, wrap the definition of 'resetUserDeta
🟡 src/components/Auth/AuthProviders/AuthProvider.tsx:813:6 react-hooks/exhaustive-deps React Hook useEffect has missing dependencies: 'handleRefreshFailed', 'handleStoreProtectedRedirectPath', 'setIsAuthenticated', and 'signOutAfterRefreshFailure'
🟡 src/components/Auth/AuthProviders/AuthProvider.tsx:815:9 react-hooks/exhaustive-deps The 'handleFailedLogin' function makes the dependencies of useMemo Hook (at line 1231) change on every render. Move it inside the useMemo callback. Alternativel
🟡 src/components/Auth/AuthProviders/AuthProvider.tsx:886:5 react-hooks/exhaustive-deps React Hook useCallback has missing dependencies: 'handledVerifiedUser', 'navigate', and 'resetUserDetails'. Either include them or remove the dependency array.
🟡 src/components/Auth/AuthProviders/AuthProvider.tsx:937:9 react-hooks/exhaustive-deps The 'initializeAxiosInterceptors' function makes the dependencies of useMemo Hook (at line 1231) change on every render. To fix this, wrap the definition of 'in
🟡 src/components/Auth/AuthProviders/AuthProvider.tsx:1220:6 react-hooks/exhaustive-deps React Hook useEffect has a missing dependency: 'fetchAuthConfig'. Either include it or remove the dependency array.

Fix locally (fast - only checks files changed in this branch):

make ui-checkstyle-changed

@github-actions

Copy link
Copy Markdown
Contributor

🚦 Removed from the merge queue — failed_checks (2026-10-27T18:56:04Z)

Blocked the queue: integration-tests-mysql-elasticsearch

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@gitar-bot

gitar-bot Bot commented Oct 28, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 2 closed / 2 findings

🔴 High risk · Rejecting bot and personal-access tokens during UI token reads changes authentication decisions and could sign out sessions relying on those tokens.

Restricts the UI to user login tokens by rejecting bot, bot-impersonation and PAT tokens in getOidcToken(), clearing the stored session and signing out with a toast. Resolved the untranslated error toast across non-English locales and added the missing decision record docs/decisions/2026-10-27-ui-runs-only-on-user-login-tokens.md. No open issues found.

✅ 2 closed
✅ Quality: New error toast left in English in all 20 non-English locales

📄 openmetadata-ui/src/main/resources/ui/src/locale/languages/ar-sa.json:6270 📄 openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json:6270 📄 openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json:6270 📄 openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json:6270 📄 openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json:6270 📄 openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json:6270
The new server.api-only-token-ui-access-denied key was copied into every non-English catalog with the English text unchanged (ar-sa, de-de, es-es, fr-fr, ja-jp, zh-cn, and the others, all at line 6270). This looks like yarn i18n output that was never translated. The project's i18n rules say these copies must be replaced with real translations, and English is only acceptable as a deliberate choice. As it stands, users in every other locale get an English sign-out toast. Please translate the string in each locale file.

✅ Quality: UI-only-accepts-login-tokens rule has no decision record

📄 openmetadata-ui/src/main/resources/ui/src/utils/SwTokenStorageUtils.ts:190-204 📄 openmetadata-ui/src/main/resources/ui/src/utils/SwTokenStorageUtils.ts:233-247 📄 openmetadata-ui/src/main/resources/ui/src/components/Auth/AuthProviders/AuthProvider.tsx:475-484
This PR adds a security rule that crosses components: the UI decides whether a session is allowed from the server-issued JWT claims isBot and tokenType (BOT / PERSONAL_ACCESS), while those same tokens stay valid against the REST API. The PR also deletes the IngestionBot UI spec because of this rule. Nothing in docs/decisions/ records it, and the record closest in subject (2026-10-26-impersonated-requests-never-reach-credentials-or-grant-privileges) covers something else. The repository squash-merges, so the description's statements ("Bot tokens and PATs continue to work against the REST APIs unchanged; only UI access is restricted", bot-impersonation tokens with tokenType BOT, isBot false are rejected too, and "OM_USER tokens, SSO provider tokens without a tokenType, and non-JWT tokens are still allowed") will not survive anywhere. If the server later renames these claims or adds a token type, the UI check would quietly stop matching. Add a record stating roughly: "The UI runs only on user-login tokens. getOidcToken drops any stored token with isBot=true or tokenType BOT or PERSONAL_ACCESS, clears the session and signs out. OM_USER, SSO-provider and opaque tokens are allowed. Bot and PAT tokens keep working against the REST API." Name isApiOnlyToken and the SwTokenStorageUtils tests as its guard, and cite it from the code as ADR:<date>-<slug>.

Verification

Rules No rules evaluated

Functional validation Not enabled

Set up

Auto-approval Not enabled

Set up

For more verification details, comment gitar display:verbose.

Options

Display: compact → Showing compact verification results.

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

This branch was successfully deployed

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

Labels

safe to test Add this label to run secure Github workflows on PRs UI UI specific issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants