Skip to main content
Version: 2.17.1

Okta API Bearer Authentication

Authenticate programmatic access to the Halo API using OAuth 2.0 Bearer tokens issued by Okta.

Overview

Halo exposes a REST API through the API-Access service. All API requests must include a valid Bearer token in the Authorization header. Tokens are obtained from Okta using the OAuth 2.0 Client Credentials flow (machine-to-machine).

This guide walks through configuring Okta as the Identity Provider for the Halo API-Access service, then testing the integration with Bearer token authentication.

Prerequisites

  • A running Halo deployment with API-Access service configured
  • An Okta organization with admin access
  • Okta authorization server configured for Halo (see Okta Authorization Server)
  • Network access from the API client to both Okta and the Halo API endpoint

Setup

Register an API client in Okta

Create a machine-to-machine application

  1. Login to the Okta Admin Console.
  2. Navigate to Applications -> Applications -> Create App Integration -> API Services.

Screenshot: Create API Services application in Okta

  1. Provide an appropriate App integration name: e.g. Glasswall Halo API

Screenshot: API service app name in Okta

Disable DPoP (proof of possession)

  1. Navigate to the application's General tab -> General Settings section -> click Edit.
  2. Under Proof of possession, untick Require Demonstrating Proof of Possession (DPoP) header in token requests.
  3. Save.

Note: Okta enables DPoP by default for API Services applications. DPoP binds tokens to the client's cryptographic key, preventing token replay attacks. However, the Halo API-Access service does not currently support DPoP token validation, so this must be disabled for Bearer token authentication to work.

Screenshot: Disable DPoP in Okta application settings

Configure client credentials

  1. Note the Client ID and generate a Client Secret.

    export CLIENT_ID="<your-client-id>"
    export CLIENT_SECRET="<your-client-secret>"

Security Note: Store client secrets securely using a secrets manager (e.g., Azure KeyVault). Never commit secrets to source control.

Screenshot: Client ID and secret from Okta application

Configure roles (optional)

  1. If your API client needs write access to license management endpoints, ensure the role claim is configured on the authorization server with the Admin value mapped to your client (see API Roles).

Note: Without the role claim, the API defaults to the User role. See API Roles to Action Mapping for details on what each role can access.

Note issuer URI and audience

  1. From the authorization server configured in the prerequisite step:

    export OKTA_ISSUER_URI="https://<your-okta-domain>/oauth2/<authorization-server-id>"
    export VALID_AUDIENCE="api://halo"

Add access policy for API client

  1. Navigate to the authorization server's Access Policies tab (Security -> API -> select your authorization server -> Access Policies).
  2. Add a new access policy:
    • Name: e.g. API Client Access
    • Description: e.g. Access policy for Halo API clients
    • Assign to: the API Services client created above (search by application name glasswall-halo-api from step 2)
  3. Add a rule:
    • Name: e.g. Allow API Clients
    • Grant type: Client Credentials
    • Leave other settings as defaults or adjust token lifetime as needed.

Note: Access policies for the Client Credentials grant control which clients (by client_id) can request tokens. Group membership does not apply because there is no user context in this flow. Secure access to the API by restricting who has the client_id and client_secret via your secrets manager.

Screenshot: API access policy on authorization server

Configure API-Access service for Okta

The cdrplatform-api-access Helm chart must be configured to validate Bearer tokens issued by your Okta authorization server. The chart exposes authentication settings under the configuration values key.

Deploy the configuration

Note: The Authority and ValidIssuer must both be set to the Okta issuer URI. The API-Access service uses Authority to discover the JWKS (JSON Web Key Set) for token signature validation, and ValidIssuer to verify the iss claim in the token.

export HALO_DOMAIN=<your-halo-domain>
helm upgrade --install cdrplatform-api-access cdrplatform-api-access --reuse-values \
--set configuration.AuthenticationScheme="Bearer" \
--set configuration.Authentication__Schemes__Bearer__Authority="${OKTA_ISSUER_URI:?}" \
--set configuration.Authentication__Schemes__Bearer__ValidIssuer="${OKTA_ISSUER_URI:?}" \
--set configuration.Authentication__Schemes__Bearer__ValidAudiences__0="${VALID_AUDIENCE:?}" \
--set strategy.type="Recreate" \
--set ingress.enabled=true \
--set ingress.tls.enabled=true \
--set ingress.tls.domain="${HALO_DOMAIN:?}" \
--atomic

Multiple audiences (optional)

If multiple API clients or applications need to access the Halo API with different audiences, add additional ValidAudiences entries:

  --set configuration.Authentication__Schemes__Bearer__ValidAudiences__0="api://halo-api" \
--set configuration.Authentication__Schemes__Bearer__ValidAudiences__1="<second-client-id>" \
--set configuration.Authentication__Schemes__Bearer__ValidAudiences__2="<third-client-id>"

Halo API usage

Obtain a bearer token

Client Credentials flow (machine-to-machine)

Use this flow for automated systems, CI/CD pipelines, and service-to-service integrations where no user interaction is required.

export TOKEN_ENDPOINT="${OKTA_ISSUER_URI}/v1/token"

Request a token:

curl -s -X POST "${TOKEN_ENDPOINT}" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=${CLIENT_ID}" \
-d "client_secret=${CLIENT_SECRET}" \
-d "scope=halo.api"

Example response:

{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6Ikp...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "halo.api"
}

Extract the token:

export ACCESS_TOKEN=$(curl -s -X POST "${TOKEN_ENDPOINT}" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=${CLIENT_ID}" \
-d "client_secret=${CLIENT_SECRET}" \
-d "scope=halo.api" | jq -r '.access_token')

Authenticate API requests

Include the Bearer token in the Authorization header of every API request:

export HALO_API_URL="https://<your-halo-domain>"

Verify authentication

Test with a protected endpoint. A request without a token should return 401, and a request with a valid token should return 200:

# Without token - expect 401
curl -s -o /dev/null -w "%{http_code}" \
"${HALO_API_URL}/api/v1/policies"

# With token - expect 200
curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
"${HALO_API_URL}/api/v1/policies"

Example: Submit a file for processing

curl -s -X POST \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-F "file=@/path/to/document.pdf" \
"${HALO_API_URL}/api/v1/rebuild/file" \
--output rebuilt_document.pdf

Troubleshooting

SymptomCauseResolution
401 UnauthorizedToken expired or invalidRequest a new token. Verify CLIENT_ID and CLIENT_SECRET are correct.
401 Unauthorized with valid tokenAudience mismatchEnsure ValidAudiences in API-Access matches the aud claim in the token.
403 Forbidden with AdminRoleMissingMissing Admin role in tokenConfigure the role claim on the authorization server (see API Roles). Required for license management write operations.
Token request failsIncorrect token endpoint URLVerify the authorization server ID and Okta domain. Check /.well-known/openid-configuration.
invalid_scope errorOIDC scopes used with Client CredentialsUse the custom halo.api scope instead of openid, profile, or email.
invalid_dpop_proof errorDPoP not disabledDisable DPoP on the API Services application (see Disable DPoP).
Network timeoutFirewall or proxy blockingEnsure the API client can reach both the Okta token endpoint and the Halo API endpoint.