Expand description
Native-app OAuth composition root — the non-OIDC sibling of
RelyingParty.
NativeAuthFlow<S> runs the authorization-code + PKCE flow for a
native client (RFC 8252): a desktop or CLI program that opens the
system browser and receives the redirect on a loopback socket it owns.
It requests resource scopes — chat.read, contact.read, … — and no
openid, so the authorization server mints no id_token.
§Why RelyingParty cannot serve this
The OIDC RP is welded to OIDC in three independent places, and a native resource client breaks all three:
RelyingParty requires | A native client |
|---|---|
S: RequestedScope, whose every impl is an openid … marker | requests resource atoms, never openid |
a nonce on every authorize request | has no id_token for a nonce to bind |
| a verified id_token in the token response | receives none — PAS mints one only for openid |
Loosening any of them would weaken the contract CWC / RCW / CTW depend on. So this is a sibling composition root, not a relaxation — and the two share their machinery (PKCE, discovery, the OAuth client) rather than their contract.
§Shape
Three calls, and the SDK holds every invariant that is easy to get wrong:
let flow = NativeAuthFlow::<S>::new(
NativeConfig::new("https://accounts.ppoppo.com".parse()?, "my_app_id")
.with_resource("https://api.ppoppo.com/grpc"),
)
.await?;
// 1. Bind your loopback listener, then hand its address to `start`.
let url = flow.start("http://127.0.0.1:54321/callback");
// …open `url` in the browser, wait for the redirect…
// 2. Hand back the raw query string. State verification, the code
// exchange, and the grant check all happen inside.
let source = flow.complete(raw_query, MemoryTokenStore::new()).await?;source is a ScopedTokenSource<S>
— feed it straight to a resource client built at the same S, and the
credential’s tier and the client’s tier cannot disagree.
On a later run there is no code to exchange: the refresh token is
already in the keystore. token_source
is that path, and complete is defined in terms of it.
§What the consumer still owns
The loopback listener — a socket in the consumer’s process, which no library can hold for it. Everything else (state generation, single-use state verification, PKCE, callback parsing, the grant check) is here, on purpose: each is a security invariant that would otherwise be re-implemented, slightly differently, by every native consumer.
Structs§
- Native
Auth Flow - Native-app OAuth composition root. See the module docs.
- Native
Config - Boot configuration for a
NativeAuthFlow.
Enums§
- Callback
Error NativeAuthFlow::completefailure surface.- Native
Auth Init Error NativeAuthFlow::newfailure surface.