auth.clients — M2M client lifecycle
cyoda-go version 0.8.2
auth.clients
Section titled “auth.clients”auth.clients — provision and manage machine-to-machine (M2M) clients that authenticate against cyoda via the client_credentials grant.
You want a backend service or CI job to call cyoda APIs. Register an M2M client to obtain a client_id + client_secret. Your service then mints JWTs via POST /api/oauth/token (documented in auth.tokens) and presents them as Authorization: Bearer … on every request.
Use this path when you control both the service and its cyoda registration. For user-facing flows, federate via auth.oidc instead.
PREREQUISITES
Section titled “PREREQUISITES”Admin (cyoda operator) sets up:
CYODA_IAM_MODE=jwt(mock mode bypasses auth entirely — fine for dev, never for prod)CYODA_JWT_SIGNING_KEY(PEM RSA key;_FILEsuffix supported)- Optionally:
CYODA_BOOTSTRAP_CLIENT_ID+CYODA_BOOTSTRAP_CLIENT_SECRETprovisions a single admin M2M at startup, useful for CI. Seeconfig.auth. - For admin-scoped M2M creation (
withAdminRole=true):CYODA_IAM_M2M_ADMIN_ROLE_ENABLED=true. Off by default; when off, thewithAdminRole=truerequest shape returns404 FEATURE_DISABLED.
Client (you) needs:
- An
Authorization: Bearer …token withROLE_ADMIN. Every/clientsendpoint (list, create, delete, reset-secret) requires admin role today. - The created client is scoped to the caller’s tenant — there is no per-request tenant parameter on these endpoints.
REQUEST FLOW
Section titled “REQUEST FLOW”The 4 /clients operations: provision, list, delete, reset-secret. Field names follow RFC 7591 (snake_case) for the credentials DTOs; list-item DTOs use cyoda’s customary camelCase.
Provision a client
Section titled “Provision a client”The request takes no body. withAdminRole is a query parameter; the only role the new client receives unconditionally is ROLE_M2M, and ROLE_ADMIN is added when withAdminRole=true AND the IAM feature is enabled.
curl -X POST "https://cyoda.example.com/api/clients?withAdminRole=false" \ -H "Authorization: Bearer ${ADMIN_TOKEN}"Response (200 OK) — schema TechnicalUserCredentialsDto:
{ "client_id": "abc523BCD", "client_secret": "mySecretKey123", "grant_type": "client_credentials", "client_secret_expires_at": 0, "roles": ["ROLE_M2M"]}client_secret is shown only at creation time. Capture it now; the server cannot return it again. client_secret_expires_at = 0 means the secret does not expire (per RFC 7591 §3.2.1).
List clients in the caller’s tenant
Section titled “List clients in the caller’s tenant”curl -X GET https://cyoda.example.com/api/clients \ -H "Authorization: Bearer ${ADMIN_TOKEN}"Response (200 OK) — array of TechnicalUserDto (no secrets):
[ { "clientId": "abc523BCD", "creationDate": "2026-06-17T10:02:27.88Z", "lastUpdateDate": "2026-06-17T10:02:27.88Z", "roles": ["ROLE_M2M"] }]Delete a client
Section titled “Delete a client”curl -X DELETE https://cyoda.example.com/api/clients/${CLIENT_ID} \ -H "Authorization: Bearer ${ADMIN_TOKEN}"Response (200 OK):
{ "message": "M2M client deleted successfully", "clientId": "abc523BCD"}The deleted client’s tokens remain valid until their natural exp; deletion stops new token issuance.
Reset a client secret
Section titled “Reset a client secret”Rotates client_secret for an existing client. The verb is PUT, not POST.
curl -X PUT https://cyoda.example.com/api/clients/${CLIENT_ID}/secret \ -H "Authorization: Bearer ${ADMIN_TOKEN}"Response (200 OK) is TechnicalUserCredentialsDto — same shape as creation, carrying the new client_secret. Capture it before the connection closes. Existing JWTs minted with the previous secret remain valid until their natural exp; only new /oauth/token requests need the new secret.
Clients are not tokens. After provisioning, the client uses auth.tokens (the /oauth/token endpoint) to mint JWTs from the client_id + client_secret. The JWT carries the client’s tenant in caas_org_id and its roles in user_roles. Full claim shape is in auth.tokens.
ERRORS
Section titled “ERRORS”errors.UNAUTHORIZED(401) — bearer token missing, expired, signature invalid, or issuer untrusted.errors.FORBIDDEN(403) — caller lacksROLE_ADMIN(required for every/clientsendpoint today).errors.M2M_CLIENT_NOT_FOUND(404) — referencedclientIddoes not exist or belongs to a different tenant.errors.FEATURE_DISABLED(404) —withAdminRole=truerequested withCYODA_IAM_M2M_ADMIN_ROLE_ENABLED=false.errors.BAD_REQUEST(400) — query-string parameter invalid (e.g. malformedwithAdminRolevalue).
SEE ALSO
Section titled “SEE ALSO”auth.tokens— the/oauth/tokenendpoint and JWT claim contractconfig.auth—CYODA_BOOTSTRAP_*,CYODA_IAM_M2M_ADMIN_ROLE_ENABLEDopenapi—cyoda help openapi tagsand look for theUser, Machinetag
See also
Section titled “See also”cyoda help auth— auth — authenticate client applications against cyoda.cyoda help auth tokens— auth.tokens — exchange credentials for a JWT atPOST /api/oauth/token. Covers every supported grant and the canonical JWT claim contract that all cyoda tokens (M2M, OBO, federated OIDC, trusted-key) conform to.cyoda help config auth— config.auth — IAM mode, JWT issuer, HMAC secret, and admin bootstrap controls.cyoda help errors M2M_CLIENT_NOT_FOUND— Returned by/clientsadmin endpoints when the suppliedclientIddoes not match any registered M2M client in the caller’s tenant:cyoda help errors FEATURE_DISABLED— Returned by trusted-key endpoints whenCYODA_IAM_TRUSTED_KEY_REGISTRATION_ENABLED=false(the default):cyoda help errors UNAUTHORIZED— Returned when theAuthorizationheader is missing, the bearer token is expired, the token signature is invalid, or the token was issued by an untrusted issuer. Also returned when a request reaches a protected route with no identity context established by the auth middleware.cyoda help errors FORBIDDEN— The request was authenticated successfully but the caller’s JWT claims do not include the role required by the endpoint (for example,adminis required for administrative operations). Tenant mismatch — where the caller’s tenant does not match the resource — also produces this error.
Raw formats
Section titled “Raw formats”/help/auth/clients.json— full descriptor (matchesGET /help/{topic}envelope)/help/auth/clients.md— body only