Skip to content

MSAL Token Renewal Fix — Safari Session Loss#27214

Merged
harshach merged 13 commits intomainfrom
msal_refresh_token
Apr 10, 2026
Merged

MSAL Token Renewal Fix — Safari Session Loss#27214
harshach merged 13 commits intomainfrom
msal_refresh_token

Conversation

@harshach
Copy link
Copy Markdown
Collaborator

@harshach harshach commented Apr 9, 2026

Summary

acquireTokenSilent was called without forceRefresh, so MSAL returned cached tokens from its internal cache without making a network call. The "renewed" idToken had the same expiry as the
original — the renewal was a no-op. The token expired immediately, triggered a 401, and the user was force-logged out.

On Safari this was worse: when MSAL eventually needed a network call (refresh token expired), it used a hidden iframe to Azure AD. Safari's ITP blocked the third-party cookies, throwing
InteractionRequiredAuthError. The existing loginPopup fallback (added in #27189) was also blocked by Safari since popups require a direct user gesture — a timer-based renewal is not one.

Additionally, TokenServiceUtil.fetchNewToken had a bug where a 'Frame window timed out' error left the refreshInProgress flag permanently set in localStorage, blocking all future renewal
attempts.

Changes

MsalAuthenticator.tsx

  • Added forceRefresh: true to acquireTokenSilent during renewal — forces MSAL to use the refresh token over the network instead of returning stale cached tokens
  • Replaced loginPopup fallback with acquireTokenPopup → acquireTokenRedirect chain — redirect is a first-party navigation that Safari's ITP won't block

TokenServiceUtil.ts

  • clearRefreshInProgress() is now always called in the finally block, preventing a single failed renewal from permanently blocking all future attempts

MsalAuthenticator.test.tsx

  • Updated tests to verify forceRefresh: true is passed during renewal
  • Updated fallback tests to cover acquireTokenPopup → acquireTokenRedirect chain
  • Removed unused mock imports

Playwright Tests added

SSO Authentication E2E Tests

OIDC Discovery

  • mock OIDC provider serves valid discovery document — Discovery endpoint returns valid OIDC config with all required fields (issuer, auth/token/jwks endpoints, supported scopes)
  • mock OIDC provider serves JWKS endpoint — JWKS endpoint returns valid signing keys with RSA/RS256

Test Control API

  • should configure token expiry — Mock OIDC /test/configure API correctly updates token TTL
  • should force interaction required — /test/force-interaction-required sets the one-shot flag
  • should reset to defaults — /test/reset restores default state (3600s TTL, refresh enabled)

OIDC Login Flow

  • should show SSO login button and authenticate on click — SSO login button appears, clicking it completes the OIDC flow and lands on the authenticated app
  • should receive valid tokens after login — After login, a valid JWT with correct email and sub claims exists in IndexedDB
  • should show user info after authentication — The authenticated user's display name appears in the UI

Silent Token Renewal

  • should handle token expiry gracefully — With short token TTL (5s), the app stays authenticated after waiting for expiry (silent renewal works)
  • should handle 401 response by triggering token renewal — After a 401 API response, navigating to a new page still works (renewal interceptor recovered)

Iframe Renewal Failure with Popup Fallback

  • should handle interaction_required gracefully — When OIDC provider forces interaction_required (iframe can't silently renew), the app handles it without crashing

Multi-Tab Token Renewal

  • should share authentication state across tabs — A second tab opened after login is immediately authenticated with a valid token
  • should propagate refreshed token to second tab — After tab 1 triggers a token renewal (via 401), tab 2 also receives the updated token

clearRefreshInProgress Recovery

  • should recover when refreshInProgress is stuck in localStorage — If refreshInProgress flag is stuck from a crashed tab, a fresh login still works
  • should handle concurrent renewal attempts gracefully — Two tabs both attempting refresh don't deadlock; at least one succeeds

401 Interceptor and Request Retry

  • should trigger session expired redirect on 401 with expired token message — A 401 on /api/v1/users/loggedInUser redirects to /signin (forced logout). Skipped in WebKit.
  • should queue multiple 401 responses and refresh only once — When multiple API calls all get 401, the app batches them into at most 1–2 refresh attempts (not N)
  • should logout when token renewal fails — When both the API returns 401 AND the OIDC provider rejects renewal (interaction_required), the app cleanly redirects to /signin. Skipped in
    WebKit.

Refresh Token Scenarios

  • should handle disabled refresh tokens gracefully — With refreshTokenEnabled: false + forceInteractionRequired: true, the app doesn't crash or show an error page

Logout Cleanup

  • should clear all auth state on logout — After logout: redirects to /signin, IndexedDB token is cleared, refreshInProgress localStorage key is removed

Token Expiry Timer

  • should remain authenticated after extended idle period — After 10s idle, navigating and making API calls still returns 200 (proactive renewal works)

@harshach harshach requested a review from a team as a code owner April 9, 2026 14:38
Copilot AI review requested due to automatic review settings April 9, 2026 14:38
@github-actions github-actions bot added backend safe to test Add this label to run secure Github workflows on PRs labels Apr 9, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR aims to reduce Safari session loss by making MSAL token renewal more resilient (earlier renewal window + improved silent/interactive fallback behavior) and by adjusting token-refresh bookkeeping in the shared TokenService.

Changes:

  • Increased the pre-expiry buffer used to schedule silent renewal from 1 minute to 5 minutes.
  • Updated TokenService refresh error handling/flag clearing (including special-casing the “Frame window timed out” case).
  • Updated MSAL renewal to use forceRefresh, switch to acquireTokenPopup, and fall back to redirect; updated related unit tests.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
openmetadata-ui/src/main/resources/ui/src/utils/AuthProvider.util.ts Adjusts the global token expiry buffer used to schedule refresh attempts.
openmetadata-ui/src/main/resources/ui/src/utils/Auth/TokenService/TokenServiceUtil.ts Modifies refresh error handling and refresh-in-progress flag lifecycle.
openmetadata-ui/src/main/resources/ui/src/components/Auth/AppAuthenticators/MsalAuthenticator.tsx Changes MSAL renewal strategy (silent refresh + popup/redirect fallback).
openmetadata-ui/src/main/resources/ui/src/components/Auth/AppAuthenticators/MsalAuthenticator.test.tsx Updates tests to match the new MSAL renewal behavior.

Comment on lines 138 to 140
} finally {
// If response is not null then clear the refresh flag
// For Callback based refresh token, response will be void
response && this.clearRefreshInProgress();
this.clearRefreshInProgress();
}
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

fetchNewToken() now clears refreshInProgress unconditionally in finally. This breaks callback-based renewal flows where renewToken() intentionally resolves to void and the refresh is completed later via a callback (e.g., OidcAuthenticator uses signinSilent() and clears the flag in handleSilentSignInSuccess). Clearing here can allow concurrent refresh attempts while the silent flow is still in progress. Suggestion: only clear in finally when the renewal returns a non-void response (e.g., response !== undefined), and clear on error paths explicitly (including the "Frame window timed out" case). Also add a unit test covering the void renewal case to prevent regressions.

Copilot uses AI. Check for mistakes.
Comment on lines 127 to +134
} catch (error) {
// Silent Frame window timeout error since it doesn't affect refresh token process
if ((error as AxiosError).message !== 'Frame window timed out') {
// Perform logout for any error
this.clearRefreshInProgress();
this.clearRefreshInProgress();

throw new Error(
`Failed to refresh token: ${(error as Error).message}`
);
// Silent Frame window timeout error since it doesn't affect refresh token process
if ((error as AxiosError).message === 'Frame window timed out') {
return null;
}
// Do nothing

Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

In this catch block, clearRefreshInProgress() is called both here and again in finally, which is redundant and makes the control flow harder to reason about. After adjusting the finally logic (see comment on the finally block), consider removing the duplicate clear from the catch and letting a single, well-defined path handle flag cleanup.

Copilot uses AI. Check for mistakes.
Comment on lines +103 to +107
// Popup blocked or failed — fall back to redirect (works on Safari)
await instance.acquireTokenRedirect(tokenRequest);

return msalResponse;
throw popupError;
}
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

After falling back from acquireTokenPopup to acquireTokenRedirect, the code rethrows popupError. In the token-renewal path this is likely to surface as an unhandled promise rejection (the refresh timer calls refreshToken() without awaiting/catching) and may also trigger higher-level error handling that treats renewal as a hard failure, even though a redirect-based auth flow has been started. Consider not throwing after initiating the redirect (e.g., return null/void to indicate "redirect in progress") and ensure the refresh-in-progress flag is handled appropriately for this redirect-based flow.

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 9, 2026

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 64%
64.18% (59598/92851) 43.73% (31062/71024) 46.89% (9373/19986)

chirag-madlani
chirag-madlani previously approved these changes Apr 9, 2026
Copilot AI review requested due to automatic review settings April 9, 2026 15:48
Copilot AI review requested due to automatic review settings April 9, 2026 21:14
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 8 comments.

Comments suppressed due to low confidence (1)

openmetadata-ui/src/main/resources/ui/src/components/Auth/AppAuthenticators/MsalAuthenticator.tsx:107

  • The renewal fallback here only tries acquireTokenPopup(). For Safari/WebKit (and any non-user-gesture renewal), popups can be blocked, which is the core issue described in the PR summary. If the intended fix is an acquireTokenPopup()acquireTokenRedirect() chain, that redirect fallback is currently missing; add a redirect fallback on popup failure (or use redirect directly for renewal) so renewal can complete without a user gesture.
    const fetchIdToken = async (
      shouldFallbackToPopup = false
    ): Promise<OidcUser> => {
      const tokenRequest = {
        account: account || accounts[0],
        scopes: msalLoginRequest.scopes,
        forceRefresh: shouldFallbackToPopup,
      };
      try {
        const response = await instance.acquireTokenSilent(tokenRequest);
        const msalResponse = await parseMSALResponse(response);

        return msalResponse;
      } catch (error) {
        if (
          error instanceof InteractionRequiredAuthError &&
          shouldFallbackToPopup
        ) {
          const response = await instance.acquireTokenPopup(tokenRequest);
          const msalResponse = await parseMSALResponse(response);

          return msalResponse;
        } else {
          // eslint-disable-next-line no-console
          console.error(error);

          throw error;
        }
      }

Comment on lines +101 to +104
// eslint-disable-next-line no-console
console.warn(
'Token persistence timed out, proceeding with callback'
);
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

Avoid leaving console.* calls in production code. Even with eslint-disable, these warnings add noise and can leak internal timing behavior; consider using an existing app logger/telemetry hook or remove the log and rely on the boolean return value/metrics instead.

Suggested change
// eslint-disable-next-line no-console
console.warn(
'Token persistence timed out, proceeding with callback'
);
// Continue with the refresh callback even if persistence confirmation times out

Copilot uses AI. Check for mistakes.
Comment on lines 127 to 142
// Call renewal method according to the provider
async fetchNewToken() {
let response: string | AccessTokenResponse | null | void = null;
if (typeof this.renewToken === 'function') {
try {
response = await this.renewToken();
} catch (error) {
// Silent Frame window timeout error since it doesn't affect refresh token process
if ((error as AxiosError).message !== 'Frame window timed out') {
// Perform logout for any error
this.clearRefreshInProgress();

throw new Error(
`Failed to refresh token: ${(error as Error).message}`
);
if ((error as AxiosError).message === 'Frame window timed out') {
return null;
}
// Do nothing

throw new Error(`Failed to refresh token: ${(error as Error).message}`);
} finally {
// If response is not null then clear the refresh flag
// For Callback based refresh token, response will be void
response && this.clearRefreshInProgress();
this.clearRefreshInProgress();
}
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

fetchNewToken() now clears refreshInProgress unconditionally in finally. For callback-based renewals (where renewToken() returns void and the actual token update happens later in the silent-callback), this drops the cross-tab lock while the renewal is still in-flight, which can allow parallel renewals (extra iframes/popups) and defeat the intended de-duping. Consider only clearing in finally when a concrete token/response is returned, and clearing on error paths separately, or introducing a separate "callbackRenewalInFlight" flag that is cleared by the silent-callback handler.

Copilot uses AI. Check for mistakes.
Comment on lines +344 to +370
useEffect(() => {
const handleVisibilityChange = async () => {
if (document.visibilityState !== 'visible') {
return;
}
try {
const token = await getOidcToken();
const { isExpired, timeoutExpiry } = extractDetailsFromToken(token);

// eslint-disable-next-line no-console
console.debug(
'[VisibilityHandler] token length:',
token?.length,
'isExpired:',
isExpired,
'timeoutExpiry:',
timeoutExpiry,
'hasTokenService:',
!!tokenService.current
);

if (isExpired || timeoutExpiry <= 0) {
tokenService.current?.refreshToken();
} else {
startTokenExpiryTimer();
}
} catch (error) {
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

This visibility-change effect uses startTokenExpiryTimer() but has an empty dependency array, so the handler will capture the initial (potentially stale) version of startTokenExpiryTimer/timeoutId/authConfig. This can lead to timers not being cleared/rescheduled correctly after state updates. Fix by memoizing startTokenExpiryTimer with useCallback and including it (and any other referenced values) in the effect deps, or by moving the needed state into refs.

Copilot uses AI. Check for mistakes.
Comment on lines +353 to +372
// eslint-disable-next-line no-console
console.debug(
'[VisibilityHandler] token length:',
token?.length,
'isExpired:',
isExpired,
'timeoutExpiry:',
timeoutExpiry,
'hasTokenService:',
!!tokenService.current
);

if (isExpired || timeoutExpiry <= 0) {
tokenService.current?.refreshToken();
} else {
startTokenExpiryTimer();
}
} catch (error) {
// eslint-disable-next-line no-console
console.error('[VisibilityHandler] error:', error);
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

Please remove the new console.debug/console.error calls from this auth flow. Even with eslint-disable, the UI guidelines for this repo treat console statements as debug code that should not ship; if you need observability here, route through the app’s logging/toast mechanisms or keep this strictly behind a development-only flag.

Suggested change
// eslint-disable-next-line no-console
console.debug(
'[VisibilityHandler] token length:',
token?.length,
'isExpired:',
isExpired,
'timeoutExpiry:',
timeoutExpiry,
'hasTokenService:',
!!tokenService.current
);
if (isExpired || timeoutExpiry <= 0) {
tokenService.current?.refreshToken();
} else {
startTokenExpiryTimer();
}
} catch (error) {
// eslint-disable-next-line no-console
console.error('[VisibilityHandler] error:', error);
if (isExpired || timeoutExpiry <= 0) {
tokenService.current?.refreshToken();
} else {
startTokenExpiryTimer();
}
} catch (error) {
// No-op: avoid shipping console statements in auth flow.

Copilot uses AI. Check for mistakes.
Comment on lines +797 to +818
// Capture console messages to verify the handler fires
const logs: string[] = [];
page.on('console', (msg) => logs.push(msg.text()));

// Simulate the tab becoming visible (user returns from idle period).
// The handler should detect the expired token and call refreshToken().
await page.evaluate(() => {
document.dispatchEvent(new Event('visibilitychange'));
});

// Wait for the async handler to complete
await page.waitForTimeout(3000);

// Verify the handler detected the expired token and attempted refresh.
// The deployed handler logs "[VisibilityHandler] token length: X isExpired: true"
const handlerDetectedExpiry = logs.some(
(l) =>
l.includes('[VisibilityHandler]') && l.includes('isExpired: true')
);

expect(handlerDetectedExpiry).toBe(true);
});
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

This test asserts behavior by scraping console output ([VisibilityHandler] ... isExpired: true). That makes the test brittle and effectively forces debug logging to remain in production auth code. Prefer asserting observable outcomes instead (e.g., token value in IndexedDB changed, mock OIDC refresh metrics incremented, or a protected API call succeeds after the visibilitychange).

Copilot uses AI. Check for mistakes.
- "openmetadata-ui/src/main/resources/ui/src/components/Auth/**"
- "openmetadata-ui/src/main/resources/ui/src/rest/auth-API*"
- "openmetadata-ui/src/main/resources/ui/src/utils/AuthProvider*"
- "openmetadata-ui/src/main/resources/ui/src/utils/TokenServiceUtil*"
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

The workflow path filter includes openmetadata-ui/src/main/resources/ui/src/utils/TokenServiceUtil*, but the TokenService util lives under src/utils/Auth/TokenService/TokenServiceUtil.ts. As written, TokenService-only changes may not trigger this workflow. Update the paths filter to match the actual directory (src/utils/Auth/TokenService/** or similar).

Suggested change
- "openmetadata-ui/src/main/resources/ui/src/utils/TokenServiceUtil*"
- "openmetadata-ui/src/main/resources/ui/src/utils/Auth/TokenService/**"

Copilot uses AI. Check for mistakes.
Comment on lines 66 to 69
permissions:
contents: read
pull-requests: write

Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

pull-requests: write is granted at the workflow level, so the main test job also runs with PR write access. For least privilege, consider moving PR write permission to the sso-summary job only (job-level permissions:), and keep the test job as contents: read.

Copilot uses AI. Check for mistakes.
);

if (isExpired || timeoutExpiry <= 0) {
tokenService.current?.refreshToken();
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

tokenService.current?.refreshToken() is invoked without await. Since refreshToken() is async and can throw, this can create an unhandled promise rejection (the surrounding try/catch won’t catch it). Await the call (and optionally handle/log the error) to keep failures contained.

Suggested change
tokenService.current?.refreshToken();
await tokenService.current?.refreshToken();

Copilot uses AI. Check for mistakes.
@gitar-bot
Copy link
Copy Markdown

gitar-bot bot commented Apr 9, 2026

Code Review ✅ Approved 2 resolved / 2 findings

Fixes MSAL token renewal to prevent Safari session loss by removing redundant clearRefreshInProgress calls and ensuring waitForTokenPersistence timeout prevents success callbacks from firing unexpectedly.

✅ 2 resolved
Quality: clearRefreshInProgress called redundantly in catch and finally

📄 openmetadata-ui/src/main/resources/ui/src/utils/Auth/TokenService/TokenServiceUtil.ts:128 📄 openmetadata-ui/src/main/resources/ui/src/utils/Auth/TokenService/TokenServiceUtil.ts:139
In fetchNewToken, clearRefreshInProgress() is called in the catch block (line 128) and unconditionally in the finally block (line 139). Since finally always runs (even after return null on line 132 or throw on line 135), the catch-block call is redundant. The method is idempotent (just localStorage.removeItem), so this is harmless but adds confusion about the intended control flow.

Edge Case: waitForTokenPersistence silently times out, success callback still fires

📄 openmetadata-ui/src/main/resources/ui/src/utils/Auth/TokenService/TokenServiceUtil.ts:99-100 📄 openmetadata-ui/src/main/resources/ui/src/utils/Auth/TokenService/TokenServiceUtil.ts:160-173
If waitForTokenPersistence exhausts all 20 polling attempts without detecting a new token (e.g., Service Worker persistence is slow or fails), it returns silently. Execution then falls through to this.refreshSuccessCallback?.() at line 100, signaling success even though the token may not actually be persisted yet.

This is still an improvement over the previous fixed 100ms delay, but the silent timeout could mask persistence failures.

Options

Display: compact → Showing less information.

Comment with these commands to change:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Apr 9, 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 9, 2026

🟡 Playwright Results — all passed (24 flaky)

✅ 3594 passed · ❌ 0 failed · 🟡 24 flaky · ⏭️ 207 skipped

Shard Passed Failed Flaky Skipped
🟡 Shard 1 452 0 5 2
🟡 Shard 2 641 0 1 32
🟡 Shard 3 649 0 2 26
🟡 Shard 4 618 0 4 47
🟡 Shard 5 604 0 3 67
🟡 Shard 6 630 0 9 33
🟡 24 flaky test(s) (passed on retry)
  • Features/DataAssetRulesDisabled.spec.ts › Verify the Pipeline Service entity item action after rules disabled (shard 1, 1 retry)
  • Flow/Metric.spec.ts › Verify Related Metrics Update (shard 1, 1 retry)
  • Flow/Tour.spec.ts › Tour should work from help section (shard 1, 1 retry)
  • Flow/Tour.spec.ts › Tour should work from URL directly (shard 1, 1 retry)
  • Pages/UserCreationWithPersona.spec.ts › Create user with persona and verify on profile (shard 1, 1 retry)
  • Features/BulkEditEntity.spec.ts › Glossary (shard 2, 1 retry)
  • Features/Permissions/GlossaryPermissions.spec.ts › Team-based permissions work correctly (shard 3, 1 retry)
  • Flow/ExploreDiscovery.spec.ts › Should display deleted assets when showDeleted is checked and deleted is not present in queryFilter (shard 3, 1 retry)
  • Pages/Customproperties-part2.spec.ts › entityReferenceList shows item count, scrollable list, no expand toggle (shard 4, 1 retry)
  • Pages/Domains.spec.ts › Rename domain with deeply nested subdomains (3+ levels) verifies FQN propagation (shard 4, 1 retry)
  • Pages/Domains.spec.ts › Subdomain rename does not affect parent domain and updates nested children (shard 4, 1 retry)
  • Pages/Entity.spec.ts › Glossary Term Add, Update and Remove (shard 4, 1 retry)
  • Pages/EntityDataSteward.spec.ts › User as Owner Add, Update and Remove (shard 5, 1 retry)
  • Pages/EntityDataSteward.spec.ts › User as Owner Add, Update and Remove (shard 5, 1 retry)
  • Pages/ExploreTree.spec.ts › Verify Database and Database Schema available in explore tree (shard 5, 1 retry)
  • Pages/HyperlinkCustomProperty.spec.ts › should accept valid http and https URLs (shard 6, 1 retry)
  • Pages/Lineage/DataAssetLineage.spec.ts › Column lineage for topic -> container (shard 6, 1 retry)
  • Pages/Lineage/DataAssetLineage.spec.ts › Column lineage for mlModel -> apiEndpoint (shard 6, 1 retry)
  • Pages/Lineage/LineageFilters.spec.ts › Verify lineage schema filter selection (shard 6, 1 retry)
  • Pages/Login.spec.ts › Refresh should work (shard 6, 1 retry)
  • Pages/ODCSImportExport.spec.ts › Multi-object ODCS contract - object selector shows all schema objects (shard 6, 1 retry)
  • Pages/ProfilerConfigurationPage.spec.ts › Non admin user (shard 6, 1 retry)
  • Pages/Tag.spec.ts › Verify Owner Add Delete (shard 6, 1 retry)
  • Pages/Users.spec.ts › Permissions for table details page for Data Consumer (shard 6, 1 retry)

📦 Download artifacts

How to debug locally
# Download playwright-test-results-<shard> artifact and unzip
npx playwright show-trace path/to/trace.zip    # view trace

@harshach harshach merged commit b2b49db into main Apr 10, 2026
50 of 51 checks passed
@harshach harshach deleted the msal_refresh_token branch April 10, 2026 00:45
chirag-madlani added a commit that referenced this pull request Apr 10, 2026
* MSAL Token Renewal Fix — Safari Session Loss

* MSAL Token Renewal Fix — Safari Session Loss

* MSAL Token Renewal Fix — Safari Session Loss

* apply lint

* MSAL Token Renewal Fix — OIDC fix

* wait for token update

* fix unit tests

* Add SSO playwright tests

* Add tests

---------

Co-authored-by: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com>
(cherry picked from commit b2b49db)
chirag-madlani added a commit that referenced this pull request Apr 10, 2026
* MSAL Token Renewal Fix — Safari Session Loss

* MSAL Token Renewal Fix — Safari Session Loss

* MSAL Token Renewal Fix — Safari Session Loss

* apply lint

* MSAL Token Renewal Fix — OIDC fix

* wait for token update

* fix unit tests

* Add SSO playwright tests

* Add tests

---------

Co-authored-by: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com>
(cherry picked from commit b2b49db)
SaaiAravindhRaja pushed a commit to SaaiAravindhRaja/OpenMetadata that referenced this pull request Apr 12, 2026
* MSAL Token Renewal Fix — Safari Session Loss

* MSAL Token Renewal Fix — Safari Session Loss

* MSAL Token Renewal Fix — Safari Session Loss

* apply lint

* MSAL Token Renewal Fix — OIDC fix

* wait for token update

* fix unit tests

* Add SSO playwright tests

* Add tests

---------

Co-authored-by: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com>
SaaiAravindhRaja pushed a commit to SaaiAravindhRaja/OpenMetadata that referenced this pull request Apr 12, 2026
* MSAL Token Renewal Fix — Safari Session Loss

* MSAL Token Renewal Fix — Safari Session Loss

* MSAL Token Renewal Fix — Safari Session Loss

* apply lint

* MSAL Token Renewal Fix — OIDC fix

* wait for token update

* fix unit tests

* Add SSO playwright tests

* Add tests

---------

Co-authored-by: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com>
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.

3 participants