test(playwright): add nightly SSO login spec starting#27164
Conversation
Extends Playwright coverage end-to-end for SSO login flows. Today's SSO coverage (Features/SSOConfiguration.spec.ts) only asserts the config form UI. This adds a new suite that configures OpenMetadata to an external identity provider, drives a real login through the provider's hosted UI, and validates the resulting session against the OM API. Phase 1 ships Okta only (integrator-9351624.okta.com). Additional providers (Auth0, Azure, Cognito, SAML, Google) plug into the same dispatcher by adding a ProviderHelper implementation. ## What's new - playwright/e2e/Auth/SSOLogin.spec.ts — two-test suite tagged @sso 1. Asserts the SSO sign-in button renders on /signin with the correct brand label and that the basic-auth form is not shown. 2. Clicks the button, drives the provider's login widget, follows the OAuth callback, completes first-run self-signup when needed, lands on /my-data, then verifies the JWT by calling GET /api/v1/users/loggedInUser and asserting the returned email matches SSO_USERNAME. - playwright/utils/ssoAuth.ts — provider-agnostic orchestration: applyProviderConfig (PUT /api/v1/system/security/config), restoreBasicAuth, buildAuthContextFromJwt, verifyLoggedInUserMatches. Composes existing getApiContext/getAuthContext/getToken helpers — no token extraction or HTTP plumbing is reimplemented. - playwright/utils/sso-providers/{index,okta}.ts — ProviderHelper interface plus the Okta Identity Engine widget driver. Defaults the dev tenant values from the committed openmetadata.yaml snippet so the spec only needs SSO_USERNAME/SSO_PASSWORD to run locally. - playwright/constant/ssoAuth.ts — env var key constants, PROVIDER_BUTTON_TEXT map, and the BASIC_AUTH_CONFIG payload used for cleanup. - playwright.config.ts — new 'sso-auth' project matching playwright/e2e/Auth/**/*.spec.ts with its own serial workers, and '**/Auth/**' added to the chromium project's testIgnore so these tests never run in the default suite. ## How provider switching works beforeAll logs in as admin via basic auth, captures the admin JWT via getToken(page) BEFORE the swap, then PUTs the Okta config. The admin JWT survives the provider swap because OM's internal JWKS stays in publicKeyUrls and the admin user's isAdmin flag is persisted in the DB. afterAll rebuilds an API context from that JWT and restores basic auth, making the spec fully idempotent — the same OM instance can run the suite repeatedly without any manual cleanup. ## Running locally export SSO_PROVIDER_TYPE=okta export SSO_USERNAME='<okta-test-user>' export SSO_PASSWORD='<okta-test-password>' npx playwright test playwright/e2e/Auth/SSOLogin.spec.ts \ --project=sso-auth --workers=1 Verified end-to-end against integrator-9351624.okta.com — both tests pass in ~12s on an already-provisioned user, ~14s on first-run self-signup. Cleanup leaves the server in basic-auth mode. ## Notes for reviewers - The existing .github/workflows/playwright-sso-tests.yml already wires up the CI matrix and secret names; this change intentionally does NOT enable the cron schedule. That lands in a follow-up once one provider is stable for a few nightly runs. - OKTA_SSO_CLIENT_ID / OKTA_SSO_DOMAIN / OKTA_SSO_PRINCIPAL_DOMAIN env vars can override the baked-in dev tenant defaults if a different Okta tenant is used in CI.
|
Hi there 👋 Thanks for your contribution! The OpenMetadata team will review the PR shortly! Once it has been labeled as Let us know if you need any help! |
Adds .github/workflows/playwright-sso-login-nightly.yml, a standalone
workflow that runs the new SSOLogin spec nightly at 03:00 UTC instead
of piggy-backing on playwright-sso-tests.yml.
The existing playwright-sso-tests.yml is left untouched — it still
covers the SSO configuration form UI via SSOConfiguration.spec.ts and
its matrix/secrets wiring is unchanged. The new workflow complements
it with a real end-to-end login round-trip:
- Schedule: cron '0 3 * * *'
- Provider matrix: okta only for Phase 1 (extended as helpers ship)
- Invokes playwright/e2e/Auth/SSOLogin.spec.ts under the new
sso-auth Playwright project with workers=1
- Wires provider credentials via secrets with the existing
{PROVIDER}_SSO_USERNAME / {PROVIDER}_SSO_PASSWORD convention plus
optional OKTA_SSO_CLIENT_ID / OKTA_SSO_DOMAIN /
OKTA_SSO_PRINCIPAL_DOMAIN overrides
- Uses the shared setup-openmetadata-test-environment composite
action, PostgreSQL, ingestion disabled — matching the existing SSO
tests workflow
- Uploads the HTML report as an artifact on every run and cleans up
the docker stack in a final always-run step
There was a problem hiding this comment.
Pull request overview
Adds a new Playwright E2E suite that exercises real SSO login via an external IdP (starting with Okta), including server-side auth config swaps and JWT validation against the OpenMetadata API.
Changes:
- Added
Auth/SSOLogin.spec.tsto perform end-to-end SSO login and validate/api/v1/users/loggedInUser. - Introduced provider-agnostic SSO orchestration helpers plus an Okta provider implementation.
- Updated Playwright projects to isolate SSO auth tests from the default chromium suite.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| openmetadata-ui/src/main/resources/ui/playwright/utils/ssoAuth.ts | Adds helpers to apply/restore auth config and verify logged-in user via API. |
| openmetadata-ui/src/main/resources/ui/playwright/utils/sso-providers/index.ts | Defines provider interface + dispatcher for SSO providers. |
| openmetadata-ui/src/main/resources/ui/playwright/utils/sso-providers/okta.ts | Implements Okta config payload builder and hosted login widget automation. |
| openmetadata-ui/src/main/resources/ui/playwright/e2e/Auth/SSOLogin.spec.ts | New serial E2E test suite covering SSO button + full login + JWT verification. |
| openmetadata-ui/src/main/resources/ui/playwright/constant/ssoAuth.ts | Adds env var constants and a basic-auth cleanup payload. |
| openmetadata-ui/src/main/resources/ui/playwright.config.ts | Adds sso-auth project and excludes Auth specs from default chromium runs. |
- verifyLoggedInUserMatches now asserts directly on the lowercased email field instead of building a candidate array and feeding it a long stringified failure message. The assertion failure already shows expected vs received, so the wrapper string was just noise. - Drop buildAuthContextFromJwt — it was a one-line wrapper around getAuthContext. The spec calls getAuthContext directly now.
- Extract OM_BASE_URL from PLAYWRIGHT_TEST_BASE_URL (with the same http://localhost:8585 default as playwright.config.ts) and export it from constant/ssoAuth.ts. okta.ts and BASIC_AUTH_CONFIG both consume it, so callbackUrl, the OM JWKS entry in publicKeyUrls, and the basic-auth restore payload all match the test target — including CI runs against non-default hosts. - Drop PROVIDER_BUTTON_TEXT. It was exported but never imported; the ProviderHelper.expectedButtonText field is the only source of truth for the SSO sign-in button label and the spec already reads from it. - Restore the OM convention adminPrincipals: ['admin'] in the Okta config (matches conf/openmetadata.yaml's AUTHORIZER_ADMIN_PRINCIPALS default). The previous code was granting admin to whichever IdP user ran the suite — verifyLoggedInUserMatches only needs an authenticated session, not admin, so the elevation was unnecessary. This also drops the now-unused requireEnv on SSO_USERNAME inside okta.ts; the spec itself still gates on the env var via test.skip. - Set workers: 1 on the sso-auth Playwright project. fullyParallel: false alone wasn't enough — the global workers: 3 on CI could still fan out across multiple Auth/**/*.spec.ts files in the future. The explicit limit enforces full isolation as more provider specs land.
Replaces the dynamic secret lookup
secrets[format('{0}_SSO_USERNAME', upper(matrix.provider))]
with a static reference
secrets.OKTA_SSO_USERNAME
CodeQL flagged the dynamic indexing because GitHub Actions can only
mask & scope secrets that are referenced statically. With a computed
key, the runner has no way to know which single secret is needed and
conservatively materializes EVERY org and repo secret into the step's
environment — even though the test only reads OKTA_SSO_*. Static
references let GitHub expose only the two credentials this step
actually uses.
Phase 1's matrix is okta-only so the change is two lines. The added
inline comment documents the convention for future providers: add a
sibling step gated by `if: matrix.provider == '<provider>'` with that
provider's static secret references — do not bring back the
secrets[format(...)] pattern.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
| const signInButton = page.locator('.signin-button'); | ||
|
|
||
| await expect(signInButton).toBeVisible(); | ||
| await expect(signInButton).toContainText(helper.expectedButtonText); | ||
| await expect(page.getByTestId('email')).toHaveCount(0); |
There was a problem hiding this comment.
Selector for the SSO button is very broad (.signin-button) and could match non-button elements or multiple nodes. Other Auth specs in this repo use button.signin-button for this control. Tightening the locator here would reduce flakiness and align with existing patterns.
Address Copilot review comments on PR #27164: - Use button.signin-button to match the pattern in SSOAuthentication.spec.ts. - Await /api/v1/users/logout POST alongside the /signin navigation in the logout test to remove the race against the server response. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…pen-metadata/OpenMetadata into test/playwright/sso-login-okta
…ogin.spec.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…e-saml Route Keycloak credentials through the same `vars[format(...)]` / `secrets[format(...)]` indirection as Okta via an `env_prefix` matrix column, removing the hardcoded fixture literals from the workflow. Password lookup falls back `vars || secrets` so fixture passwords can live as vars while real provider secrets stay in secrets. Also drop the keycloak-google-saml variant — same IdP and realm shape as the Azure variant, so it adds CI cost without meaningful coverage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a per-provider Slack notification step mirroring the pattern used by the postgresql/mysql nightly workflows — reuses the existing `slack-cli.config.json` and `playwright-slack-report` CLI against the `results.json` that the global JSON reporter already emits. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
OktaAuthenticator.logout clears tokens locally with no backend call, and
GenericAuthenticator (SAML) hits `GET /auth/logout` — neither triggers
the `POST /api/v1/users/logout` the test was waiting on. The listener
never matched, so `Promise.all` hung past the 180s test timeout even
though the page had already navigated to /signin.
Rely on `waitForURL('**/signin')` + the signin button assertion, which
are the actual cross-provider success signals.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Code Review 👍 Approved with suggestions 6 resolved / 7 findingsAdds nightly Okta SSO login test spec with comprehensive fixes for JWT context building, GitHub Actions expressions, test cleanup, and environment variable handling. Pin the 💡 Security: Unpinned third-party action uses mutable
|
| Compact |
|
Was this helpful? React with 👍 / 👎 | Gitar
|



Docs: https://www.notion.so/collate-inc/SSO-LOGIN-NIGHTLY-33db4572302480f5b8dcda6e9949969c
Nightly end-to-end Playwright suite that drives a real login round-trip through an external IdP and verifies the resulting OpenMetadata session. Okta in phase 1.
Changes
playwright/e2e/Auth/SSOLogin.spec.ts— asserts the SSO button renders, completes login + self-signup, verifies the JWT via/api/v1/users/loggedInUser.playwright/utils/ssoAuth.ts—fetchSecurityConfig/applyProviderConfig/restoreSecurityConfig. Snapshot the server config, merge the provider override, restore the exact snapshot inafterAll.playwright/utils/sso-providers/{index,okta}.ts—ProviderHelperinterface + Okta widget driver. Public OAuth identifiers live in anOKTA_TENANTconstant..github/workflows/playwright-sso-login-nightly.yml— single-file workflow, matrix-driven, one job per provider. Credentials resolved viavars[format(...)]/secrets[format(...)]against the<PROVIDER>_SSO_*convention.playwright.config.ts— newsso-authproject, single worker, excluded from the default chromium run.Adding a new provider
sso-providers/<provider>.ts+caseinsso-providers/index.ts.<PROVIDER>_SSO_USERNAME(var) +<PROVIDER>_SSO_PASSWORD(secret) to thetestenvironment.Test plan
yarn lint:playwright+tsc --noEmitclean