Skip to content

Repository files navigation

Learning Center Reference

Tour: https://nick-bellows.github.io/learning-center-reference/ · static walkthrough with screenshots; the application itself is not hosted.

Status: working portfolio vertical slice. A synthetic learner can authenticate, resolve a database role, browse a course, enroll, complete ordered lessons, and see persisted progress. A synthetic administrator can inspect eligibility derived live from safeguarding and credential records. A local OIDC fixture now proves the browser redirect/session/logout path. External-provider login, CMS content, certificates, and cloud deployment remain explicitly unclaimed.

Independent portfolio project — not affiliated with, endorsed by, or containing data from U.S. Soccer or any member organization. Every name and record is fictional.

Twenty-five-second walkthrough: the landing page, a learner signing in through the local OIDC fixture, enrolling and completing two lessons, being refused the administrator view, then the administrator roster with derived eligibility

Recorded against the local Compose stack with the OIDC overlay by scripts/record-screencast.ps1; nothing is hosted. The still screenshots below show the same two views at full resolution.

This reference implementation models one hard product problem rather than a broad mock: education progress and participation eligibility have to remain traceable as roles, credentials, expirations, and holds change.

Learner dashboard showing persisted course progress

The implemented workflow

verified identity → PostgreSQL role → published course → idempotent enrollment
                  → ordered lesson completion events → dashboard projection

admin identity → PostgreSQL admin role → current credential facts
               → derived eligibility → compliance roster
  • The API validates OIDC signature, issuer, audience, and expiry in AUTH_MODE=oidc.
  • Application roles come from PostgreSQL—not token claims or request parameters.
  • The default local stack uses an explicit demo verifier; the optional OIDC overlay exercises Authorization Code + PKCE, signed tokens, callback/session handling, and explicit role switching with two fixed fictional identities. Neither local mode is suitable for internet exposure.
  • The browser boundary is tested on its failure paths too: tampered, forged, expired, and token-less session cookies; callbacks with no transaction, a mismatched state, a provider error= response, a forged code, an expired transaction, or a replayed code; and hostile returnTo values. Each ends signed out or on the generic error page (web/tests/auth-negative.spec.ts).
  • Enrollment and lesson-completion retries are idempotent.
  • Sequential courses reject an out-of-order completion with 409 Conflict.
  • Every error, including an unknown path (404), a wrong method (405), throttling (429), a recovered handler panic (500), and unconfigured dependencies (503), is a JSON {"error": ...} body documented in the OpenAPI contract, and every response carries a server-generated X-Request-Id that matches its log line.
  • An append-only progress_event record and its enrollment_progress projection update in one transaction. Event sourcing is deliberately confined to learner progress.
  • Participation eligibility is never stored. It is recalculated from background-check, SafeSport, role-credential, and disciplinary-hold facts on each request.

Administrator compliance view with fictional members

Architecture

flowchart LR
    Browser[Browser] --> Web[Next.js / TypeScript\nServer Components + Actions]
    Browser -->|Authorization Code + PKCE| IdP[OIDC provider]
    IdP -->|Callback + signed tokens| Web
    Web -->|Bearer token, server side| API[Go / chi API]
    API -->|Discovery + JWKS validation| IdP
    API -->|Resolve subject and roles| PG[(PostgreSQL)]
    API -->|Append completion| Events[(progress_event)]
    Events -->|Same transaction| Projection[(enrollment_progress)]
    API -->|Derived safeguarding status| Web
Loading

The default local demo substitutes fixed synthetic subjects so a clean clone needs no account or secret. compose.oidc.yml supplies a standards-based local provider and proves the complete browser boundary without pretending it is Auth0. Hosted-provider interoperability is not claimed.

Quick start

Requirements: Docker with Compose.

git clone https://github.com/nick-bellows/learning-center-reference
cd learning-center-reference
docker compose up --build

Open:

  • http://localhost:3000/learn — learner enrollment and progress
  • http://localhost:3000/admin/compliance — role-protected compliance view
  • http://localhost:3000/members — three focused eligibility-rule examples
  • http://localhost:8080/health — readiness, including a PostgreSQL ping

The API embeds and applies migrations, then loads an idempotent synthetic seed. Ports bind to 127.0.0.1; Compose does not expose the demo outside the local machine.

To exercise real redirect/callback/session behavior locally, use the OIDC overlay:

docker compose -f compose.yml -f compose.oidc.yml up --build

Open /learn, sign in as Alex Coach, then sign out and choose Casey Admin for the administrator path. The guided landing page links each UI behavior to its implementation and tests. Run ./scripts/reset-demo.ps1 from PowerShell to clear mutable fictional enrollment/progress state, and ./scripts/reconcile-progress.ps1 (add -Apply to repair) to compare the dashboard projection with the append-only event log.

What is implemented

