Skip to content

Commit

Permalink
SsoPolicy support for PCA. (#4659)
Browse files Browse the repository at this point in the history
* SsoPolicy initial

* Remove SSOPolicy project

* Update LibsAndSamples.sln

* Delete files not needed

* Introduce WithSSoPolicy in broker project and rename RuntimeBroker to MsalCppRuntime

* Revert "Introduce WithSSoPolicy in broker project and rename RuntimeBroker to MsalCppRuntime"

This reverts commit 5e9f794.

* Update ApplicationConfiguration.cs

* Update InternalsVisibleTo.cs

* Hook up the E2E for adding SsoPolicy headers in OAuth2 implementation

* Update NuGet.Config

* Inject headers in RequestBase

* Update src/client/Microsoft.Identity.Client/Platforms/Features/RuntimeBroker/RuntimeBroker.cs

Co-authored-by: Gladwin Johnson <[email protected]>

* Update src/client/Microsoft.Identity.Client/MsalErrorMessage.cs

Co-authored-by: Bogdan Gavril <[email protected]>

* Fix for review comments

* Fix for review comments

* Update src/client/Microsoft.Identity.Client/MsalErrorMessage.cs

Co-authored-by: Bogdan Gavril <[email protected]>

* Update src/client/Microsoft.Identity.Client/AppConfig/ApplicationConfiguration.cs

Co-authored-by: Peter <[email protected]>

* Update src/client/Microsoft.Identity.Client/Platforms/Features/WinFormsLegacyWebUi/WindowsFormsWebAuthenticationDialogBase.cs

Co-authored-by: Peter <[email protected]>

* Update src/client/Microsoft.Identity.Client/Internal/Broker/NullBroker.cs

Co-authored-by: Peter <[email protected]>

* Fix for review comments

* Update RuntimeBroker.cs

* Fix mobile build breaks

---------

Co-authored-by: Bogdan Gavril <[email protected]>
Co-authored-by: Gladwin Johnson <[email protected]>
Co-authored-by: Peter <[email protected]>
  • Loading branch information
4 people authored Mar 26, 2024
1 parent e065471 commit 80faef7
Show file tree
Hide file tree
Showing 14 changed files with 128 additions and 11 deletions.
2 changes: 1 addition & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<!-- Version of the Microsoft.Identity.Client.NativeInterop package. -->
<MSALRuntimeNativeInteropVersion>0.13.14</MSALRuntimeNativeInteropVersion>
<MSALRuntimeNativeInteropVersion>0.16.0</MSALRuntimeNativeInteropVersion>
<!-- Version of MSAL if not defined by the CI-->
<MsalInternalVersion>4.51.0</MsalInternalVersion>
</PropertyGroup>
Expand Down
25 changes: 20 additions & 5 deletions src/client/Microsoft.Identity.Client.Broker/BrokerExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,20 +47,35 @@ public static PublicClientApplicationBuilder WithBrokerPreview(this PublicClient
/// parameters, and to create a public client application instance</returns>
public static PublicClientApplicationBuilder WithBroker(this PublicClientApplicationBuilder builder, BrokerOptions brokerOptions)
{
AddRuntimeSupportForWam(builder);
AddRuntimeSupport(builder);
builder.Config.BrokerOptions = brokerOptions;
builder.Config.IsBrokerEnabled = brokerOptions.IsBrokerEnabledOnCurrentOs();
return builder;
}

private static void AddRuntimeSupportForWam(PublicClientApplicationBuilder builder)
/// <summary>
/// Use this API to enable SsoPolicy enforcement.
/// Should only be utilized by Microsoft 1st party applications.
/// This is applicable only when broker is not enabled and embedded webview is the preferred choice.
/// By default, the broker supports SsoPolicy, and system webview SsoPolicy is also supported at the OS level.
/// <param name="builder"></param>
/// <returns>A <see cref="PublicClientApplicationBuilder"/> from which to set more
/// parameters, and to create a public client application instance</returns>
public static PublicClientApplicationBuilder WithSsoPolicy(this PublicClientApplicationBuilder builder)
{
AddRuntimeSupport(builder);
builder.Config.IsWebviewSsoPolicyEnabled = true;
return builder;
}

private static void AddRuntimeSupport(PublicClientApplicationBuilder builder)
{
if (DesktopOsHelper.IsWin10OrServerEquivalent())
{
builder.Config.BrokerCreatorFunc =
builder.Config.BrokerCreatorFunc =
(uiParent, appConfig, logger) =>
{
logger.Info("[RuntimeBroker] WAM supported OS.");
logger.Info("[Runtime] WAM supported OS.");
return new RuntimeBroker(uiParent, appConfig, logger);
};
}
Expand All @@ -69,7 +84,7 @@ private static void AddRuntimeSupportForWam(PublicClientApplicationBuilder build
builder.Config.BrokerCreatorFunc =
(uiParent, appConfig, logger) =>
{
logger.Info("[RuntimeBroker] Not a Windows 10 or Server equivalent machine. WAM is not available.");
logger.Info("[RuntimeBroker] Not a Windows 10 or Server equivalent machine. Runtime broker or SsoPolicy support is not available.");
return new NullBroker(logger);
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ public string ClientVersion

public bool IsBrokerEnabled { get; internal set; }

/// <summary>
/// Applicable to only public client applications to enforce SSO policy with embedded webview.
/// </summary>
public bool IsWebviewSsoPolicyEnabled { get; internal set; }

// Legacy options for UWP. .NET broker options are in BrokerOptions
public WindowsBrokerOptions UwpBrokerOptions { get; set; }

Expand Down Expand Up @@ -121,6 +126,9 @@ public string ClientVersion
public ManagedIdentityId ManagedIdentityId { get; internal set; }

public bool IsManagedIdentity { get; }
public bool IsConfidentialClient { get; }
public bool IsPublicClient => !IsConfidentialClient && !IsManagedIdentity;


public Func<AppTokenProviderParameters, Task<AppTokenProviderResult>> AppTokenProvider;

Expand Down Expand Up @@ -201,8 +209,7 @@ public X509Certificate2 ClientCredentialCertificate
public ITokenCacheInternal UserTokenCacheInternalForTest { get; set; }
public ITokenCacheInternal AppTokenCacheInternalForTest { get; set; }

public IDeviceAuthManager DeviceAuthManagerForTest { get; set; }
public bool IsConfidentialClient { get; }
public IDeviceAuthManager DeviceAuthManagerForTest { get; set; }
public bool IsInstanceDiscoveryEnabled { get; internal set; } = true;
#endregion

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Task<MsalTokenResponse> AcquireTokenByUsernamePasswordAsync(
AuthenticationRequestParameters authenticationRequestParameters,
AcquireTokenByUsernamePasswordParameters acquireTokenByUsernamePasswordParameters);

IReadOnlyDictionary<string, string> GetSsoPolicyHeaders();
/// <summary>
/// If device auth is required but the broker is not enabled, AAD will
/// signal this by returning an URL pointing to the broker app that needs to be installed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Microsoft.Identity.Client.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Microsoft.Identity.Client.Internal.Broker
Expand Down Expand Up @@ -80,5 +81,10 @@ public Task<MsalTokenResponse> AcquireTokenByUsernamePasswordAsync(Authenticatio
_logger.Info("NullBroker - returning null on ROPC request.");
return Task.FromResult<MsalTokenResponse>(null);
}

public IReadOnlyDictionary<string, string> GetSsoPolicyHeaders()
{
return CollectionHelpers.GetEmptyDictionary<string, string>();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
using Microsoft.IdentityModel.Abstractions;
using Microsoft.Identity.Client.TelemetryCore.TelemetryClient;
using Microsoft.Identity.Client.TelemetryCore.OpenTelemetry;
using Microsoft.Identity.Client.Internal.Broker;

namespace Microsoft.Identity.Client.Internal.Requests
{
Expand Down Expand Up @@ -422,13 +423,32 @@ protected Task<MsalTokenResponse> SendTokenRequestAsync(
tokenClient.AddHeaderToClient(CcsHeader.Value.Key, CcsHeader.Value.Value);
}

InjectPcaSsoPolicyHeader(tokenClient);

return tokenClient.SendTokenRequestAsync(
additionalBodyParameters,
scopes,
tokenEndpoint,
cancellationToken);
}

private void InjectPcaSsoPolicyHeader(TokenClient tokenClient)
{
if (ServiceBundle.Config.IsPublicClient && ServiceBundle.Config.IsWebviewSsoPolicyEnabled)
{
IBroker broker = ServiceBundle.Config.BrokerCreatorFunc(
null,
ServiceBundle.Config,
AuthenticationRequestParameters.RequestContext.Logger);

var ssoPolicyHeaders = broker.GetSsoPolicyHeaders();
foreach (KeyValuePair<string, string> kvp in ssoPolicyHeaders)
{
tokenClient.AddHeaderToClient(kvp.Key, kvp.Value);
}
}
}

//The AAD backup authentication system header is used by the AAD backup authentication system service
//to help route requests to resources in Azure during requests to speed up authentication.
//It consists of either the ObjectId.TenantId or the upn of the account signing in.
Expand Down Expand Up @@ -570,6 +590,6 @@ private static RegionDetails CreateRegionDetails(ApiEvent apiEvent)
apiEvent.RegionOutcome,
apiEvent.RegionUsed,
apiEvent.RegionDiscoveryFailureReason);
}
}
}
}
2 changes: 2 additions & 0 deletions src/client/Microsoft.Identity.Client/OAuth2/OAuth2Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
using Microsoft.Identity.Client.Instance.Oidc;
using Microsoft.Identity.Client.Internal;
using Microsoft.Identity.Client.Utils;
using Microsoft.Identity.Client.Internal.Broker;

#if SUPPORTS_SYSTEM_TEXT_JSON
using System.Text.Json;
#else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -459,5 +459,10 @@ public Task<MsalTokenResponse> AcquireTokenByUsernamePasswordAsync(Authenticatio
{
return Task.FromResult<MsalTokenResponse>(null); // nop
}

public IReadOnlyDictionary<string, string> GetSsoPolicyHeaders()
{
return CollectionHelpers.GetEmptyDictionary<string, string>();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -400,5 +400,10 @@ public Task<MsalTokenResponse> AcquireTokenByUsernamePasswordAsync(Authenticatio
{
return Task.FromResult<MsalTokenResponse>(null); // nop
}

public IReadOnlyDictionary<string, string> GetSsoPolicyHeaders()
{
return CollectionHelpers.GetEmptyDictionary<string, string>();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,32 @@ public async Task<IReadOnlyList<IAccount>> GetAccountsAsync(
}
}
}
public IReadOnlyDictionary<string, string> GetSsoPolicyHeaders()
{
using LogEventWrapper logEventWrapper = new LogEventWrapper(this);
Debug.Assert(s_lazyCore.Value != null, "Should not call this API if MSAL runtime init failed");

NativeInterop.SsoPolicy ssoPolicy = new SsoPolicy();
_logger.Info(() => $"[RuntimeBroker] Broker returned SsoPolicyType {ssoPolicy.SsoPolicyType} and errorCode {ssoPolicy.ErrorCode}.");

var ssoPolicyHeaders = new Dictionary<string, string>();
if (ssoPolicy.SsoPolicyType == SsoPolicyType.PermissionRequired)
{
ssoPolicyHeaders.Add("x-ms-SsoFlags", "SsoRestr");
}
else if (ssoPolicy.SsoPolicyType == SsoPolicyType.Error)
{
ssoPolicyHeaders.Add("x-ms-SsoFlags", "SsoPolicyError");
string subStatusValue = "SsoRestrError:" + ssoPolicy.ErrorCode.ToString();
ssoPolicyHeaders.Add("x-ms-SsoFlagsSubstatus", Base64UrlHelpers.Encode(subStatusValue));
}
else if (ssoPolicy.SsoPolicyType == SsoPolicyType.Unknown)
{
ssoPolicyHeaders.Add("x-ms-SsoFlags", "SsoRestrUndefined");
}

return ssoPolicyHeaders;
}

public void HandleInstallUrl(string appLink)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.Net.Http;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
using Microsoft.Identity.Client.Core;
using Microsoft.Identity.Client.Http;
using Microsoft.Identity.Client.Internal;
using Microsoft.Identity.Client.Internal.Broker;
using Microsoft.Identity.Client.Platforms.Features.DesktopOs;
using Microsoft.Identity.Client.UI;
using Microsoft.Identity.Client.Utils;
Expand Down Expand Up @@ -276,7 +281,21 @@ internal AuthorizationResult AuthenticateAAD(Uri requestUri, Uri callbackUri, Ca
_webBrowser.Navigated += WebBrowserNavigatedHandler;
_webBrowser.NavigateError += WebBrowserNavigateErrorHandler;

_webBrowser.Navigate(requestUri);
if(RequestContext.ServiceBundle.Config.IsWebviewSsoPolicyEnabled)
{
IBroker broker = RequestContext.ServiceBundle.Config.BrokerCreatorFunc(null, RequestContext.ServiceBundle.Config, RequestContext.Logger);
var ssoPolicyHeaders = broker.GetSsoPolicyHeaders();
string ssoPolicyHeadersString="";
foreach (KeyValuePair<string, string> kvp in ssoPolicyHeaders)
{
ssoPolicyHeadersString += kvp.Key + ":" + kvp.Value + Environment.NewLine;
}
_webBrowser.Navigate(requestUri, null, null, ssoPolicyHeadersString);
}
else
{
_webBrowser.Navigate(requestUri);
}
OnAuthenticate(cancellationToken);

return Result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,11 @@ public static void SetBrokerResponse(NSUrl responseUrl)
s_brokerResponseReady?.Release();
}

public IReadOnlyDictionary<string, string> GetSsoPolicyHeaders()
{
return CollectionHelpers.GetEmptyDictionary<string, string>();
}

#region Silent Flow - not supported
/// <summary>
/// iOS broker does not handle silent flow
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -927,5 +927,10 @@ public Task<MsalTokenResponse> AcquireTokenByUsernamePasswordAsync(Authenticatio
{
return Task.FromResult<MsalTokenResponse>(null); // nop
}

public IReadOnlyDictionary<string, string> GetSsoPolicyHeaders()
{
return CollectionHelpers.GetEmptyDictionary<string, string>();
}
}
}
3 changes: 2 additions & 1 deletion tests/devapps/WAM/NetCoreWinFormsWam/Form1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ private IPublicClientApplication CreatePca(AuthMethod? authMethod)
break;
case AuthMethod.SystemBrowser:
builder.WithBroker(new BrokerOptions(BrokerOptions.OperatingSystems.None));
builder = builder.WithBroker(false);
builder = builder.WithBroker(false)
.WithSsoPolicy();
break;
case AuthMethod.EmbeddedBrowser:
builder.WithBroker(new BrokerOptions(BrokerOptions.OperatingSystems.None));
Expand Down

0 comments on commit 80faef7

Please sign in to comment.