Browser authentication in Scout
Use the browserAuth fixture to authenticate UI tests. Scout uses SAML, so the same approach works across deployment types. For the overall auth model and which fixture uses which identity, see Authentication in Scout.
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.
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.