Exosphere.ATProto.OAuth.Discovery (Exosphere v0.5.0)

Copy Markdown View Source

Identity-to-authorization-server discovery for ATProto OAuth.

The authorization server for an account is reached through its identity:

  1. Resolve the login hint (handle or DID) to a DID document — Exosphere.ATProto.Identity.
  2. Take the PDS service endpoint (#atproto_pds) from the document.
  3. Fetch the PDS's resource-server metadata (/.well-known/oauth-protected-resource) to find its authorization server.
  4. Fetch and validate the authorization server metadata (/.well-known/oauth-authorization-server).

Starting from a bare server URL ("server flow") skips identity resolution; the account is instead verified after the token exchange via verify_subject/3.

Examples

{:ok, resolved} = Exosphere.ATProto.OAuth.Discovery.resolve("alice.example.com")
resolved.auth_server.issuer

Summary

Functions

Find the authorization server for a PDS (or any resource server) origin: resource-server metadata, then authorization-server metadata.

Resolve an identity (handle, DID, or server URL) to its authorization server.

Resolve a DID (with optional expected handle for the bidirectional check) through to its authorization server.

Verify a sub DID from a server-flow token exchange against the expected authorization-server issuer: the DID's PDS must declare (via its resource-server metadata) an authorization server whose issuer matches.

Types

opts()

@type opts() :: [timeout: pos_integer(), http: module()]

t()

@type t() :: %Exosphere.ATProto.OAuth.Discovery{
  auth_server: Exosphere.ATProto.OAuth.ServerMetadata.t(),
  did: String.t() | nil,
  did_document: Exosphere.ATProto.Identity.Document.t() | nil,
  handle: String.t() | nil,
  pds: String.t() | nil
}

Functions

authorization_server(pds_url, opts)

@spec authorization_server(String.t(), opts()) ::
  {:ok, Exosphere.ATProto.OAuth.ServerMetadata.t()} | {:error, term()}

Find the authorization server for a PDS (or any resource server) origin: resource-server metadata, then authorization-server metadata.

resolve(identifier, opts \\ [])

@spec resolve(String.t(), opts()) :: {:ok, t()} | {:error, term()}

Resolve an identity (handle, DID, or server URL) to its authorization server.

Handles are resolved to DIDs and verified bidirectionally against the DID document's alsoKnownAs. Server URLs resolve directly to an authorization server (or, with a 404 resource-server document, the server itself acting as its own AS); identity fields stay nil until verify_subject/3.

resolve_did(did, handle \\ nil, opts)

@spec resolve_did(String.t(), String.t() | nil, opts()) ::
  {:ok, t()} | {:error, term()}

Resolve a DID (with optional expected handle for the bidirectional check) through to its authorization server.

verify_subject(did, expected_issuer, opts)

@spec verify_subject(String.t(), String.t(), opts()) :: {:ok, t()} | {:error, term()}

Verify a sub DID from a server-flow token exchange against the expected authorization-server issuer: the DID's PDS must declare (via its resource-server metadata) an authorization server whose issuer matches.