Keycloak · Arazzo Workflow

Keycloak Decommission a Client

Version 1.0.0

Snapshot a client registration, disable it, and optionally delete it after a soak period.

1 workflow 1 source API 1 provider
View Spec View on GitHub AuthenticationAuthorizationIdentity ManagementOAuthOpenID ConnectSecuritySSOArazzoWorkflows

Provider

keycloak

Workflows

decommission-client
Capture, disable, and optionally delete a client registration.
Resolves a client by clientId, snapshots its representation for rebuild, disables it, and permanently deletes the registration only when hardDelete is true.
5 steps inputs: clientId, hardDelete, realm outputs: clientEnabled, clientId, clientUuid, rebuildSnapshot
1
resolveClient
getClients
Exchange the clientId for the internal client UUID that the detail and delete endpoints require.
2
captureClientSnapshot
getClient
Read and retain the full client representation before touching it. This snapshot is the only rebuild record once the registration is deleted, since Keycloak keeps nothing recoverable afterwards.
3
disableClient
updateClient
Disable the client. Token requests start failing immediately, which is the point: it surfaces any forgotten consumer while the change is still a one-field edit away from being undone.
4
confirmDisabled
getClient
Read the client back to prove it is disabled, and stop here unless the caller explicitly asked for deletion.
5
deleteClientRegistration
deleteClient
Permanently remove the client registration. Only reached when hardDelete is true, and only recoverable from the snapshot captured earlier.

Source API Descriptions

Arazzo Workflow Specification

Raw ↑
arazzo: 1.0.1
info:
  title: Keycloak Decommission a Client
  summary: Snapshot a client registration, disable it, and optionally delete it after a soak period.
  description: >-
    Retiring a client is the change most likely to break something nobody
    remembered was calling, so this workflow does it the safe way round: capture
    the full registration first as a rebuild record, disable the client so
    traffic fails visibly and reversibly, then delete only when explicitly told
    to. Disabling is the reversible probe; deletion is not. In practice the
    workflow is run once with hardDelete false to break traffic and watch for
    fallout, then again with hardDelete true once the soak period is clean. Every
    step spells out its request inline so the flow can be read and executed
    without opening the underlying OpenAPI description.
  version: 1.0.0
sourceDescriptions:
- name: keycloakAdminApi
  url: ../openapi/keycloak-admin-rest-api-openapi.yml
  type: openapi
workflows:
- workflowId: decommission-client
  summary: Capture, disable, and optionally delete a client registration.
  description: >-
    Resolves a client by clientId, snapshots its representation for rebuild,
    disables it, and permanently deletes the registration only when hardDelete is
    true.
  inputs:
    type: object
    required:
    - realm
    - clientId
    properties:
      realm:
        type: string
        description: The name of the realm the client is registered in.
      clientId:
        type: string
        description: The OAuth client identifier to decommission.
      hardDelete:
        type: boolean
        description: >-
          When true the registration is permanently deleted after being disabled.
          When false the client is left disabled and restorable, which is the
          recommended first pass.
  steps:
  - stepId: resolveClient
    description: >-
      Exchange the clientId for the internal client UUID that the detail and
      delete endpoints require.
    operationId: getClients
    parameters:
    - name: realm
      in: path
      value: $inputs.realm
    - name: clientId
      in: query
      value: $inputs.clientId
    - name: max
      in: query
      value: 1
    successCriteria:
    - condition: $statusCode == 200
    - context: $response.body
      condition: $.length > 0
      type: jsonpath
    outputs:
      clientUuid: $response.body#/0/id
      resolvedClientId: $response.body#/0/clientId
  - stepId: captureClientSnapshot
    description: >-
      Read and retain the full client representation before touching it. This
      snapshot is the only rebuild record once the registration is deleted, since
      Keycloak keeps nothing recoverable afterwards.
    operationId: getClient
    parameters:
    - name: realm
      in: path
      value: $inputs.realm
    - name: clientUuid
      in: path
      value: $steps.resolveClient.outputs.clientUuid
    successCriteria:
    - condition: $statusCode == 200
    outputs:
      snapshot: $response.body
      clientName: $response.body#/name
      protocol: $response.body#/protocol
      redirectUris: $response.body#/redirectUris
      serviceAccountsEnabled: $response.body#/serviceAccountsEnabled
  - stepId: disableClient
    description: >-
      Disable the client. Token requests start failing immediately, which is the
      point: it surfaces any forgotten consumer while the change is still a
      one-field edit away from being undone.
    operationId: updateClient
    parameters:
    - name: realm
      in: path
      value: $inputs.realm
    - name: clientUuid
      in: path
      value: $steps.resolveClient.outputs.clientUuid
    requestBody:
      contentType: application/json
      payload:
        clientId: $steps.resolveClient.outputs.resolvedClientId
        name: $steps.captureClientSnapshot.outputs.clientName
        protocol: $steps.captureClientSnapshot.outputs.protocol
        enabled: false
    successCriteria:
    - condition: $statusCode == 204
  - stepId: confirmDisabled
    description: >-
      Read the client back to prove it is disabled, and stop here unless the
      caller explicitly asked for deletion.
    operationId: getClient
    parameters:
    - name: realm
      in: path
      value: $inputs.realm
    - name: clientUuid
      in: path
      value: $steps.resolveClient.outputs.clientUuid
    successCriteria:
    - condition: $statusCode == 200
    outputs:
      finalEnabled: $response.body#/enabled
    onSuccess:
    - name: retainDisabledClient
      type: end
      criteria:
      - condition: $inputs.hardDelete == false
  - stepId: deleteClientRegistration
    description: >-
      Permanently remove the client registration. Only reached when hardDelete is
      true, and only recoverable from the snapshot captured earlier.
    operationId: deleteClient
    parameters:
    - name: realm
      in: path
      value: $inputs.realm
    - name: clientUuid
      in: path
      value: $steps.resolveClient.outputs.clientUuid
    successCriteria:
    - condition: $statusCode == 204
  outputs:
    clientUuid: $steps.resolveClient.outputs.clientUuid
    clientId: $steps.resolveClient.outputs.resolvedClientId
    rebuildSnapshot: $steps.captureClientSnapshot.outputs.snapshot
    clientEnabled: $steps.confirmDisabled.outputs.finalEnabled