aboutsummaryrefslogtreecommitdiff
path: root/src/zenhttp/auth/oidc.cpp
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2023-05-02 12:31:53 +0200
committerGitHub <[email protected]>2023-05-02 12:31:53 +0200
commite3086573d2244def22ecbe1e6b4b3da8b47e0f14 (patch)
tree627066debdddf7474783893f6b9b6631bb9a4833 /src/zenhttp/auth/oidc.cpp
parentmoved source directories into `/src` (#264) (diff)
downloadzen-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.cpp127
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