diff options
| author | Stefan Boberg <[email protected]> | 2023-05-02 12:31:53 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-05-02 12:31:53 +0200 |
| commit | e3086573d2244def22ecbe1e6b4b3da8b47e0f14 (patch) | |
| tree | 627066debdddf7474783893f6b9b6631bb9a4833 /src/zenhttp/auth/oidc.cpp | |
| parent | moved source directories into `/src` (#264) (diff) | |
| download | zen-e3086573d2244def22ecbe1e6b4b3da8b47e0f14.tar.xz zen-e3086573d2244def22ecbe1e6b4b3da8b47e0f14.zip | |
move auth code from zenserver into zenhttp (#265)
this code should be usable outside of zenserver, so this moves it out into zenhttp where it can be used from lower level components
Diffstat (limited to 'src/zenhttp/auth/oidc.cpp')
| -rw-r--r-- | src/zenhttp/auth/oidc.cpp | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/src/zenhttp/auth/oidc.cpp b/src/zenhttp/auth/oidc.cpp new file mode 100644 index 000000000..318110c7d --- /dev/null +++ b/src/zenhttp/auth/oidc.cpp @@ -0,0 +1,127 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "zenhttp/auth/oidc.h" + +ZEN_THIRD_PARTY_INCLUDES_START +#include <cpr/cpr.h> +#include <fmt/format.h> +#include <json11.hpp> +ZEN_THIRD_PARTY_INCLUDES_END + +namespace zen { + +namespace details { + + using StringArray = std::vector<std::string>; + + 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<int64_t>(Json["expires_in"].int_value()), + .Ok = true}; +} + +} // namespace zen |