// Copyright Epic Games, Inc. All Rights Reserved. #include "authutils.h" #include #include #include #include #include #include ZEN_THIRD_PARTY_INCLUDES_START #include ZEN_THIRD_PARTY_INCLUDES_END namespace zen { using namespace std::literals; std::string_view GetDefaultAccessTokenEnvVariableName() { #if ZEN_PLATFORM_WINDOWS return "UE-CloudDataCacheAccessToken"sv; #endif #if ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC return "UE_CloudDataCacheAccessToken"sv; #endif } std::string ReadAccessTokenFromJsonFile(const std::filesystem::path& Path) { if (!IsFile(Path)) { throw std::runtime_error(fmt::format("the file '{}' does not exist", Path)); } IoBuffer Body = IoBufferBuilder::MakeFromFile(Path); std::string JsonText(reinterpret_cast(Body.GetData()), Body.GetSize()); std::string JsonError; json11::Json TokenInfo = json11::Json::parse(JsonText, JsonError); if (!JsonError.empty()) { throw std::runtime_error(fmt::format("failed parsing json file '{}'. Reason: '{}'", Path, JsonError)); } const std::string AuthToken = TokenInfo["Token"].string_value(); if (AuthToken.empty()) { throw std::runtime_error(fmt::format("the json file '{}' does not contain a value for \"Token\"", Path)); } return AuthToken; } std::filesystem::path FindOidcTokenExePath(std::string_view OidcTokenAuthExecutablePath) { 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; } OidcTokenPath = (std::filesystem::current_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 {}; }; void AuthCommandLineOptions::AddOptions(cxxopts::Options& Ops) { // Direct access token (may expire) Ops.add_option("auth-token", "", "access-token", "Remote host access token", cxxopts::value(m_AccessToken), ""); Ops.add_option("auth-token", "", "access-token-env", "Name of environment variable that holds the remote host access token", cxxopts::value(m_AccessTokenEnv)->default_value(std::string(GetDefaultAccessTokenEnvVariableName())), ""); Ops.add_option("auth-token", "", "access-token-path", "Path to json file that holds the remote host access token", cxxopts::value(m_AccessTokenPath), ""); // Auth manager token encryption Ops.add_option("security", "", "encryption-aes-key", "256 bit AES encryption key", cxxopts::value(m_EncryptionKey), ""); Ops.add_option("security", "", "encryption-aes-iv", "128 bit AES encryption initialization vector", cxxopts::value(m_EncryptionIV), ""); // OpenId acccess token Ops.add_option("openid", "", "openid-provider-name", "Open ID provider name", cxxopts::value(m_OpenIdProviderName), "Default"); Ops.add_option("openid", "", "openid-provider-url", "Open ID provider url", cxxopts::value(m_OpenIdProviderUrl), ""); Ops.add_option("openid", "", "openid-client-id", "Open ID client id", cxxopts::value(m_OpenIdClientId), ""); Ops.add_option("openid", "", "openid-refresh-token", "Open ID refresh token", cxxopts::value(m_OpenIdRefreshToken), ""); // OAuth acccess token Ops.add_option("oauth", "", "oauth-url", "OAuth provier url", cxxopts::value(m_OAuthUrl)->default_value(""), ""); Ops.add_option("oauth", "", "oauth-clientid", "OAuth client id", cxxopts::value(m_OAuthClientId)->default_value(""), ""); Ops.add_option("oauth", "", "oauth-clientsecret", "OAuth client secret", cxxopts::value(m_OAuthClientSecret)->default_value(""), ""); Ops.add_option("auth", "", "oidctoken-exe-path", "Path to OidcToken executable", cxxopts::value(m_OidcTokenAuthExecutablePath)->default_value(""), ""); Ops.add_option("auth", "", "oidctoken-exe-unattended", "Set mode to unattended when launcing OidcToken executable", cxxopts::value(m_OidcTokenUnattended), ""); }; void AuthCommandLineOptions::ParseOptions(cxxopts::Options& Ops, const std::filesystem::path& SystemRootDir, HttpClientSettings& ClientSettings, std::string_view HostUrl, std::unique_ptr& Auth, bool Quiet, bool Hidden) { auto CreateAuthMgr = [&]() { ZEN_ASSERT(!SystemRootDir.empty()); if (!Auth) { if (m_EncryptionKey.empty()) { m_EncryptionKey = "abcdefghijklmnopqrstuvxyz0123456"; if (!Quiet) { ZEN_CONSOLE_WARN("Using default encryption key"); } } if (m_EncryptionIV.empty()) { m_EncryptionIV = "0123456789abcdef"; if (!Quiet) { ZEN_CONSOLE_WARN("Using default encryption initialization vector"); } } AuthConfig AuthMgrConfig = {.RootDirectory = SystemRootDir / "auth", .EncryptionKey = AesKey256Bit::FromString(m_EncryptionKey), .EncryptionIV = AesIV128Bit::FromString(m_EncryptionIV)}; if (!AuthMgrConfig.EncryptionKey.IsValid()) { throw OptionParseException(fmt::format("'--encryption-aes-key' ('{}') is malformed", m_EncryptionKey), Ops.help()); } if (!AuthMgrConfig.EncryptionIV.IsValid()) { throw OptionParseException(fmt::format("'--encryption-aes-iv' ('{}') is malformed", m_EncryptionIV), Ops.help()); } Auth = AuthMgr::Create(AuthMgrConfig); } }; if (!m_OpenIdProviderUrl.empty() && !m_OpenIdClientId.empty()) { CreateAuthMgr(); std::string ProviderName = m_OpenIdProviderName.empty() ? "Default" : m_OpenIdProviderName; Auth->AddOpenIdProvider({.Name = ProviderName, .Url = m_OpenIdProviderUrl, .ClientId = m_OpenIdClientId}); if (!m_OpenIdRefreshToken.empty()) { Auth->AddOpenIdToken({.ProviderName = ProviderName, .RefreshToken = m_OpenIdRefreshToken}); } } auto GetEnvAccessToken = [](const std::string& AccessTokenEnv) -> std::string { if (!AccessTokenEnv.empty()) { return GetEnvVariable(AccessTokenEnv); } return {}; }; if (!m_AccessToken.empty()) { ClientSettings.AccessTokenProvider = httpclientauth::CreateFromStaticToken(m_AccessToken); } else if (!m_AccessTokenPath.empty()) { MakeSafeAbsolutePathÍnPlace(m_AccessTokenPath); std::string ResolvedAccessToken = ReadAccessTokenFromJsonFile(m_AccessTokenPath); if (!ResolvedAccessToken.empty()) { ClientSettings.AccessTokenProvider = httpclientauth::CreateFromStaticToken(ResolvedAccessToken); } } else if (!m_OAuthUrl.empty()) { ClientSettings.AccessTokenProvider = httpclientauth::CreateFromOAuthClientCredentials( {.Url = m_OAuthUrl, .ClientId = m_OAuthClientId, .ClientSecret = m_OAuthClientSecret}); } else if (!m_OpenIdProviderName.empty()) { CreateAuthMgr(); ClientSettings.AccessTokenProvider = httpclientauth::CreateFromOpenIdProvider(*Auth, m_OpenIdProviderName); } else if (std::string ResolvedAccessToken = GetEnvAccessToken(m_AccessTokenEnv); !ResolvedAccessToken.empty()) { ClientSettings.AccessTokenProvider = httpclientauth::CreateFromStaticToken(ResolvedAccessToken); } else if (std::filesystem::path OidcTokenExePath = FindOidcTokenExePath(m_OidcTokenAuthExecutablePath); !OidcTokenExePath.empty()) { ClientSettings.AccessTokenProvider = httpclientauth::CreateFromOidcTokenExecutable(OidcTokenExePath, HostUrl, Quiet, m_OidcTokenUnattended, Hidden); } if (!ClientSettings.AccessTokenProvider) { CreateAuthMgr(); ClientSettings.AccessTokenProvider = httpclientauth::CreateFromDefaultOpenIdProvider(*Auth); } } } // namespace zen