- add execution summary with verification and deviations - update state, roadmap progress, and completed auth requirements
9.0 KiB
phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed
| phase | plan | subsystem | tags | requires | provides | affects | tech-stack | key-files | key-decisions | patterns-established | requirements-completed | duration | completed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 02-authentication-foundation | 02 | auth |
|
|
|
|
|
|
|
|
|
13min | 2026-04-28 |
Phase 02 Plan 02: Server JWT Validation and JIT Users Summary
Ktor Authentik JWT validation with cached JWKS, atomic Postgres user provisioning by OIDC sub, and protected /api/v1/me.
Performance
- Duration: 13 min for final executor verification and summary; task commits already existed on this branch when this executor resumed.
- Started: 2026-04-28T11:18:15Z
- Completed: 2026-04-28T11:31:08Z
- Tasks: 3 completed
- Files modified: 13 code/config/test files plus this summary
Accomplishments
- Added JWT validation coverage for missing, expired, wrong-issuer, wrong-audience, blank-sub, and valid RS256 tokens.
- Installed Ktor
jwt("authentik")with issuer/audience checks, 30-second max leeway, non-emptysub, cached JWKS, and rate limiting. - Added
usersFlyway migration, Exposed table mapping, Hikari-backed Exposed connection, atomic JIT upsert bysub, and protected/api/v1/me. - Added Testcontainers Postgres integration coverage proving first request creates a user row and later requests update mutable claims without duplication.
Task Commits
Each task was committed atomically:
- Task 1: Create JWT validation tests before auth implementation -
614b57c(test) - Task 2: Implement AuthConfig, JWT plugin, logging redaction, and verify Exposed suspend API -
36c1b2c(feat) - Task 3: Add users migration, Exposed resolver, /api/v1/me route, and JIT tests -
8cf112a(feat)
No tracked file deletions were present in the task commits.
Files Created/Modified
server/src/main/resources/application.conf- Adds OIDC issuer/audience/JWKS/leeway config with env overrides.server/src/main/resources/db/migration/V1__users.sql- Creates theuserstable andusers_sub_idx.server/src/main/kotlin/dev/ulfrx/recipe/Database.kt- Adds Hikari-backed Exposed connection after Flyway migration.server/src/main/kotlin/dev/ulfrx/recipe/Application.kt- Installs safe CallLogging, authentication, DB migration/connection, and auth route wiring.server/src/main/kotlin/dev/ulfrx/recipe/auth/AuthConfig.kt- Reads server OIDC config from HOCON.server/src/main/kotlin/dev/ulfrx/recipe/auth/AuthPlugin.kt- Installs the Authentik JWT verifier.server/src/main/kotlin/dev/ulfrx/recipe/auth/UsersTable.kt- Exposed DSL mapping forusers.server/src/main/kotlin/dev/ulfrx/recipe/auth/PrincipalResolver.kt- ResolvesJWTPrincipaltoMeResponsethrough atomic upsert.server/src/main/kotlin/dev/ulfrx/recipe/auth/MeRoute.kt- Provides protectedGET /api/v1/me.server/src/test/kotlin/dev/ulfrx/recipe/auth/JwtTestSupport.kt- Generates RSA keys, JWKS provider, and configurable RS256 JWTs for tests.server/src/test/kotlin/dev/ulfrx/recipe/auth/AuthJwtTest.kt- Covers JWT validation positive and negative cases.server/src/test/kotlin/dev/ulfrx/recipe/auth/MeRouteTest.kt- Covers JIT provisioning against Testcontainers Postgres.server/src/test/kotlin/dev/ulfrx/recipe/ApplicationTest.kt- Keeps/healthtest wiring compatible with authenticated route registration.
Decisions Made
- Used
newSuspendedTransactionfromorg.jetbrains.exposed.sql.transactions.experimentalafter confirmingorg.jetbrains.exposed:exposed-jdbc:0.55.0. - Used raw SQL through Exposed
execforINSERT ... ON CONFLICT ... RETURNING, because the resolver needs the returned row and generated UUID in one atomic operation. - Kept logging to method, path, and status only; no header logging or bearer-token redaction API is used.
Deviations from Plan
Auto-fixed Issues
1. [Rule 1 - Bug] Kept /health test route registration compatible with authenticated routes
- Found during: Task 3
- Issue: Once
configureRouting()registeredmeRoute, tests that installed routing without Authentication would fail route setup. - Fix: Updated
ApplicationTestto install the test JWT authentication plugin before callingconfigureRouting(). - Files modified:
server/src/test/kotlin/dev/ulfrx/recipe/ApplicationTest.kt - Verification:
./gradlew :server:test - Committed in:
8cf112a
2. [Rule 3 - Blocking] Used Exposed StatementType.SELECT for Postgres upsert returning rows
- Found during: Task 3
- Issue:
INSERT ... RETURNINGmust be executed as a result-producing statement; otherwise Postgres reports that a result was returned when none was expected. - Fix: Added
explicitStatementType = StatementType.SELECTto the Exposedexeccall. - Files modified:
server/src/main/kotlin/dev/ulfrx/recipe/auth/PrincipalResolver.kt - Verification:
./gradlew :server:test --tests "*MeRouteTest*" --tests "*AuthJwtTest*" - Committed in:
8cf112a
Total deviations: 2 auto-fixed (1 bug, 1 blocking issue) Impact on plan: Both fixes were required for the planned tests and route behavior. No extra feature scope was added.
Issues Encountered
- Testcontainers Postgres made the first filtered test run take several minutes while the container image/runtime initialized. Subsequent server test runs completed from cache.
Authentication Gates
None.
Known Stubs
None.
User Setup Required
None for this plan. Real Authentik provider setup remains covered by the Phase 2 setup documentation from plan 02-01.
Verification
./gradlew :server:dependencyInsight --dependency exposed-jdbc --configuration runtimeClasspath- passed; Exposed JDBC version is0.55.0../gradlew :server:test --tests "*AuthJwtTest*" --tests "*MeRouteTest*"- passed../gradlew :server:test- passed.- Task acceptance greps for OIDC config, JWT verifier settings, logging safety, migration shape, no DAO imports, no blocking
transaction {}in auth code,/api/v1/me, Testcontainers,postgres:16, and Flyway all passed.
Next Phase Readiness
Phase 3 can extend PrincipalResolver from user identity to household-scoped principal resolution. The server now has the stable users.sub anchor and /api/v1/me boundary that Phase 3 onboarding and household membership can build on.
Self-Check: PASSED
- Created/modified key files exist.
- Task commits found:
614b57c,36c1b2c,8cf112a. - Required verification commands passed.
- No unplanned tracked file deletions were detected in task commits.
Phase: 02-authentication-foundation Completed: 2026-04-28