The ATProto OAuth authorization-code flow: PAR → authorize redirect → callback → token exchange.
authorize_url/3 starts the flow (returns the URL to send the user to
plus an opaque RequestContext the caller must persist — Plug session,
database, GenServer — between requests). callback/3 completes it with
the query parameters the authorization server redirected back with, and
produces a Exosphere.ATProto.OAuth.Session.
Examples
{:ok, resolved} = Discovery.resolve("alice.example.com")
client = Client.new!(metadata: metadata, key: key, redirect_uri: "https://app.example.com/oauth/callback")
{:ok, {url, ctx}} = Flow.authorize_url(client, resolved)
# ... user approves, AS redirects to redirect_uri?code=..&state=..&iss=..
{:ok, session} = Flow.callback(ctx, conn.query_params)
Summary
Functions
Push a PAR request and build the authorization URL.
Complete the flow: validate the callback parameters and exchange the authorization code for a DPoP-bound session.
Functions
@spec authorize_url( Exosphere.ATProto.OAuth.Client.t(), Exosphere.ATProto.OAuth.Discovery.t(), keyword() ) :: {:ok, {String.t(), Exosphere.ATProto.OAuth.RequestContext.t()}} | {:error, term()}
Push a PAR request and build the authorization URL.
Options
:scope- scopes to request (default: the client metadata scope, filtered to what the server supports):state- explicit state value (default: generated):http- HTTP module implementingHTTP.Behaviour(testing):timeout- request timeout in milliseconds:dpop_key- pre-generated DPoP private JWK (default: a fresh P-256 key)
@spec callback(Exosphere.ATProto.OAuth.RequestContext.t(), map(), keyword()) :: {:ok, Exosphere.ATProto.OAuth.Session.t()} | {:error, term()}
Complete the flow: validate the callback parameters and exchange the authorization code for a DPoP-bound session.
params are the redirect query parameters ("code", "state", "iss",
or "error" / "error_description" on failure).