Authentication.cpp (10111B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include "Public/winget/Authentication.h" 5 #include "WebAccountManagerAuthenticator.h" 6 #include <AppInstallerStrings.h> 7 #include <AppInstallerLogging.h> 8 9 using namespace std::string_view_literals; 10 11 namespace AppInstaller::Authentication 12 { 13 namespace 14 { 15 constexpr std::string_view s_BearerTokenPrefix = "Bearer "sv; 16 // Default Azure Blob Storage resource value. Used when manifest author did not provide specific blob resource. 17 constexpr std::string_view s_DefaultAzureBlobStorageResource = "https://storage.azure.com/"sv; 18 } 19 20 Authenticator::Authenticator(AuthenticationInfo info, AuthenticationArguments args) 21 { 22 THROW_HR_IF(E_UNEXPECTED, args.Mode == AuthenticationMode::Unknown); 23 THROW_HR_IF(APPINSTALLER_CLI_ERROR_AUTHENTICATION_TYPE_NOT_SUPPORTED, info.Type == AuthenticationType::Unknown); 24 THROW_HR_IF(E_UNEXPECTED, info.Type == AuthenticationType::None); 25 THROW_HR_IF(APPINSTALLER_CLI_ERROR_INVALID_AUTHENTICATION_INFO, !info.ValidateIntegrity()); 26 27 AICLI_LOG(Core, Info, << "AuthenticationArguments values. Mode: " << AuthenticationModeToString(args.Mode) << ", Account: " << args.AuthenticationAccount); 28 29 if (info.Type == AuthenticationType::MicrosoftEntraId || info.Type == AuthenticationType::MicrosoftEntraIdForAzureBlobStorage) 30 { 31 AICLI_LOG(Core, Info, << "Creating WebAccountManagerAuthenticator for " << AuthenticationTypeToString(info.Type)); 32 m_authProvider = std::make_unique<WebAccountManagerAuthenticator>(std::move(info), std::move(args)); 33 } 34 } 35 36 #ifndef AICLI_DISABLE_TEST_HOOKS 37 static AuthenticationResult* s_AuthenticationResult_TestHook_Override = nullptr; 38 39 void TestHook_SetAuthenticationResult_Override(Authentication::AuthenticationResult* authResult) 40 { 41 s_AuthenticationResult_TestHook_Override = authResult; 42 } 43 #endif 44 45 // Each authentication provider uses its own mechanism for caching. 46 // Here we directly call authentication provider to authenticate. 47 AuthenticationResult Authenticator::AuthenticateForToken() 48 { 49 #ifndef AICLI_DISABLE_TEST_HOOKS 50 if (s_AuthenticationResult_TestHook_Override) 51 { 52 return *s_AuthenticationResult_TestHook_Override; 53 } 54 #endif 55 56 THROW_HR_IF(E_UNEXPECTED, !m_authProvider); 57 58 return m_authProvider->AuthenticateForToken(); 59 } 60 61 bool MicrosoftEntraIdAuthenticationInfo::operator<(const MicrosoftEntraIdAuthenticationInfo& other) const 62 { 63 // std::tie implements tuple comparison, wherein it checks the first item in the tuple, 64 // iff the first elements are equal, then the second element is used for comparison, and so on 65 return std::tie(Resource, Scope) < std::tie(other.Resource, other.Scope); 66 } 67 68 bool AuthenticationInfo::operator<(const AuthenticationInfo& other) const 69 { 70 // std::tie implements tuple comparison, wherein it checks the first item in the tuple, 71 // iff the first elements are equal, then the second element is used for comparison, and so on 72 return std::tie(Type, MicrosoftEntraIdInfo) < std::tie(other.Type, other.MicrosoftEntraIdInfo); 73 } 74 75 void AuthenticationInfo::UpdateRequiredFieldsIfNecessary() 76 { 77 // If MicrosoftEntraIdForAzureBlobStorage, populate default resource value if missing. 78 if (Type == AuthenticationType::MicrosoftEntraIdForAzureBlobStorage) 79 { 80 if (MicrosoftEntraIdInfo.has_value()) 81 { 82 if (MicrosoftEntraIdInfo->Resource.empty()) 83 { 84 MicrosoftEntraIdInfo->Resource = s_DefaultAzureBlobStorageResource; 85 MicrosoftEntraIdInfo->Scope = ""; 86 } 87 } 88 else 89 { 90 MicrosoftEntraIdAuthenticationInfo authInfo; 91 authInfo.Resource = s_DefaultAzureBlobStorageResource; 92 MicrosoftEntraIdInfo = std::move(authInfo); 93 } 94 } 95 } 96 97 bool AuthenticationInfo::ValidateIntegrity() const 98 { 99 // For MicrosoftEntraId, Resource is required. 100 if (Type == AuthenticationType::MicrosoftEntraId || Type == AuthenticationType::MicrosoftEntraIdForAzureBlobStorage) 101 { 102 return MicrosoftEntraIdInfo.has_value() && !MicrosoftEntraIdInfo->Resource.empty(); 103 } 104 105 return true; 106 } 107 108 AuthenticationWindowBase::AuthenticationWindowBase() 109 { 110 InitializeWindowThread(); 111 } 112 113 HWND AuthenticationWindowBase::GetHandle() 114 { 115 return m_windowHandle; 116 } 117 118 AuthenticationWindowBase::~AuthenticationWindowBase() 119 { 120 if (!PostMessageW(m_windowHandle, WM_CLOSE, 0, 0)) 121 { 122 m_terminateWindowThread = true; 123 } 124 125 if (m_windowThread.joinable()) 126 { 127 m_windowThread.join(); 128 } 129 } 130 131 void AuthenticationWindowBase::InitializeWindowThread() 132 { 133 static std::once_flag s_registerWindowClassOnce; 134 static LPCWSTR s_windowsClassName = L"WingetAuthenticationParentWindowClass"; 135 static HMODULE hModule = GetModuleHandle(NULL); 136 THROW_LAST_ERROR_IF_NULL_MSG(hModule, "Failed to get resource module for authentication window"); 137 138 std::call_once(s_registerWindowClassOnce, 139 [&]() 140 { 141 WNDCLASS wc = {}; 142 wc.lpfnWndProc = AuthenticationWindowBase::WindowProcessFunction; 143 wc.hInstance = hModule; 144 wc.lpszClassName = s_windowsClassName; 145 THROW_LAST_ERROR_IF_MSG(!RegisterClassW(&wc), "Failed to get resource module for authentication window"); 146 }); 147 148 wil::unique_event waitForWindowReady; 149 waitForWindowReady.create(); 150 151 m_windowThread = std::thread( 152 [&]() 153 { 154 m_windowHandle = CreateWindowW( 155 s_windowsClassName, 156 L"WingetAuthenticationParentWindow", 157 WS_OVERLAPPEDWINDOW, 158 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, /* size and position */ 159 NULL, /* hWndParent */ 160 NULL, /* hMenu */ 161 hModule, 162 NULL); /* lpParam */ 163 THROW_LAST_ERROR_IF_NULL_MSG(hModule, "Failed to create authentication parent window"); 164 165 // Best effort only 166 SetForegroundWindow(m_windowHandle); 167 168 m_windowThreadId = GetCurrentThreadId(); 169 170 // Set window ready event 171 waitForWindowReady.SetEvent(); 172 173 // Message loop 174 MSG msg; 175 BOOL getMsgResult; 176 while ((getMsgResult = GetMessage(&msg, NULL, 0, 0)) != 0) 177 { 178 if (m_terminateWindowThread || getMsgResult == -1) 179 { 180 return; 181 } 182 else 183 { 184 TranslateMessage(&msg); 185 DispatchMessage(&msg); 186 } 187 } 188 }); 189 190 THROW_HR_IF_MSG(APPINSTALLER_CLI_ERROR_AUTHENTICATION_FAILED, !waitForWindowReady.wait(10000), "Creating authentication parent window timed out"); 191 } 192 193 LRESULT __stdcall AuthenticationWindowBase::WindowProcessFunction(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 194 { 195 switch (uMsg) 196 { 197 case WM_ENDSESSION: 198 case WM_CLOSE: 199 DestroyWindow(hWnd); 200 break; 201 case WM_DESTROY: 202 PostQuitMessage(0); 203 break; 204 default: 205 return DefWindowProc(hWnd, uMsg, wParam, lParam); 206 } 207 208 return 0; 209 } 210 211 std::string_view AuthenticationTypeToString(AuthenticationType in) 212 { 213 switch (in) 214 { 215 case AuthenticationType::None: 216 return "none"sv; 217 case AuthenticationType::MicrosoftEntraId: 218 return "microsoftEntraId"sv; 219 case AuthenticationType::MicrosoftEntraIdForAzureBlobStorage: 220 return "microsoftEntraIdForAzureBlobStorage"sv; 221 } 222 223 return "unknown"sv; 224 } 225 226 AuthenticationType ConvertToAuthenticationType(std::string_view in) 227 { 228 std::string inStrLower = Utility::ToLower(in); 229 AuthenticationType result = AuthenticationType::Unknown; 230 231 if (inStrLower == "none") 232 { 233 result = AuthenticationType::None; 234 } 235 else if (inStrLower == "microsoftentraid") 236 { 237 result = AuthenticationType::MicrosoftEntraId; 238 } 239 else if (inStrLower == "microsoftentraidforazureblobstorage") 240 { 241 result = AuthenticationType::MicrosoftEntraIdForAzureBlobStorage; 242 } 243 244 return result; 245 } 246 247 std::string_view AuthenticationModeToString(AuthenticationMode in) 248 { 249 switch (in) 250 { 251 case AuthenticationMode::Silent: 252 return "silent"sv; 253 case AuthenticationMode::SilentPreferred: 254 return "silentPreferred"sv; 255 case AuthenticationMode::Interactive: 256 return "interactive"sv; 257 } 258 259 return "unknown"sv; 260 } 261 262 AuthenticationMode ConvertToAuthenticationMode(std::string_view in) 263 { 264 std::string inStrLower = Utility::ToLower(in); 265 AuthenticationMode result = AuthenticationMode::Unknown; 266 267 if (inStrLower == "silent") 268 { 269 result = AuthenticationMode::Silent; 270 } 271 else if (inStrLower == "silentpreferred") 272 { 273 result = AuthenticationMode::SilentPreferred; 274 } 275 else if (inStrLower == "interactive") 276 { 277 result = AuthenticationMode::Interactive; 278 } 279 280 return result; 281 } 282 283 std::string AppInstaller::Authentication::CreateBearerToken(std::string rawToken) 284 { 285 return std::string{ s_BearerTokenPrefix } + rawToken; 286 } 287 }