atproto-oauth#
ATProto OAuth client for OCaml.
atproto-oauth implements the ATProto-specific OAuth 2.0 profile on
top of oauth: PAR (RFC 9126) + PKCE
(RFC 7636) + DPoP (RFC 9449) with mandatory
dpop_bound_access_tokens, client metadata built to the ATProto
defaults, and full discovery from handle to PDS to authorization
server per the ATProto OAuth clients spec.
Packages#
atproto-oauth— profile rules, client metadata builders, session type with refresh.atproto-oauth.discovery— handle → DID → PDS → resource → AS chain with profile validation at every step.atproto-oauth.login— full public-client CLI login flow (loopback listener + PAR + PKCE + DPoP).
Installation#
Install with opam:
$ opam install atproto-oauth
If opam cannot find the package, it may not yet be released in the
public opam-repository. Add the overlay repository, then install
it:
$ opam repo add samoht https://tangled.org/gazagnaire.org/opam-overlay.git
$ opam update
$ opam install atproto-oauth
Usage#
Scopes and client metadata#
The profile mandates the atproto scope; ATProto clients typically
combine it with transition:generic for legacy API access:
# Atproto_oauth.Profile.default_scope
- : string = "atproto transition:generic"
Build a public loopback-client metadata document for a CLI tool:
# let meta =
Atproto_oauth.Client_metadata.public_loopback
~client_id:"https://example.com/client-metadata.json"
~redirect_uris:[ "http://127.0.0.1/callback" ]
() in
Oauth.Client.(meta.scope, meta.dpop_bound_access_tokens)
- : string option * bool = (Some "atproto transition:generic", true)
Full login flow (public loopback client)#
Atproto_oauth_login.login walks handle resolution, discovery,
profile validation, PAR, browser consent, and the DPoP-bound token
exchange. The caller hands it an Eio switch, clock, net, and a
Requests.t, plus the client id of a hosted metadata document:
let login env ~http handle =
Eio.Switch.run @@ fun sw ->
match
Atproto_oauth_login.login
~sw
~clock:(Eio.Stdenv.clock env)
~net:(Eio.Stdenv.net env)
~http
~client_id:"https://example.com/client-metadata.json"
handle
with
| Ok session -> Fmt.pr "logged in: %a@." Atproto_oauth.Session.pp session
| Error e -> Fmt.pr "login failed: %a@." Atproto_oauth_login.pp_error e
Discovery on its own#
If you already hold a DID or handle and want the PDS + AS metadata without running the full flow:
let discover env ~http handle =
Eio.Switch.run @@ fun sw ->
Atproto_oauth_discovery.of_handle
~sw
~clock:(Eio.Stdenv.clock env)
~net:(Eio.Stdenv.net env)
~http handle
The result bundles the DID, the PDS URL, the RFC 9728 resource metadata and the RFC 8414 server metadata — all already validated against the ATProto profile.
Session refresh and persistence#
Atproto_oauth.Session.refresh rotates the access token using the
stored refresh token and DPoP key, handling DPoP-Nonce retries
transparently. Sessions serialise to JSON via
Atproto_oauth.Session.save / load (write at 0600; the file
contains private key material and the refresh token).
Licence#
ISC