// Copyright Epic Games, Inc. All Rights Reserved. #include "zenhttp/auth/oidc.h" #include ZEN_THIRD_PARTY_INCLUDES_START #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() { HttpClient Http{m_BaseUrl}; HttpClient::Response Response = Http.Get("/.well-known/openid-configuration"sv); if (!Response) { return {.Reason = Response.ErrorMessage("")}; } if (Response.StatusCode != HttpResponseCode::OK) { return {.Reason = std::string{ToString(Response.StatusCode)}}; } std::string JsonError; json11::Json Json = json11::Json::parse(std::string{Response.AsText()}, 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); HttpClient Http{m_Config.TokenEndpoint}; HttpClient::KeyValueMap Headers{{"Content-Type", "application/x-www-form-urlencoded"}}; HttpClient::Response Response = Http.Post("", IoBufferBuilder::MakeFromMemory(MemoryView{Body.data(), Body.size()}), Headers); if (!Response) { return {.Reason = std::string{Response.ErrorMessage("")}}; } if (Response.StatusCode != HttpResponseCode::OK) { return {.Reason = fmt::format("{} ({})", ToString(Response.StatusCode), Response.AsText())}; } std::string JsonError; json11::Json Json = json11::Json::parse(std::string{Response.AsText()}, 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