winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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 }