26#include <winpr/library.h>
27#include <winpr/path.h>
28#include <winpr/string.h>
29#include <freerdp/client.h>
30#include <freerdp/client/aad_helper.h>
31#include <freerdp/log.h>
32#include <freerdp/utils/aad.h>
34#include "sdl_aad_helper.hpp"
36#define TAG CLIENT_TAG("SDL.aadhelper")
40#define AAD_AUTH_HELPER_AUTODETECT "autodetect"
42SdlAadAuthHelper::SdlAadAuthHelper(AadAuthHelper* helper) : _helper(helper)
46SdlAadAuthHelper::~SdlAadAuthHelper()
51void SdlAadAuthHelper::stop()
55 aad_auth_helper_stop(_helper);
60AadAuthHelper* SdlAadAuthHelper::get()
const
70#if defined(WITH_XDG_AAD_AUTH_HELPER) || defined(WITH_WEBVIEW_AAD_AUTH_HELPER) || \
71 defined(WITH_QT_AAD_AUTH_HELPER)
72#define SDL_AAD_HELPER_HAVE_CANDIDATES 1
80 constexpr const char* kHelperCandidates[] = {
81#if defined(WITH_XDG_AAD_AUTH_HELPER)
82 "freerdp-xdg-aad-helper",
84#if defined(WITH_WEBVIEW_AAD_AUTH_HELPER)
85 "freerdp-webview-aad-helper",
87#if defined(WITH_QT_AAD_AUTH_HELPER)
88 "freerdp-qt-aad-helper",
94#if defined(SDL_AAD_HELPER_HAVE_CANDIDATES)
97static std::string sdl_aad_helper_binary_dir()
100 if (GetModuleFileNameA(
nullptr, path,
sizeof(path)) == 0)
102 WLog_ERR(TAG,
"[aad-auth] GetModuleFileNameA failed");
106 char* sep = strrchr(path,
'/');
108 char* sepWin = strrchr(path,
'\\');
109 if (!sep || (sepWin && (sepWin > sep)))
119#if defined(SDL_AAD_HELPER_HAVE_CANDIDATES)
120static std::string sdl_aad_helper_path_for_binary(
const std::string& dir,
const char* binaryName)
122 std::string path = dir;
134static std::string sdl_aad_helper_auto_locate()
136#if defined(SDL_AAD_HELPER_HAVE_CANDIDATES)
137 auto dir = sdl_aad_helper_binary_dir();
141 for (
const auto& binaryName : kHelperCandidates)
143 auto path = sdl_aad_helper_path_for_binary(dir, binaryName);
144 if (PathFileExistsA(path.c_str()))
154static AadAuthHelper* sdl_aad_helper_get(rdpContext* context, SdlAadAuthHelperPtr& helper)
157 return helper->get();
160 const char* source =
nullptr;
162 const rdpSettings* settings = context->settings;
163 const char* fromSettings =
165 if (fromSettings && fromSettings[0] && (strcmp(fromSettings, AAD_AUTH_HELPER_AUTODETECT) != 0))
168 source =
"/azure:auth-helper:";
173 path = sdl_aad_helper_auto_locate();
174 source =
"auto-detected";
179 WLog_ERR(TAG,
"[aad-auth] could not determine expected helper binary location");
183 if (!PathFileExistsA(path.c_str()))
187 "[aad-auth] helper binary not found at '%s' (from %s) - was FreeRDP built and "
188 "installed with -DWITH_XDG_AAD_AUTH_HELPER=ON, -DWITH_WEBVIEW_AAD_AUTH_HELPER=ON or "
189 "-DWITH_QT_AAD_AUTH_HELPER=ON? Falling back to manual copy/paste login",
190 path.c_str(), source);
194 WLog_DBG(TAG,
"[aad-auth] using helper path from %s: %s", source, path.c_str());
195 auto* raw = aad_auth_helper_start(path.c_str());
198 WLog_ERR(TAG,
"[aad-auth] failed to start '%s'", path.c_str());
202 helper = std::make_shared<SdlAadAuthHelper>(raw);
207static std::string sdl_aad_helper_extract_query_param(
const std::string& url,
208 const std::string& name)
210 auto qpos = url.find(
'?');
211 if (qpos == std::string::npos)
214 std::istringstream stream(url.substr(qpos + 1));
216 while (std::getline(stream, pair,
'&'))
218 auto eq = pair.find(
'=');
219 if (eq == std::string::npos)
221 if (pair.compare(0, eq, name) != 0)
224 auto value = pair.substr(eq + 1);
225 auto decoded = winpr_str_url_decode(value.c_str(), value.length());
226 std::string result = decoded ? decoded :
"";
240static AadAuthHelperNavigateStatus
241sdl_aad_helper_navigate(rdpContext* context, SdlAadAuthHelperPtr& helper,
const std::string& title,
242 const std::string& url, std::string& redirectUrl)
244 auto redirectUri = sdl_aad_helper_extract_query_param(url,
"redirect_uri");
245 if (redirectUri.empty())
247 WLog_ERR(TAG,
"[aad-auth] url %s has no redirect_uri parameter", url.c_str());
248 return AAD_AUTH_HELPER_NAVIGATE_ERROR;
251 auto* rawHelper = sdl_aad_helper_get(context, helper);
253 return AAD_AUTH_HELPER_NAVIGATE_ERROR;
256 const AadAuthHelperNavigateStatus status = aad_auth_helper_navigate(
257 rawHelper, title.c_str(), url.c_str(), redirectUri.c_str(), 180000, &out);
258 if (status != AAD_AUTH_HELPER_NAVIGATE_OK)
263 return AAD_AUTH_HELPER_NAVIGATE_OK;
266static BOOL sdl_aad_helper_get_rdsaad_access_token(freerdp* instance, SdlAadAuthHelperPtr& helper,
267 const char* scope,
const char* req_cnf,
270 WINPR_ASSERT(instance);
272 WINPR_ASSERT(req_cnf);
275 auto context = instance->context;
276 WINPR_ASSERT(context);
277 WINPR_ASSERT(context->settings);
279 std::shared_ptr<char> request(
280 freerdp_client_get_aad_url(
reinterpret_cast<rdpClientContext*
>(instance->context),
281 FREERDP_CLIENT_AAD_AUTH_REQUEST, scope),
284 std::string redirectUrl;
285 const AadAuthHelperNavigateStatus status = sdl_aad_helper_navigate(
286 context, helper,
"FreeRDP WebView - AAD access token", request.get(), redirectUrl);
287 if (status == AAD_AUTH_HELPER_NAVIGATE_CANCELLED)
289 WLog_INFO(TAG,
"[aad-auth] user cancelled the authentication");
292 if (status == AAD_AUTH_HELPER_NAVIGATE_TIMEOUT)
294 WLog_ERR(TAG,
"[aad-auth] authentication timed out");
297 if (status != AAD_AUTH_HELPER_NAVIGATE_OK)
298 return client_cli_get_access_token(instance, ACCESS_TOKEN_TYPE_AAD, token, 2, scope,
301 std::unique_ptr<char, void (*)(
char*)> code(
302 freerdp_client_extract_aad_code(
reinterpret_cast<rdpClientContext*
>(instance->context),
303 redirectUrl.c_str(), redirectUrl.size()),
307 return client_cli_get_access_token(instance, ACCESS_TOKEN_TYPE_AAD, token, 2, scope,
310 std::shared_ptr<char> token_request(
311 freerdp_client_get_aad_url(
reinterpret_cast<rdpClientContext*
>(instance->context),
312 FREERDP_CLIENT_AAD_TOKEN_REQUEST, scope, code.get(), req_cnf),
314 return client_common_get_access_token(instance, token_request.get(), token);
317static BOOL sdl_aad_helper_get_avd_access_token(freerdp* instance, SdlAadAuthHelperPtr& helper,
321 WINPR_ASSERT(instance);
322 WINPR_ASSERT(instance->context);
324 std::shared_ptr<char> request(
325 freerdp_client_get_aad_url(
reinterpret_cast<rdpClientContext*
>(instance->context),
326 FREERDP_CLIENT_AAD_AVD_AUTH_REQUEST),
329 std::string redirectUrl;
330 const AadAuthHelperNavigateStatus status =
331 sdl_aad_helper_navigate(instance->context, helper,
"FreeRDP WebView - AVD access token",
332 request.get(), redirectUrl);
333 if (status == AAD_AUTH_HELPER_NAVIGATE_CANCELLED)
335 WLog_INFO(TAG,
"[aad-auth] user cancelled the authentication");
338 if (status == AAD_AUTH_HELPER_NAVIGATE_TIMEOUT)
340 WLog_ERR(TAG,
"[aad-auth] authentication timed out");
343 if (status != AAD_AUTH_HELPER_NAVIGATE_OK)
344 return client_cli_get_access_token(instance, ACCESS_TOKEN_TYPE_AVD, token, 0);
346 std::unique_ptr<char, void (*)(
char*)> code(
347 freerdp_client_extract_aad_code(
reinterpret_cast<rdpClientContext*
>(instance->context),
348 redirectUrl.c_str(), redirectUrl.size()),
351 return client_cli_get_access_token(instance, ACCESS_TOKEN_TYPE_AVD, token, 0);
353 std::shared_ptr<char> token_request(
354 freerdp_client_get_aad_url(
reinterpret_cast<rdpClientContext*
>(instance->context),
355 FREERDP_CLIENT_AAD_AVD_TOKEN_REQUEST, code.get()),
357 return client_common_get_access_token(instance, token_request.get(), token);
360BOOL sdl_aad_helper_get_access_token_v(freerdp* instance, SdlAadAuthHelperPtr& helper,
361 AccessTokenType tokenType,
char** token,
size_t count,
364 WINPR_ASSERT(instance);
368 case ACCESS_TOKEN_TYPE_AAD:
373 "ACCESS_TOKEN_TYPE_AAD expected 2 additional arguments, but got %" PRIuz
380 "ACCESS_TOKEN_TYPE_AAD expected 2 additional arguments, but got %" PRIuz
383 const char* scope = va_arg(args,
const char*);
384 const char* req_cnf = va_arg(args,
const char*);
385 return sdl_aad_helper_get_rdsaad_access_token(instance, helper, scope, req_cnf, token);
387 case ACCESS_TOKEN_TYPE_AVD:
390 "ACCESS_TOKEN_TYPE_AVD expected 0 additional arguments, but got %" PRIuz
393 return sdl_aad_helper_get_avd_access_token(instance, helper, token);
395 WLog_ERR(TAG,
"Unexpected value for AccessTokenType [%" PRIu32
"], aborting",
401BOOL sdl_aad_helper_get_access_token(freerdp* instance, SdlAadAuthHelperPtr& helper,
402 AccessTokenType tokenType,
char** token,
size_t count, ...)
407 sdl_aad_helper_get_access_token_v(instance, helper, tokenType, token, count, ap);
WINPR_ATTR_NODISCARD FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.