---
phase: 02-authentication-foundation
plan: 03
subsystem: auth
tags: [oidc, appauth, kmp, wasm, jvm, authstate]

requires:
  - phase: 02-authentication-foundation
    provides: 02-01 shared OIDC constants and Phase 2 client dependencies
provides:
  - Common `OidcClient` expect seam with suspend login, refresh, and logout
  - Common `OidcResult` model for AuthSession and LoginViewModel consumers
  - Common `SecureAuthStateStore` expect contract for opaque AppAuth AuthState JSON
  - JVM dev-only `DEV_AUTH_TOKEN` OIDC actual and in-memory AuthState store actual
  - Wasm v2 OIDC stubs and in-memory AuthState store actual
  - SecureAuthStateStore common contract tests for write, overwrite, read, and clear
affects: [02-04-android-auth-actuals, 02-05-ios-auth-actuals, 02-06-auth-session-ui]

tech-stack:
  added: []
  patterns:
    - "OIDC seam pattern: common expects pin AppAuth/scopes/logout semantics while target actuals own platform mechanics."
    - "Secondary target pattern: JVM uses explicit DEV_AUTH_TOKEN dev behavior; Wasm throws the documented v2 NotImplementedError boundary."

key-files:
  created:
    - composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/auth/OidcClient.kt
    - composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/auth/OidcResult.kt
    - composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/auth/SecureAuthStateStore.kt
    - composeApp/src/jvmMain/kotlin/dev/ulfrx/recipe/auth/OidcClient.jvm.kt
    - composeApp/src/jvmMain/kotlin/dev/ulfrx/recipe/auth/SecureAuthStateStore.jvm.kt
    - composeApp/src/webMain/kotlin/dev/ulfrx/recipe/auth/OidcClient.wasmJs.kt
    - composeApp/src/webMain/kotlin/dev/ulfrx/recipe/auth/SecureAuthStateStore.wasmJs.kt
    - composeApp/src/commonTest/kotlin/dev/ulfrx/recipe/auth/SecureAuthStateStoreContractTest.kt
  modified: []

key-decisions:
  - "JVM actuals were added with Task 1 because the required `:composeApp:jvmTest` acceptance gate cannot compile common expect classes without JVM actual declarations."
  - "Kotlin expect/actual beta diagnostics are suppressed at the auth seam file level to satisfy the existing `-Werror` build without changing Gradle configuration."
  - "Wasm OIDC remains an explicit v2 boundary by throwing `NotImplementedError(\"Wasm OIDC: v2\")` from login, refresh, and logout."

patterns-established:
  - "AuthState JSON is treated as opaque common data; secure mobile storage actuals remain owned by Android/iOS plans."
  - "Desktop auth is dev-only and requires an externally supplied `DEV_AUTH_TOKEN`; no usable bearer token is hardcoded."

requirements-completed: [AUTH-01, AUTH-02, AUTH-04, AUTH-05]

duration: 31m
completed: 2026-04-28
---

# Phase 02 Plan 03: Common OIDC and AuthState Store Contracts Summary

**Stable KMP auth seams for AppAuth-backed mobile login, explicit JVM dev-token behavior, Wasm v2 stubs, and contract-tested AuthState JSON storage semantics.**

## Performance

- **Duration:** 31 min
- **Started:** 2026-04-28T11:18:45Z
- **Completed:** 2026-04-28T11:49:40Z
- **Tasks:** 2
- **Files modified:** 8

## Accomplishments

- Added common auth contracts: `OidcClient`, `OidcResult`, and `SecureAuthStateStore`.
- Pinned native OIDC behavior in common KDoc: AppAuth, `suspendCancellableCoroutine`, exact `openid profile email offline_access` scopes, fresh-token refresh, and RP-initiated logout.
- Added JVM actuals for desktop/dev test compilation with explicit `DEV_AUTH_TOKEN` behavior and no hardcoded bearer token.
- Added Wasm actuals that preserve the documented v2 OIDC boundary while keeping `compileKotlinWasmJs` green.
- Added common contract tests proving store write overwrite, latest read, and clear semantics.

## Task Commits

1. **Task 1 RED: SecureAuthStateStore contract test** - `7ef222e` (test)
2. **Task 1 GREEN: Common auth contracts plus JVM actuals** - `edc2a1d` (feat)
3. **Task 2: Wasm auth stubs** - `0dbd374` (feat)

_Note: Task 1 was TDD and produced RED + GREEN commits. No refactor commit was needed._

## Files Created/Modified

