wif.tax

Opal Security

D Opal (access management platform API)

Workloads call the Opal API, Terraform provider and MCP servers with a static bearer token (service-user or personal); expiry is optional and Opal is not a GitHub secret-scanning partner. Into your clouds only AWS is keyless (cross-account role with External ID); GCP, Google Workspace, Azure, Okta and most SaaS connectors want a key, secret or token pasted in.

Docs: docs.opal.dev · Checked 2026-10-13 · source YAML · dispute via PR

Inbound D

How a workload running anywhere authenticates to this product.

Static bearer API token, either a personal access token (read-only or full-access, inherits the user's Opal permissions) or a token minted for a service user scoped by Opal roles; no federation or client-credentials flow exists for the REST API.

The public OpenAPI spec (app.opal.dev/openapi.yaml) declares a single security scheme, HTTP bearer. The Terraform provider takes the same static token (`OPAL_AUTH_TOKEN`); the Opal CLI uses an interactive login session, not a workload credential. Two newer paths exist only for the MCP servers and do not change the grade: MCP OAuth (beta) is interactive authorization-code + PKCE for humans, and Okta Cross App Access (limited beta) lets an Okta-registered AI agent act on behalf of a human user by redeeming an Okta ID-JAG, but the agent still authenticates to Opal's token endpoint with a static client secret, only Okta is accepted as issuer, and service users are excluded. No on-prem agent/collector authenticates back to Opal Cloud; the alternative is a fully self-hosted Opal deployment. Custom connectors are customer-hosted HTTP services that Opal calls, verified by an HMAC signing secret.

Outbound B

How this product reaches into your cloud, repos, or other SaaS.

Keyless cross-account IAM role with External ID for AWS and a GitHub App created through Opal's registration flow for GitHub organizations; GCP and Google Workspace want an uploaded service-account JSON key, Azure/Entra wants a client secret from a customer app registration, and Okta wants either a customer-generated private key or an SSWS API token.

Connectors checked: AWS (keyless: cross-account OpalIngester role with External ID, Opal's AWS account as principal; self-hosted Opal recommends IRSA but also offers IAM user access keys), GitHub organization (keyless: GitHub App created in your org via Opal's Register App flow, no secret pasted), Slack notifications on Opal Cloud (keyless: install of Opal's Slack app). Static: GCP (service-account JSON key, no impersonation/WIF option documented), Google Groups/Workspace (same JSON key plus domain-wide delegation to your own service account, not to an Opal-owned one), Azure/Entra (client secret on your own app registration, no federated credential or Opal multi-tenant app documented), Okta (private key for your own API Services app, which is short-lived-from-static-root, or a static SSWS API token), GitHub Enterprise (client secret + private key, optional classic PAT with admin:enterprise), GitLab (OAuth app secret), Slack user groups (your own Slack app's client secret), Salesforce (connected-app consumer secret plus a service user's password), Snowflake (key pair), Datadog (API + application key), Active Directory/LDAP (bind password). AWS sessions additionally need an OIDC client ID and client secret from your IdP so Opal can broker AssumeRoleWithWebIdentity for users. Keyless on AWS, so B; most other connectors hand over a long-lived secret.

Hygiene for any static credential the product still issues

✗ Expiry enforceable
Expiry is optional (`expires_at` is nullable). No organization setting to force a maximum API token lifetime is documented; the only lifetime settings found cover MCP OAuth tokens and login sessions.
  • docs.opal.dev checked 2026-10-13
    “Optional expiration time for the token.”
  • docs.opal.dev checked 2026-10-13
    “API keys for service users can be created under the API Keys tab and can be set to expire.”
✓ Rotation via API
POST /tokens, GET /tokens and DELETE /tokens/{id} exist. The 2-active-token cap per service user allows overlap rotation (create new, swap, delete old).
  • docs.opal.dev checked 2026-10-13
    “Set `user_id` to a service user's ID to mint a token that authenticates as that service user.”
  • docs.opal.dev checked 2026-10-13
    “Deletes a first-party API token. Admins can delete any token.”
✓ Scoped keys
Service users are a purpose-built machine principal with their own role assignments. Personal access tokens inherit the creating user's permissions (optionally read-only).
  • docs.opal.dev checked 2026-10-13
    “Service users have their own identity in Opal and can be assigned Opal roles to scope their permissions.”
  • docs.opal.dev checked 2026-10-13
    “Read-only tokens can only perform GET requests, limited to resources you have permission to view.”
✓ Last-used visible
The Token object returned by the API carries a nullable last_used_at field.
  • docs.opal.dev checked 2026-10-13
    “The date and time the token was last used.”
✗ Leak revocation
Opal does not appear on GitHub's secret-scanning partner list, although its tokens carry a recognizable prefix (the API reference example shows `opal_pat_`).

Notes

Grade covers Opal Cloud (app.opal.dev / api.opal.dev); self-hosted Opal uses the same token model. Docs are public (Mintlify, with llms.txt and a public OpenAPI spec), so nothing was gated. Inbound moves to C or better only if the REST API accepts a federated or client-credentials token for service users; outbound moves toward A if GCP gets impersonation/WIF, Azure gets a federated credential or an Opal multi-tenant app, and Okta/Google Workspace get an Opal-owned app.

Badge

wif.tax grade D

[![wif.tax grade](https://wif.tax/badge/opal-security.svg)](https://wif.tax/p/opal-security/)