feat(okta): add provider to the SDK with 1 security check (#11079)

This commit is contained in:
Daniel Barranquero authored and GitHub committed 2026-05-13 15:57:57 +02:00
1 parent 934d995661
commit 4dd5baadf6
60 files changed
+2905 -68

No files matched your search

@@ -158,6 +158,15 @@ The following list includes all the Vercel checks with configurable variables th
| `team_member_role_least_privilege` | `max_owners` | Integer |
| `team_no_stale_invitations` | `stale_invitation_threshold_days` | Integer |
## Okta
### Configurable Checks
The following list includes all the Okta checks with configurable variables that can be changed in the configuration YAML file:
| Check Name | Value | Type |
|---------------------------------------------------------------|------------------------------------|---------|
| `signon_global_session_idle_timeout_15min` | `okta_max_session_idle_minutes` | Integer |
## Config YAML File Structure
<Note>
@@ -0,0 +1,186 @@
---
title: 'Okta Authentication in Prowler'
---
import { VersionBadge } from "/snippets/version-badge.mdx"
<VersionBadge version="5.27.0" />
Prowler authenticates to Okta as a **service application** using **OAuth 2.0 with a private-key JWT** (Client Credentials grant). The integration is read-only by scope and follows DISA STIG guidance for least-privilege access.
## Common Setup
### Prerequisites
- An Okta organization. The UI examples below use **Identity Engine** terminology such as **Global Session Policy**; Classic Engine exposes equivalent sign-on policy concepts under older naming.
- A **Super Administrator** account on that organization for the one-time service-app setup.
- An **API Services** app integration created in the Okta Admin Console.
### Authentication Method Overview
| Method | Status | Use Case |
|---|---|---|
| **OAuth 2.0 (private-key JWT)** | Supported | Production scans, CI/CD, Prowler App. |
The private-key JWT flow is the only supported authentication method in the initial release. The service application proves possession of a private key on every token request; Okta returns a short-lived access token, refreshed automatically by the SDK.
<Note>
If a different authentication method is needed (SSWS API token, OAuth with user delegation, etc.), please open a [feature request](https://github.com/prowler-cloud/prowler/issues/new?template=feature-request.yml) describing the use case.
</Note>
### Required OAuth Scopes
For the initial check (`signon_global_session_idle_timeout_15min`) only one scope is required:
- `okta.policies.read`
Additional scopes will be needed as more services and checks are added, this are the current ones needed:
| Scope | Used by |
|---|---|
| `okta.policies.read` | Sign-on / password / authentication policies |
### Required Admin Role
The service application must be assigned the built-in **Read-Only Administrator** role.
Okta's Management API enforces a two-layer authorization model: an OAuth **scope** decides which API endpoints the token can call, and an **admin role** decides whether the call returns data. With only a scope granted, the token mint succeeds but every read returns `403 Forbidden`. The Read-Only Administrator role is the minimum that lets the granted `okta.*.read` scopes actually return configuration data to Prowler's checks — without it, the credential probe at provider startup fails and the scan never gets to evaluate any check.
Read-Only Administrator is intentionally the narrowest role that satisfies this requirement and aligns with the least-privilege guidance in DISA STIG.
## Step-by-Step Setup
### 1. Go to the admin console
![Okta — admin console page](/user-guide/providers/okta/images/select-admin-console.png)
### 2. [Optional] - Disable the privilege-escalation bypass (org-wide, one-time)
In the Okta Admin Console, go to **Settings → Account → Public client app admins** and ensure it is **off**. When enabled, every API Services app can be auto-assigned the Super Administrator role after scopes are granted, which would invalidate the read-only premise of this integration.
![Okta — disable Public client app admins](/user-guide/providers/okta/images/public-client-app-admins.png)
### 3. Create the API Services app
1. Go to **Applications → Applications**.
![Okta — create API Services app](/user-guide/providers/okta/images/go-to-applications.png)
2. **Create App Integration**
![Okta — create App integration](/user-guide/providers/okta/images/create-new-application.png)
3. Sign-in method: **API Services**. Click **Next**.
4. Name the app (for example, `Prowler Scanner`) and click **Save**.
5. Copy the displayed **Client ID** — you'll use it as `OKTA_CLIENT_ID`.
![Okta — copy client id](/user-guide/providers/okta/images/copy-client-id.png)
### 4. Switch to private-key authentication and generate a keypair
On the new app's **General** tab, scroll to **Client Credentials**:
1. Click **Edit**.
2. Set **Client authentication** to **Public key / Private key**.
3. Under **Public Keys**, click **Add key**.
4. In the modal, click **Generate new key**. Okta creates a JWK pair.
5. Click the **PEM** tab to switch the displayed format (or keep JWK — Prowler accepts both).
6. Copy the entire `-----BEGIN PRIVATE KEY-----` block (or the JWK JSON).
7. Click **Done**, then **Save**.
<Warning>
Okta displays the private key **only once**. If you close the modal without copying, you must generate a new key.
</Warning>
![Okta — create Public Key](/user-guide/providers/okta/images/create-public-key.png)
### 5. Grant the required OAuth scopes
On the app, open the **Okta API Scopes** tab and click **Grant** on every scope Prowler needs. For the initial release, granting only `okta.policies.read` is sufficient.
![Okta — grant OAuth scopes](/user-guide/providers/okta/images/grant-permissions.png)
### 6. Assign the Read-Only Administrator role
On the app, open the **Admin roles** tab and click **Edit assignments → Add assignment**:
- **Role:** Read-Only Administrator
- **Resources:** All resources
Save the changes.
![Okta — grant Read-Only role](/user-guide/providers/okta/images/grant-roles.png)
### 7. [Optional] Verify DPoP setting
Prowler sends DPoP (Demonstrating Proof of Possession) proofs on every token request. The integration works whether the **Require Demonstrating Proof of Possession (DPoP) header in token requests** setting on the service app is on or off — but enabling it is the more secure default.
## Prowler CLI Authentication
### Using Environment Variables (Required for Secrets)
Private key material **must** be supplied via environment variables — Prowler does not accept secrets through CLI flags.
```bash
export OKTA_ORG_DOMAIN="YOUR-ORG.okta.com"
export OKTA_CLIENT_ID="0oa1234567890abcdef"
# Either of the two — content takes precedence over file when both are set.
export OKTA_PRIVATE_KEY_FILE="/secure/path/to/prowler-okta.pem"
# or
export OKTA_PRIVATE_KEY="$(cat /secure/path/to/prowler-okta.pem)"
# Optional — defaults to "okta.policies.read"
export OKTA_SCOPES="okta.policies.read"
poetry run python prowler-cli.py okta
```
### Non-Secret CLI Flags
Non-secret values are also available as CLI flags for ergonomic overrides:
| Flag | Equivalent env var |
|---|---|
| `--okta-org-domain` | `OKTA_ORG_DOMAIN` |
| `--okta-client-id` | `OKTA_CLIENT_ID` |
| `--okta-scopes` | `OKTA_SCOPES` |
Run a single check directly:
```bash
poetry run python prowler-cli.py okta --check signon_global_session_idle_timeout_15min
```
## Troubleshooting
### `OktaInvalidOrgDomainError`
The org domain must be `<org>.okta.com` (or `.oktapreview.com` / `.okta-emea.com` / `.okta-gov.com` / `.okta.mil` / `.okta-miltest.com` / `.trex-govcloud.com`). Pass the bare hostname only — no `https://` scheme, no path, no trailing slash. Custom (vanity) domains are not currently accepted.
### `OktaPrivateKeyFileError`
The file at `OKTA_PRIVATE_KEY_FILE` is missing, unreadable, or empty. Confirm the path and that the file contains a non-empty PEM block or JWK JSON document.
### `OktaInvalidCredentialsError` at provider init
Prowler validates credentials at startup by listing one sign-on policy. This error indicates the credential material itself was rejected:
- **`invalid_client`** — the public key registered in Okta does not match the private key on disk. Generate a fresh keypair and try again.
### `OktaInsufficientPermissionsError` at provider init
Raised when the credential probe succeeds at the OAuth layer but the request is rejected because the service app lacks the required scope or admin role:
- **`invalid_scope`** — the `okta.policies.read` scope is not granted on the service app. Grant it from **Okta API Scopes**.
- **`Forbidden` / `not authorized`** — the **Read-Only Administrator** role is not assigned to the service app. Assign it from **Admin roles**.
### `invalid_dpop_proof`
The org or the service app requires DPoP. The provider always sends DPoP proofs, so this error indicates the SDK could not build a valid proof — typically because the private key on disk does not match the public key uploaded to Okta. Regenerate the keypair.
## Additional Resources
- [Implement OAuth 2.0 for an Okta service app](https://developer.okta.com/docs/guides/implement-oauth-for-okta-serviceapp/main/)
- [Okta Policy API reference](https://developer.okta.com/docs/api/openapi/okta-management/management/tag/Policy/)
- [DISA STIG for Okta (V-273186)](https://stigviewer.com/stigs/okta/)
@@ -0,0 +1,144 @@
---
title: 'Getting Started With Okta on Prowler'
---
import { VersionBadge } from "/snippets/version-badge.mdx"
Prowler for Okta scans an Okta organization for identity and session-management misconfigurations. The provider authenticates as a service application using **OAuth 2.0 with a private-key JWT** (Client Credentials grant) — no end-user login, read-only by scope.
## Prerequisites
Set up authentication for Okta with the [Okta Authentication](/user-guide/providers/okta/authentication) guide before starting:
- An Okta organization. The UI examples below use **Identity Engine** terminology such as **Global Session Policy**; Classic Engine exposes the equivalent sign-on policy concepts under older names.
- A **Super Administrator** account on that organization for the one-time service-app setup.
- An **API Services** app integration in the Okta Admin Console with the `okta.policies.read` scope granted and the **Read-Only Administrator** role assigned.
- Python 3.10+ and Prowler 5.27.0 or later installed locally.
<CardGroup cols={2}>
<Card title="Prowler Cloud" icon="cloud" href="#prowler-cloud">
Onboard Okta using Prowler Cloud
</Card>
<Card title="Prowler CLI" icon="terminal" href="#prowler-cli">
Onboard Okta using Prowler CLI
</Card>
</CardGroup>
## Prowler Cloud
<Note>
Prowler Cloud onboarding for Okta is coming soon. Track the [Prowler GitHub repository](https://github.com/prowler-cloud/prowler) for release updates. Use the [Prowler CLI](#prowler-cli) workflow below in the meantime.
</Note>
---
## Prowler CLI
<VersionBadge version="5.27.0" />
### Step 1: Set Up Authentication
Follow the [Okta Authentication](/user-guide/providers/okta/authentication) guide to create the service application, generate a keypair, grant scopes, and assign the Read-Only Administrator role. Then export the credentials:
```bash
export OKTA_ORG_DOMAIN="acme.okta.com"
export OKTA_CLIENT_ID="0oa1234567890abcdef"
export OKTA_PRIVATE_KEY_FILE="/secure/path/to/prowler-okta.pem"
# Optional — defaults to "okta.policies.read"
export OKTA_SCOPES="okta.policies.read"
```
The private key file may contain either a PEM-encoded RSA key or a JWK JSON document.
#### Supplying the Private Key as Content
For automated environments where writing the key to disk is not desirable (CI runners, container secrets, etc.), the private key may be passed directly as a string:
```bash
export OKTA_ORG_DOMAIN="acme.okta.com"
export OKTA_CLIENT_ID="0oa1234567890abcdef"
export OKTA_PRIVATE_KEY="$(cat /secure/path/to/prowler-okta.pem)"
```
`OKTA_PRIVATE_KEY` takes precedence over `OKTA_PRIVATE_KEY_FILE` when both are set. The private key is intentionally not exposed as a CLI flag — secrets must be supplied via environment variables only.
### Step 2: Run the First Scan
Run a baseline scan after credentials are configured:
```bash
prowler okta
```
Or run a specific check directly:
```bash
prowler okta --check signon_global_session_idle_timeout_15min
```
Prowler prints a summary table; full findings are written to the configured output formats.
### Step 3: Use a Custom Configuration (Optional)
Prowler uses a configuration file to customize check thresholds. The Okta configuration currently includes:
```yaml
okta:
# okta.signon_global_session_idle_timeout_15min
# Defaults to 15 minutes per DISA STIG V-273186.
okta_max_session_idle_minutes: 15
```
To use a custom configuration:
```bash
prowler okta --config-file /path/to/config.yaml
```
## Supported Services
Prowler for Okta includes security checks across the following services:
| Service | Description |
| ----------- | ----------------------------------------------------------------------------------- |
| **Sign-On** | Global session policy controls (idle timeout, lifetime, rule priority and ordering) |
## Troubleshooting
### STIG Rule Ordering
The initial check is mapped to DISA STIG `V-273186` / `OKTA-APP-000020`. Prowler implements the STIG procedure as written: the **Default Policy** must have a **Priority 1** rule that is **not** `Default Rule`, and that rule must set **Maximum Okta global session idle time** to 15 minutes or less.
This is stricter than simply finding the same timeout value somewhere else in the policy set. A compliant custom rule in another policy, or a compliant timeout on the built-in `Default Rule`, does not satisfy this STIG procedure.
### Default Scopes
Prowler requests a fixed set of OAuth scopes on every token exchange. The default is a single scope that covers the bundled initial check:
- `okta.policies.read`
The service app must have that scope granted in the **Okta API Scopes** tab. When the granted set is narrower than the requested set, the token request fails with an `invalid_scope` error and the scan stops at provider initialization.
When additional checks are enabled — or when running against a service app that exposes a different scope set — override the default with `OKTA_SCOPES` (comma-separated string for the env var) or `--okta-scopes` (space-separated list for the CLI):
```bash
# Environment variable — comma-separated
export OKTA_SCOPES="okta.policies.read,okta.apps.read,okta.users.read"
# CLI flag — space-separated
prowler okta --okta-scopes okta.policies.read okta.apps.read okta.users.read
```
For the full catalog of OAuth scopes exposed by the Okta Management API, refer to the [Okta OAuth 2.0 scopes documentation](https://developer.okta.com/docs/api/oauth2/).
<Note>
As new services and checks land in the Okta provider, the default scope list grows alongside them. Re-check the granted scopes on the service app after each Prowler upgrade and grant any newly required `okta.*.read` scopes in the Admin Console.
</Note>
### Common Errors
- **`OktaInvalidOrgDomainError`** — the org domain must be `<org>.okta.com` (or `.oktapreview.com` / `.okta-emea.com` / `.okta-gov.com` / `.okta.mil` / `.okta-miltest.com` / `.trex-govcloud.com`). Pass the bare hostname only — no `https://` scheme, no path, no trailing slash.
- **`OktaPrivateKeyFileError`** — confirm the file is readable and contains a non-empty PEM or JWK body.
- **`OktaInsufficientPermissionsError`** — the credential probe reached Okta but the service app cannot perform the request. The error string carries `invalid_scope`, `Forbidden`, `not authorized`, or `permission`. Fix by granting the missing `okta.*.read` scope from **Okta API Scopes** and confirming the **Read-Only Administrator** role is assigned to the service app.
- **`OktaInvalidCredentialsError`** — the credential probe reached Okta but Okta rejected the JWT. Typically the private key on disk does not match the public JWK uploaded to the service app, or the JWT signing parameters are wrong. Regenerate the keypair and re-upload the public JWK.
- **Token requests failing for an unknown scope** — the app was granted a narrower scope set than `OKTA_SCOPES` requests. Either narrow `OKTA_SCOPES` or grant the missing scopes in the Admin Console.
Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB