Create Keycloak service account for programmatic admin API access #260

Closed
opened 2026-04-04 04:21:12 +00:00 by forgejo_admin · 1 comment
Contributor

Type

Feature

Lineage

Standalone — discovered while trying to send test emails via basketball-api admin endpoints. No programmatic auth path exists. westside-app is a public client (PKCE only), admin-cli on master realm doesn't pass JWT validation against service realm JWKS.

Repo

forgejo_admin/pal-e-services (Keycloak clients are managed via Terraform in pal-e-services, not pal-e-platform)

User Story

As a platform operator,
I want a service account that can authenticate programmatically against Keycloak
So that CLI tools, agents, and scripts can hit admin API endpoints without a browser login flow.

Context

Basketball-api admin endpoints (email blast, schedule CRUD, user management) require a JWT with the admin role in the westside-basketball realm. Currently the only way to get one is browser login via westside-app (public client, PKCE) — not scriptable.

The infrastructure already exists. pal-e-services/terraform/keycloak.tf has:

  • keycloak_openid_client with service_accounts_enabled support
  • keycloak_openid_client_service_account_realm_role for role binding
  • A commented-out example in k3s.tfvars.example (lines 42-49)

This is a 5-line tfvars addition + tofu apply.

Environment

  • Keycloak: keycloak.tail5b443a.ts.net
  • Realm: westside-basketball
  • Terraform: ~/pal-e-services/terraform/
  • Provider: mrparkers/keycloak

File Targets

Files to modify:

  • ~/pal-e-services/terraform/k3s.tfvars — add platform-service client entry to keycloak_clients map

Files NOT to touch:

  • keycloak.tf — resources already support service accounts
  • variables.tf — variable schema already supports all needed fields
  • Any pal-e-platform files

Post-apply:

  • Retrieve client secret from Keycloak admin console or tofu output
  • Store in ~/secrets/pal-e-services/keycloak-service-account.env
  • Create SOP note in pal-e-docs: sop-keycloak-service-account

Acceptance Criteria

  • platform-service entry added to keycloak_clients in k3s.tfvars
  • tofu plan shows 3 new resources (client, role mapper, service account role binding)
  • tofu apply succeeds
  • Token acquisition works: curl -X POST .../token -d grant_type=client_credentials -d client_id=platform-service -d client_secret=...
  • Token passes basketball-api admin endpoint auth (200, not 401)
  • Client secret stored in ~/secrets/pal-e-services/keycloak-service-account.env
  • SOP note created

Test Expectations

  • curl -X POST /admin/email/welcome-practice?test_email=draneylucas@gmail.com with service account token returns 200
  • Token contains admin role in realm_access.roles
  • Run command: tofu plan -lock=false -var-file=k3s.tfvars

Constraints

  • Use client_credentials grant, NOT password grant
  • Client secret must NOT be committed to any repo
  • Follow the existing pattern in k3s.tfvars.example lines 42-49
  • Per convention: tofu plan -lock=false before apply

Checklist

  • Terraform change applied
  • Credentials stored
  • SOP written
  • Test email sent successfully
  • pal-e-platform — project this affects
  • basketball-api#311, #312, #313 — blocked by lack of programmatic auth
