feat: Auto Keycloak account creation on registration + fix email-mismatch bug + fix account script names #93

Closed
opened 2026-03-16 20:58:16 +00:00 by forgejo_admin · 0 comments

Lineage

plan-wkq → Phase 11 (Girls Tryout — March 21)

Repo

forgejo_admin/basketball-api

User Story

As a parent registering my child for tryouts,
I want my Keycloak account to be created automatically after I pay,
So that I can log in immediately without waiting for a manual batch script.

Context

Three fixes bundled because they're tightly coupled — all relate to the registration → account creation pipeline.

Bug 1: Email-mismatch (register.py:796)
The registration POST handler looks up parents by Parent.email == parent_email. If a parent uses a different email than what was used in Stripe checkout, a new parent record is created — the waiver lands on the wrong parent. This happened during March 13 tryout. Fix: when a registration token is present, match by Parent.registration_token == token. Only fall back to email for the walk-up (no-token) flow.

Bug 2: Account creation script missing names (create_keycloak_accounts.py:113)
The create_keycloak_user() function payload doesn't include firstName/lastName. This causes Keycloak to enforce VERIFY_PROFILE required action on first login, forcing every parent to fill in their name. The parent name data IS available — _parent_name is passed in get_paid_families() at line 270 but ignored in the payload. Workaround was applied March 14 (backfilled 50 accounts via Admin API), but the script is still broken for future runs.

Feature: Auto Keycloak account creation in registration flow
After a parent completes registration with successful Stripe payment, automatically create their Keycloak account via the Keycloak Admin API. This eliminates the manual batch script (scripts/create_keycloak_accounts.py) for the happy path. The script remains for edge cases (backfills, cash payments marked paid later by admin).

Architecture decision: The registration flow is backend (basketball-api). This integration survives the upcoming SSR → SPA frontend migration (Phase 15). Building it correctly now means it serves both the current app and the future SPA.

Keycloak config:

  • Base URL: https://keycloak.tail5b443a.ts.net
  • Realm: westside-basketball
  • Admin credentials: KEYCLOAK_ADMIN_PASSWORD env var (already available in the pod)
  • Roles: admin, coach, player (all realm roles, already exist)
  • Admin emails: draneylucas@gmail.com, mldraney3@gmail.com

File Targets

Files to modify:

  • src/basketball_api/routes/register.py — fix email-mismatch (line 796 area), add auto Keycloak account creation after successful payment
  • scripts/create_keycloak_accounts.py — add firstName/lastName to create_keycloak_user() payload (line 113 area)
  • src/basketball_api/config.py — add KEYCLOAK_BASE_URL, KEYCLOAK_ADMIN_PASSWORD config vars if not already present

Files to create:

  • src/basketball_api/services/keycloak.py — extract Keycloak Admin API operations (create user, assign role, check existing) into a reusable service module. Reuse the logic from scripts/create_keycloak_accounts.py — don't duplicate it.

Files NOT to touch:

  • src/basketball_api/routes/teams.py — unrelated
  • src/basketball_api/routes/players.py — unrelated
  • src/basketball_api/routes/subscriptions.py — Stripe subscription management is separate

Acceptance Criteria

  • When a parent registers with a token and pays via Stripe, a Keycloak account is created automatically (email as username, generated password, player role, firstName/lastName set)
  • When the parent's email differs from the token-linked parent, the token-linked parent is used (not a new record created by email)
  • When a Keycloak account already exists for that email, skip creation gracefully (no error, no duplicate)
  • When Stripe payment fails or is pending, no Keycloak account is created
  • When running create_keycloak_accounts.py, new accounts include firstName/lastName (no VERIFY_PROFILE prompt)
  • The confirmation page after successful registration shows the generated login credentials (email + password)
  • All existing 269+ tests still pass
  • Keycloak service module is importable from both the registration route and the batch script

Test Expectations

  • Unit test: test_register_token_lookup — registration with token uses token-based parent lookup, not email
  • Unit test: test_register_auto_keycloak_account — successful paid registration creates Keycloak user with correct firstName, lastName, role
  • Unit test: test_register_duplicate_keycloak_skip — if Keycloak user already exists, skip without error
  • Unit test: test_register_unpaid_no_keycloak — unpaid registration does NOT create Keycloak account
  • Unit test: test_keycloak_script_includes_names — batch script payload includes firstName/lastName
  • Run command: pytest tests/ -v

Constraints

  • Follow existing route patterns in register.py — the file uses inline HTML rendering, keep that style
  • Password generation: reuse scripts/password_gen.py (generate_password(first_name)) — import it or move to a shared location
  • Keycloak API calls should be wrapped in try/except — a Keycloak failure should log an error but NOT fail the registration. The parent's data is saved; account can be created later via batch script.
  • The Keycloak service module should work with both the route handler (async context) and the batch script (sync context). Consider using httpx (already a dependency) for both.
  • Do NOT modify Keycloak realm settings or client configs — that's platform work, not API work.

Checklist

  • PR opened
  • Tests pass
  • No unrelated changes
  • project-westside-basketball — project this affects
  • Girls tryout March 21, 2026 — this is the critical path
