Packages
oidcc
3.0.0-alpha.3
3.7.2
3.7.1
3.7.0
3.6.0
3.5.2
3.5.1
3.5.0
retired
3.4.0
3.3.0
3.2.6
3.2.5
3.2.4
3.2.3
3.2.2
3.2.1
3.2.0
3.2.0-beta.3
3.2.0-beta.2
retired
3.2.0-beta.1
retired
3.1.2
3.1.2-beta.1
retired
3.1.1
retired
3.1.0
retired
3.1.0-beta.2
retired
3.1.0-beta.1
retired
3.0.2
3.0.1
retired
3.0.0
retired
3.0.0-rc.6
retired
3.0.0-rc.5
retired
3.0.0-rc.4
retired
3.0.0-rc.3
retired
3.0.0-rc.2
retired
3.0.0-rc.1
retired
3.0.0-alpha.5
retired
3.0.0-alpha.4
retired
3.0.0-alpha.3
retired
3.0.0-alpha.2
retired
3.0.0-alpha.1
retired
2.0.0-alpha.2
retired
2.0.0-alpha.1
retired
1.8.1
retired
1.8.0
retired
1.7.0
retired
1.6.0
retired
1.5.1
retired
1.5.0
retired
1.4.0
retired
1.3.0
retired
1.2.1
retired
1.2.0
retired
1.1.0
retired
1.0.1
retired
1.0.0
retired
OpenID Connect client library for the BEAM.
Retired package: Security issue - DoS Vulnerability, see https://github.com/erlef/oidcc/security/advisories/GHSA-mj35-2rgf-cv8p
Current section
Files
Jump to
Current section
Files
src/oidcc_token_introspection.erl
%%%-------------------------------------------------------------------
%% @doc OAuth Token Introspection
%%
%% See [https://datatracker.ietf.org/doc/html/rfc7662]
%%
%% <h2>Records</h2>
%%
%% To use the records, import the definition:
%%
%% ```
%% -include_lib(["oidcc/include/oidcc_token_introspection.hrl"]).
%% '''
%%
%% <h2>Telemetry</h2>
%%
%% See {@link 'Elixir.Oidcc.TokenIntrospection'}
%% @end
%%%-------------------------------------------------------------------
-module(oidcc_token_introspection).
-feature(maybe_expr, enable).
-include("oidcc_client_context.hrl").
-include("oidcc_provider_configuration.hrl").
-include("oidcc_token.hrl").
-include("oidcc_token_introspection.hrl").
-export([introspect/3]).
-export_type([error/0]).
-export_type([opts/0]).
-export_type([t/0]).
-type t() :: #oidcc_token_introspection{
active :: boolean(),
client_id :: binary(),
exp :: pos_integer(),
scope :: oidcc_scope:scopes(),
username :: binary()
}.
%% Introspection Result
%%
%% See [https://datatracker.ietf.org/doc/html/rfc7662#section-2.2]
-type opts() :: #{request_opts => oidcc_http_util:request_opts()}.
-type error() :: client_id_mismatch | introspection_not_supported | oidcc_http_util:error().
-telemetry_event(#{
event => [oidcc, load_configuration, start],
description => <<"Emitted at the start of introspecting the token">>,
measurements => <<"#{system_time => non_neg_integer()}">>,
metadata => <<"#{issuer => uri_string:uri_string(), client_id => binary()}">>
}).
-telemetry_event(#{
event => [oidcc, load_configuration, stop],
description => <<"Emitted at the end of introspecting the token">>,
measurements => <<"#{duration => integer(), monotonic_time => integer()}">>,
metadata => <<"#{issuer => uri_string:uri_string(), client_id => binary()}">>
}).
-telemetry_event(#{
event => [oidcc, load_configuration, exception],
description => <<"Emitted at the end of introspecting the token">>,
measurements => <<"#{duration => integer(), monotonic_time => integer()}">>,
metadata => <<"#{issuer => uri_string:uri_string(), client_id => binary()}">>
}).
%% @doc
%% Introspect the given access token
%%
%% For a high level interface using {@link oidcc_provider_configuration_worker}
%% see {@link oidcc:introspect_token/5}.
%%
%% <h2>Examples</h2>
%%
%% ```
%% {ok, ClientContext} =
%% oidcc_client_context:from_configuration_worker(provider_name,
%% <<"client_id">>,
%% <<"client_secret">>),
%%
%% %% Get AccessToken
%%
%% {ok, #oidcc_token_introspection{active = True}} =
%% oidcc_token_introspection:introspect(AccessToken, ClientContext, #{}).
%% '''
%% @end
-spec introspect(Token, ClientContext, Opts) ->
{ok, t()}
| {error, error()}
when
Token :: oidcc_token:t() | binary(),
ClientContext :: oidcc_client_context:t(),
Opts :: opts().
introspect(#oidcc_token{access = #oidcc_token_access{token = AccessToken}},
ClientContext,
Opts) ->
introspect(AccessToken, ClientContext, Opts);
introspect(AccessToken, ClientContext, Opts) ->
#oidcc_client_context{provider_configuration = Configuration,
client_id = ClientId,
client_secret = ClientSecret} =
ClientContext,
#oidcc_provider_configuration{introspection_endpoint = Endpoint,
issuer = Issuer} = Configuration,
case Endpoint of
undefined ->
{error, introspection_not_supported};
_ ->
Header =
[{"accept", "application/json"},
oidcc_http_util:basic_auth_header(ClientId, ClientSecret)],
Body = [{<<"token">>, AccessToken}],
Request =
{Endpoint, Header, "application/x-www-form-urlencoded", uri_string:compose_query(Body)},
RequestOpts = maps:get(request_opts, Opts, #{}),
TelemetryOpts = #{topic => [oidcc, introspect_token],
extra_meta => #{issuer => Issuer, client_id => ClientId}},
maybe
{ok, {{json, Token}, _Headers}} ?= oidcc_http_util:request(post, Request, TelemetryOpts, RequestOpts),
extract_response(Token, ClientContext)
end
end.
-spec extract_response(TokenMap, ClientContext) ->
{ok, t()} | {error, error()}
when
TokenMap :: map(),
ClientContext :: oidcc_client_context:t().
extract_response(TokenMap, #oidcc_client_context{client_id = ClientId}) ->
Active =
case maps:get(<<"active">>, TokenMap, undefined) of
true ->
true;
_ ->
false
end,
Scope = maps:get(<<"scope">>, TokenMap, <<"">>),
Username = maps:get(<<"username">>, TokenMap, undefined),
Exp = maps:get(<<"exp">>, TokenMap, undefined),
case maps:get(<<"client_id">>, TokenMap, undefined) of
IntrospectionClientId when
IntrospectionClientId == ClientId; IntrospectionClientId == undefined
->
{ok, #oidcc_token_introspection{
active = Active,
scope = oidcc_scope:parse(Scope),
client_id = ClientId,
username = Username,
exp = Exp
}};
_ ->
{error, client_id_mismatch}
end.