Access API user guide

1. How authentication works

RADKit Access does not maintain its own user database. All user identities come from external Identity Providers (IdPs) — Duo, Okta, or CDNA. When you authenticate, RADKit validates your identity against the IdP and issues its own short-lived session token on your behalf.

The only partial exception is certificate-based authentication: RADKit services can authenticate using client certificates issued by the RADKit CA, where the identity is derived from the certificate subject rather than an upstream IdP. API tokens and client credentials (4. Getting long-lived credentials) are also RADKit-managed, but they are always tied to an identity that was originally established through an IdP.

Every RADKit Access API call is authenticated via the Authorization header. There are two ways to authenticate:

  • JWT credentials (API token, Duo/Okta/CDNA JWT) can be used directly on any endpoint with Authorization: JWT <token>. The server validates the credential and internally mints a short-lived session token for that request. However, the recommended pattern is to exchange first (see 2. Getting a session token) so you get a reusable Bearer session token that you can use across multiple calls without re-presenting the long-lived credential each time.

  • Client credentials (client_id / client_secret) cannot be used directly. They must always be exchanged for a session token at POST /oauth2/token.

The recommended pattern for automation is therefore:

┌─────────────────────────────┐       exchange       ┌─────────────────────┐
│  long-lived credential      │ ──────────────────►  │  session token      │
│  (API token / client creds  │                      │  (radkit_access_    │
│   / Duo or Okta JWT)        │                      │   token, 1 h TTL)   │
└─────────────────────────────┘                      └──────────┬──────────┘
                                                                 │
                                                   Authorization: Bearer <token>
                                                                 │
                                                                 ▼
                                                        API calls (GET, POST, …)

Header formats used in this guide

Header value

When used

Authorization: JWT <token>

Presenting a JWT credential directly to any endpoint, or exchanging it at POST /auth/token.

Authorization: Basic <base64(client_id:client_secret)>

Exchanging client credentials at POST /oauth2/token.

Authorization: Bearer <radkit_access_token>

Authenticating every API call after obtaining a session token.

Note

The token exchange response contains two fields — access_token and radkit_access_token. For all programmatic flows (API token, client credentials, direct JWT exchange) both fields carry the same RADKit-signed JWT. Always use radkit_access_token as the Bearer token; access_token exists for integrations that expect a standard OAuth2 response shape.

2. Getting a session token

2.1 From an API token or an external JWT (Duo / Okta / CDNA)

Pass any JWT-based credential using the JWT scheme. This covers:

  • RADKit API tokens (see 4.1 API tokens (recommended for automation) for how to generate one)

  • Duo / Okta / CDNA JWTs obtained through your SSO provider

  • Method: POST

  • URL: /auth/token

  • Auth: Authorization: JWT <your_token>

Example:

curl -X POST 'https://{{baseURL}}/auth/token' \
  -H 'Authorization: JWT eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9...'

Success response (HTTP 200):

{
  "access_token": "eyJhbGciOi...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_remaining_lifetime": 600,
  "radkit_access_token": "eyJhbGciOi...",
  "identity": "automation@example.com",
  "admin_level": 0
}

Error response (HTTP 401):

{
  "success": false,
  "details": "Invalid credentials",
  "retry": false
}

2.2 From client credentials (OAuth2 client credentials grant)

Pass client_id:client_secret as HTTP Basic auth (Base64-encoded).

  • Method: POST

  • URL: /oauth2/token

  • Auth: Authorization: Basic <base64(client_id:client_secret)>

  • Query / form param: grant_type=client_credentials

Example:

curl -X POST 'https://{{baseURL}}/oauth2/token?grant_type=client_credentials' \
  -H 'Authorization: Basic bXlfY2xpZW50X2lkOm15X3NlY3JldA=='

Success response (HTTP 200):

{
  "access_token": "eyJhbGciOi...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": ""
}

Note

The /oauth2/token response uses the standard OAuth2 shape and does not include a separate radkit_access_token field. The returned access_token is a RADKit-signed JWT — use it directly as Authorization: Bearer <access_token>.

3. Using the session token

Include the session token in every API call:

Authorization: Bearer <radkit_access_token>

Example:

curl 'https://{{baseURL}}/public/admin/service/status/my-service-id' \
  -H 'Authorization: Bearer eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9...'

Session tokens expire after 1 hour by default. Refresh (see 6.2 Refresh a session token) before expiry or re-exchange the original long-lived credential.

4. Getting long-lived credentials

You need a long-lived credential before you can obtain a session token (see 2. Getting a session token). Generating API tokens and client credentials requires elevated admin claims on your account.

Important

