test(ui): add Okta provider form E2E test (#11600)

Co-authored-by: Pablo F.G <pablo.fernandez@prowler.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Alejandro Bailo <59607668+alejandrobailo@users.noreply.github.com>
This commit is contained in:
Pablo Fernandez Guerra (PFE)
2026-06-17 16:17:35 +02:00
committed by GitHub
parent e8ffe59ce2
commit 5ecfd6ea20
5 changed files with 239 additions and 0 deletions
+3
View File
@@ -77,6 +77,9 @@ jobs:
E2E_ALIBABACLOUD_ACCESS_KEY_ID: ${{ secrets.E2E_ALIBABACLOUD_ACCESS_KEY_ID }}
E2E_ALIBABACLOUD_ACCESS_KEY_SECRET: ${{ secrets.E2E_ALIBABACLOUD_ACCESS_KEY_SECRET }}
E2E_ALIBABACLOUD_ROLE_ARN: ${{ secrets.E2E_ALIBABACLOUD_ROLE_ARN }}
E2E_OKTA_DOMAIN: ${{ secrets.E2E_OKTA_DOMAIN }}
E2E_OKTA_CLIENT_ID: ${{ secrets.E2E_OKTA_CLIENT_ID }}
E2E_OKTA_BASE64_PRIVATE_KEY: ${{ secrets.E2E_OKTA_BASE64_PRIVATE_KEY }}
E2E_GOOGLEWORKSPACE_CUSTOMER_ID: ${{ secrets.E2E_GOOGLEWORKSPACE_CUSTOMER_ID }}
E2E_GOOGLEWORKSPACE_SERVICE_ACCOUNT_JSON: ${{ secrets.E2E_GOOGLEWORKSPACE_SERVICE_ACCOUNT_JSON }}
E2E_GOOGLEWORKSPACE_DELEGATED_USER: ${{ secrets.E2E_GOOGLEWORKSPACE_DELEGATED_USER }}
+76
View File
@@ -81,6 +81,28 @@ export interface GoogleWorkspaceProviderCredential {
delegatedUser: string;
}
// Okta provider data
export interface OktaProviderData {
orgDomain: string;
alias?: string;
}
// Okta credential options
export const OKTA_CREDENTIAL_OPTIONS = {
OKTA_PRIVATE_KEY_JWT: "private_key_jwt",
} as const;
// Okta credential type
type OktaCredentialType =
(typeof OKTA_CREDENTIAL_OPTIONS)[keyof typeof OKTA_CREDENTIAL_OPTIONS];
// Okta provider credential (OAuth 2.0 Private Key JWT)
export interface OktaProviderCredential {
type: OktaCredentialType;
clientId: string;
privateKey: string;
}
// AWS credential options
export const AWS_CREDENTIAL_OPTIONS = {
AWS_ROLE_ARN: "role",
@@ -267,12 +289,18 @@ export class ProvidersPage extends BasePage {
readonly ociProviderRadio: Locator;
readonly alibabacloudProviderRadio: Locator;
readonly googleworkspaceProviderRadio: Locator;
readonly oktaProviderRadio: Locator;
// Google Workspace provider form elements
readonly googleworkspaceCustomerIdInput: Locator;
readonly googleworkspaceServiceAccountJsonInput: Locator;
readonly googleworkspaceDelegatedUserInput: Locator;
// Okta provider form elements
readonly oktaOrgDomainInput: Locator;
readonly oktaClientIdInput: Locator;
readonly oktaPrivateKeyInput: Locator;
// Vercel provider form elements
readonly vercelProviderRadio: Locator;
readonly vercelTeamIdInput: Locator;
@@ -522,6 +550,18 @@ export class ProvidersPage extends BasePage {
name: /Delegated User Email/i,
});
// Okta
this.oktaProviderRadio = page.getByRole("option", {
name: /Okta/i,
});
this.oktaOrgDomainInput = page.getByRole("textbox", {
name: "Org Domain",
});
this.oktaClientIdInput = page.getByRole("textbox", { name: "Client ID" });
this.oktaPrivateKeyInput = page.getByRole("textbox", {
name: "Private Key",
});
// Vercel
this.vercelProviderRadio = page.getByRole("option", {
name: /Vercel/i,
@@ -1322,6 +1362,41 @@ export class ProvidersPage extends BasePage {
await expect(this.googleworkspaceDelegatedUserInput).toBeVisible();
}
async selectOktaProvider(): Promise<void> {
await this.selectProviderRadio(this.oktaProviderRadio);
}
async fillOktaProviderDetails(data: OktaProviderData): Promise<void> {
// Fill the Okta provider details (org domain is lowercased by the form)
await this.oktaOrgDomainInput.fill(data.orgDomain);
if (data.alias) {
await this.aliasInput.fill(data.alias);
}
}
async fillOktaCredentials(
credentials: OktaProviderCredential,
): Promise<void> {
// Fill the Okta OAuth 2.0 Private Key JWT credentials form
if (credentials.clientId) {
await this.oktaClientIdInput.fill(credentials.clientId);
}
if (credentials.privateKey) {
await this.oktaPrivateKeyInput.fill(credentials.privateKey);
}
}
async verifyOktaCredentialsPageLoaded(): Promise<void> {
// Verify the Okta credentials page is loaded
await this.verifyPageHasProwlerTitle();
await expect(this.oktaClientIdInput).toBeVisible();
await expect(this.oktaPrivateKeyInput).toBeVisible();
}
async selectVercelProvider(): Promise<void> {
await this.selectProviderRadio(this.vercelProviderRadio);
}
@@ -1374,6 +1449,7 @@ export class ProvidersPage extends BasePage {
await expect(this.githubProviderRadio).toBeVisible();
await expect(this.alibabacloudProviderRadio).toBeVisible();
await expect(this.googleworkspaceProviderRadio).toBeVisible();
await expect(this.oktaProviderRadio).toBeVisible();
await expect(this.vercelProviderRadio).toBeVisible();
}
+60
View File
@@ -1071,3 +1071,63 @@
- API Token is a masked (password) field; the only credential required for Vercel
- Provider cleanup performed before each test to ensure clean state
- Requires a valid Vercel API Token with read permissions to the resources to assess
---
## Test Case: `PROVIDER-E2E-019` - Add Okta Provider with OAuth 2.0 Private Key JWT Credentials
**Priority:** `critical`
**Tags:**
- type → @e2e, @serial
- feature → @providers
- provider → @okta
**Description/Objective:** Validates the complete flow of adding a new Okta provider using OAuth 2.0 Private Key JWT authentication (Client ID and PEM-encoded private key) tied to an Okta Org Domain.
**Preconditions:**
- Admin user authentication required (admin.auth.setup setup)
- Environment variables configured: E2E_OKTA_DOMAIN, E2E_OKTA_CLIENT_ID, E2E_OKTA_BASE64_PRIVATE_KEY
- Remove any existing provider with the same Org Domain before starting the test
- This test must be run serially and never in parallel with other tests, as it requires the Org Domain not to be already registered beforehand.
### Flow Steps
1. Navigate to providers page
2. Click "Add Provider" button
3. Select Okta provider type
4. Fill provider details (org domain and alias)
5. Verify Okta credentials page is loaded
6. Fill Okta credentials (client ID and PEM-encoded private key)
7. Launch initial scan
8. Verify redirect to Scans page
9. Verify scheduled scan status in Scans table (provider exists and scan name is "scheduled scan")
### Expected Result
- Okta provider successfully added with OAuth 2.0 Private Key JWT credentials
- Initial scan launched successfully
- User redirected to Scans page
- Scheduled scan appears in Scans table with correct provider and scan name
### Key verification points
- Provider page loads correctly
- Connect account page displays Okta option
- Provider details form accepts org domain (Okta-managed domain, e.g. acme.okta.com) and alias
- Credentials page loads with Client ID input and Private Key textarea
- Credentials are properly filled in the correct fields
- Launch scan page appears
- Successful redirect to Scans page after scan launch
- Provider exists in Scans table (verified by org domain)
- Scan name field contains "scheduled scan"
### Notes
- Test uses environment variables for Okta credentials
- Org Domain is lowercased by the form; cleanup and scan verification use the normalized value
- Private Key is provided as base64-encoded PEM content and decoded before use (multi-line content)
- Provider cleanup performed before each test to ensure clean state
- Requires a valid Okta API Services app with a registered public key (JWK) matching the provided private key
+95
View File
@@ -30,6 +30,9 @@ import {
GoogleWorkspaceProviderData,
GoogleWorkspaceProviderCredential,
GOOGLEWORKSPACE_CREDENTIAL_OPTIONS,
OktaProviderData,
OktaProviderCredential,
OKTA_CREDENTIAL_OPTIONS,
VercelProviderData,
VercelProviderCredential,
VERCEL_CREDENTIAL_OPTIONS,
@@ -1517,6 +1520,98 @@ test.describe("Add Provider", () => {
},
);
});
test.describe.serial("Add Okta Provider", () => {
let providersPage: ProvidersPage;
let scansPage: ScansPage;
// Test data from environment variables
// Org Domain is lowercased by the form, so normalize here to match the
// stored provider UID used for cleanup and scan verification.
const orgDomain = (process.env.E2E_OKTA_DOMAIN ?? "").toLowerCase();
const clientId = process.env.E2E_OKTA_CLIENT_ID ?? "";
const privateKeyB64 = process.env.E2E_OKTA_BASE64_PRIVATE_KEY ?? "";
// Setup before each test
test.beforeEach(async ({ page }) => {
test.skip(
!orgDomain || !clientId || !privateKeyB64,
"Okta E2E env vars are not set",
);
providersPage = new ProvidersPage(page);
await deleteProviderIfExists(providersPage, orgDomain!);
});
// Use admin authentication for provider management
test.use({ storageState: "playwright/.auth/admin_user.json" });
test(
"should add a new Okta provider with OAuth 2.0 Private Key JWT credentials",
{
tag: [
"@critical",
"@e2e",
"@providers",
"@okta",
"@serial",
"@PROVIDER-E2E-019",
],
},
async ({ page }) => {
// The Okta app private key is PEM-encoded (multi-line), so it is passed
// base64-encoded via the environment variable and decoded here.
const privateKey = Buffer.from(privateKeyB64, "base64").toString(
"utf8",
);
// Prepare test data for Okta provider
const oktaProviderData: OktaProviderData = {
orgDomain: orgDomain,
alias: "Test E2E Okta Account - Private Key JWT",
};
// Prepare OAuth 2.0 Private Key JWT credentials
const oktaCredentials: OktaProviderCredential = {
type: OKTA_CREDENTIAL_OPTIONS.OKTA_PRIVATE_KEY_JWT,
clientId: clientId,
privateKey: privateKey,
};
// Navigate to providers page
await providersPage.goto();
await providersPage.verifyPageLoaded();
// Start adding new provider
await providersPage.clickAddProvider();
await providersPage.verifyConnectAccountPageLoaded();
// Select Okta provider
await providersPage.selectOktaProvider();
// Fill provider details (org domain and alias)
await providersPage.fillOktaProviderDetails(oktaProviderData);
await providersPage.clickNext();
// Verify Okta credentials page is loaded
await providersPage.verifyOktaCredentialsPageLoaded();
// Fill OAuth 2.0 Private Key JWT credentials
await providersPage.fillOktaCredentials(oktaCredentials);
await providersPage.clickNext();
// Launch scan
await providersPage.verifyLaunchScanPageLoaded();
await providersPage.clickNext();
// Wait for redirect to scan page
scansPage = new ScansPage(page);
await scansPage.verifyPageLoaded();
// Verify scan status is "Scheduled scan"
await scansPage.verifyScheduledScanStatus(orgDomain);
},
);
});
});
test.describe("Update Provider Credentials", () => {
+5
View File
@@ -119,6 +119,11 @@ declare global {
E2E_GOOGLEWORKSPACE_SERVICE_ACCOUNT_JSON?: string;
E2E_GOOGLEWORKSPACE_DELEGATED_USER?: string;
// E2E Okta
E2E_OKTA_DOMAIN?: string;
E2E_OKTA_CLIENT_ID?: string;
E2E_OKTA_BASE64_PRIVATE_KEY?: string;
// E2E Vercel
E2E_VERCEL_TEAM_ID?: string;
E2E_VERCEL_API_TOKEN?: string;