Create or replace the OIDC configuration of a connection
PUT /v1/connections/{connection_id}/oidc · permission connections:write
Creates or fully replaces the relying-party configuration of an enterprise OIDC connection.
- First write: send no
If-Matchheader and givesp_host(an active host of this environment). The callbackhttps://<sp_host>/sso/<connection_id>/callbackis fixed from then on. The response is201 Created. SendingIf-Matchwhile no configuration exists fails with412. - Update:
If-Matchis required. Without it the request fails with428and nothing is written, so a request without the header can never overwrite an existing configuration. If the configuration changed since you read it (or the value does not match) the request fails with412.*matches the current configuration.sp_hostmust be empty or the stored host.
The client secret is write-only: it is never returned, logged or written to the audit log. When you update without client_secret, the stored secret is kept; private_key_jwt removes it (the environment's signing key signs the client assertion). A changed issuer clears the stored discovery document; a changed issuer or client_id invalidates the last passed connection test. Sending the same configuration writes nothing. Writes are audited as connection.protocol_config_created or connection.oidc_config_updated (changed fields only; the secret appears only as client_secret_set).
On an active connection, an update that changes issuer or client_id is not applied. The whole request (a new secret included) becomes a draft revision: the response is 200 OK with the unchanged live configuration, its unchanged ETag and a revision field. Test the draft with Test a connection and revision_id, then publish it. Other updates apply directly.
Request
| Field | Type | Required | Description |
|---|---|---|---|
connection_id |
path | yes | Enterprise OIDC connection ID (conn_…). |
If-Match |
header | on update | The ETag from the last read or write. Missing on update: 428; stale: 412. Not sent on the first write. |
sp_host |
string | on first write | Active host of this environment (lower case). On update empty or the stored host. |
issuer |
string | yes | Absolute https URL without query or fragment, at most 512 bytes. |
client_id |
string | yes | 1–255 bytes, no control characters. |
client_auth_method |
string | yes | private_key_jwt (recommended; no secret is stored), client_secret_basic or client_secret_post. |
client_secret |
string | no | Write-only, at most 1024 bytes. Kept when omitted on update. Not allowed with private_key_jwt. |
client_secret_expires_at |
string | null | no | When the identity provider expires the secret (RFC 3339). Only together with client_secret. |
provider_profile |
string | no | entra, okta, google, keycloak, ping, onelogin, jumpcloud or generic (default). |
entra_tenant_id |
string | for entra |
The Microsoft Entra tenant ID (UUID); only for the entra profile. |
Response
201 Created on the first write, 200 OK on an update, with the configuration (see Get the OIDC configuration) and the new ETag. The client secret is not in the response. When the update became a draft revision, the body also has revision (the draft) and the configuration and ETag are the live ones.
Errors
| HTTP | code | When |
|---|---|---|
| 404 | not_found |
The connection does not exist in this environment or is not an enterprise connection. |
| 409 | conflict |
The connection is not an OIDC connection, sp_host differs from the stored host, or a secret method has no stored or given secret. |
| 412 | precondition_failed |
If-Match does not match the current version, the configuration changed concurrently, or If-Match was sent before the first write. |
| 422 | validation_failed |
A field breaks the rules above; errors[] points at the field (/issuer, /client_id, /client_auth_method, /client_secret, /client_secret_expires_at, /provider_profile, /entra_tenant_id, /sp_host). |
| 428 | precondition_required |
The configuration exists and the If-Match header is missing. |
| 400 | unknown_field |
The body has an unknown field. |
| 401 | unauthenticated |
The credential is missing, invalid or revoked. |
| 403 | insufficient_permissions |
The credential lacks the required permission. |
| 429 | rate_limited |
The rate limit was exceeded; retry after the Retry-After period. |
| 503 | service_unavailable |
A dependency is temporarily unavailable; retry after the Retry-After period. |
Example
Keep the secret in a file, not on the command line (oidc.json):
{"sp_host":"acme.eu1.keenauth.app","issuer":"https://idp.example.com","client_id":"keenauth-acme","client_auth_method":"client_secret_basic","client_secret":"<secret from the identity provider>"}
curl -X PUT "https://api.eu.keenauth.com/v1/connections/conn_01j9xp6q0r1s5t7v9w1x3y5z7b/oidc" \
-H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
-H "Content-Type: application/json" \
-d @oidc.json
keenauth connections oidc put conn_01j9xp6q0r1s5t7v9w1x3y5z7b --data @oidc.json
keenauth connections oidc put conn_01j9xp6q0r1s5t7v9w1x3y5z7b --data @oidc-update.json --if-match '"6f1c…"'