An integration or script authenticates to the Dawiso REST API as an OAuth2 client, using the client-credentials grant. An integration client is a purpose-built client for unattended services: it proves its identity with a key pair it generates itself, so no secret is exchanged when it’s provisioned or when it authenticates. For the separate, secret-based OAuth2 client flow, see Machine identity for services.

How a machine identity gets its permissions

A machine identity acts as the Dawiso user it’s bound to and inherits that user’s role. Binding an integration to an administrator account grants it every administrator endpoint, not just the one it needs.

Application scopes narrow that down. An endpoint can declare a scope, and only a client granted that scope can call it. The bound user’s role is still evaluated first; the scope is an extra check on top of it, not a replacement.

Warning

A scope is checked only on endpoints that declare one. An endpoint with no scope requirement still goes by the bound user’s role.

Provision an integration client

Provisioning has two parts: the integration generates its own key pair, and an administrator registers the public half against a bound account and scopes.

Generate a key pair

Generate an EC P-256 (ES256) key pair on the machine that runs the integration. Keep the private key in your secret store; you will send only the public JWK set to the Dawiso administrator.

{
  "keys": [
    {
      "kty": "EC",
      "crv": "P-256",
      "kid": "8f14e45fceea167a5a36dedd4bea2543",
      "alg": "ES256",
      "x": "…",
      "y": "…"
    }
  ]
}

Every key needs a kid. A set containing private key material (a d member) is rejected.

Register the client

An administrator sends a POST request to /api/oauth2/integration-clients.

{
  "clientName": "Contoso user sync",
  "boundUserLogin": "technical.admin",
  "scopes": ["dawiso:user_sync"],
  "jwkSet": "{\"keys\":[{\"kty\":\"EC\",\"crv\":\"P-256\",\"kid\":\"…\",\"x\":\"…\",\"y\":\"…\"}]}",
  "accessTokenLifetimeMinutes": 10
}

The response returns the client_id. There is no client secret; the key pair is the credential. accessTokenLifetimeMinutes is optional, defaults to 10, and must be between 1 and 60.

Warning

When external user synchronization is enabled, bind the client to technical.admin. The synchronization deletes every account that isn’t in the pushed payload, except the built-in technical.admin, technical.contributor and technical.viewer logins, so a purpose-made service account would be removed by the integration’s own first run.

Request an access token

The client signs a short-lived JWT assertion with its private key and sends it to /api/oauth2/token as form fields:

Form fieldValue
grant_typeclient_credentials
client_idThe value returned by the registration endpoint
client_assertion_typeurn:ietf:params:oauth:client-assertion-type:jwt-bearer
client_assertionThe signed JWT (see below)
scopeThe scope the call needs, for example dawiso:user_sync

The assertion is signed with ES256 and needs:

  • a typ header of client-authentication+jwt
  • iss and sub set to the client_id
  • aud set to the issuer value from /.well-known/openid-configuration. This isn’t necessarily the API base address: an instance served behind a separate public host issues tokens under the public one
  • a unique jti and a short exp

Send the resulting access_token on every subsequent request as Authorization: Bearer <access_token>. An assertion signed by a key the client hasn’t registered is refused with 401 and invalid_client. Requesting a scope the client wasn’t granted fails with 400.

Rotate the key

Rotation needs no administrator: the client replaces its own key set, and only its own.

  1. Generate a new key pair.
  2. PUT /api/oauth2/integration-clients/{clientId}/keys with a set containing both public keys, authenticated with an access token obtained using the current key.
  3. Switch the integration to sign with the new key.
  4. PUT the same endpoint again with the new key alone. The old key stops working immediately.

Both keys are accepted during the overlap in step 2, so the switch causes no downtime. A client cannot change another client’s keys; the attempt is refused with 403.

Available application scopes

Dawiso defines its own scopes with the dawiso: prefix. They are additional to the standard OIDC scopes: openid, profile, email, and offline_access.

ScopeRequired by
dawiso:user_syncPOST /api/auth/external-user-sync

Scope required for the external user sync

POST /api/auth/external-user-sync requires an administrator account. When the caller presents an OAuth2 token, the call also requires the dawiso:user_sync scope. Browser sessions and existing personal-token integrations are unaffected.

The External User Sync application is the main consumer of this endpoint. For where it sits among the Dawiso authentication methods, see Authentication methods.

Compared with a personal API token

A personal API token carries the full role of its owner for as long as it stays valid, and it has to be handed to whoever operates the integration.

An integration client’s private key never leaves the machine that generated it. The access token it buys expires in minutes, and its scopes limit what it reaches until then. Prefer it for any integration.