Browser authentication in Scout
Use the browserAuth fixture to authenticate UI tests. Scout uses SAML, so the same approach works across deployment types.
Available methods:
| Method | Description |
|---|---|
loginAsViewer() |
Read-only flows using the built-in viewer role |
loginAsPrivilegedUser() |
Resolves to editor, or developer for Elasticsearch serverless projects |
loginAsAdmin() |
Admin-only behavior (full access); avoid unless required |
loginAs(role) |
Log in with a specific built-in role by name (must exist in the deployment) |
loginWithCustomRole(roleDescriptor) |
Log in with a specific set of Kibana and Elasticsearch privileges |
import { tags } from '@kbn/scout';
import { test } from '../fixtures';
test.describe('My suite', { tag: tags.deploymentAgnostic }, () => {
test.beforeEach(async ({ browserAuth, pageObjects }) => {
await browserAuth.loginAsViewer();
await pageObjects.dashboard.goto();
});
// ...
});
Local runs can create on-demand identities via a trusted mock IdP. Cloud runs authenticate using pre-provisioned users (internal provisioning details live in internal AppEx QA documentation).
Use loginWithCustomRole() to test permission boundaries with least privilege:
await browserAuth.loginWithCustomRole({
kibana: [{ spaces: ['*'], base: [], feature: { discover: ['read'] } }],
elasticsearch: {
indices: [{ names: ['logstash-*'], privileges: ['read', 'view_index_metadata'] }],
},
});
If the same login/role is needed across many tests, extend browserAuth in your solution/package/plugin fixtures instead of repeating role descriptors.
This is useful because it:
- keeps role descriptors centralized (one place to review/update)
- makes tests read like intent (
loginAsPlatformEngineer()) instead of plumbing - reduces drift/typos when multiple suites need the same privileges
The Security solution’s Scout package extends browserAuth with a helper that picks the right auth strategy depending on the environment (serverless vs stateful).
// x-pack/solutions/security/packages/kbn-scout-security/src/playwright/fixtures/test/browser_auth/index.ts
import { browserAuthFixture, mergeTests } from '@kbn/scout';
import type {
BrowserAuthFixture,
ElasticsearchRoleDescriptor,
KibanaRole,
SamlAuth,
ScoutLogger,
ScoutTestConfig,
} from '@kbn/scout';
import type { RoleDescriptorsFixture } from '../../worker';
import { roleDescriptorsFixture } from '../../worker';
export interface SecurityBrowserAuthFixture extends BrowserAuthFixture {
loginAsPlatformEngineer: () => Promise<void>;
}
export const securityBrowserAuthFixture = mergeTests(
browserAuthFixture,
roleDescriptorsFixture
).extend<{
browserAuth: SecurityBrowserAuthFixture;
}>({
browserAuth: async ({ browserAuth, config, roleDescriptors, samlAuth, log }, use) => {
const loginWithCustomRole = async (role: KibanaRole | ElasticsearchRoleDescriptor) => {
await samlAuth.setCustomRole(role);
return browserAuth.loginAs(samlAuth.customRoleName);
};
const loginAsPlatformEngineer = async () => {
const roleName = 'platform_engineer';
if (!config.serverless) {
const roleDescriptor = roleDescriptors.serverless?.get(
roleName
) as ElasticsearchRoleDescriptor;
if (!roleDescriptor) throw new Error(`No role descriptors found for ${roleName}`);
log.debug(`Using "${roleName}" role to execute the test`);
return loginWithCustomRole(roleDescriptor);
}
return browserAuth.loginAs(roleName);
};
await use({ ...browserAuth, loginWithCustomRole, loginAsPlatformEngineer });
},
});
Then tests can call the helper directly (example from Security Solution):
// x-pack/solutions/security/plugins/security_solution/test/scout/ui/parallel_tests/flyout/alert_details_url_sync.spec.ts
import { spaceTest, tags } from '@kbn/scout-security';
import { CUSTOM_QUERY_RULE } from '@kbn/scout-security/src/playwright/constants/detection_rules';
spaceTest.describe(
'Expandable flyout state sync',
{ tag: [...tags.stateful.classic, ...tags.serverless.security.complete] },
() => {
spaceTest.beforeEach(async ({ browserAuth, apiServices, scoutSpace }) => {
await apiServices.detectionRule.createCustomQueryRule({
...CUSTOM_QUERY_RULE,
name: `${CUSTOM_QUERY_RULE.name}_${scoutSpace.id}_${Date.now()}`,
});
await browserAuth.loginAsPlatformEngineer();
});
}
);
Examples in the repo:
You can also extend browserAuth at the plugin level. Add helpers in your plugin’s Scout fixtures (for example <plugin-root>/test/scout/ui/fixtures/index.ts) so all specs in that plugin can call browserAuth.loginAsMyRole() without repeating role descriptors.
- Avoid
adminunless you’re explicitly testing admin-only behavior. - Prefer
loginAsPrivilegedUser()for suites that run across multiple environments. - Keep custom roles minimal and document why they exist.