To obtain the required admin claims, contact the RADKit team. Without them the POST /admin/api/token and POST /admin/client_credentials endpoints will return HTTP 403.

Important

Never hard-code credentials or API tokens in source code. Store them in environment variables, a secrets manager, or a vault.

4.2 Client credentials

Client credentials provide a standard OAuth2 client_id / client_secret pair. Lifetime: 1–180 days.

Generate via the Python SDK:

from radkit_client.sync import sso_login

client = sso_login("someone@example.com")
creds = client.admin_client().generate_client_credentials()
print(creds.client_id)
print(creds.client_secret)   # shown only once — store immediately
print(creds.expires_at)

Generate via REST:

  • Method: POST

  • URL: /admin/client_credentials

  • Auth: Authorization: Bearer <radkit_access_token>

Request body:

{
  "endpoint_id": "automation@example.com",
  "lifetime": 7,
  "description": "CI pipeline token"
}

Example:

curl -X POST 'https://{{baseURL}}/admin/client_credentials' \
  -H 'Authorization: Bearer eyJhbGciOi...' \
  -H 'Content-Type: application/json' \
  -d '{"endpoint_id": "automation@example.com", "lifetime": 7, "description": "CI"}'

Success response (HTTP 200):

{
  "client_id": "550e8400-e29b-41d4-a716-446655440000",
  "client_secret": "plaintext-secret-shown-once",
  "expires_at": "2026-03-18T12:00:00Z"
}

Important

client_secret is displayed only once at creation time and is never retrievable afterwards. Store it immediately in a secure location.

Note