Capability Evidence
Go REST API and contract api/internal/httpapi; api/openapi.yaml is validated semantically and every handler status/body (including 429/500/503) is checked against it in openapi_conformance_test.go
Authentication and RBAC OIDC verifier, Authorization Code + PKCE web session, local provider fixture, explicit demo adapter; roles resolved by internal/store; browser happy path and 16 negative paths in web/tests/auth*.spec.ts
Course workflow Published catalog, idempotent enrollment, sequential lesson completion, learner dashboard
PostgreSQL state Seven versioned migrations, embedded transactional runner, idempotent synthetic seed, DML-only runtime role (0007)
Bounded event sourcing Immutable completion events and transactional progress projection in migration 0005
Eligibility Pure, boundary-tested Go rule derived from expiring facts and active holds
Credentials contract v1 Service-token GET /v1/members/{subject}/credentials implementing learning-center.credentials.v1 (scope credentials:read); shape pinned by the consumer's fixtures under api/testdata/contracts
Administrator workflow Role-protected compliance roster with current reasons and earliest credential expiry
Web and accessibility Next.js 16, TypeScript, semantic UI, keyboard focus/reduced motion, automated axe WCAG A/AA gate
Operations JSON request logs without tokens/PII, DB-aware health check, timeouts, graceful shutdown, request/body limits, scoped demo reset, projection drift check/rebuild (/reconcileprogress)
Delivery Non-root container images; GitHub Actions for vet/race/tests, real-Postgres integration, vulnerability checks, web build, Compose/OIDC e2e, and accessibility

Verification

API unit and integration tests (the integration tests require the local database):

docker compose up -d db
cd api
go vet ./...
DATABASE_URL="postgres://lcr:change-me-locally@localhost:5432/lcr?sslmode=disable" go test ./...

Web checks:

cd web
npm ci
npm run lint
npm run build
npx playwright install chromium
PLAYWRIGHT_BASE_URL=http://localhost:3000 npm run test:a11y

CI also starts the complete Compose stack and exercises authentication failures, role boundaries, enrollment retry behavior, ordered progress, dashboard persistence, projection drift detection and rebuild, the admin view, all five rendered routes plus the 404 page, and, under the OIDC overlay, the signed-out, refused, and error states at desktop and phone width. See .github/workflows/ci.yml.

Engineering decisions

Security and privacy boundaries

  • The OIDC verifier fails closed; unsupported or missing auth configuration cannot expose protected routes.
  • Ownership is checked before a learner can append progress to an enrollment.
  • Request handling runs as a DML-only database role (lcr_runtime, migration 0007): it cannot change the schema, read the migration ledger, or update/delete rows in the append-only progress_event log. Migrations, the seed, and the operator commands run as the owner. Public mode refuses to start without a runtime role or a separate runtime connection string.
  • Logs record request metadata, never bearer tokens or member details.
  • UUIDs are validated before database casts. SQL uses pgx parameters throughout.
  • The public eligibility example contains fixed synthetic records only. A real deployment would protect member-level eligibility and apply organization-level authorization.
  • .env files are ignored. No real member data, provider secrets, or cloud credentials are required or committed.
  • Public-mode startup rejects demo auth, HTTP application/provider URLs, missing confidential client/session secrets, and the known local placeholder.

Deliberate limitations

  • No hosted demo or paid cloud resources have been created.
  • Browser OIDC login/callback/session/logout is proven against the local fixture. No Auth0 tenant or other hosted provider has been created or verified.
  • content_ref is the headless-CMS integration seam; no Sanity project is provisioned.
  • Assessment attempts, passing scores, credentials issued from course completion, certificates, i18n, uploads, notifications, and organization tenancy are not implemented.
  • Published course content is treated as immutable after learners enroll; schema evolution for an in-progress course would require a versioning policy.
  • Automated axe checks catch only a subset of accessibility issues; manual keyboard, screen-reader, zoom, and contrast review remains necessary before a WCAG conformance claim. The checklist and findings log for that review is docs/accessibility-manual-review.md; it has not been run yet.
  • The compliance query favors readable code over large-roster optimization and would need pagination and a set-based query at production scale.

Repository map

api/      Go service, OpenAPI contract, OIDC adapter, domain/store code, migrations
web/      Next.js/TypeScript learner and administrator experiences, axe tests
db/       Explicitly synthetic, idempotent local demonstration data
docs/     Domain model, ADRs, screenshots, deployment notes, interview guide

Next bounded milestone

Add assessment attempts and a passing rule, then issue an expiring credential that changes the derived eligibility roster and audit history. A hosted recruiter demo is separately prepared but remains gated on explicit account and spending approval.

License

Released under the MIT License. The data, names, and records in this repository are fictional and synthetic; the license covers the code, not any real member information.

About

Soccer learning and eligibility reference implementation with Go, Next.js, PostgreSQL, OIDC/RBAC, OpenAPI, Docker, and accessibility tests.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages