Packages

The shared sans-io core of the gleam-atproto client packages: XRPC error vocabulary and response handling, plus the OAuth effect kernel.

Current section

Files

Jump to
atproto_core src atproto_core@oauth@flow.erl
Raw

src/atproto_core@oauth@flow.erl

-module(atproto_core@oauth@flow).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/atproto_core/oauth/flow.gleam").
-export([start/8]).
-export_type([start_error/0, authorized/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 as an `Effect`: resolve the PDS, discover the\n"
" authorization server, push the request (PAR) with PKCE + DPoP, and return\n"
" the authorization URL plus the discovered endpoints. The one-shot inputs\n"
" the platform computes (PKCE challenge, `state`) come in as parameters; the\n"
" DPoP key never appears here (PAR's proof is a `DpopProof` effect). The\n"
" per-target wrapper adds the key and its own `Flow` record.\n"
).
-type start_error() :: {resolve_failed, binary()} |
{discover_failed, binary()} |
{par_failed, binary()}.
-type authorized() :: {authorized, binary(), binary(), binary(), binary()}.
-file("src/atproto_core/oauth/flow.gleam", 95).
-spec push_par(
atproto_core@oauth@metadata:auth_server_metadata(),
list({binary(), binary()})
) -> atproto_core@oauth@effect:effect({ok, binary()} | {error, start_error()}).
push_par(Server_metadata, Form) ->
atproto_core@oauth@effect:then(
atproto_core@oauth@transport:post_form_with_dpop(
erlang:element(5, Server_metadata),
Form
),
fun(Resp) -> case Resp of
{error, E} ->
atproto_core@oauth@effect:done({error, {par_failed, E}});
{ok, Resp@1} ->
case (erlang:element(2, Resp@1) >= 200) andalso (erlang:element(
2,
Resp@1
)
< 300) of
true ->
atproto_core@oauth@effect:done(
case atproto_core@xrpc:parse(
erlang:element(4, Resp@1),
gleam@dynamic@decode:at(
[<<"request_uri"/utf8>>],
{decoder,
fun gleam@dynamic@decode:decode_string/1}
)
) of
{ok, Request_uri} ->
{ok, Request_uri};
{error, E@1} ->
{error,
{par_failed,
gleam@string:inspect(E@1)}}
end
);
false ->
atproto_core@oauth@effect:done(
{error,
{par_failed,
<<<<<<"PAR "/utf8,
(erlang:integer_to_binary(
erlang:element(
2,
Resp@1
)
))/binary>>/binary,
": "/utf8>>/binary,
(erlang:element(4, Resp@1))/binary>>}}
)
end
end end
).
-file("src/atproto_core/oauth/flow.gleam", 150).
-spec mini_doc_pds_decoder() -> gleam@dynamic@decode:decoder(binary()).
mini_doc_pds_decoder() ->
gleam@dynamic@decode:field(
<<"did"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(_) ->
gleam@dynamic@decode:field(
<<"pds"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Pds) -> gleam@dynamic@decode:success(Pds) end
)
end
).
-file("src/atproto_core/oauth/flow.gleam", 126).
-spec resolve_pds(binary(), binary()) -> atproto_core@oauth@effect:effect({ok,
binary()} |
{error, atproto_core@xrpc:xrpc_error()}).
resolve_pds(Resolver, Identifier) ->
Query = gleam@uri:query_to_string([{<<"identifier"/utf8>>, Identifier}]),
Url = <<<<Resolver/binary,
"/xrpc/com.bad-example.identity.resolveMiniDoc?"/utf8>>/binary,
Query/binary>>,
case gleam@http@request:to(Url) of
{error, _} ->
atproto_core@oauth@effect:done(
{error, {request_failed, {invalid_url, Url}}}
);
{ok, Req} ->
atproto_core@oauth@effect:then(
atproto_core@oauth@effect:fetch(Req),
fun(Resp) -> case Resp of
{error, E} ->
atproto_core@oauth@effect:done(
{error, {request_failed, E}}
);
{ok, Resp@1} ->
case atproto_core@xrpc:check_ok(Resp@1) of
{error, E@1} ->
atproto_core@oauth@effect:done({error, E@1});
{ok, Resp@2} ->
atproto_core@oauth@effect:done(
atproto_core@xrpc:parse(
erlang:element(4, Resp@2),
mini_doc_pds_decoder()
)
)
end
end end
)
end.
-file("src/atproto_core/oauth/flow.gleam", 36).
-spec start(
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
list({binary(), binary()})
) -> atproto_core@oauth@effect:effect({ok, authorized()} |
{error, start_error()}).
start(
Resolver,
Identifier,
Client_id,
Redirect_uri,
Scope,
Pkce_challenge,
State,
Extra_form
) ->
atproto_core@oauth@effect:then(
resolve_pds(Resolver, Identifier),
fun(Pds) -> case Pds of
{error, E} ->
atproto_core@oauth@effect:done(
{error, {resolve_failed, gleam@string:inspect(E)}}
);
{ok, Pds@1} ->
atproto_core@oauth@effect:then(
atproto_core@oauth@metadata:discover(Pds@1),
fun(Server_metadata) -> case Server_metadata of
{error, E@1} ->
atproto_core@oauth@effect:done(
{error,
{discover_failed,
gleam@string:inspect(E@1)}}
);
{ok, Server_metadata@1} ->
Form = lists:append(
[{<<"client_id"/utf8>>, Client_id},
{<<"response_type"/utf8>>,
<<"code"/utf8>>},
{<<"code_challenge"/utf8>>,
Pkce_challenge},
{<<"code_challenge_method"/utf8>>,
<<"S256"/utf8>>},
{<<"redirect_uri"/utf8>>,
Redirect_uri},
{<<"scope"/utf8>>, Scope},
{<<"state"/utf8>>, State},
{<<"login_hint"/utf8>>, Identifier}],
Extra_form
),
atproto_core@oauth@effect:then(
push_par(Server_metadata@1, Form),
fun(Request_uri) -> case Request_uri of
{error, E@2} ->
atproto_core@oauth@effect:done(
{error, E@2}
);
{ok, Request_uri@1} ->
Redirect_url = <<<<(erlang:element(
3,
Server_metadata@1
))/binary,
"?"/utf8>>/binary,
(gleam@uri:query_to_string(
[{<<"client_id"/utf8>>,
Client_id},
{<<"request_uri"/utf8>>,
Request_uri@1}]
))/binary>>,
atproto_core@oauth@effect:done(
{ok,
{authorized,
Redirect_url,
Pds@1,
erlang:element(
2,
Server_metadata@1
),
erlang:element(
4,
Server_metadata@1
)}}
)
end end
)
end end
)
end end
).