API Reference

Complete REST API reference for all five admin-service controllers — Keycloak clients, realm roles, Vault AppRoles, KV secrets, and HCL policies.

Authentication

All endpoints require a valid Keycloak-issued JWT passed as a Bearer token. Obtain a token from the Keycloak token endpoint before making requests.

Bash code-highlight
# Get a token (client credentials flow)
curl -X POST http://localhost:5433/realms/event-based-banking-application/protocol/openid-connect/token \
  -d "grant_type=client_credentials" \
  -d "client_id=admin-service-client" \
  -d "client_secret=<secret>"
All admin endpoints additionally require the ROLE_ADMIN realm role to be present in the JWT's realm_access.roles claim. The /internal/** paths require ROLE_INTERNAL_SERVICE instead.

Swagger UI

The interactive API documentation is available at:

Text code-highlight
http://localhost:8089/swagger-ui/index.html

The OpenAPI JSON spec is available at:

Text code-highlight
http://localhost:8089/v3/api-docs

Keycloak — Client Management

Controller: ClientCreationController
Base Path: /api/admin

MethodPathRequired RoleDescription
POST/api/admin/inter-service-clientscreate-client (ROLE_ADMIN)Creates a new confidential Keycloak client for inter-service OAuth2

POST /api/admin/inter-service-clients

Creates a new Keycloak OAuth2 client configured for the client_credentials (service accounts) flow.

Request:

Bash code-highlight
curl -X POST http://localhost:8089/api/admin/inter-service-clients?clientName=my-new-service \
  -H "Authorization: Bearer <token>"

Response:

Json code-highlight
{
  "clientId": "my-new-service",
  "clientSecret": "generated-secret-value"
}

Error cases:

HTTP StatusCondition
409 ConflictClient with that name already exists
403 ForbiddenCaller does not have ROLE_ADMIN

What it does internally:

  1. Checks if a client with the given name already exists — throws KeyCloakClientAlreadyExists (409) if so.
  2. Builds a ClientRepresentation with service accounts enabled, standard flow disabled, access type confidential.
  3. Creates the client via Keycloak REST; throws KeyCloakServiceException if the response is not 201.
  4. Assigns the INTERNAL_SERVICE realm role to the newly created client's service account user.
  5. Returns the clientId and generated clientSecret.

Keycloak — Realm Roles

Controller: KeyCloakRolesController
Base Path: /api/admin

MethodPathRequired RoleDescription
GET/api/admin/realm-rolesquery-realm (ROLE_ADMIN)Lists all realm roles
GET/api/admin/realm-role?roleName=query-realm (ROLE_ADMIN)Fetches a single realm role by name
POST/api/admin/realm-rolesquery-realm (ROLE_ADMIN)Creates a new realm role

GET /api/admin/realm-roles

Bash code-highlight
curl http://localhost:8089/api/admin/realm-roles \
  -H "Authorization: Bearer <token>"

Returns List<KeycloakRole>.

GET /api/admin/realm-role

Bash code-highlight
curl "http://localhost:8089/api/admin/realm-role?roleName=ROLE_ADMIN" \
  -H "Authorization: Bearer <token>"

Returns a single KeycloakRole or 404 if the role does not exist.

POST /api/admin/realm-roles

Bash code-highlight
curl -X POST http://localhost:8089/api/admin/realm-roles \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"name": "ROLE_ANALYST", "description": "Read-only analyst role"}'

Returns a KeyCloakResponse with responseCode: KEYCLOAK_ROLE_CREATED_200.


Vault — AppRole Management

Controller: VaultAppRoleController
Base Path: /api/admin
Required Role: vault-ops (ROLE_ADMIN) for all endpoints.

MethodPathDescription
GET/api/admin/vault-approleLists all AppRole names
GET/api/admin/vault-approle/{role}Gets details for a specific AppRole
POST/api/admin/vault-approleCreates a new AppRole with policies
DELETE/api/admin/vault-approle?role=Deletes an AppRole
GET/api/admin/vault-approle/secrets?appRole=Generates fresh roleId + secretId credentials

POST /api/admin/vault-approle

Bash code-highlight
curl -X POST http://localhost:8089/api/admin/vault-approle \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "roleName": "user-service",
    "policies": ["user-service-policy"]
  }'

Creates the AppRole with token_ttl=1h and token_max_ttl=4h. Returns an AppRoleResponseDto containing the generated roleId and secretId.

GET /api/admin/vault-approle/secrets

Bash code-highlight
curl "http://localhost:8089/api/admin/vault-approle/secrets?appRole=user-service" \
  -H "Authorization: Bearer <token>"

Generates and returns a fresh secretId for an existing AppRole. Use this to rotate credentials after expiry.


Vault — KV Secrets

Controller: VaultOperationsController
Base Path: /api/admin

Secret path pattern: secret/arya-banking/{service}/dev

MethodPathRequired RoleDescription
POST/api/admin/vault-secretsvault-ops (ROLE_ADMIN)Creates a KV secret
PUT/api/admin/vault-secretsvault-ops (ROLE_ADMIN)Updates (patches) a KV secret
GET/api/admin/vault-secrets?service=vault-ops (ROLE_ADMIN)Reads all secrets for a service
DELETE/api/admin/vault-secrets?service=⚠️ NoneDeletes all secrets for a service path
The DELETE /api/admin/vault-secrets endpoint is missing @PreAuthorize. Any authenticated user (not just ROLE_ADMIN) can call it. This is a known issue and must be fixed before any production use.

POST /api/admin/vault-secrets

Bash code-highlight
curl -X POST http://localhost:8089/api/admin/vault-secrets \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "service": "user-service",
    "secretKey": "db.password",
    "secretValue": "s3cr3tP@ss"
  }'

Writes the key-value pair to secret/arya-banking/user-service/dev.

PUT /api/admin/vault-secrets

Uses KV v2 PATCH semantics — only the specified key is updated; other keys in the same path are preserved.


Vault — Policy Management

Controller: VaultPolicyController
Base Path: /api/admin
Required Role: vault-ops (ROLE_ADMIN) for all endpoints.

MethodPathDescription
GET/api/admin/vault/policiesLists all ACL policy names from sys/policies/acl/
POST/api/admin/vault/policies?service=Uploads {service}-policy.hcl from classpath to Vault
DELETE/api/admin/vault/policies?service=Deletes the ACL policy for a service

POST /api/admin/vault/policies

Bash code-highlight
# Upload the admin-service policy
curl -X POST "http://localhost:8089/api/admin/vault/policies?service=admin-service" \
  -H "Authorization: Bearer <token>"

# Upload the user-service policy
curl -X POST "http://localhost:8089/api/admin/vault/policies?service=user-service" \
  -H "Authorization: Bearer <token>"

The service resolves the policy file as {service}-policy.hcl from the classpath (src/main/resources/). The file is read via CommonUtils.loadConfig(path) and written to /sys/policies/acl/{service}-policy in Vault.


DTOs

DTOTypeFields
AppRole@Data classroleId, secretId
AppRoleResponseDtoJava recordroleId, secretId
CreateAppRoleDtoJava recordroleName, policies: List<String>
KeyCloakClientResponseJava recordclientId, clientSecret
KeycloakRoleJava recordcomposite, name, description, composites, attributes
VaultApiResponseDtoJava recorddata: Map<String,Object>, leastDuration, leaseId, requestId
VaultResponseDtoJava recordresponseCode, responseMessage
VaultSecretDtoJava recordservice, secretKey, secretValue
All DTOs except AppRole are immutable Java records. AppRole is a @Data class because it is used as a configuration binding target, which requires a mutable JavaBean.
apirestendpointsswagger