### Type Feature ### Lineage Standalone — discovered while trying to send test emails via basketball-api admin endpoints. No programmatic auth path exists. `westside-app` is a public client (PKCE only), `admin-cli` on master realm doesn't pass JWT validation against service realm JWKS. ### Repo `forgejo_admin/pal-e-services` (Keycloak clients are managed via Terraform in pal-e-services, not pal-e-platform) ### User Story As a platform operator, I want a service account that can authenticate programmatically against Keycloak So that CLI tools, agents, and scripts can hit admin API endpoints without a browser login flow. ### Context Basketball-api admin endpoints (email blast, schedule CRUD, user management) require a JWT with the `admin` role in the `westside-basketball` realm. Currently the only way to get one is browser login via `westside-app` (public client, PKCE) — not scriptable. **The infrastructure already exists.** `pal-e-services/terraform/keycloak.tf` has: - `keycloak_openid_client` with `service_accounts_enabled` support - `keycloak_openid_client_service_account_realm_role` for role binding - A commented-out example in `k3s.tfvars.example` (lines 42-49) This is a 5-line tfvars addition + `tofu apply`. ### Environment - Keycloak: `keycloak.tail5b443a.ts.net` - Realm: `westside-basketball` - Terraform: `~/pal-e-services/terraform/` - Provider: `mrparkers/keycloak` ### File Targets Files to modify: - `~/pal-e-services/terraform/k3s.tfvars` — add `platform-service` client entry to `keycloak_clients` map Files NOT to touch: - `keycloak.tf` — resources already support service accounts - `variables.tf` — variable schema already supports all needed fields - Any pal-e-platform files Post-apply: - Retrieve client secret from Keycloak admin console or `tofu output` - Store in `~/secrets/pal-e-services/keycloak-service-account.env` - Create SOP note in pal-e-docs: `sop-keycloak-service-account` ### Acceptance Criteria - [ ] `platform-service` entry added to `keycloak_clients` in k3s.tfvars - [ ] `tofu plan` shows 3 new resources (client, role mapper, service account role binding) - [ ] `tofu apply` succeeds - [ ] Token acquisition works: `curl -X POST .../token -d grant_type=client_credentials -d client_id=platform-service -d client_secret=...` - [ ] Token passes basketball-api admin endpoint auth (200, not 401) - [ ] Client secret stored in `~/secrets/pal-e-services/keycloak-service-account.env` - [ ] SOP note created ### Test Expectations - [ ] `curl -X POST /admin/email/welcome-practice?test_email=draneylucas@gmail.com` with service account token returns 200 - [ ] Token contains `admin` role in `realm_access.roles` - Run command: `tofu plan -lock=false -var-file=k3s.tfvars` ### Constraints - Use `client_credentials` grant, NOT `password` grant - Client secret must NOT be committed to any repo - Follow the existing pattern in `k3s.tfvars.example` lines 42-49 - Per convention: `tofu plan -lock=false` before apply ### Checklist - [ ] Terraform change applied - [ ] Credentials stored - [ ] SOP written - [ ] Test email sent successfully ### Related - `pal-e-platform` — project this affects - basketball-api#311, #312, #313 — blocked by lack of programmatic auth
Author
Contributor

Scope Review: BLOCK

Review note: review-785-2026-04-03

Core assumption is invalid. k3s.tfvars already contains westside-ai-bot — a confidential service account client with service_accounts_enabled = true and service_account_realm_roles = ["admin"] in the westside-basketball realm (lines 102-112). This is exactly what the ticket proposes to create.

Issues found:

  • [SCOPE] Verify whether westside-ai-bot already satisfies this need before creating a duplicate client. If a separate client is justified, explain why in the Context section.
  • [LABEL] story:platform-S1 does not exist in project-pal-e-platform user stories. Change to story:superuser-deploy or story:superuser-onboard-service.
  • [SCOPE] No arch-keycloak architecture note exists in pal-e-docs — create it.
  • [BODY] Issue filed on pal-e-platform but work is in pal-e-services. Refile on correct repo.
## Scope Review: BLOCK Review note: `review-785-2026-04-03` **Core assumption is invalid.** `k3s.tfvars` already contains `westside-ai-bot` — a confidential service account client with `service_accounts_enabled = true` and `service_account_realm_roles = ["admin"]` in the `westside-basketball` realm (lines 102-112). This is exactly what the ticket proposes to create. ### Issues found: - **[SCOPE]** Verify whether `westside-ai-bot` already satisfies this need before creating a duplicate client. If a separate client is justified, explain why in the Context section. - **[LABEL]** `story:platform-S1` does not exist in project-pal-e-platform user stories. Change to `story:superuser-deploy` or `story:superuser-onboard-service`. - **[SCOPE]** No `arch-keycloak` architecture note exists in pal-e-docs — create it. - **[BODY]** Issue filed on `pal-e-platform` but work is in `pal-e-services`. Refile on correct repo.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ldraney/pal-e-platform#260
No description provided.