// Copyright Epic Games, Inc. All Rights Reserved. #include "zenhttp/auth/oidc.h" ZEN_THIRD_PARTY_INCLUDES_START #include #include #include ZEN_THIRD_PARTY_INCLUDES_END namespace zen { namespace details { using StringArray = std::vector; StringArray ToStringArray(const json11::Json JsonArray) { StringArray Result; const auto& Items = JsonArray.array_items(); for (const auto& Item : Items) { Result.push_back(Item.string_value()); } return Result; } } // namespace details using namespace std::literals; OidcClient::OidcClient(const OidcClient::Options& Options) { m_BaseUrl = std::string(Options.BaseUrl); m_ClientId = std::string(Options.ClientId); } OidcClient::InitResult OidcClient::Initialize() { ExtendableStringBuilder<256> Uri; Uri << m_BaseUrl << "/.well-known/openid-configuration"sv; cpr::Session Session; Session.SetOption(cpr::Url{Uri.c_str()}); cpr::Response Response = Session.Get(); if (Response.error) { return {.Reason = std::move(Response.error.message)}; } if (Response.status_code != 200) { return {.Reason = std::move(Response.reason)}; } std::string JsonError; json11::Json Json = json11::Json::parse(Response.text, JsonError); if (JsonError.empty() == false) { return {.Reason = std::move(JsonError)}; } m_Config = {.Issuer = Json["issuer"].string_value(), .AuthorizationEndpoint = Json["authorization_endpoint"].string_value(), .TokenEndpoint = Json["token_endpoint"].string_value(), .UserInfoEndpoint = Json["userinfo_endpoint"].string_value(), .RegistrationEndpoint = Json["registration_endpoint"].string_value(), .JwksUri = Json["jwks_uri"].string_value(), .SupportedResponseTypes = details::ToStringArray(Json["response_types_supported"]), .SupportedResponseModes = details::ToStringArray(Json["response_modes_supported"]), .SupportedGrantTypes = details::ToStringArray(Json["grant_types_supported"]), .SupportedScopes = details::ToStringArray(Json["scopes_supported"]), .SupportedTokenEndpointAuthMethods = details::ToStringArray(Json["token_endpoint_auth_methods_supported"]), .SupportedClaims = details::ToStringArray(Json["claims_supported"])}; return {.Ok = true}; } OidcClient::RefreshTokenResult OidcClient::RefreshToken(std::string_view RefreshToken) { const std::string Body = fmt::format("grant_type=refresh_token&refresh_token={}&client_id={}", RefreshToken, m_ClientId); cpr::Session Session; Session.SetOption(cpr::Url{m_Config.TokenEndpoint.c_str()}); Session.SetOption(cpr::Header{{"Content-Type", "application/x-www-form-urlencoded"}}); Session.SetBody(cpr::Body{Body.data(), Body.size()}); cpr::Response Response = Session.Post(); if (Response.error) { return {.Reason = std::move(Response.error.message)}; } if (Response.status_code != 200) { return {.Reason = fmt::format("{} ({})", Response.reason, Response.text)}; } std::string JsonError; json11::Json Json = json11::Json::parse(Response.text, JsonError); if (JsonError.empty() == false) { return {.Reason = std::move(JsonError)}; } return {.TokenType = Json["token_type"].string_value(), .AccessToken = Json["access_token"].string_value(), .RefreshToken = Json["refresh_token"].string_value(), .IdentityToken = Json["id_token"].string_value(), .Scope = Json["scope"].string_value(), .ExpiresInSeconds = static_cast(Json["expires_in"].int_value()), .Ok = true}; } } // namespace zen