feat(02-03): define common auth contracts

- Add OIDC result and expect client seam with pinned native AppAuth semantics
- Add secure AuthState JSON store contract and JVM dev actuals for test compilation
This commit is contained in:
2026-04-28 13:48:25 +02:00
parent 3122fdaf37
commit edc2a1d4c8
5 changed files with 124 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
@file:Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
package dev.ulfrx.recipe.auth
/**
* Common seam for Authentik OIDC.
*
* Native Android/iOS actuals must use AppAuth (D-01) and bridge AppAuth callback
* APIs with `suspendCancellableCoroutine`, cancelling the underlying AppAuth
* request when the coroutine is cancelled (D-04). Login requests must be public
* PKCE-compatible OIDC requests with exactly these scopes:
* `openid profile email offline_access` (D-06). AppAuth owns state and nonce
* verification.
*
* Refresh must go through AppAuth fresh-token APIs such as
* `performActionWithFreshTokens`, then return the updated AuthState JSON for
* persistence (D-16). Logout must use AppAuth RP-initiated end-session APIs
* before local state is cleared; callers still clear local state if remote
* logout fails so users are never trapped in a stale session (D-19, D-20).
*/
expect class OidcClient() {
suspend fun login(): OidcResult
suspend fun refresh(authStateJson: String): OidcResult
suspend fun logout(authStateJson: String)
}

View File

@@ -0,0 +1,25 @@
package dev.ulfrx.recipe.auth
/**
* Result returned by platform OIDC clients.
*
* `authStateJson` is the opaque AppAuth AuthState JSON blob persisted by
* [SecureAuthStateStore]. Callers must not parse token values out of it directly.
*/
sealed interface OidcResult {
data class Success(
val authStateJson: String,
val accessToken: String,
val idToken: String?,
val expiresAtEpochMillis: Long,
) : OidcResult
data object Cancelled : OidcResult
data object NetworkError : OidcResult
data class AuthError(
val message: String,
val cause: Throwable? = null,
) : OidcResult
}

View File

@@ -0,0 +1,19 @@
@file:Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
package dev.ulfrx.recipe.auth
/**
* Persists the full AppAuth AuthState JSON blob for the current app install.
*
* Mobile actuals must use explicit secure platform storage for token material
* (D-13): iOS Keychain and Android encrypted/Keystore-backed storage. Do not use
* no-arg or default insecure settings implementations for tokens. The stored
* blob is global to the install and must be deleted on logout (D-15).
*/
expect class SecureAuthStateStore() {
fun read(): String?
fun write(authStateJson: String)
fun clear()
}