Current section
Files
Jump to
Current section
Files
src/atproto@oauth@flow.erl
-module(atproto@oauth@flow).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/atproto/oauth/flow.gleam").
-export([start/7]).
-export_type([flow/0, flow_error/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Start an authorization flow: resolve the PDS, discover the authorization\n"
" server, push the request (PAR) with PKCE + DPoP, return the authorization\n"
" URL and the pending `Flow` the caller must hold (server-side by state, or\n"
" in-process for a CLI) until the callback returns with the code.\n"
"\n"
" Confidential clients pass their client-assertion fields via `extra_form`;\n"
" public and loopback clients pass an empty list.\n"
"\n"
" A thin sync wrapper over the effect kernel in `atproto_core/oauth/flow`:\n"
" this module generates the platform one-shots (PKCE, DPoP key, state) and\n"
" folds the key into the `Flow` record the kernel deliberately never sees.\n"
).
-type flow() :: {flow,
binary(),
binary(),
binary(),
binary(),
gose:key(binary()),
binary(),
binary(),
binary()}.
-type flow_error() :: {resolve_failed, binary()} |
{discover_failed, binary()} |
{par_failed, binary()}.
-file("src/atproto/oauth/flow.gleam", 89).
-spec to_flow_error(atproto_core@oauth@flow:start_error()) -> flow_error().
to_flow_error(E) ->
case E of
{resolve_failed, M} ->
{resolve_failed, M};
{discover_failed, M@1} ->
{discover_failed, M@1};
{par_failed, M@2} ->
{par_failed, M@2}
end.
-file("src/atproto/oauth/flow.gleam", 97).
-spec base64url(bitstring()) -> binary().
base64url(Bits) ->
gleam@bit_array:base64_url_encode(Bits, false).
-file("src/atproto/oauth/flow.gleam", 44).
?DOC(
" Returns the authorization-server URL to send the user to, and the pending\n"
" flow. The caller must verify the callback's `state` against `flow.state`\n"
" before exchanging the code.\n"
).
-spec start(
atproto@xrpc:client(),
binary(),
binary(),
binary(),
binary(),
binary(),
list({binary(), binary()})
) -> {ok, {binary(), flow()}} | {error, flow_error()}.
start(Client, Resolver, Identifier, Client_id, Redirect_uri, Scope, Extra_form) ->
Pkce_pair = atproto@oauth@pkce:generate(),
Dpop_key = atproto@oauth@dpop:generate_key(),
State = base64url(crypto:strong_rand_bytes(16)),
Started = begin
_pipe = atproto_core@oauth@flow:start(
Resolver,
Identifier,
Client_id,
Redirect_uri,
Scope,
erlang:element(3, Pkce_pair),
State,
Extra_form
),
atproto@oauth@runner:run(_pipe, Client, Dpop_key)
end,
case Started of
{error, E} ->
{error, to_flow_error(E)};
{ok, Auth} ->
{ok,
{erlang:element(2, Auth),
{flow,
Identifier,
erlang:element(3, Auth),
erlang:element(4, Auth),
erlang:element(5, Auth),
Dpop_key,
erlang:element(2, Pkce_pair),
Client_id,
State}}}
end.