aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDan Engelbrecht <[email protected]>2025-04-02 20:35:53 +0200
committerGitHub Enterprise <[email protected]>2025-04-02 20:35:53 +0200
commitf535a53cdda32b2b4950e32901dcd333bea82c23 (patch)
treea50181e526db53cc609ac44defcea7609585fd31 /src
parentMerge branch 'main' of https://github.ol.epicgames.net/ue-foundation/zen (diff)
downloadzen-f535a53cdda32b2b4950e32901dcd333bea82c23.tar.xz
zen-f535a53cdda32b2b4950e32901dcd333bea82c23.zip
use oidctoken executable to generate auth (#336)
- Feature: `zen builds` auth option `--oidctoken-exe-path` to let zen run the OidcToken executable to get and refresh authentication token
Diffstat (limited to 'src')
-rw-r--r--src/zen/cmds/builds_cmd.cpp36
-rw-r--r--src/zen/cmds/builds_cmd.h2
-rw-r--r--src/zencore/include/zencore/fmtutils.h14
-rw-r--r--src/zenhttp/httpclientauth.cpp108
-rw-r--r--src/zenhttp/include/zenhttp/httpclient.h2
-rw-r--r--src/zenhttp/include/zenhttp/httpclientauth.h4
-rw-r--r--src/zenstore/include/zenstore/gc.h13
7 files changed, 161 insertions, 18 deletions
diff --git a/src/zen/cmds/builds_cmd.cpp b/src/zen/cmds/builds_cmd.cpp
index ba2564fad..f6b7299cb 100644
--- a/src/zen/cmds/builds_cmd.cpp
+++ b/src/zen/cmds/builds_cmd.cpp
@@ -8347,6 +8347,12 @@ BuildsCommand::BuildsCommand()
"OAuth client secret",
cxxopts::value<std::string>(m_OAuthClientSecret)->default_value(""),
"");
+ Ops.add_option("auth",
+ "",
+ "oidctoken-exe-path",
+ "Path to OidcToken executable",
+ cxxopts::value<std::string>(m_OidcTokenAuthExecutablePath)->default_value(""),
+ "");
};
auto AddCloudOptions = [this, &AddAuthOptions](cxxopts::Options& Ops) {
@@ -8750,6 +8756,27 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
return {};
};
+ auto FindOidcTokenExePath = [](const std::string& OidcTokenAuthExecutablePath) -> std::filesystem::path {
+ if (OidcTokenAuthExecutablePath.empty())
+ {
+ const std::string OidcExecutableName = "OidcToken" ZEN_EXE_SUFFIX_LITERAL;
+ std::filesystem::path OidcTokenPath = (GetRunningExecutablePath().parent_path() / OidcExecutableName).make_preferred();
+ if (IsFile(OidcTokenPath))
+ {
+ return OidcTokenPath;
+ }
+ }
+ else
+ {
+ std::filesystem::path OidcTokenPath = std::filesystem::absolute(StringToPath(OidcTokenAuthExecutablePath)).make_preferred();
+ if (IsFile(OidcTokenPath))
+ {
+ return OidcTokenPath;
+ }
+ }
+ return {};
+ };
+
if (!m_AccessToken.empty())
{
ClientSettings.AccessTokenProvider = httpclientauth::CreateFromStaticToken(m_AccessToken);
@@ -8776,15 +8803,16 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
{
ClientSettings.AccessTokenProvider = httpclientauth::CreateFromStaticToken(ResolvedAccessToken);
}
- else
+ else if (std::filesystem::path OidcTokenExePath = FindOidcTokenExePath(m_OidcTokenAuthExecutablePath); !OidcTokenExePath.empty())
{
- CreateAuthMgr();
- ClientSettings.AccessTokenProvider = httpclientauth::CreateFromDefaultOpenIdProvider(*Auth);
+ const std::string& CloudHost = m_OverrideHost.empty() ? m_Host : m_OverrideHost;
+ ClientSettings.AccessTokenProvider = httpclientauth::CreateFromOidcTokenExecutable(OidcTokenExePath, CloudHost);
}
if (!ClientSettings.AccessTokenProvider)
{
- ZEN_CONSOLE("Warning: No auth provider given, attempting operation without credentials.");
+ CreateAuthMgr();
+ ClientSettings.AccessTokenProvider = httpclientauth::CreateFromDefaultOpenIdProvider(*Auth);
}
};
diff --git a/src/zen/cmds/builds_cmd.h b/src/zen/cmds/builds_cmd.h
index b40c3e08f..535d2b1d2 100644
--- a/src/zen/cmds/builds_cmd.h
+++ b/src/zen/cmds/builds_cmd.h
@@ -80,6 +80,8 @@ private:
std::string m_OAuthClientId;
std::string m_OAuthClientSecret;
+ std::string m_OidcTokenAuthExecutablePath;
+
std::string m_Verb; // list, upload, download
cxxopts::Options m_ListOptions{"list", "List available builds"};
diff --git a/src/zencore/include/zencore/fmtutils.h b/src/zencore/include/zencore/fmtutils.h
index 8482157fb..10dfa5393 100644
--- a/src/zencore/include/zencore/fmtutils.h
+++ b/src/zencore/include/zencore/fmtutils.h
@@ -12,6 +12,7 @@ ZEN_THIRD_PARTY_INCLUDES_START
#include <fmt/format.h>
ZEN_THIRD_PARTY_INCLUDES_END
+#include <chrono>
#include <string_view>
// Custom formatting for some zencore types
@@ -102,3 +103,16 @@ struct fmt::formatter<T> : fmt::formatter<std::string_view>
return fmt::formatter<std::string_view>::format(a.ToView(), ctx);
}
};
+
+template<>
+struct fmt::formatter<std::chrono::system_clock::time_point> : formatter<string_view>
+{
+ template<typename FormatContext>
+ auto format(const std::chrono::system_clock::time_point& TimePoint, FormatContext& ctx) const
+ {
+ std::time_t Time = std::chrono::system_clock::to_time_t(TimePoint);
+ char TimeString[std::size("yyyy-mm-ddThh:mm:ss")];
+ std::strftime(std::data(TimeString), std::size(TimeString), "%FT%T", std::localtime(&Time));
+ return fmt::format_to(ctx.out(), "{}", TimeString);
+ }
+};
diff --git a/src/zenhttp/httpclientauth.cpp b/src/zenhttp/httpclientauth.cpp
index 7fb3224f1..916b25bff 100644
--- a/src/zenhttp/httpclientauth.cpp
+++ b/src/zenhttp/httpclientauth.cpp
@@ -2,15 +2,25 @@
#include <zenhttp/httpclientauth.h>
+#include <zencore/fmtutils.h>
#include <zencore/logging.h>
+#include <zencore/process.h>
+#include <zencore/scopeguard.h>
+#include <zencore/timer.h>
#include <zenhttp/auth/authmgr.h>
+#include <ctime>
+
ZEN_THIRD_PARTY_INCLUDES_START
#include <cpr/cpr.h>
#include <fmt/format.h>
#include <json11.hpp>
ZEN_THIRD_PARTY_INCLUDES_END
+#if ZEN_PLATFORM_WINDOWS
+# define timegm _mkgmtime
+#endif // ZEN_PLATFORM_WINDOWS
+
namespace zen { namespace httpclientauth {
using namespace std::literals;
@@ -76,4 +86,102 @@ namespace zen { namespace httpclientauth {
return CreateFromOpenIdProvider(AuthManager, "Default"sv);
}
+ static HttpClientAccessToken GetOidcTokenFromExe(const std::filesystem::path& OidcExecutablePath,
+ std::string_view CloudHost,
+ bool Unattended)
+ {
+ Stopwatch Timer;
+
+ CreateProcOptions ProcOptions;
+
+ const std::string AuthTokenPath = ".zen-auth-oidctoken";
+
+ auto _ = MakeGuard([AuthTokenPath]() { RemoveFile(AuthTokenPath); });
+
+ const std::string ProcArgs = fmt::format("{} --AuthConfigUrl {} --OutFile {} --Unattended={}",
+ OidcExecutablePath,
+ CloudHost,
+ AuthTokenPath,
+ Unattended ? "true"sv : "false"sv);
+ ZEN_DEBUG("Running: {}", ProcArgs);
+ ProcessHandle Proc;
+ Proc.Initialize(CreateProc(OidcExecutablePath, ProcArgs, ProcOptions));
+ if (!Proc.IsValid())
+ {
+ throw std::runtime_error(fmt::format("failed to launch '{}'", OidcExecutablePath));
+ }
+
+ int ExitCode = Proc.WaitExitCode();
+
+ auto EndTime = std::chrono::system_clock::now();
+
+ if (ExitCode == 0)
+ {
+ IoBuffer Body = IoBufferBuilder::MakeFromFile(AuthTokenPath);
+ std::string JsonText(reinterpret_cast<const char*>(Body.GetData()), Body.GetSize());
+
+ std::string JsonError;
+ json11::Json Json = json11::Json::parse(JsonText, JsonError);
+
+ if (JsonError.empty() == false)
+ {
+ ZEN_WARN("Unable to parse Oidcs json response from {}. Reason: '{}'", AuthTokenPath, JsonError);
+ return HttpClientAccessToken{};
+ }
+ std::string Token = Json["Token"].string_value();
+ std::string ExpiresAtUTCString = Json["ExpiresAtUtc"].string_value();
+ ZEN_ASSERT(!ExpiresAtUTCString.empty());
+
+ int Year = 0;
+ int Month = 0;
+ int Day = 0;
+ int Hour = 0;
+ int Minute = 0;
+ int Second = 0;
+ int Millisecond = 0;
+ sscanf(ExpiresAtUTCString.c_str(), "%d-%d-%dT%d:%d:%d.%dZ", &Year, &Month, &Day, &Hour, &Minute, &Second, &Millisecond);
+
+ std::tm Time = {
+ Second,
+ Minute,
+ Hour,
+ Day,
+ Month - 1,
+ Year - 1900,
+ };
+
+ time_t UTCTime = timegm(&Time);
+ HttpClientAccessToken::TimePoint ExpireTime = std::chrono::system_clock::from_time_t(UTCTime);
+ ExpireTime += std::chrono::microseconds(Millisecond);
+
+ return HttpClientAccessToken{.Value = fmt::format("Bearer {}"sv, Token), .ExpireTime = ExpireTime};
+ }
+ else
+ {
+ ZEN_WARN("Failed running {} to get auth token, error code {}", OidcExecutablePath, ExitCode);
+ }
+ return HttpClientAccessToken{};
+ }
+
+ std::optional<std::function<HttpClientAccessToken()>> CreateFromOidcTokenExecutable(const std::filesystem::path& OidcExecutablePath,
+ std::string_view CloudHost)
+ {
+ HttpClientAccessToken InitialToken = GetOidcTokenFromExe(OidcExecutablePath, CloudHost, false);
+ if (InitialToken.IsValid())
+ {
+ return [OidcExecutablePath = std::filesystem::path(OidcExecutablePath),
+ CloudHost = std::string(CloudHost),
+ InitialToken]() mutable {
+ if (InitialToken.IsValid())
+ {
+ HttpClientAccessToken Result = InitialToken;
+ InitialToken = {};
+ return Result;
+ }
+ return GetOidcTokenFromExe(OidcExecutablePath, CloudHost, true);
+ };
+ }
+ return {};
+ }
+
}} // namespace zen::httpclientauth
diff --git a/src/zenhttp/include/zenhttp/httpclient.h b/src/zenhttp/include/zenhttp/httpclient.h
index 4d901bdb5..c991a71ea 100644
--- a/src/zenhttp/include/zenhttp/httpclient.h
+++ b/src/zenhttp/include/zenhttp/httpclient.h
@@ -34,7 +34,7 @@ struct HttpClientAccessToken
using Clock = std::chrono::system_clock;
using TimePoint = Clock::time_point;
- static constexpr int64_t ExpireMarginInSeconds = 30;
+ static constexpr int64_t ExpireMarginInSeconds = 60 * 5;
std::string Value;
TimePoint ExpireTime;
diff --git a/src/zenhttp/include/zenhttp/httpclientauth.h b/src/zenhttp/include/zenhttp/httpclientauth.h
index aa07620ca..5b9b9d305 100644
--- a/src/zenhttp/include/zenhttp/httpclientauth.h
+++ b/src/zenhttp/include/zenhttp/httpclientauth.h
@@ -3,6 +3,7 @@
#pragma once
#include <zenhttp/httpclient.h>
+#include <optional>
namespace zen {
@@ -24,6 +25,9 @@ namespace httpclientauth {
std::function<HttpClientAccessToken()> CreateFromOpenIdProvider(AuthMgr& AuthManager, std::string_view OpenIdProvider);
std::function<HttpClientAccessToken()> CreateFromDefaultOpenIdProvider(AuthMgr& AuthManager);
+
+ std::optional<std::function<HttpClientAccessToken()>> CreateFromOidcTokenExecutable(const std::filesystem::path& OidcExecutablePath,
+ std::string_view CloudHost);
} // namespace httpclientauth
} // namespace zen
diff --git a/src/zenstore/include/zenstore/gc.h b/src/zenstore/include/zenstore/gc.h
index 67aadef71..3223fba39 100644
--- a/src/zenstore/include/zenstore/gc.h
+++ b/src/zenstore/include/zenstore/gc.h
@@ -586,16 +586,3 @@ private:
void gc_forcelink();
} // namespace zen
-
-template<>
-struct fmt::formatter<zen::GcClock::TimePoint> : formatter<string_view>
-{
- template<typename FormatContext>
- auto format(const zen::GcClock::TimePoint& TimePoint, FormatContext& ctx) const
- {
- std::time_t Time = std::chrono::system_clock::to_time_t(TimePoint);
- char TimeString[std::size("yyyy-mm-ddThh:mm:ss")];
- std::strftime(std::data(TimeString), std::size(TimeString), "%FT%T", std::localtime(&Time));
- return fmt::format_to(ctx.out(), "{}", TimeString);
- }
-};