### Lineage `plan-wkq` → Phase 11 (Girls Tryout — March 21) ### Repo `forgejo_admin/basketball-api` ### User Story As a parent registering my child for tryouts, I want my Keycloak account to be created automatically after I pay, So that I can log in immediately without waiting for a manual batch script. ### Context Three fixes bundled because they're tightly coupled — all relate to the registration → account creation pipeline. **Bug 1: Email-mismatch (register.py:796)** The registration POST handler looks up parents by `Parent.email == parent_email`. If a parent uses a different email than what was used in Stripe checkout, a new parent record is created — the waiver lands on the wrong parent. This happened during March 13 tryout. Fix: when a registration token is present, match by `Parent.registration_token == token`. Only fall back to email for the walk-up (no-token) flow. **Bug 2: Account creation script missing names (create_keycloak_accounts.py:113)** The `create_keycloak_user()` function payload doesn't include `firstName`/`lastName`. This causes Keycloak to enforce `VERIFY_PROFILE` required action on first login, forcing every parent to fill in their name. The parent name data IS available — `_parent_name` is passed in `get_paid_families()` at line 270 but ignored in the payload. Workaround was applied March 14 (backfilled 50 accounts via Admin API), but the script is still broken for future runs. **Feature: Auto Keycloak account creation in registration flow** After a parent completes registration with successful Stripe payment, automatically create their Keycloak account via the Keycloak Admin API. This eliminates the manual batch script (`scripts/create_keycloak_accounts.py`) for the happy path. The script remains for edge cases (backfills, cash payments marked paid later by admin). **Architecture decision:** The registration flow is backend (basketball-api). This integration survives the upcoming SSR → SPA frontend migration (Phase 15). Building it correctly now means it serves both the current app and the future SPA. **Keycloak config:** - Base URL: `https://keycloak.tail5b443a.ts.net` - Realm: `westside-basketball` - Admin credentials: `KEYCLOAK_ADMIN_PASSWORD` env var (already available in the pod) - Roles: `admin`, `coach`, `player` (all realm roles, already exist) - Admin emails: `draneylucas@gmail.com`, `mldraney3@gmail.com` ### File Targets Files to modify: - `src/basketball_api/routes/register.py` — fix email-mismatch (line 796 area), add auto Keycloak account creation after successful payment - `scripts/create_keycloak_accounts.py` — add `firstName`/`lastName` to `create_keycloak_user()` payload (line 113 area) - `src/basketball_api/config.py` — add `KEYCLOAK_BASE_URL`, `KEYCLOAK_ADMIN_PASSWORD` config vars if not already present Files to create: - `src/basketball_api/services/keycloak.py` — extract Keycloak Admin API operations (create user, assign role, check existing) into a reusable service module. Reuse the logic from `scripts/create_keycloak_accounts.py` — don't duplicate it. Files NOT to touch: - `src/basketball_api/routes/teams.py` — unrelated - `src/basketball_api/routes/players.py` — unrelated - `src/basketball_api/routes/subscriptions.py` — Stripe subscription management is separate ### Acceptance Criteria - [ ] When a parent registers with a token and pays via Stripe, a Keycloak account is created automatically (email as username, generated password, `player` role, firstName/lastName set) - [ ] When the parent's email differs from the token-linked parent, the token-linked parent is used (not a new record created by email) - [ ] When a Keycloak account already exists for that email, skip creation gracefully (no error, no duplicate) - [ ] When Stripe payment fails or is pending, no Keycloak account is created - [ ] When running `create_keycloak_accounts.py`, new accounts include `firstName`/`lastName` (no VERIFY_PROFILE prompt) - [ ] The confirmation page after successful registration shows the generated login credentials (email + password) - [ ] All existing 269+ tests still pass - [ ] Keycloak service module is importable from both the registration route and the batch script ### Test Expectations - [ ] Unit test: `test_register_token_lookup` — registration with token uses token-based parent lookup, not email - [ ] Unit test: `test_register_auto_keycloak_account` — successful paid registration creates Keycloak user with correct firstName, lastName, role - [ ] Unit test: `test_register_duplicate_keycloak_skip` — if Keycloak user already exists, skip without error - [ ] Unit test: `test_register_unpaid_no_keycloak` — unpaid registration does NOT create Keycloak account - [ ] Unit test: `test_keycloak_script_includes_names` — batch script payload includes firstName/lastName - Run command: `pytest tests/ -v` ### Constraints - Follow existing route patterns in `register.py` — the file uses inline HTML rendering, keep that style - Password generation: reuse `scripts/password_gen.py` (`generate_password(first_name)`) — import it or move to a shared location - Keycloak API calls should be wrapped in try/except — a Keycloak failure should log an error but NOT fail the registration. The parent's data is saved; account can be created later via batch script. - The Keycloak service module should work with both the route handler (async context) and the batch script (sync context). Consider using `httpx` (already a dependency) for both. - Do NOT modify Keycloak realm settings or client configs — that's platform work, not API work. ### Checklist - [ ] PR opened - [ ] Tests pass - [ ] No unrelated changes ### Related - `project-westside-basketball` — project this affects - Girls tryout March 21, 2026 — this is the critical path
forgejo_admin 2026-03-16 21:14:55 +00:00
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
forgejo_admin/basketball-api#93
No description provided.