Package-level declarations

Types

Link copied to clipboard
data class AuthorizationCodeFlow(val authorizeUrl: String, val tokenUrl: String, val clientId: String, val redirect: LoopbackRedirect, val scopes: String? = null, val clientSecret: String? = null, val extraAuthorizeParams: Map<String, String> = emptyMap(), val extraTokenParams: Map<String, String> = emptyMap(), val nonce: Boolean = false, val echoChallengeOnExchange: Boolean = false, val redirectParam: String = "redirect_uri", val usesState: Boolean = true) : SignInFlow

The authorization-code grant with PKCE (RFC 6749 §4.1 + RFC 7636) through a loopback redirect (RFC 8252 §7.3): the app listens on LoopbackRedirect.port while the user signs in in the browser.

Link copied to clipboard
data class DeviceAuthorization(val deviceCode: String, val userCode: String, val verificationUri: String, val verificationUriComplete: String?, val expiresInSeconds: Long, val intervalSeconds: Long)

What the user needs to approve a device-flow sign-in.

Link copied to clipboard
data class DeviceCodeFlow(val deviceAuthorizationUrl: String, val tokenUrl: String, val clientId: String, val scopes: String? = null) : SignInFlow

The device authorization grant (RFC 8628): show a code, the user approves it elsewhere.

Link copied to clipboard

A one-shot HTTP listener on the loopback interface for an OAuth redirect (RFC 8252 §7.3). Bound to 127.0.0.1 only — never reachable from the network.

Link copied to clipboard
data class LoopbackRedirect(val host: String = "localhost", val port: Int, val path: String = "/callback")

How the browser hands the authorization code back to the app.

Link copied to clipboard
class OAuthBackend(tokens: TokenSource, backendFor: (OAuthTokens) -> ChatBackend) : ChatBackend

A ChatBackend for signed-in providers: takes a fresh access token each turn and builds the protocol backend with it. If the provider rejects the token (401) before anything was streamed, it refreshes once and retries.

Link copied to clipboard
class OAuthClient(http: OkHttpClient = DefaultHttpClient)

The network half of OAuth: build authorize URLs, exchange and refresh tokens, run the device flow. No Android dependencies, so it is unit-tested on the JVM against a local server. Tokens and codes never appear in exception messages.

Link copied to clipboard
class OAuthException(message: String) : Exception

A sign-in that failed in a way the user should see (denied, expired, wrong state…).

Link copied to clipboard
@Serializable
enum OAuthProvider : Enum<OAuthProvider>

Sign-in methods the app can offer, and what each one produces.

Link copied to clipboard
@Serializable
data class OAuthTokens(val accessToken: String, val refreshToken: String? = null, val expiresAtMs: Long? = null, val idToken: String? = null, val accountId: String? = null, val email: String? = null)

Tokens from an OAuth sign-in. Persist them encrypted (the demo uses SecretStore); never log them.

Link copied to clipboard
object Pkce

PKCE (RFC 7636) with the S256 method.

Link copied to clipboard
sealed interface SignInFlow

A way to sign in: AuthorizationCodeFlow (browser) or DeviceCodeFlow (code on another screen).

Link copied to clipboard
class TokenSource(provider: OAuthProvider, store: TokenStore, client: OAuthClient = OAuthClient(), now: () -> Long = System::currentTimeMillis, refreshUrlOverride: String? = null)

Hands out a usable access token, refreshing it when it's about to expire. Refreshes are single-flight: concurrent callers wait for one refresh instead of racing (providers that rotate refresh tokens would otherwise invalidate each other's).

Link copied to clipboard
interface TokenStore

Where signed-in tokens live; implement it over encrypted storage.

Functions

Link copied to clipboard
fun jwtClaims(jwt: String?): JsonObject?

Reads a JWT's claims without verifying it (only for display / routing, never for trust).