CI HUB · Authentication Profile

Ci Hub Authentication

Authentication

CI HUB secures its APIs with http, apiKey, and oauth2 across 0 declared security schemes, as derived from its OpenAPI definitions.

CompanyDigital Asset ManagementContent ManagementProduct Information ManagementIntegrationConnectorsCreative ToolsMarketingBrand ManagementModel Context ProtocolCloud StorageGermany
Methods: http, apiKey, oauth2 Schemes: 0 OAuth flows: API key in: header

Security Schemes

Source

Authentication Profile

Raw ↑
generated: '2026-08-12'
method: searched
source: https://developer.ci-hub.com/access/authentication
derived_from: openapi/ci-hub-access-openapi.yml
note: >-
  CI HUB runs two entirely different authentication models side by side, and a partner touching
  both surfaces has to implement both. The Access SDK uses a proprietary partner-JWT token
  exchange with a second, per-DAM token layered on top — no OAuth, no scopes, no authorization
  endpoint. The MCP server uses standards-compliant OAuth 2.1 with discovery metadata and dynamic
  client registration. Neither is wrong; they just are not the same system, and nothing in the
  docs connects them.
summary:
  types:
  - http
  - apiKey
  - oauth2
  api_key_in:
  - header
  models: 2
  scopes: 1
  mfa_or_user_consent_step: true
models:
- id: access-sdk
  applies_to: ci-hub:access-sdk
  style: two-token — partner JWT exchange, plus a per-DAM connection token
  standard: none (JWT/JWKS building blocks, but not OAuth 2.0 or RFC 7523)
  docs: https://developer.ci-hub.com/access/authentication
  bootstrap:
    step: POST /auth/exchangeToken
    operation: exchangeToken
    presented_credential: partner-signed JWT in Authorization Bearer
    algorithm: RS256 (HS256 explicitly rejected)
    root_of_trust: >-
      the partner's published JWKS URL, registered with CI HUB at onboarding and cached by CI HUB
      for 10 minutes; the JWT `kid` must match a key in that set
    required_claims:
    - iss — the registered partner issuer, matched exactly including any trailing slash
    - aud — the registered audience, default https://api.ci-hub.com
    - sub — stable identifier for the user in the partner system
    - iat — unix seconds; up to 30s future tolerance; rejected once older than maxTokenAge
    - exp — unix seconds, must be in the future
    - email — required in the JWT or the JSON request body; drives JIT user resolution
    optional_claims:
    - given_name
    - family_name
    - name
    max_token_age: 3600 seconds by default, measured from iat
    single_use: the partner JWT is consumed once per exchange and discarded
    trap: >-
      `aud` is always https://api.ci-hub.com on EVERY environment. It identifies CI HUB in the
      partner registration and does not change with the client's baseUrl. Setting it to the host
      you are calling returns 403 cihub-sdk-audience-invalid.
  schemes:
  - name: cihubAccessToken
    type: http
    scheme: bearer
    bearerFormat: JWT
    header: Authorization
    algorithm: HS256
    lifetime: 1 hour
    required_on: every call after the exchange
    description: >-
      CI HUB access token returned by POST /auth/exchangeToken. Identifies the user inside CI HUB.
      On POST /auth/exchangeToken itself this header instead carries the partner-signed RS256 JWT.
    sources:
    - openapi/ci-hub-access-openapi.yml
  - name: damToken
    type: apiKey
    in: header
    parameter: provider-authorization
    format: Bearer <DAM connection token>
    lifetime: provider-dependent; not reported at login
    required_on: >-
      calls that reach a specific DAM — folder browse, search, asset detail, versions, download,
      thumbnail
    description: >-
      DAM connection token obtained from the provider login flow. Authorizes access to the DAM
      itself, which authenticates separately from CI HUB. The partner platform obtains and stores
      these; CI HUB does not persist them server-side.
    overload_warning: >-
      On GET /auth/refreshToken this same header carries the CI HUB REFRESH token rather than a DAM
      token, and the refresh token's sub must match the access token's sub. It is the one place the
      two-token rule does not hold.
    sources:
    - openapi/ci-hub-access-openapi.yml
  token_lifetimes:
  - token: partner JWT
    lifetime: up to maxTokenAge (1 hour default)
    algorithm: RS256
    storage: minted per exchange, discarded
  - token: CI HUB access token
    lifetime: 1 hour
    algorithm: HS256
    storage: cached by the partner, sent on every call
  - token: CI HUB refresh token
    lifetime: 30 days
    algorithm: HS256
    storage: cached by the partner, used to mint new access tokens
  - token: DAM connection token
    lifetime: provider-dependent, not reported
    storage: held by the partner platform; not persisted by CI HUB
  refresh:
    operation: refreshTokenSdk
    endpoint: GET /auth/refreshToken
    behavior: >-
      Mints a new access token AND a new refresh token. The previous access token is superseded but
      remains valid until its exp; the previous refresh token remains valid for its full 30 days,
      so a slow client switch-over is safe. Refresh the CI HUB session proactively from expires_in;
      refresh a DAM session reactively on the first 401, since providers report no lifetime.
  dam_login:
    operations:
    - damLoginInitiate
    - damLoginPoll
    style: browser redirect at the DAM plus server-side polling on a one-time `state`
    note: >-
      Most DAMs use OAuth on their own side, so the redirect is part of the DAM's flow, not CI
      HUB's. The CI HUB token exchange itself has no browser redirect at all.
    provider_params:
      serverUrl: >-
        the DAM instance origin, read by bynder, dash, fotoware, frontify, picturepark and purered;
        supplying it removes the instance-prompt page from the user's flow
  session_end:
    operation: logoutSdk
    endpoint: GET /auth/logout
  validation:
    operation: checkTokenSdk
    endpoint: GET /auth/checkToken
    returns: >-
      the user profile envelope plus licenseState, licenseExpires and isTrialLicense describing the
      SDK subscription. A 401 means start a new exchange; a 402 means the subscription state
      changed and is an operational, not technical, problem.
