-module(aws@internal@providers@sso).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/aws/internal/providers/sso.gleam").
-export([default_endpoint/1, fetch/2]).
-export_type([options/0, sso_credentials/0, error/0, raw_role_creds/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(
" AWS SSO (IAM Identity Center) provider — the GetRoleCredentials portal\n"
" call given an already-cached SSO access token. We don't implement the\n"
" device-grant flow that produces the cached token in the first place; the\n"
" AWS CLI's `aws sso login` does that and writes the token to\n"
" `~/.aws/sso/cache/<hash>.json`. We just consume it.\n"
"\n"
" Endpoint shape:\n"
" GET https://portal.sso.<region>.amazonaws.com/federation/credentials\n"
" ?account_id=<id>&role_name=<name>\n"
" Header: x-amz-sso_bearer_token: <access_token>\n"
"\n"
" Response:\n"
" { \"roleCredentials\": { accessKeyId, secretAccessKey, sessionToken,\n"
" expiration (millis since epoch) } }\n"
).
-type options() :: {options, binary(), binary(), binary(), binary(), binary()}.
-type sso_credentials() :: {sso_credentials,
binary(),
binary(),
binary(),
integer()}.
-type error() :: {failed, binary()} | {unreachable, binary()}.
-type raw_role_creds() :: {raw_role_creds,
binary(),
binary(),
binary(),
integer()}.
-file("src/aws/internal/providers/sso.gleam", 38).
-spec default_endpoint(binary()) -> binary().
default_endpoint(Region) ->
<<<<"https://portal.sso."/utf8, Region/binary>>/binary,
".amazonaws.com"/utf8>>.
-file("src/aws/internal/providers/sso.gleam", 119).
-spec raw_decoder() -> gleam@dynamic@decode:decoder(raw_role_creds()).
raw_decoder() ->
gleam@dynamic@decode:subfield(
[<<"roleCredentials"/utf8>>, <<"accessKeyId"/utf8>>],
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Access_key_id) ->
gleam@dynamic@decode:subfield(
[<<"roleCredentials"/utf8>>, <<"secretAccessKey"/utf8>>],
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Secret_access_key) ->
gleam@dynamic@decode:subfield(
[<<"roleCredentials"/utf8>>, <<"sessionToken"/utf8>>],
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Session_token) ->
gleam@dynamic@decode:subfield(
[<<"roleCredentials"/utf8>>,
<<"expiration"/utf8>>],
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Expiration_ms) ->
gleam@dynamic@decode:success(
{raw_role_creds,
Access_key_id,
Secret_access_key,
Session_token,
Expiration_ms}
)
end
)
end
)
end
)
end
).
-file("src/aws/internal/providers/sso.gleam", 144).
-spec decode_credentials(bitstring()) -> {ok, sso_credentials()} |
{error, error()}.
decode_credentials(Body) ->
gleam@result:'try'(
begin
_pipe = gleam@bit_array:to_string(Body),
gleam@result:replace_error(
_pipe,
{failed, <<"non-utf8 SSO response body"/utf8>>}
)
end,
fun(Text) ->
gleam@result:'try'(
begin
_pipe@1 = gleam@json:parse(Text, raw_decoder()),
gleam@result:map_error(
_pipe@1,
fun(_) ->
{failed,
<<"SSO response is not the expected JSON shape"/utf8>>}
end
)
end,
fun(Raw) ->
{ok,
{sso_credentials,
erlang:element(2, Raw),
erlang:element(3, Raw),
erlang:element(4, Raw),
erlang:element(5, Raw) div 1000}}
end
)
end
).
-file("src/aws/internal/providers/sso.gleam", 99).
-spec describe_http(aws@internal@http_send:http_error()) -> binary().
describe_http(Error) ->
case Error of
{connect_failed, Reason} ->
<<"connect failed: "/utf8, Reason/binary>>;
timeout ->
<<"timeout"/utf8>>;
{invalid_body, Reason@1} ->
<<"invalid body: "/utf8, Reason@1/binary>>;
{other, Reason@2} ->
Reason@2
end.
-file("src/aws/internal/providers/sso.gleam", 83).
-spec build_request(binary(), binary()) -> {ok,
gleam@http@request:request(bitstring())} |
{error, binary()}.
build_request(Url, Access_token) ->
gleam@result:'try'(
begin
_pipe = gleam@http@request:to(Url),
gleam@result:replace_error(
_pipe,
<<"invalid SSO URL: "/utf8, Url/binary>>
)
end,
fun(Base) ->
{ok,
begin
_pipe@1 = Base,
_pipe@2 = gleam@http@request:set_method(_pipe@1, get),
_pipe@3 = gleam@http@request:set_body(
_pipe@2,
gleam_stdlib:identity(<<""/utf8>>)
),
gleam@http@request:set_header(
_pipe@3,
<<"x-amz-sso_bearer_token"/utf8>>,
Access_token
)
end}
end
).
-file("src/aws/internal/providers/sso.gleam", 59).
-spec fetch(
fun((gleam@http@request:request(bitstring())) -> {ok,
gleam@http@response:response(bitstring())} |
{error, aws@internal@http_send:http_error()}),
options()
) -> {ok, sso_credentials()} | {error, error()}.
fetch(Send, Options) ->
Url = <<<<<<<<(erlang:element(6, Options))/binary,
"/federation/credentials?account_id="/utf8>>/binary,
(aws@internal@uri:encode_component(erlang:element(3, Options)))/binary>>/binary,
"&role_name="/utf8>>/binary,
(aws@internal@uri:encode_component(erlang:element(4, Options)))/binary>>,
gleam@result:'try'(
begin
_pipe = build_request(Url, erlang:element(5, Options)),
gleam@result:map_error(_pipe, fun(Reason) -> {failed, Reason} end)
end,
fun(Req) ->
gleam@result:'try'(
begin
_pipe@1 = Send(Req),
gleam@result:map_error(
_pipe@1,
fun(E) ->
{unreachable,
<<"SSO portal transport: "/utf8,
(describe_http(E))/binary>>}
end
)
end,
fun(Resp) -> case erlang:element(2, Resp) of
Code when (Code >= 200) andalso (Code < 300) ->
decode_credentials(erlang:element(4, Resp));
Code@1 ->
{error,
{failed,
<<"SSO portal returned status "/utf8,
(erlang:integer_to_binary(Code@1))/binary>>}}
end end
)
end
).