4#include "Stroika/Frameworks/StroikaPreComp.h"
7#include "Stroika/Foundation/Containers/Association.h"
15#include "Stroika/Foundation/IO/Network/Transfer/Connection.h"
28using namespace Stroika::Frameworks;
29using namespace Stroika::Frameworks::Auth::OAuth;
41String TokenRequest::ToString ()
const
47 sb <<
", code: "sv <<
code;
48 sb <<
", grant_type: authorization_code"sv;
52 sb <<
", grant_type: refresh_token"sv;
83TypedBLOB TokenRequest::ToWireFormat ()
const
90 static const auto kExcept_ =
RuntimeErrorException{
"Cannot combine authorization code/refresh_token"sv};
97 BLOB reqBody = [&] () {
101 params.Add ({
"code"sv, *
code});
102 params.Add ({
"grant_type"sv,
"authorization_code"sv});
106 params.Add ({
"grant_type"sv,
"refresh_token"sv});
119 return TypedBLOB{reqBody, InternetMediaTypes::kWWWFormURLEncoded};
125 static const auto kExcept_ =
RuntimeErrorException{
"Expected {}"_f(InternetMediaTypes::kWWWFormURLEncoded)};
139 static const auto kExcept_ =
RuntimeErrorException{
"Cannot combine authorization code/refresh_token"sv};
145 .client_secret = params.
LookupOne (
"client_secret"sv),
146 .redirect_uri = params.
LookupOne (
"redirect_uri"sv)};
154String TokenResponse::ToString ()
const
160 sb <<
", scope: "sv << scope;
165 sb <<
", id_token: "sv << id_token;
168 sb <<
", token_type: "sv << token_type;
187 return VariantValue{(objOfType->AsUTC () - DateTime::NowUTC ()).
As<
int> ()};
191 *into = DateTime::NowUTC ().AddSeconds (d.As<
int> ());
194 {
"scope"sv, &TokenResponse::scope,
197 return objOfType->Join (
" "sv);
204 {
"id_token"sv, &TokenResponse::id_token},
205 {
"token_type"sv, &TokenResponse::token_type},
210TypedBLOB TokenResponse::ToWireFormat ()
const
229String TokenRevocationRequest::ToString ()
const
233 sb <<
"access_token: "sv << access_token;
235 sb <<
", refresh_token: "sv << *refresh_token;
238 sb <<
", client_id: "sv << *client_id;
241 sb <<
", client_secret: "sv << *client_secret;
251 mapper.
AddClass<TokenRevocationRequest> ({
252 {
"access_token"sv, &TokenRevocationRequest::access_token},
253 {
"refresh_token"sv, &TokenRevocationRequest::refresh_token},
254 {
"client_id"sv, &TokenRevocationRequest::client_id},
255 {
"client_secret"sv, &TokenRevocationRequest::client_secret},
260TypedBLOB TokenRevocationRequest::ToWireFormat ()
const
262 if (access_token.empty ()) {
266 BLOB reqBody = [&] () {
269 params.
Add ({
"token_type_hint"sv,
"refresh_token"sv});
270 params.
Add ({
"token"sv, *refresh_token});
273 params.
Add ({
"token_type_hint"sv,
"access_token"sv});
274 params.
Add ({
"token"sv, access_token});
277 params.
Add ({
"client_id"sv, *client_id});
280 params.
Add ({
"client_secret"sv, *client_secret});
284 return TypedBLOB{reqBody, InternetMediaTypes::kWWWFormURLEncoded};
292String TokenIntrospectionResponse::ToString ()
const
314 return VariantValue{(objOfType->AsUTC () - DateTime::NowUTC ()).
As<
int> ()};
318 *into = DateTime::NowUTC ().AddSeconds (d.As<
int> ());
324TypedBLOB TokenIntrospectionResponse::ToWireFormat ()
const
343String UserInfo::ToString ()
const
348 sb <<
"name: "sv << name;
351 sb <<
", given_name: "sv << given_name;
354 sb <<
", family_name: "sv << family_name;
357 sb <<
", email: "sv << email;
360 sb <<
", picture: "sv << picture;
373 {
"name"sv, &UserInfo::name},
374 {
"given_name"sv, &UserInfo::given_name},
375 {
"family_name"sv, &UserInfo::family_name},
376 {
"email"sv, &UserInfo::email},
377 {
"picture"sv, &UserInfo::picture},
382UserInfo UserInfo::FromWireFormat (
const TypedBLOB& src)
397 : fProviderConfiguration_{providerConfiguration}
399 , fCache_{options.fCaching ? make_unique<Cache_> () : nullptr}
404 : fProviderConfiguration_{src.fProviderConfiguration_}
405 , fOptions_{src.fOptions_}
406 , fCache_{src.fCache_ ? make_unique<Cache_> () : nullptr}
412#if USE_NOISY_TRACE_IN_THIS_MODULE_
416 using namespace IO::Network::Transfer;
418 auto connection = Connection::New ();
421 Response r = connection.POST (tokenRequestURI, tr.ToWireFormat ());
423 return TokenResponse::FromWireFormat (r.GetTypedData ());
426 DbgTrace (
"Fetcher::Token: exception={}"_f, current_exception ());
430 auto r = nonCachingFetcher ();
432 optional<UserInfo> uInfo = nullopt;
447#if USE_NOISY_TRACE_IN_THIS_MODULE_
455#if USE_NOISY_TRACE_IN_THIS_MODULE_
460 fCache_->fAccessToken2UserInfo.Remove (tr.access_token);
463 using namespace IO::Network::Transfer;
464 auto connection = Connection::New ();
467 [[maybe_unused]] Response r = connection.POST (*revokeURI, tr.ToWireFormat ());
470 DbgTrace (
"Fetcher::RevokeTokens: exception={}"_f, current_exception ());
475 DbgTrace (
"Fetcher::RevokeTokens: skipping due to missing revocation_endpoint"_f);
481#if USE_NOISY_TRACE_IN_THIS_MODULE_
486 if (optional<optional<UserInfo>> oou = fCache_->fAccessToken2UserInfo.Lookup (accessToken); oou and *oou) {
490 auto nonCachingFetcher = [&] () -> UserInfo {
491#if USE_NOISY_TRACE_IN_THIS_MODULE_
494 using namespace IO::Network::Transfer;
496 auto authInfo = Connection::Options::Authentication{
"Bearer "sv + accessToken};
497 auto connection = Connection::New (Connection::Options{.fAuthentication = authInfo});
499 Response r = connection.GET (userInfoRequestURI);
501 return UserInfo::FromWireFormat (r.GetTypedData ());
504 DbgTrace (
"Fetcher::UserInfo: exception={}"_f, current_exception ());
508 UserInfo userInfo = nonCachingFetcher ();
510 optional<Time::TimePointSeconds> accessTokenExpiresAt = fCache_->fAccessToken2UserInfo.GetExpiration (accessToken);
511 if (accessTokenExpiresAt == nullopt) {
514 if (optional<TokenIntrospectionResponse> o = FetchTokenIntrospectionQueitly_ (accessToken)) {
519 static constexpr auto kWAG_ = 30s;
520 Time::DateTime t = Time::DateTime::NowUTC () + kWAG_;
524 Assert (accessTokenExpiresAt);
525 fCache_->fAccessToken2UserInfo.Add (accessToken, userInfo, *accessTokenExpiresAt);
527#if USE_NOISY_TRACE_IN_THIS_MODULE_
528 DbgTrace (
"returning: {}"_f, userInfo);
533optional<TokenIntrospectionResponse> Fetcher::FetchTokenIntrospectionQueitly_ (
const String& accessToken)
const
542 using namespace IO::Network::Transfer;
543 auto authInfo = Connection::Options::Authentication{
"Bearer "sv + accessToken};
544 auto connection = Connection::New (Connection::Options{.fAuthentication = authInfo});
584 return TokenIntrospectionResponse::FromWireFormat (r.GetTypedData ());
587 DbgTrace (
"Fetcher::FetchTokenIntrospectionQueitly_: exception={} - BEING IGNORED"_f, current_exception ());
#define AssertNotImplemented()
time_point< RealtimeClock, DurationSeconds > TimePointSeconds
TimePointSeconds is a simpler approach to chrono::time_point, which doesn't require using templates e...
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
String is like std::u32string, except it is much easier to use, often much more space efficient,...
An Association pairs key values with (possibly multiple or none) mapped_type values....
nonvirtual optional< mapped_type > LookupOne(ArgByValueType< key_type > key) const
Lookup and return the first (maybe arbitrarily chosen which is first) value with this key,...
nonvirtual void Add(ArgByValueType< key_type > key, ArgByValueType< mapped_type > newElt)
nonvirtual mapped_type LookupOneChecked(ArgByValueType< key_type > key, const THROW_IF_MISSING &throwIfMissing) const
Lookup and return the first (maybe arbitrarily chosen which is first) value with this key,...
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
ObjectVariantMapper can be used to map C++ types to and from variant-union types, which can be transp...
nonvirtual void AddClass(const Traversal::Iterable< StructFieldInfo > &fieldDescriptions, const ClassMapperOptions< CLASS > &mapperOptions={})
function< VariantValue(const ObjectVariantMapper &mapper, const T *objOfType)> FromObjectMapperType
nonvirtual void AddCommonType(ARGS &&... args)
nonvirtual VariantValue FromObject(const T &from) const
nonvirtual T ToObject(const VariantValue &v) const
function< void(const ObjectVariantMapper &mapper, const VariantValue &d, T *into)> ToObjectMapperType
TypedBLOB is a named tuple<Memory::BLOB, optional<InternetMediaType>> - with friendlier names,...
nonvirtual Memory::BLOB WriteAsBLOB(const VariantValue &v) const
Simple variant-value (case variant union) object, with (variant) basic types analogous to a value in ...
nonvirtual RETURNTYPE As() const
nonvirtual CONTAINER_OF_T As(CONTAINER_OF_T_CONSTRUCTOR_ARGS... args) const
simple wrapper on IO::Network::Transfer to do fetching (more configurability to do)
nonvirtual UserInfo GetUserInfo(const String &accessToken) const
nonvirtual TokenResponse GetToken(const TokenRequest &tr) const
nonvirtual void RevokeTokens(const TokenRevocationRequest &tr) const
Track configuration data about stuff that differentiates different OAuth providers - what URLs to use...
optional< URI > token_uri
optional< URI > tokeninfo_endpoint
logically similar to introspection_endpoint, but googles incompatible way
optional< URI > revocation_endpoint
optional< URI > introspection_endpoint
RFC 7662 compatible API for finding info about a token - https://datatracker.ietf....
optional< URI > userinfo_endpoint
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
RFC 7662 compatible API for finding info about a token - https://datatracker.ietf....
this is the argument to the Fetcher::GetToken () API. It typically consists of a client_id,...
optional< String > code_verifier
optional< String > refresh_token
optional< String > client_secret
optional< URI > redirect_uri
this is the response to the Fetcher::GetToken () API. It typically provides an 'access token' with a ...
optional< String > refresh_token