@samitouri / QOSAMI-WSL / commits / 2f68e20e

Auth API update (#41197)

Updates the auth API result to make it more easily used with the pull inputs.

JohnMcPMS committed Jul 29, 2026 at 14:30 UTC 2f68e20e9eafaa0c94cf14d3287fc6f6096f2226
10 files changed +166 -28
src/windows/WslcSDK/winrt/AuthenticateResult.cpp new
+22
@@ -0,0 +1,22 @@
1 +// Copyright (C) Microsoft Corporation. All rights reserved.
2 +
3 +#include "precomp.h"
4 +#include "AuthenticateResult.h"
5 +#include "Microsoft.WSL.Containers.AuthenticateResult.g.cpp"
6 +
7 +namespace winrt::Microsoft::WSL::Containers::implementation {
8 +AuthenticateResult::AuthenticateResult(hstring identityToken, winrt::Microsoft::WSL::Containers::IdentityTokenType tokenType) :
9 + m_identityToken(std::move(identityToken)), m_tokenType(tokenType)
10 +{
11 +}
12 +
13 +hstring AuthenticateResult::IdentityToken()
14 +{
15 + return m_identityToken;
16 +}
17 +
18 +winrt::Microsoft::WSL::Containers::IdentityTokenType AuthenticateResult::TokenType()
19 +{
20 + return m_tokenType;
21 +}
22 +} // namespace winrt::Microsoft::WSL::Containers::implementation
src/windows/WslcSDK/winrt/AuthenticateResult.h new
+20
@@ -0,0 +1,20 @@
1 +// Copyright (C) Microsoft Corporation. All rights reserved.
2 +
3 +#pragma once
4 +#include "Microsoft.WSL.Containers.AuthenticateResult.g.h"
5 +
6 +namespace winrt::Microsoft::WSL::Containers::implementation {
7 +struct AuthenticateResult : AuthenticateResultT<AuthenticateResult>
8 +{
9 + AuthenticateResult(hstring identityToken, winrt::Microsoft::WSL::Containers::IdentityTokenType tokenType);
10 +
11 + hstring IdentityToken();
12 + winrt::Microsoft::WSL::Containers::IdentityTokenType TokenType();
13 +
14 +private:
15 + hstring m_identityToken{};
16 + winrt::Microsoft::WSL::Containers::IdentityTokenType m_tokenType{};
17 +};
18 +} // namespace winrt::Microsoft::WSL::Containers::implementation
19 +
20 +DEFINE_TYPE_HELPERS(AuthenticateResult);
src/windows/WslcSDK/winrt/CMakeLists.txt
+2
@@ -2,6 +2,7 @@ add_idl_winrt(wslcsdkwinrtidl "wslcsdk.idl")
2 set_target_properties(wslcsdkwinrtidl PROPERTIES FOLDER windows)
3
4 set(SOURCES
5 + AuthenticateResult.cpp
6 Container.cpp
7 ContainerNamedVolume.cpp
8 ContainerPortMapping.cpp
@@ -26,6 +27,7 @@ set(SOURCES
27 )
28
29 set(HEADERS
30 + AuthenticateResult.h
31 Container.h
32 ContainerNamedVolume.h
33 ContainerPortMapping.h
src/windows/WslcSDK/winrt/Session.cpp
+6 -2
@@ -14,6 +14,7 @@ Abstract:
14
15 #include "precomp.h"
16 #include "Session.h"
17 +#include "AuthenticateResult.h"
18 #include "ProcessCrashInformation.h"
19 #include "SessionSettings.h"
20 #include "Microsoft.WSL.Containers.Session.g.cpp"
@@ -319,7 +320,7 @@ void Session::DeleteVhdVolume(hstring const& name)
320 THROW_MSG_IF_FAILED(hr, errorMessage);
321 }
322
322 -hstring Session::Authenticate(Uri const& serverAddress, hstring const& username, hstring const& password)
323 +winrt::Microsoft::WSL::Containers::AuthenticateResult Session::Authenticate(Uri const& serverAddress, hstring const& username, hstring const& password)
324 {
325 if (!serverAddress)
326 {
@@ -335,15 +336,18 @@ hstring Session::Authenticate(Uri const& serverAddress, hstring const& username,
336
337 wil::unique_cotaskmem_string errorMessage;
338 wil::unique_cotaskmem_ansistring token;
339 + WslcIdentityTokenType tokenType{};
340 auto hr = WslcSessionAuthenticate(
341 ToHandle(),
342 winrt::to_string(serverAddress.ToString()).c_str(),
343 winrt::to_string(username).c_str(),
344 winrt::to_string(password).c_str(),
345 token.put(),
346 + &tokenType,
347 errorMessage.put());
348 THROW_MSG_IF_FAILED(hr, errorMessage);
346 - return winrt::to_hstring(token.get());
349 + return winrt::make<implementation::AuthenticateResult>(
350 + winrt::to_hstring(token.get()), static_cast<winrt::Microsoft::WSL::Containers::IdentityTokenType>(tokenType));
351 }
352
353 winrt::event_token Session::Terminated(winrt::Microsoft::WSL::Containers::SessionTerminationHandler const& handler)
src/windows/WslcSDK/winrt/Session.h
+2 -1
@@ -38,7 +38,8 @@ struct Session : SessionT<Session>
38 void TagImage(winrt::Microsoft::WSL::Containers::TagImageOptions const& options);
39 void CreateVhdVolume(winrt::Microsoft::WSL::Containers::VhdOptions const& options);
40 void DeleteVhdVolume(hstring const& name);
41 - hstring Authenticate(winrt::Windows::Foundation::Uri const& serverAddress, hstring const& username, hstring const& password);
41 + winrt::Microsoft::WSL::Containers::AuthenticateResult Authenticate(
42 + winrt::Windows::Foundation::Uri const& serverAddress, hstring const& username, hstring const& password);
43 winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::WSL::Containers::ImageInfo> GetImages();
44 winrt::event_token Terminated(winrt::Microsoft::WSL::Containers::SessionTerminationHandler const& handler);
45 void Terminated(winrt::event_token const& token) noexcept;
src/windows/WslcSDK/winrt/wslcsdk.idl
+14 -1
@@ -34,6 +34,19 @@ namespace Microsoft.WSL.Containers
34
35 delegate void ProcessCrashHandler(ProcessCrashInformation information);
36
37 + enum IdentityTokenType
38 + {
39 + Unknown = 0,
40 + Token = 1,
41 + Credentials = 2,
42 + };
43 +
44 + runtimeclass AuthenticateResult
45 + {
46 + String IdentityToken { get; };
47 + IdentityTokenType TokenType { get; };
48 + };
49 +
50 runtimeclass SessionSettings
51 {
52 SessionSettings(String name, String storagePath);
@@ -73,7 +86,7 @@ namespace Microsoft.WSL.Containers
86 void CreateVhdVolume(VhdOptions options);
87 void DeleteVhdVolume(String name);
88
76 - String Authenticate(Windows.Foundation.Uri serverAddress, String username, String password);
89 + AuthenticateResult Authenticate(Windows.Foundation.Uri serverAddress, String username, String password);
90
91 IVectorView<ImageInfo> GetImages();
92
src/windows/WslcSDK/wslcsdk.cpp
+28 -3
@@ -1536,6 +1536,7 @@ STDAPI WslcSessionAuthenticate(
1536 _In_z_ PCSTR username,
1537 _In_z_ PCSTR password,
1538 _Outptr_result_z_ PSTR* identityToken,
1539 + _Out_opt_ WslcIdentityTokenType* tokenType,
1540 _Outptr_opt_result_z_ PWSTR* errorMessage)
1541 try
1542 {
@@ -1548,12 +1549,36 @@ try
1549 RETURN_HR_IF_NULL(E_POINTER, identityToken);
1550
1551 *identityToken = nullptr;
1552 + if (tokenType != nullptr)
1553 + {
1554 + *tokenType = WSLC_IDENTITY_TOKEN_TYPE_UNKNOWN;
1555 + }
1556
1552 - wil::unique_cotaskmem_ansistring token;
1553 - auto hr = errorInfoWrapper.CaptureResult(internalType->session->Authenticate(serverAddress, username, password, &token));
1557 + wil::unique_cotaskmem_ansistring rawToken;
1558 + auto hr = errorInfoWrapper.CaptureResult(internalType->session->Authenticate(serverAddress, username, password, &rawToken));
1559 if (SUCCEEDED(hr))
1560 {
1556 - *identityToken = token.release();
1561 + std::string authHeader;
1562 + WslcIdentityTokenType type;
1563 +
1564 + if (rawToken && strlen(rawToken.get()) > 0)
1565 + {
1566 + authHeader = BuildRegistryAuthHeader(std::string{rawToken.get()});
1567 + type = WSLC_IDENTITY_TOKEN_TYPE_TOKEN;
1568 + }
1569 + else
1570 + {
1571 + authHeader = BuildRegistryAuthHeader(std::string{username}, std::string{password});
1572 + type = WSLC_IDENTITY_TOKEN_TYPE_CREDENTIALS;
1573 + }
1574 +
1575 + auto result = wil::make_unique_ansistring<wil::unique_cotaskmem_ansistring>(authHeader.c_str());
1576 + *identityToken = result.release();
1577 +
1578 + if (tokenType != nullptr)
1579 + {
1580 + *tokenType = type;
1581 + }
1582 }
1583
1584 return errorInfoWrapper;
src/windows/WslcSDK/wslcsdk.h
+35 -6
@@ -530,7 +530,15 @@ typedef struct WslcPushImageOptions
530
531 STDAPI WslcPushSessionImage(_In_ WslcSession session, _In_ const WslcPushImageOptions* options, _Outptr_opt_result_z_ PWSTR* errorMessage);
532
533 -// Authenticates with a container registry and returns an identity token.
533 +typedef enum WslcIdentityTokenType
534 +{
535 + WSLC_IDENTITY_TOKEN_TYPE_UNKNOWN = 0,
536 + WSLC_IDENTITY_TOKEN_TYPE_TOKEN = 1,
537 + WSLC_IDENTITY_TOKEN_TYPE_CREDENTIALS = 2,
538 +} WslcIdentityTokenType;
539 +
540 +// Authenticates with a container registry and returns an identity token suitable for use as
541 +// the registryAuth value in WslcPullSessionImage and WslcPushSessionImage.
542 //
543 // Parameters:
544 // session
@@ -546,21 +554,42 @@ STDAPI WslcPushSessionImage(_In_ WslcSession session, _In_ const WslcPushImageOp
554 // The password for authentication.
555 //
556 // identityToken
549 -// On success, receives a pointer to a null-terminated ANSI string
550 -// containing the identity token.
557 +// On success, receives a pointer to a null-terminated ANSI string containing a
558 +// base64-encoded JSON object that can be passed directly as the registryAuth value to
559 +// WslcPullSessionImage or WslcPushSessionImage.
560 //
552 -// The string is allocated using CoTaskMemAlloc. The caller takes
553 -// ownership of the returned memory and must free it by calling
554 -// CoTaskMemFree when it is no longer needed.
561 +// The string is allocated using CoTaskMemAlloc. The caller takes ownership of the
562 +// returned memory and must free it by calling CoTaskMemFree when it is no longer needed.
563 +//
564 +// tokenType
565 +// Optional. On success, receives the type of credential embedded in identityToken:
566 +// WSLC_IDENTITY_TOKEN_TYPE_TOKEN - the server returned an identity token;
567 +// identityToken encodes {"identitytoken": ...}
568 +// WSLC_IDENTITY_TOKEN_TYPE_CREDENTIALS - the server returned no token; the supplied
569 +// username/password are embedded instead as
570 +// {"username": ..., "password": ...}
571 +// On failure, set to WSLC_IDENTITY_TOKEN_TYPE_UNKNOWN.
572 +// May be null if the caller does not need the token type.
573 +//
574 +// errorMessage
575 +// Optional. On failure, receives a human-readable error message. May be null.
576 //
577 // Return Value:
578 // S_OK on success. Otherwise, an HRESULT error code indicating the failure.
579 +//
580 +// Outcome table:
581 +// Outcome | HRESULT | identityToken | tokenType
582 +// ------------------------------|---------|--------------------------------------|----------
583 +// Failure | FAILED | nullptr | UNKNOWN
584 +// Success, server returns token | S_OK | base64({"identitytoken": "<token>"}) | TOKEN
585 +// Success, no token returned | S_OK | base64({"username":..,"password":..})| CREDENTIALS
586 STDAPI WslcSessionAuthenticate(
587 _In_ WslcSession session,
588 _In_z_ PCSTR serverAddress,
589 _In_z_ PCSTR username,
590 _In_z_ PCSTR password,
591 _Outptr_result_z_ PSTR* identityToken,
592 + _Out_opt_ WslcIdentityTokenType* tokenType,
593 _Outptr_opt_result_z_ PWSTR* errorMessage);
594
595 // Retrieves the list of container images
test/windows/WslcSdkTests.cpp
+26 -10
@@ -2335,19 +2335,27 @@ class WslcSdkTests
2335 {
2336 wil::unique_cotaskmem_ansistring token;
2337 wil::unique_cotaskmem_string errorMsg;
2338 + WslcIdentityTokenType tokenType{};
2339 VERIFY_ARE_EQUAL(
2339 - WslcSessionAuthenticate(m_defaultSession, registryAddress.c_str(), c_username, "wrong-password", &token, &errorMsg), E_FAIL);
2340 + WslcSessionAuthenticate(m_defaultSession, registryAddress.c_str(), c_username, "wrong-password", &token, &tokenType, &errorMsg),
2341 + E_FAIL);
2342 VERIFY_IS_NOT_NULL(errorMsg.get());
2343 + VERIFY_ARE_EQUAL(tokenType, WSLC_IDENTITY_TOKEN_TYPE_UNKNOWN);
2344 }
2345
2343 - // Positive: correct credentials must succeed and return a non-null token.
2346 + // Positive: correct credentials must succeed and return a non-null, registry-auth-ready token.
2347 {
2348 wil::unique_cotaskmem_ansistring token;
2349 wil::unique_cotaskmem_string errorMsg;
2347 - VERIFY_SUCCEEDED(WslcSessionAuthenticate(m_defaultSession, registryAddress.c_str(), c_username, c_password, &token, &errorMsg));
2350 + WslcIdentityTokenType tokenType{};
2351 + VERIFY_SUCCEEDED(WslcSessionAuthenticate(
2352 + m_defaultSession, registryAddress.c_str(), c_username, c_password, &token, &tokenType, &errorMsg));
2353 VERIFY_IS_NOT_NULL(token.get());
2354 + // The local test registry does not return an identity token, so credentials are embedded.
2355 + VERIFY_ARE_EQUAL(tokenType, WSLC_IDENTITY_TOKEN_TYPE_CREDENTIALS);
2356 }
2357
2358 + // The local registry requires auth; push the test image for the pull tests below.
2359 auto xRegistryAuth = wsl::windows::common::wslutil::BuildRegistryAuthHeader(c_username, c_password);
2360 PushImageToRegistry("hello-world", "latest", registryAddress, xRegistryAuth);
2361
@@ -2356,11 +2364,16 @@ class WslcSdkTests
2364 auto imageCleanup = wil::scope_exit_log(
2365 WI_DIAGNOSTICS_INFO, [&]() { LOG_IF_FAILED(WslcDeleteSessionImage(m_defaultSession, image.c_str(), nullptr)); });
2366
2359 - // Pulling with credentials should succeed.
2367 + // Positive: pulling with the identityToken from WslcSessionAuthenticate directly should succeed,
2368 + // demonstrating that the output can be passed as registryAuth without any transformation.
2369 {
2370 + wil::unique_cotaskmem_ansistring authToken;
2371 + VERIFY_SUCCEEDED(WslcSessionAuthenticate(
2372 + m_defaultSession, registryAddress.c_str(), c_username, c_password, &authToken, nullptr, nullptr));
2373 +
2374 WslcPullImageOptions opts{};
2375 opts.uri = image.c_str();
2363 - opts.registryAuth = xRegistryAuth.c_str();
2376 + opts.registryAuth = authToken.get();
2377 VERIFY_SUCCEEDED(WslcPullSessionImage(m_defaultSession, &opts, nullptr));
2378 VERIFY_IS_TRUE(HasImage(image));
2379 }
@@ -2388,13 +2401,16 @@ class WslcSdkTests
2401 VERIFY_IS_NOT_NULL(errorMsg.get());
2402 }
2403
2391 - // Negative: null parameters must fail.
2404 + // Negative: null parameters must fail; tokenType is optional and may be null.
2405 {
2406 wil::unique_cotaskmem_ansistring token;
2394 - VERIFY_ARE_EQUAL(WslcSessionAuthenticate(m_defaultSession, nullptr, c_username, c_password, &token, nullptr), E_POINTER);
2395 - VERIFY_ARE_EQUAL(WslcSessionAuthenticate(m_defaultSession, registryAddress.c_str(), nullptr, c_password, &token, nullptr), E_POINTER);
2396 - VERIFY_ARE_EQUAL(WslcSessionAuthenticate(m_defaultSession, registryAddress.c_str(), c_username, nullptr, &token, nullptr), E_POINTER);
2397 - VERIFY_ARE_EQUAL(WslcSessionAuthenticate(m_defaultSession, registryAddress.c_str(), c_username, c_password, nullptr, nullptr), E_POINTER);
2407 + VERIFY_ARE_EQUAL(WslcSessionAuthenticate(m_defaultSession, nullptr, c_username, c_password, &token, nullptr, nullptr), E_POINTER);
2408 + VERIFY_ARE_EQUAL(
2409 + WslcSessionAuthenticate(m_defaultSession, registryAddress.c_str(), nullptr, c_password, &token, nullptr, nullptr), E_POINTER);
2410 + VERIFY_ARE_EQUAL(
2411 + WslcSessionAuthenticate(m_defaultSession, registryAddress.c_str(), c_username, nullptr, &token, nullptr, nullptr), E_POINTER);
2412 + VERIFY_ARE_EQUAL(
2413 + WslcSessionAuthenticate(m_defaultSession, registryAddress.c_str(), c_username, c_password, nullptr, nullptr, nullptr), E_POINTER);
2414 }
2415 }
2416
test/windows/WslcSdkWinRTTests.cpp
+11 -5
@@ -1624,8 +1624,13 @@ class WslcSdkWinRtTests
1624 // Negative: wrong password must fail.
1625 VERIFY_THROWS_HR(m_defaultSession.Authenticate(serverUri, winrt::to_hstring(c_username), L"wrong-password"), E_FAIL);
1626
1627 - // Positive: correct credentials
1628 - VERIFY_NO_THROW(m_defaultSession.Authenticate(serverUri, winrt::to_hstring(c_username), winrt::to_hstring(c_password)));
1627 + // Positive: correct credentials must return a non-null token of the expected type.
1628 + {
1629 + const auto result = m_defaultSession.Authenticate(serverUri, winrt::to_hstring(c_username), winrt::to_hstring(c_password));
1630 + VERIFY_IS_FALSE(result.IdentityToken().empty());
1631 + // The local test registry does not return an identity token, so credentials are embedded.
1632 + VERIFY_ARE_EQUAL(result.TokenType(), WSLCSDK::IdentityTokenType::Credentials);
1633 + }
1634
1635 const auto xRegistryAuth = wsl::windows::common::wslutil::BuildRegistryAuthHeader(c_username, c_password);
1636 PushImageToRegistry("hello-world", "latest", registryAddress, xRegistryAuth);
@@ -1634,11 +1639,12 @@ class WslcSdkWinRtTests
1639
1640 auto cleanup = SCOPE_CLEANUP(m_defaultSession.DeleteImage(image));
1641
1637 - // Positive: pulling with correct credentials must succeed.
1642 + // Positive: the IdentityToken from Authenticate can be passed directly as RegistryAuth.
1643 {
1644 + const auto authResult = m_defaultSession.Authenticate(serverUri, winrt::to_hstring(c_username), winrt::to_hstring(c_password));
1645 auto opts = WSLCSDK::PullImageOptions(image);
1640 - opts.RegistryAuth(winrt::to_hstring(xRegistryAuth));
1641 - m_defaultSession.PullImageAsync(opts).get();
1646 + opts.RegistryAuth(authResult.IdentityToken());
1647 + VERIFY_NO_THROW(m_defaultSession.PullImageAsync(opts).get());
1648 VERIFY_IS_TRUE(HasImage(image));
1649 }
1650