- `composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/auth/OidcClient.kt` - Common expect OIDC client seam with pinned AppAuth/scopes/refresh/logout semantics.
- `composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/auth/OidcResult.kt` - Sealed result model for success, cancellation, network failure, and auth failure.
- `composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/auth/SecureAuthStateStore.kt` - Common expect secure store contract for opaque AppAuth AuthState JSON.
- `composeApp/src/jvmMain/kotlin/dev/ulfrx/recipe/auth/OidcClient.jvm.kt` - Desktop dev actual using `DEV_AUTH_TOKEN`.
- `composeApp/src/jvmMain/kotlin/dev/ulfrx/recipe/auth/SecureAuthStateStore.jvm.kt` - In-memory desktop AuthState store actual.
- `composeApp/src/webMain/kotlin/dev/ulfrx/recipe/auth/OidcClient.wasmJs.kt` - Wasm v2 OIDC boundary stubs.
- `composeApp/src/webMain/kotlin/dev/ulfrx/recipe/auth/SecureAuthStateStore.wasmJs.kt` - In-memory Wasm AuthState store actual.
- `composeApp/src/commonTest/kotlin/dev/ulfrx/recipe/auth/SecureAuthStateStoreContractTest.kt` - Store read/write/overwrite/clear contract tests.

## Decisions Made

See frontmatter `key-decisions`.

## Deviations from Plan

### Auto-fixed Issues

**1. [Rule 3 - Blocking] Added JVM actuals during Task 1 GREEN**

- **Found during:** Task 1 verification
- **Issue:** The plan required `./gradlew :composeApp:jvmTest` to pass after adding common `expect class` declarations, but JVM compilation requires matching JVM `actual` declarations.
- **Fix:** Added the JVM dev `OidcClient` actual and in-memory `SecureAuthStateStore` actual in the Task 1 GREEN commit. Task 2 then added the Wasm actuals as planned.
- **Files modified:** `composeApp/src/jvmMain/kotlin/dev/ulfrx/recipe/auth/OidcClient.jvm.kt`, `composeApp/src/jvmMain/kotlin/dev/ulfrx/recipe/auth/SecureAuthStateStore.jvm.kt`
- **Verification:** `./gradlew :composeApp:jvmTest`
- **Committed in:** `edc2a1d`

**2. [Rule 3 - Blocking] Suppressed expect/actual beta diagnostics at file level**

- **Found during:** Task 1 verification
- **Issue:** Kotlin emitted expect/actual beta warnings and the project treats warnings as errors.
- **Fix:** Added targeted `@file:Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")` to the auth expect/actual files.
- **Files modified:** `OidcClient.kt`, `SecureAuthStateStore.kt`, JVM actual files, Wasm actual files
- **Verification:** `./gradlew :composeApp:jvmTest :composeApp:compileKotlinWasmJs`
- **Committed in:** `edc2a1d`, `0dbd374`

---

**Total deviations:** 2 auto-fixed (2 x Rule 3).
**Impact on plan:** No behavior scope changed. The deviations only made the required verification gates compatible with Kotlin expect/actual compilation under the project build settings.

## Known Stubs

| File | Line | Reason |
|------|------|--------|
| `composeApp/src/webMain/kotlin/dev/ulfrx/recipe/auth/OidcClient.wasmJs.kt` | 7 | Intentional v2 boundary per D-03 and plan acceptance criteria. |
| `composeApp/src/webMain/kotlin/dev/ulfrx/recipe/auth/OidcClient.wasmJs.kt` | 11 | Intentional v2 boundary per D-03 and plan acceptance criteria. |
| `composeApp/src/webMain/kotlin/dev/ulfrx/recipe/auth/OidcClient.wasmJs.kt` | 15 | Intentional v2 boundary per D-03 and plan acceptance criteria. |

## Issues Encountered

- Concurrent Wave 2 work landed `02-02` commits while this plan was executing. No conflicts touched this plan's owned files.
- `gsd-sdk query init.execute-phase 02` updated `.planning/STATE.md` at startup before task work began. Final state updates are handled in the metadata step.

## User Setup Required

None.

## Verification

- `./gradlew :composeApp:jvmTest` - PASS
- `./gradlew :composeApp:jvmTest :composeApp:compileKotlinWasmJs` - PASS
- Task 1 acceptance greps - PASS
- Task 2 acceptance greps - PASS

## Next Phase Readiness

Android and iOS auth actual plans can now implement AppAuth behind stable common seams. AuthSession/UI plans can consume `OidcResult` and `SecureAuthStateStore` without platform-specific APIs.

## Self-Check: PASSED

- Created files exist: all 8 plan-owned source/test files plus this summary were found.
- Commits exist: `7ef222e`, `edc2a1d`, and `0dbd374` were found in git history.
- Acceptance criteria: all required grep checks passed.
- Plan-level verification: `./gradlew :composeApp:jvmTest :composeApp:compileKotlinWasmJs` passed.

---
*Phase: 02-authentication-foundation*
*Completed: 2026-04-28*
