Generic coach signup + JWT auth fix #30

Closed
opened 2026-03-10 04:17:50 +00:00 by forgejo_admin · 0 comments

Lineage

plan-2026-03-08-tryout-prep → Phase 3a-iii (Demo prep)

Repo

forgejo_admin/basketball-api

User Story

As Marcus (program director)
I want ONE coach signup link I can text to all coaches
So that coaches can self-register without me creating individual invites

As an admin
I want JWT auth to work on admin endpoints
So that I can call /admin/generate-tokens and /admin/invite-coach

Context

Coach signup: Current system requires admin to call POST /admin/invite-coach to pre-create each coach with an individual token. Marcus wants a single generic link (/coach/signup) — no token, no admin pre-creation. Coach fills in name/email/phone, signs the same contractor agreement, proceeds to Stripe Connect. Same flow, just open access.

JWT fix: Admin endpoints return "Invalid or expired token". Root cause: _build_auth_config() in main.py doesn't set token_issuer. The pal_e_auth library defaults token_issuer to "pal-e-auth" in AuthConfig (see ~/pal-e-auth/src/pal_e_auth/config.py:15). decode_token() validates iss claim matches config.token_issuer. Need to explicitly set it so tokens from Google OAuth flow validate correctly.

File Targets

Files to modify:

  • src/basketball_api/routes/coach.py — add GET /signup and POST /signup routes (open access, no token). Reuse existing _render_agreement_page HTML helper but without pre-filled coach data. Create Coach record on POST, sign agreement, redirect to Stripe Connect.
  • src/basketball_api/main.py — add token_issuer="pal-e-auth" to _build_auth_config() AuthConfig constructor (line 28-34)
  • tests/test_coach_signup.py — new test file for generic signup flow

Files NOT to touch:

  • src/basketball_api/routes/admin.py — existing admin endpoints are fine, just need JWT fix
  • src/basketball_api/services/coach_onboarding.py — existing service functions work for both flows

Acceptance Criteria

  • GET /coach/signup renders branded contractor agreement form with empty fields (no token required)
  • POST /coach/signup creates a Coach record with tenant_id=1, signs agreement, records timestamp + IP
  • After POST, coach is redirected to Stripe Connect onboarding
  • Existing token-based /coach/onboard?token=xxx flow still works unchanged
  • Admin endpoints accept valid JWT from Google OAuth flow (no more "Invalid or expired token")
  • All existing tests pass

Test Expectations

  • Unit test: GET /coach/signup returns 200 with agreement HTML
  • Unit test: POST /coach/signup with valid form data creates Coach record, returns redirect
  • Unit test: POST /coach/signup without agreeing returns 400
  • Unit test: existing token-based onboard routes still work
  • Run command: pytest tests/ -v

Constraints

  • Reuse existing _AGREEMENT_TEXT and _STYLE from coach.py — same look and feel
  • Hardcode tenant_id=1 (Westside) — single tenant for now
  • Follow existing route patterns in coach.py
  • The /coach/signup routes should NOT require any authentication
  • Stripe Connect calls may fail in test (mock them like existing tests do)

Checklist

  • PR opened
  • Tests pass
  • No unrelated changes
  • project-westside-basketball
  • Phase 2 (coach onboarding) built the token-based flow
  • PR #24 (admin invite endpoint)
  • PR #27 (CI test fix for invite)
### Lineage `plan-2026-03-08-tryout-prep` → Phase 3a-iii (Demo prep) ### Repo `forgejo_admin/basketball-api` ### User Story As Marcus (program director) I want ONE coach signup link I can text to all coaches So that coaches can self-register without me creating individual invites As an admin I want JWT auth to work on admin endpoints So that I can call `/admin/generate-tokens` and `/admin/invite-coach` ### Context **Coach signup**: Current system requires admin to call `POST /admin/invite-coach` to pre-create each coach with an individual token. Marcus wants a single generic link (`/coach/signup`) — no token, no admin pre-creation. Coach fills in name/email/phone, signs the same contractor agreement, proceeds to Stripe Connect. Same flow, just open access. **JWT fix**: Admin endpoints return "Invalid or expired token". Root cause: `_build_auth_config()` in `main.py` doesn't set `token_issuer`. The `pal_e_auth` library defaults `token_issuer` to `"pal-e-auth"` in `AuthConfig` (see `~/pal-e-auth/src/pal_e_auth/config.py:15`). `decode_token()` validates `iss` claim matches `config.token_issuer`. Need to explicitly set it so tokens from Google OAuth flow validate correctly. ### File Targets Files to modify: - `src/basketball_api/routes/coach.py` — add `GET /signup` and `POST /signup` routes (open access, no token). Reuse existing `_render_agreement_page` HTML helper but without pre-filled coach data. Create Coach record on POST, sign agreement, redirect to Stripe Connect. - `src/basketball_api/main.py` — add `token_issuer="pal-e-auth"` to `_build_auth_config()` AuthConfig constructor (line 28-34) - `tests/test_coach_signup.py` — new test file for generic signup flow Files NOT to touch: - `src/basketball_api/routes/admin.py` — existing admin endpoints are fine, just need JWT fix - `src/basketball_api/services/coach_onboarding.py` — existing service functions work for both flows ### Acceptance Criteria - [ ] `GET /coach/signup` renders branded contractor agreement form with empty fields (no token required) - [ ] `POST /coach/signup` creates a Coach record with `tenant_id=1`, signs agreement, records timestamp + IP - [ ] After POST, coach is redirected to Stripe Connect onboarding - [ ] Existing token-based `/coach/onboard?token=xxx` flow still works unchanged - [ ] Admin endpoints accept valid JWT from Google OAuth flow (no more "Invalid or expired token") - [ ] All existing tests pass ### Test Expectations - [ ] Unit test: `GET /coach/signup` returns 200 with agreement HTML - [ ] Unit test: `POST /coach/signup` with valid form data creates Coach record, returns redirect - [ ] Unit test: `POST /coach/signup` without agreeing returns 400 - [ ] Unit test: existing token-based onboard routes still work - Run command: `pytest tests/ -v` ### Constraints - Reuse existing `_AGREEMENT_TEXT` and `_STYLE` from coach.py — same look and feel - Hardcode `tenant_id=1` (Westside) — single tenant for now - Follow existing route patterns in coach.py - The `/coach/signup` routes should NOT require any authentication - Stripe Connect calls may fail in test (mock them like existing tests do) ### Checklist - [ ] PR opened - [ ] Tests pass - [ ] No unrelated changes ### Related - `project-westside-basketball` - Phase 2 (coach onboarding) built the token-based flow - PR #24 (admin invite endpoint) - PR #27 (CI test fix for invite)
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#30
No description provided.