epack install collector okta
Adds to epack.yaml, resolves dependencies, downloads binary.
Run all configured collectors and build a pack:
epack collect
Runs all collectors in epack.yaml and outputs an evidence pack.
Or add manually to epack.yaml:
collectors:
okta:
source: https://github.com/locktivity/epack-collector-okta
Then run epack install to lock and sync.
The Okta collector gathers security posture metrics from your Okta organization.
At the default trust level these are org-level aggregates (percentages 0-100,
counts, and policy settings) designed to be actionable and comparable across
organizations. Higher collection levels add per-entity detail and
additional surfaces (see Collection levels below).
The example below is trust-level output. The collected_at_level field records
the level a given artifact was produced at.
{
"schema_version": "1.0.0",
"collected_at": "2026-02-25T19:46:39Z",
"collected_at_level": "trust",
"org_domain": "company.okta.com",
"posture": {
"mfa_coverage": 85,
"mfa_phishing_resistant": 20,
"sso_coverage": 90
},
"users": {
"password_expired": 2,
"locked_out": 0,
"inactive": 15,
"suspended": 3
},
"apps": {
"provisioning_enabled": 40,
"deprovisioning_enabled": 30
},
"privileged_access": {
"privileged_users_count": 6,
"super_admin_count": 2,
"standing_privileged_users_count": 6,
"privileged_mfa_coverage_pct": 100,
"privileged_phishing_resistant_pct": 50
},
"policy": {
"policy_count": 2,
"mfa_required_all": false,
"mfa_required_any": true,
"mfa_required_coverage_pct": 33,
"session_lifetime_min_minutes": 15,
"session_lifetime_max_minutes": 1440,
"idle_timeout_min_minutes": 5,
"idle_timeout_max_minutes": 120
},
"device_access": {
"managed_device_required": true,
"managed_device_required_coverage_pct": 75,
"managed_device_required_for_admins": true,
"device_assurance_policies_count": 3
}
}
High-level security scores for quick assessment.
| Metric | Why It Matters |
|---|---|
mfa_coverage |
Account takeover protection. MFA significantly reduces credential-based attacks. Low coverage leaves accounts vulnerable to password spraying and phishing. |
mfa_phishing_resistant |
Strong authentication. Phishing-resistant factors (WebAuthn/FIDO2, U2F, and Okta FastPass) can't be phished, unlike SMS or TOTP. This is the gold standard for sensitive accounts. |
sso_coverage |
Credential sprawl reduction. Apps not using SSO require separate passwords, increasing password fatigue and reuse risk. |
User account health indicators.
| Metric | Why It Matters |
|---|---|
password_expired |
Compliance and access issues. Users with expired passwords may be locked out or using workarounds that bypass security controls. |
locked_out |
Potential attack indicator. Spikes in lockout rates may indicate brute force or credential stuffing attacks. |
inactive |
Orphan account risk. Inactive accounts (90+ days no login) are prime targets for attackers. They may belong to departed employees or unused service accounts. |
suspended |
Disabled account coverage. Suspended accounts should usually represent intentionally blocked access. Tracking the rate helps distinguish active workforce posture from dormant or intentionally disabled identities. |
Application lifecycle management health.
| Metric | Why It Matters |
|---|---|
provisioning_enabled |
Onboarding automation. Manual provisioning delays access and increases admin burden. Automated provisioning ensures consistent access based on role. |
deprovisioning_enabled |
Offboarding security. Without automated deprovisioning, departing employees retain app access. This is a major source of data breaches. |
Effective admin access across directly assigned and group-inherited role assignments.
| Metric | Why It Matters |
|---|---|
privileged_users_count |
Admin blast radius. Counts active users with any effective Okta admin role. This is the main denominator for privileged identity risk. |
super_admin_count |
Highest-risk accounts. Super admins can fully control the tenant. Keep this count as low as possible. |
standing_privileged_users_count |
Always-on privilege. In Okta, standard admin roles are effectively standing access unless additional JIT tooling is used outside the collector. |
privileged_mfa_coverage_pct |
Privileged account protection. Measures how many active admins have any MFA factor enrolled. |
privileged_phishing_resistant_pct |
Strong admin auth. Tracks phishing-resistant MFA coverage for privileged users, the highest-value identity target set. |
Aggregated security policy settings across active sign-on and MFA-enrollment evaluation.
| Metric | Why It Matters |
|---|---|
policy_count |
Policy complexity. Number of active sign-on policies. More policies mean more nuanced access control but also more complexity to audit. |
mfa_required_all |
Universal MFA enforcement. True only if all non-deprovisioned users are covered by an MFA requirement after policy evaluation. |
mfa_required_any |
Any MFA enforcement. True if at least one non-deprovisioned user is covered by an MFA requirement. |
mfa_required_coverage_pct |
Actual MFA enforcement coverage. Percentage of non-deprovisioned users covered by an MFA requirement after evaluating policy and rule scope, including group targeting. |
session_lifetime_min_minutes |
Strictest session policy. The shortest session lifetime across active sign-on rules. Indicates your most restrictive access control. |
session_lifetime_max_minutes |
Most permissive session. The longest session lifetime across active sign-on rules. Users under this policy have extended access windows. |
idle_timeout_min_minutes |
Strictest idle policy. The shortest idle timeout across active sign-on rules. Protects high-risk users from unattended sessions. |
idle_timeout_max_minutes |
Most permissive idle timeout. The longest idle timeout across active sign-on rules. Users under this policy stay logged in longer when inactive. |
Device access enforcement posture based on sign-on policy rule conditions.
| Metric | Why It Matters |
|---|---|
managed_device_required |
Device trust enforcement. True if any sign-on policy rule requires managed devices. Indicates that device-based access control is configured somewhere in the org. |
managed_device_required_coverage_pct |
Device trust coverage. Percentage of non-deprovisioned users covered by a managed device requirement after evaluating policy and rule scope, including group targeting. |
managed_device_required_for_admins |
Admin device trust. True if all privileged users are covered by a managed device requirement. Critical for protecting high-value admin accounts from untrusted endpoints. |
device_assurance_policies_count |
Device assurance policies. Number of device assurance policies defined. These policies specify device requirements (OS version, encryption, etc.) that can be referenced in sign-on rules. |
The surfaces above are the trust level. The level config knob (trust /
audit / internal) controls collection depth; levels are cumulative. See
levels.md for the full per-surface, per-level breakdown.
User profile attributes beyond login, passwords, session tokens, signing keys,
client secrets, and hook auth headers are never emitted at any level.
When a surface can't be collected (a missing scope, an unlicensed feature, or an
insufficient admin role), the run still succeeds and records why under a
top-level diagnostics.warnings array.
OAuth 2.0 with private key JWT is the recommended authentication method. It provides:
- Scoped access (only the permissions you need)
- Short-lived tokens
- Better audit logging
~/.okta/epack-private-key.pem)abc123xyz...). You'll need this for your epack config.Go to the Okta API Scopes tab
The scopes you need depend on the collection level. A scope you
don't grant isn't fatal: the surfaces that need it are skipped with a
diagnostics.warnings entry rather than failing the run.
Click Grant next to each scope you need:
Trust level (the default):
- okta.users.read
- okta.groups.read
- okta.apps.read
- okta.policies.read
- okta.roles.read
- okta.deviceAssurance.read (device-access surface; needs the Device Assurance feature)
Audit and internal additionally need: okta.authenticators.read,
okta.networkZones.read, okta.idps.read, okta.eventHooks.read,
okta.inlineHooks.read, okta.logs.read, okta.orgs.read,
okta.apiTokens.read.
OAuth scopes alone aren't enough - the service app needs an admin role to access org-wide resources.
Privileged-access note: listing who holds admin roles is gated behind a
Super Administrator-level role; Read-Only Administrator cannot. With Read-Only
Admin, the privileged_access surface is skipped with a diagnostic. Grant the
app Super Administrator if you need privileged-user counts; otherwise
Read-Only Admin is sufficient for every other surface.
collectors:
okta:
source: locktivity/epack-collector-okta@^0.2
config:
org_domain: your-org.okta.com
client_id: 0oa1234567890abcdef # From app settings
key_id: abc123xyz # From public key settings
level: trust # trust (default) | audit | internal
secrets:
- OKTA_PRIVATE_KEY # PEM-encoded private key
Set the environment variable (use the PEM file you downloaded):
export OKTA_PRIVATE_KEY="$(cat ~/.okta/epack-private-key.pem)"
API tokens are simpler to set up but less secure:
- Tokens don't expire automatically
- Tokens have full permissions of the user who created them
- Harder to audit
collectors:
okta:
source: locktivity/epack-collector-okta@^0.2
config:
org_domain: your-org.okta.com
secrets:
- OKTA_API_TOKEN
Set the environment variable:
export OKTA_API_TOKEN="00abcdef..."
| Option | Required | Description |
|---|---|---|
org_domain |
Yes | Your Okta organization domain (e.g., company.okta.com) |
client_id |
For OAuth | OAuth 2.0 client ID from your service app |
key_id |
For OAuth | Key ID from the public key settings in your Okta app |
level |
No | Collection depth: trust (default), audit, or internal. See levels.md. Unrecognized values fall back to trust. |
| Variable | Description |
|---|---|
OKTA_PRIVATE_KEY |
PEM-encoded RSA private key for OAuth 2.0 |
OKTA_API_TOKEN |
SSWS API token (legacy authentication) |
Ensure either:
- client_id, key_id config and OKTA_PRIVATE_KEY env var are all set (for OAuth), OR
- OKTA_API_TOKEN env var is set (for API token auth)
For OAuth 2.0:
- Verify the client_id is correct
- Verify the key_id matches the Key ID shown in Okta (General → Client Credentials → Public Keys)
- Ensure the private key matches the public key configured in Okta (they're a matched pair)
- Make sure DPoP is disabled
(A missing OAuth scope does not fail the token exchange — Okta drops ungranted
scopes from the issued token, and the affected surface degrades; see "Missing
data" below.)
The collector handles rate limits automatically with exponential backoff. If you see persistent rate limit errors:
- Reduce collection frequency
- Contact Okta support to increase rate limits
When a surface can't be collected, the run still succeeds and records why under
diagnostics.warnings in the output. Common causes:
privileged_access is zero. Listing admin-role assignments needs a Super
Administrator-level role; Read-Only Admin can't (see the privileged-access note
in Step 4).device_access is empty. Needs okta.deviceAssurance.read and the Device
Assurance feature.stream: myorg/okta-posture
collectors:
okta:
source: locktivity/epack-collector-okta@^0.2
config:
org_domain: company.okta.com
client_id: 0oa1234567890abcdef
key_id: abc123xyz
level: trust # optional: trust (default) | audit | internal — see levels.md
secrets:
- OKTA_PRIVATE_KEY
Then run:
export OKTA_PRIVATE_KEY="$(cat /path/to/private-key.pem)"
epack collect
See Configuration for OAuth 2.0 setup instructions.
stream: myorg/okta-posture
collectors:
okta:
source: locktivity/epack-collector-okta@^0.2
config:
org_domain: company.okta.com
secrets:
- OKTA_API_TOKEN
Then run:
export OKTA_API_TOKEN=00abcdef...
epack collect
{
"protocol_version": 1,
"data": {
"schema_version": "1.0.0",
"collected_at": "2026-02-25T14:00:00Z",
"collected_at_level": "trust",
"org_domain": "company.okta.com",
"posture": {
"mfa_coverage": 85,
"mfa_phishing_resistant": 20,
"sso_coverage": 90
},
"users": {
"password_expired": 2,
"locked_out": 0,
"inactive": 15,
"suspended": 3
},
"apps": {
"provisioning_enabled": 40,
"deprovisioning_enabled": 30
},
"privileged_access": {
"privileged_users_count": 6,
"super_admin_count": 2,
"standing_privileged_users_count": 6,
"privileged_mfa_coverage_pct": 100,
"privileged_phishing_resistant_pct": 50
},
"policy": {
"policy_count": 2,
"mfa_required_all": false,
"mfa_required_any": true,
"mfa_required_coverage_pct": 33,
"session_lifetime_min_minutes": 15,
"session_lifetime_max_minutes": 1440,
"idle_timeout_min_minutes": 5,
"idle_timeout_max_minutes": 120
}
}
}
This is trust-level output (org-level aggregates; coverage values are percentages
0-100). The audit and internal levels add per-entity inventories and extra
surfaces. See Overview for metric descriptions and levels.md
for the per-level breakdown.
name: Collect Evidence
on:
schedule:
- cron: "0 0 * * 1" # Weekly on Monday
workflow_dispatch:
jobs:
collect:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install epack
run: |
curl -sSL https://github.com/locktivity/epack/releases/latest/download/epack-linux-amd64 -o epack
chmod +x epack
sudo mv epack /usr/local/bin/
- name: Collect evidence
run: epack collect --frozen
env:
OKTA_PRIVATE_KEY: ${{ secrets.OKTA_PRIVATE_KEY }}
- name: Upload pack
uses: actions/upload-artifact@v4
with:
name: evidence-pack
path: "*.pack"
Store the Okta private key as a repository secret named OKTA_PRIVATE_KEY.
To collect from multiple Okta orgs, define separate collectors:
stream: mycompany/identity-posture
collectors:
okta-prod:
source: locktivity/epack-collector-okta@^0.2
config:
org_domain: company.okta.com
client_id: 0oa1234567890abcdef
key_id: prodkey123
secrets:
- OKTA_PROD_PRIVATE_KEY
okta-preview:
source: locktivity/epack-collector-okta@^0.2
config:
org_domain: company.oktapreview.com
client_id: 0oa0987654321fedcba
key_id: previewkey456
secrets:
- OKTA_PREVIEW_PRIVATE_KEY
Then set both environment variables:
export OKTA_PROD_PRIVATE_KEY="$(cat prod-key.pem)"
export OKTA_PREVIEW_PRIVATE_KEY="$(cat preview-key.pem)"
epack collect
**Full Changelog**: https://github.com/locktivity/epack-collector-okta/compare/v0.1.7...v0.2.0
**Full Changelog**: https://github.com/locktivity/epack-collector-okta/compare/v0.1.6...v0.1.7
**Full Changelog**: https://github.com/locktivity/epack-collector-okta/compare/v0.1.5...v0.1.6
**Full Changelog**: https://github.com/locktivity/epack-collector-okta/compare/v0.1.4...v0.1.5
**Full Changelog**: https://github.com/locktivity/epack-collector-okta/compare/v0.1.2...v0.1.4
**Full Changelog**: https://github.com/locktivity/epack-collector-okta/compare/v0.1.0...v0.1.3
**Full Changelog**: https://github.com/locktivity/epack-collector-okta/compare/v0.1.0...v0.1.2
**Full Changelog**: https://github.com/locktivity/epack-collector-okta/commits/v0.1.1
**Full Changelog**: https://github.com/locktivity/epack-collector-okta/commits/v0.1.0