feat(oauth)!: hardened OAuth client with confidential-client support #132
Labels
No labels
breaking-change
bug
documentation
duplicate
enhancement
good first issue
help wanted
performance
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Grandiras/ATProto.NET#132
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Part of the v0.7.0 update cycle (wave 6). Harden the core OAuth client and add confidential-client support. The Server-side hosting move is a separate issue.
Transport and validation
PostFormWithDpopAsync<T>for PAR, token, refresh and revoke. It rebuilds the form on each attempt, retries once onuse_dpop_nonce, throwsOAuthExceptionon error bodies, remembers the nonce, and disposes responses. This replaces three copies; today no response is disposed and the PAR nonce isn't reused for the token exchange.XrpcClient, per-request clients,SpaceReaderand the OAuth client. Today every per-request client pays an extra401 use_dpop_nonceround trip on its first call.subequals the session DID,scopecontainsatproto, and the DID's authorization server is still the issuer.httpsURL with no query or fragment;require_pushed_authorization_requests,authorization_response_iss_parameter_supportedandclient_id_metadata_document_supported;resourcevalue matches the PDS.Confidential clients
private_key_jwt: an ES256client_assertion(iss=sub= client_id,aud= issuer,jti, shortexp) on PAR, token, refresh and revoke. Persist the key id per session. Client metadata carriesjwks/jwks_uri. Today the docs claim this works, but no assertion is ever sent, so sessions are capped at the public-client lifetime (2 weeks vs 180 days).State and scopes
IOAuthStateStorefor pending authorizations: in-memory by default with per-client limits and TTL, plus anIDistributedCacheoption. It replaces the global cap of 100, which lets anyone block every login for 10 minutes and breaks logins across restarts and instances.StartAuthorizationAsyncreturns a record instead of a tuple.IdentityAction/identity:*?action=, which servers reject;rpc/includeaudas*ordid#fragment;transition:*as legacy;/oauth-client-metadata.jsonin the docs.GetPendingAuthorizationState+OAuthAuthorizationState, a write-only export of the PKCE verifier;AtProtoClientOptions.OAuthandOAuthOptions.DefaultPdsUrl, which nothing reads;CHANGELOG
Added(confidential clients, the state store),Security(validation, the state DoS),Breaking changes(removed or changed API).Items deferred here by earlier PRs:
OAuthOptions.AllowPrivateNetworks/MetadataHttpClient). PAR, token and revocation requests still go through the caller'sHttpClientwith no address check, to endpoints named in the validated AS metadata. Apply the same policy, or at least an https + public-address check, to those endpoints.submust equal the session DID,scopemust containatproto, and the issuer must still be the DID's AS.PostFormWithDpopAsync) that refresh and revocation already use.