ATProto OAuth: client, discovery, and session management
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

oauth: lift RFC 8414 + RFC 9728 discovery types out of atproto-oauth

The ATProto OAuth profile's discovery metadata types were spec-level
(RFC 8414 authorization-server metadata with the RFC 9126 PAR and RFC
9449 DPoP extensions, and RFC 9728 protected-resource metadata) with
zero ATProto-specific content. Move them where they belong:

- lib/resource.ml (Oauth.Resource.metadata) is RFC 9728.
- lib/server.ml (Oauth.Server.metadata) is RFC 8414 + PAR + DPoP.

Both carry a Json codec and a pp.

In the same commit: Oauth.Provider.custom is restructured so it
embeds a Server.metadata directly instead of duplicating nine of the
same URLs. Constructor custom_provider keeps its signature (and
builds a minimal Server.metadata under the hood); new helper
provider_of_server builds a custom provider from an already-parsed
discovery document. Access paths that used to be c.token_url now go
through c.server.token_endpoint. This is a breaking API change for
pattern-matching callers, in line with the earlier oauth.ml split.

ocaml-atproto-oauth shrinks to the error type for now; the
ATProto-specific pieces (handle resolution, DID:PLC key discovery,
confidential-client profile policy) will land in follow-ups.

93 oauth tests pass (including 3 resource + 4 server shape tests that
moved over).

+93
+1
.ocamlformat
··· 1 + profile = default
+30
atproto-oauth.opam
··· 1 + # This file is generated by dune, edit dune-project instead 2 + opam-version: "2.0" 3 + synopsis: "ATProto OAuth: client, discovery, and session management" 4 + maintainer: ["Thomas Gazagnaire <thomas@gazagnaire.org>"] 5 + authors: ["Thomas Gazagnaire <thomas@gazagnaire.org>"] 6 + license: "ISC" 7 + tags: ["org:blacksun" "identity" "atproto" "oauth"] 8 + depends: [ 9 + "dune" {>= "3.21"} 10 + "ocaml" {>= "5.1"} 11 + "fmt" 12 + "json" 13 + "alcotest" {with-test} 14 + "odoc" {with-doc} 15 + ] 16 + build: [ 17 + ["dune" "subst"] {dev} 18 + [ 19 + "dune" 20 + "build" 21 + "-p" 22 + name 23 + "-j" 24 + jobs 25 + "@install" 26 + "@runtest" {with-test} 27 + "@doc" {with-doc} 28 + ] 29 + ] 30 + x-maintenance-intent: ["(latest)"]
+3
dune
··· 1 + (env 2 + (dev 3 + (flags :standard %{dune-warnings})))
+18
dune-project
··· 1 + (lang dune 3.21) 2 + (name atproto-oauth) 3 + 4 + (generate_opam_files true) 5 + 6 + (license ISC) 7 + (authors "Thomas Gazagnaire <thomas@gazagnaire.org>") 8 + (maintainers "Thomas Gazagnaire <thomas@gazagnaire.org>") 9 + 10 + (package 11 + (name atproto-oauth) 12 + (synopsis "ATProto OAuth: client, discovery, and session management") 13 + (tags (org:blacksun identity atproto oauth)) 14 + (depends 15 + (ocaml (>= 5.1)) 16 + fmt 17 + json 18 + (alcotest :with-test)))
+3
lib/atproto_oauth.ml
··· 1 + type error = [ `Msg of string ] 2 + 3 + let pp_error ppf (`Msg s) = Fmt.string ppf s
+15
lib/atproto_oauth.mli
··· 1 + (** ATProto OAuth. 2 + 3 + The RFC-level building blocks (protected-resource metadata per RFC 9728, 4 + authorization-server metadata per RFC 8414 with the PAR and DPoP extensions, 5 + the Flow orchestration, etc.) live in [ocaml-oauth]. This module hosts the 6 + ATProto-specific pieces: handle-to-PDS resolution, confidential-client 7 + metadata, and the profile-level policy that requires PAR + DPoP on every 8 + request. *) 9 + 10 + (** {1:errors Errors} *) 11 + 12 + type error = [ `Msg of string ] 13 + 14 + val pp_error : error Fmt.t 15 + (** [pp_error] formats an error for humans. *)
+4
lib/dune
··· 1 + (library 2 + (name atproto_oauth) 3 + (public_name atproto-oauth) 4 + (libraries fmt))
+3
test/dune
··· 1 + (test 2 + (name test) 3 + (libraries atproto-oauth json alcotest fmt))
+1
test/test.ml
··· 1 + let () = Alcotest.run "atproto-oauth" [ Test_atproto_oauth.suite ]
+11
test/test_atproto_oauth.ml
··· 1 + (* The RFC-level metadata shape tests moved to [ocaml-oauth] (see 2 + test_resource.ml, test_server.ml). This file holds ATProto-specific 3 + tests as they land. *) 4 + 5 + let test_error_pp () = 6 + Alcotest.(check string) 7 + "pp_error" "boom" 8 + (Fmt.str "%a" Atproto_oauth.pp_error (`Msg "boom")) 9 + 10 + let suite : string * unit Alcotest.test_case list = 11 + ("atproto-oauth", [ Alcotest.test_case "error/pp" `Quick test_error_pp ])
+4
test/test_atproto_oauth.mli
··· 1 + (** ATProto OAuth discovery metadata tests. *) 2 + 3 + val suite : string * unit Alcotest.test_case list 4 + (** [suite] is the atproto-oauth test group. *)