Back to Checks
okta

Okta API tokens are not owned by Super Admin users

apitoken_not_super_admin
Service: apitokenSeverity: medium
OfficialBuilt-inVerified
Categories:identity-access
Resource Type:NotDefined

Okta API token ownership should avoid Super Admin users because API tokens inherit the admin permissions of the user that created them.

Risk

Super Admin-owned API tokens become high-impact secrets: if one is exposed, an attacker can perform broad organization administration with the token owner privileges.

Run this check with Prowler

Run in Prowler Cloud
Run
prowler okta --checks apitoken_not_super_admin

Recommendation

Use dedicated Okta service accounts for API tokens and assign only the least-privilege admin roles required; rotate and revoke tokens created by Super Admin users.

Remediation

Other

1Create a dedicated service account, assign only required admin roles, rotate the API token, and revoke Super Admin-owned tokens.

Check Code

from prowler.lib.check.models import Check, CheckReportOkta
from prowler.providers.okta.services.apitoken.api_token_client import api_token_client
from prowler.providers.okta.services.apitoken.lib.api_token_helpers import (
    missing_api_token_scope_finding,
    missing_user_roles_scope_for_token_finding,
    owner_has_super_admin,
)


class apitoken_not_super_admin(Check):
    """Ensure Okta API tokens are not owned by Super Admin users."""

    def execute(self) -> list[CheckReportOkta]:
        """Evaluate every active API token owner's assigned admin roles."""
        org_domain = api_token_client.provider.identity.org_domain
        missing_api_token_scope = api_token_client.missing_scope.get("api_tokens")
        if missing_api_token_scope:
            return [
                missing_api_token_scope_finding(
                    self.metadata(),
                    org_domain,
                    missing_api_token_scope,
                    additional_required=["okta.roles.read", "okta.groups.read"],
                )
            ]

        missing_user_roles_scope = api_token_client.missing_scope.get("user_roles")
        # `okta.groups.read` is needed to resolve admin roles inherited via
        # group membership. Without it we fall back to direct-only role
        # assignments, which Okta returns for `/api/v1/users/{id}/roles` —
        # commonly empty for trial accounts where Super Admin is granted
        # through the default admin group. The finding stays evaluable but
        # is flagged as best-effort so operators know to grant the scope.
        missing_user_groups_scope = api_token_client.missing_scope.get("user_groups")
        findings: list[CheckReportOkta] = []
        for token in api_token_client.api_tokens.values():
            report = CheckReportOkta(
                metadata=self.metadata(), resource=token, org_domain=org_domain
            )
            if missing_user_roles_scope:
                report = missing_user_roles_scope_for_token_finding(
                    self.metadata(), org_domain, token, missing_user_roles_scope
                )
            elif owner_has_super_admin(token):
                report.status = "FAIL"
                report.status_extended = (
                    f"API token {token.name} is owned by user {token.user_id} "
                    "with the Super Admin role. Use a dedicated service account "
                    "with least-privilege admin roles instead."
                )
            else:
                roles = (
                    ", ".join(token.owner_roles)
                    if token.owner_roles
                    else "no admin roles returned"
                )
                caveat = (
                    " Group-inherited roles were not checked because the "
                    f"`{missing_user_groups_scope}` scope is missing — grant "
                    "it to detect Super Admin assigned via group membership."
                    if missing_user_groups_scope
                    else ""
                )
                report.status = "PASS"
                report.status_extended = (
                    f"API token {token.name} owner {token.user_id} is not "
                    f"assigned Super Admin ({roles}).{caveat}"
                )
            findings.append(report)
        return findings