Tokens obtained via client credentials cannot generate further API tokens or client credentials, and cannot access most /admin/* management endpoints.

5. Revoking tokens

5.1 Revoke a session token

Immediately invalidates the current session. Subsequent calls with the same token return HTTP 401.

  • Method: DELETE

  • URL: /auth/token

  • Auth: Authorization: Bearer <radkit_access_token>

Example:

curl -X DELETE 'https://{{baseURL}}/auth/token' \
  -H 'Authorization: Bearer eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9...'

Returns HTTP 204 No Content on success.

Note

Passing radkit_access_token removes only the RADKit session — the upstream Duo / Okta token at the provider is not revoked. To revoke all active sessions including the upstream provider token, use POST /auth/oauth/logout.

5.2 Revoke a client-credentials token

Revokes a token issued by POST /oauth2/token. Requires the original client credentials.

  • Method: POST

  • URL: /oauth2/revoke

  • Auth: Authorization: Basic <base64(client_id:client_secret)>

  • Form param: token=<access_token_to_revoke>

Example:

curl -X POST 'https://{{baseURL}}/oauth2/revoke' \
  -H 'Authorization: Basic bXlfY2xpZW50X2lkOm15X3NlY3JldA==' \
  --data-urlencode 'token=eyJhbGciOi...'

Returns HTTP 204 No Content on success.

5.3 Revoke API token(s)

Revoke one or more API tokens by JWT string, by jti claim, or in bulk by issuance date. At least one of the three fields must be provided.

  • Method: DELETE

  • URL: /admin/api/token

  • Auth: Authorization: Bearer <radkit_access_token>

Request body:

{
  "tokens": ["eyJhbGciOi..."],
  "jtis": ["a1b2c3d4e5f6..."],
  "issued_before": "2026-01-01T00:00:00Z"
}

Field

Effect

tokens

Revoke by full JWT string

jtis

Revoke by the jti claim inside the JWT

issued_before

Revoke all API tokens issued before this ISO 8601 date

Example (revoke a single token by JWT):

curl -X DELETE 'https://{{baseURL}}/admin/api/token' \
  -H 'Authorization: Bearer eyJhbGciOi...' \
  -H 'Content-Type: application/json' \
  -d '{"tokens": ["eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9..."]}'

Example (revoke all tokens issued before a date):

curl -X DELETE 'https://{{baseURL}}/admin/api/token' \
  -H 'Authorization: Bearer eyJhbGciOi...' \
  -H 'Content-Type: application/json' \
  -d '{"issued_before": "2026-01-01T00:00:00Z"}'

Returns HTTP 204 No Content on success.

6. Token introspection and refresh

6.1 Inspect a session token

Returns the current state of the session token without decoding the JWT locally.

  • Method: GET

  • URL: /auth/token

  • Auth: Authorization: Bearer <radkit_access_token>

Example:

curl 'https://{{baseURL}}/auth/token' \
  -H 'Authorization: Bearer eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9...'

Response (HTTP 200):

{
  "active": true,
  "type": "client_access_token",
  "expires_at": 1741996800,
  "expires_in": 3542,
  "user_info": {
    "type": "OAuthUser",
    "id": "automation@example.com",
    "provider": "duo",
    "max_admin_level": 51,
    "claims": []
  }
}

If the token is expired or not found, active will be false.

6.2 Refresh a session token

Extends an active session without re-presenting the original credential.

  • Method: PUT

  • URL: /auth/token

  • Auth: Authorization: Bearer <radkit_access_token>

Example:

curl -X PUT 'https://{{baseURL}}/auth/token' \
  -H 'Authorization: Bearer eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9...'

Returns the same AuthResponse shape as 2.1 From an API token or an external JWT (Duo / Okta / CDNA).

Behaviour by credential type

Session origin

What refresh does

API token

Issues a new RADKit-signed radkit_access_token (1 h TTL). No contact with any external system.

Duo / Okta JWT (direct exchange, no SSO)

Issues a new RADKit-signed radkit_access_token (1 h TTL). No contact with any external system.

Client credentials

Not supported. Returns HTTP 403. Re-exchange client_id / client_secret at POST /oauth2/token to obtain a new token.

Note

Refresh is a no-op if more than 600 seconds remain on the current session — the existing token is returned unchanged. The refresh_remaining_lifetime field in every AuthResponse tells you this threshold so you can schedule refresh calls correctly.

7. JWKS and OIDC discovery

RADKit Access acts as an OIDC relying party — it delegates authentication to upstream providers (Duo, Okta) and issues its own short-lived JWTs signed with RS512. It is not a full OIDC identity provider: there is no authorization endpoint and no ID token.

OIDC-compatible discovery document (endpoint locations):

curl 'https://{{baseURL}}/.well-known/openid-configuration'

Response:

{
  "issuer": "https://{{baseURL}}",
  "jwks_uri": "https://{{baseURL}}/oauth2/keys",
  "token_endpoint": "https://{{baseURL}}/oauth2/token",
  "revocation_endpoint": "https://{{baseURL}}/oauth2/revoke",
  "userinfo_endpoint": "https://{{baseURL}}/oauth2/user_info"
}

JWKS — public keys for offline token verification

External services that receive a radkit_access_token can verify its signature using the public keys at /oauth2/keys without contacting RADKit Access:

curl 'https://{{baseURL}}/oauth2/keys'

Response:

{
  "keys": [
    {
      "kty": "RSA",
      "alg": "RS512",
      "use": "sig",
      "kid": "a3f2b1c4...",
      "n": "0vx7agoebG...",
      "e": "AQAB"
    }
  ]
}

Keys are rotated every 30 days; up to 6 keys are retained to cover recently issued tokens.

8. Public API endpoints

8.1 Generate a service enrollment OTP

  • Method: POST

  • URL: /public/otp/certificate

  • Auth: Authorization: Bearer <radkit_access_token>

Request body:

{
  "owner": "user@example.com",
  "endpoint_id": "endpoint-identifier",
  "description": "CI enrollment"
}

Example:

curl -X POST 'https://{{baseURL}}/public/otp/certificate' \
  -H 'Authorization: Bearer eyJhbGciOi...' \
  -H 'Content-Type: application/json' \
  -d '{"owner": "user@example.com", "endpoint_id": "my-service", "description": "CI"}'

Success response (HTTP 200):

{ "otp": "abcd-1234-efgh-5678" }

8.2 Get service status

  • Method: GET

  • URL: /public/admin/service/status/{service_id}

  • Auth: Authorization: Bearer <radkit_access_token>

Example:

curl 'https://{{baseURL}}/public/admin/service/status/123-123-123' \
  -H 'Authorization: Bearer eyJhbGciOi...'

Success response (HTTP 200):

{
  "service_id": "123-123-123",
  "active": true,
  "online": true
}

9. Common errors

Code

Meaning

Action

400

Bad request parameters

Check request body and query parameters

401

Invalid or expired token

Re-exchange your credential (see 2. Getting a session token)

403

Forbidden

Verify account claims / permissions

404

Resource not found

Check service ID or resource path

500

Server error

Contact RADKit Access Cloud support

10. Appendix

  • Base URL: replace {{baseURL}} with your environment URL, e.g. https://devel.radkit-cloud.cisco.com.

  • Session token TTL: 3600 s (1 hour) by default.

  • Refresh window: session refreshed only if < 600 s remain (PUT /auth/token).

  • API token max lifetime: 180 days; max 50 active tokens per user.

  • Client credentials max lifetime: 180 days.

  • JWT signing algorithm: RS512 (RSA-2048) by default; ES256 also supported.

  • Content-Type: application/json for all request bodies.

  • Discover endpoints at runtime: always prefer /.well-known/openid-configuration over hard-coding URLs.

  • Download Postman Collection: Download here