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@tokens.erl
Raw

src/atproto_core@oauth@tokens.erl

-module(atproto_core@oauth@tokens).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/atproto_core/oauth/tokens.gleam").
-export([exchange_code/6, refresh/4, revoke/4]).
-export_type([tokens/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(
" Token endpoint calls as `Effect`s: code exchange, refresh, and\n"
" best-effort revocation. Confidential clients pass client-assertion\n"
" fields via `extra_form`.\n"
).
-type tokens() :: {tokens, binary(), binary(), binary(), integer()}.
-file("src/atproto_core/oauth/tokens.gleam", 110).
-spec describe(atproto_core@xrpc:xrpc_error()) -> binary().
describe(E) ->
case E of
{decode_failed, M} ->
<<"decode token response: "/utf8, M/binary>>;
{request_failed, M@1} ->
atproto_core@xrpc:transport_error_to_string(M@1);
{bad_status, Status, _, _, Body} ->
<<<<(erlang:integer_to_binary(Status))/binary, ": "/utf8>>/binary,
Body/binary>>
end.
-file("src/atproto_core/oauth/tokens.gleam", 118).
-spec decoder() -> gleam@dynamic@decode:decoder(tokens()).
decoder() ->
gleam@dynamic@decode:field(
<<"access_token"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Access_token) ->
gleam@dynamic@decode:field(
<<"refresh_token"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Refresh_token) ->
gleam@dynamic@decode:field(
<<"sub"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Sub) ->
gleam@dynamic@decode:optional_field(
<<"expires_in"/utf8>>,
3600,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Expires_in) ->
gleam@dynamic@decode:success(
{tokens,
Access_token,
Refresh_token,
Sub,
Expires_in}
)
end
)
end
)
end
)
end
).
-file("src/atproto_core/oauth/tokens.gleam", 99).
-spec submit(binary(), list({binary(), binary()})) -> atproto_core@oauth@effect:effect({ok,
tokens()} |
{error, binary()}).
submit(Token_endpoint, Form) ->
atproto_core@oauth@effect:'try'(
atproto_core@oauth@transport:post_form_with_dpop(Token_endpoint, Form),
fun(Resp) ->
atproto_core@oauth@effect:done(
case (erlang:element(2, Resp) >= 200) andalso (erlang:element(
2,
Resp
)
< 300) of
true ->
_pipe = atproto_core@xrpc:parse(
erlang:element(4, Resp),
decoder()
),
gleam@result:map_error(_pipe, fun describe/1);
false ->
{error,
<<<<<<"token "/utf8,
(erlang:integer_to_binary(
erlang:element(2, Resp)
))/binary>>/binary,
": "/utf8>>/binary,
(erlang:element(4, Resp))/binary>>}
end
)
end
).
-file("src/atproto_core/oauth/tokens.gleam", 24).
-spec exchange_code(
binary(),
binary(),
binary(),
binary(),
binary(),
list({binary(), binary()})
) -> atproto_core@oauth@effect:effect({ok, tokens()} | {error, binary()}).
exchange_code(
Token_endpoint,
Code,
Redirect_uri,
Client_id,
Pkce_verifier,
Extra_form
) ->
submit(
Token_endpoint,
lists:append(
[{<<"grant_type"/utf8>>, <<"authorization_code"/utf8>>},
{<<"code"/utf8>>, Code},
{<<"code_verifier"/utf8>>, Pkce_verifier},
{<<"redirect_uri"/utf8>>, Redirect_uri},
{<<"client_id"/utf8>>, Client_id}],
Extra_form
)
).
-file("src/atproto_core/oauth/tokens.gleam", 47).
-spec refresh(binary(), binary(), binary(), list({binary(), binary()})) -> atproto_core@oauth@effect:effect({ok,
tokens()} |
{error, binary()}).
refresh(Token_endpoint, Refresh_token, Client_id, Extra_form) ->
submit(
Token_endpoint,
lists:append(
[{<<"grant_type"/utf8>>, <<"refresh_token"/utf8>>},
{<<"refresh_token"/utf8>>, Refresh_token},
{<<"client_id"/utf8>>, Client_id}],
Extra_form
)
).
-file("src/atproto_core/oauth/tokens.gleam", 69).
?DOC(
" Best-effort refresh-token revocation at logout: discover the AS revocation\n"
" endpoint from the issuer and revoke. Failures are ignored (the caller\n"
" clears its local session regardless).\n"
).
-spec revoke(binary(), binary(), binary(), list({binary(), binary()})) -> atproto_core@oauth@effect:effect(nil).
revoke(Issuer, Refresh_token, Client_id, Extra_form) ->
atproto_core@oauth@effect:then(
atproto_core@oauth@metadata:fetch_authorization_server(Issuer),
fun(Server_metadata) -> case Server_metadata of
{ok, Server_metadata@1} ->
case erlang:element(6, Server_metadata@1) of
{some, Endpoint} ->
atproto_core@oauth@effect:then(
atproto_core@oauth@transport:post_form_with_dpop(
Endpoint,
lists:append(
[{<<"token"/utf8>>, Refresh_token},
{<<"token_type_hint"/utf8>>,
<<"refresh_token"/utf8>>},
{<<"client_id"/utf8>>, Client_id}],
Extra_form
)
),
fun(_) ->
atproto_core@oauth@effect:done(nil)
end
);
none ->
atproto_core@oauth@effect:done(nil)
end;
{error, _} ->
atproto_core@oauth@effect:done(nil)
end end
).