- id: mcp
  applies_to: ci-hub:mcp
  style: OAuth 2.1 authorization code with PKCE
  standard: OAuth 2.1, RFC 8414, RFC 9728, RFC 7591, RFC 7636
  resource: https://mcp.ci-hub.com
  authorization_server: https://mcp-auth.ci-hub.com
  discovery:
  - https://mcp.ci-hub.com/.well-known/oauth-protected-resource
  - https://mcp-auth.ci-hub.com/.well-known/oauth-authorization-server
  - https://mcp-auth.ci-hub.com/.well-known/openid-configuration
  challenge: >-
    WWW-Authenticate: Bearer error="unauthorized", error_description="Authorization needed",
    resource_metadata="https://mcp.ci-hub.com/.well-known/oauth-protected-resource"
  grant_types:
  - authorization_code
  - refresh_token
  pkce: S256 only
  dynamic_client_registration: true
  token_endpoint_auth_methods:
  - client_secret_basic
  - client_secret_post
  - none
  scopes:
  - cihub
  second_factor: >-
    The OAuth grant is only half the authorization. Each end user separately authenticates the DAM
    accounts they want reachable, and CI HUB maps that user's existing DAM permissions onto every
    tool call. Users change what is reachable by disconnecting and reconnecting.
  see: scopes/ci-hub-scopes.yml
authorization_model:
  cihub_layer: identity only — the CI HUB token says who the user is, not what they may touch
  dam_layer: >-
    every permission decision is the DAM's. integration-forbidden (403) forwards a DAM permission
    denial verbatim, and the docs are explicit that "permission decisions are governed by the DAM".
  consequence: >-
    There is no CI HUB-side permission surface to inspect, grant or revoke. An auditor asking what
    a CI HUB token can reach has to ask each connected DAM instead.
x-evidence:
  fetched: '2026-08-12'
  checks:
  - url: https://developer.ci-hub.com/access/authentication
    http_status: 200
  - url: https://live.ci-hub.com/api/v1/system/providerInfo
    http_status: 401
    note: 'anonymous call returns errorCode cihub-access-token-missing, confirming the Authorization
      requirement'
  - url: https://mcp.ci-hub.com/.well-known/oauth-protected-resource
    http_status: 200
  - url: https://mcp-auth.ci-hub.com/.well-known/oauth-authorization-server
    http_status: 200