Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Identity.Client.ApiConfig.Executors;
using Microsoft.Identity.Client.ApiConfig.Parameters;
using Microsoft.Identity.Client.TelemetryCore.Internal.Events;

namespace Microsoft.Identity.Client
{
/// <summary>
/// Parameter builder for the <see cref="IByUserFederatedIdentityCredential.AcquireTokenByUserFederatedIdentityCredential(IEnumerable{string}, string, string)"/>
/// operation.
/// </summary>
#if !SUPPORTS_CONFIDENTIAL_CLIENT
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] // hide confidential client on mobile
#endif
public sealed class AcquireTokenByUserFederatedIdentityCredentialParameterBuilder :
AbstractConfidentialClientAcquireTokenParameterBuilder<AcquireTokenByUserFederatedIdentityCredentialParameterBuilder>
{
private AcquireTokenByUserFederatedIdentityCredentialParameters Parameters { get; } = new AcquireTokenByUserFederatedIdentityCredentialParameters();

internal AcquireTokenByUserFederatedIdentityCredentialParameterBuilder(
IConfidentialClientApplicationExecutor confidentialClientApplicationExecutor,
string username,
string assertion)
: base(confidentialClientApplicationExecutor)
{
Parameters.Username = username;
Parameters.Assertion = assertion;
}

internal static AcquireTokenByUserFederatedIdentityCredentialParameterBuilder Create(
IConfidentialClientApplicationExecutor confidentialClientApplicationExecutor,
IEnumerable<string> scopes,
string username,
string assertion)
{
if (string.IsNullOrEmpty(username))
{
throw new ArgumentNullException(nameof(username));
}

if (string.IsNullOrEmpty(assertion))
{
throw new ArgumentNullException(nameof(assertion));
}

return new AcquireTokenByUserFederatedIdentityCredentialParameterBuilder(
confidentialClientApplicationExecutor, username, assertion)
.WithScopes(scopes);
}

/// <summary>
/// Forces MSAL to refresh the token from the identity provider even if a cached token is available.
/// </summary>
/// <param name="forceRefresh"><c>true</c> to bypass the cache; otherwise <c>false</c>. Default is <c>false</c>.</param>
/// <returns>The builder to chain the .With methods</returns>
public AcquireTokenByUserFederatedIdentityCredentialParameterBuilder WithForceRefresh(bool forceRefresh)
{
Parameters.ForceRefresh = forceRefresh;
return this;
}

/// <summary>
/// Applicable to first-party applications only, this method also allows to specify
/// if the <see href="https://datatracker.ietf.org/doc/html/rfc7517#section-4.7">x5c claim</see> should be sent to Azure AD.
/// Sending the x5c enables application developers to achieve easy certificate roll-over in Azure AD:
/// this method will send the certificate chain to Azure AD along with the token request,
/// so that Azure AD can use it to validate the subject name based on a trusted issuer policy.
/// This saves the application admin from the need to explicitly manage the certificate rollover
/// (either via portal or PowerShell/CLI operation). For details see https://aka.ms/msal-net-sni
/// </summary>
/// <param name="withSendX5C"><c>true</c> if the x5c should be sent. Otherwise <c>false</c>.
/// The default is <c>false</c></param>
/// <returns>The builder to chain the .With methods</returns>
public AcquireTokenByUserFederatedIdentityCredentialParameterBuilder WithSendX5C(bool withSendX5C)
{
Parameters.SendX5C = withSendX5C;
return this;
}

/// <inheritdoc/>
internal override Task<AuthenticationResult> ExecuteInternalAsync(CancellationToken cancellationToken)
{
return ConfidentialClientApplicationExecutor.ExecuteAsync(CommonParameters, Parameters, cancellationToken);
}

/// <inheritdoc/>
internal override ApiEvent.ApiIds CalculateApiEventId()
{
return ApiEvent.ApiIds.AcquireTokenByUserFederatedIdentityCredential;
}

/// <inheritdoc/>
protected override void Validate()
{
base.Validate();

if (Parameters.SendX5C == null)
{
Parameters.SendX5C = ServiceBundle.Config?.SendX5C ?? false;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -164,5 +164,28 @@ public async Task<AuthenticationResult> ExecuteAsync(

return await handler.RunAsync(cancellationToken).ConfigureAwait(false);
}

public async Task<AuthenticationResult> ExecuteAsync(
AcquireTokenCommonParameters commonParameters,
AcquireTokenByUserFederatedIdentityCredentialParameters userFicParameters,
CancellationToken cancellationToken)
{
RequestContext requestContext = CreateRequestContextAndLogVersionInfo(commonParameters.CorrelationId, commonParameters.MtlsCertificate, cancellationToken);

AuthenticationRequestParameters requestParams = await _confidentialClientApplication.CreateRequestParametersAsync(
commonParameters,
requestContext,
_confidentialClientApplication.UserTokenCacheInternal,
cancellationToken).ConfigureAwait(false);

requestParams.SendX5C = userFicParameters.SendX5C ?? false;

var handler = new UserFederatedIdentityCredentialRequest(
ServiceBundle,
requestParams,
userFicParameters);

return await handler.RunAsync(cancellationToken).ConfigureAwait(false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,10 @@ Task<AuthenticationResult> ExecuteAsync(
AcquireTokenCommonParameters commonParameters,
AcquireTokenByUsernamePasswordParameters usernamePasswordParameters,
CancellationToken cancellationToken);

Task<AuthenticationResult> ExecuteAsync(
AcquireTokenCommonParameters commonParameters,
AcquireTokenByUserFederatedIdentityCredentialParameters userFicParameters,
CancellationToken cancellationToken);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Microsoft.Identity.Client.Core;

namespace Microsoft.Identity.Client.ApiConfig.Parameters
{
internal class AcquireTokenByUserFederatedIdentityCredentialParameters : IAcquireTokenParameters
{
public string Username { get; set; }
public string Assertion { get; set; }
public bool? SendX5C { get; set; }
public bool ForceRefresh { get; set; }

/// <inheritdoc/>
public void LogParameters(ILoggerAdapter logger)
{
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ public sealed partial class ConfidentialClientApplication
IConfidentialClientApplication,
IByRefreshToken,
ILongRunningWebApi,
IByUsernameAndPassword
IByUsernameAndPassword,
IByUserFederatedIdentityCredential
{
/// <summary>
/// Instructs MSAL to try to auto discover the Azure region.
Expand Down Expand Up @@ -183,6 +184,19 @@ AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder IByUsernameAndPass
password);
}

/// <inheritdoc/>
AcquireTokenByUserFederatedIdentityCredentialParameterBuilder IByUserFederatedIdentityCredential.AcquireTokenByUserFederatedIdentityCredential(
IEnumerable<string> scopes,
string username,
string assertion)
{
return AcquireTokenByUserFederatedIdentityCredentialParameterBuilder.Create(
ClientExecutorFactory.CreateConfidentialClientExecutor(this),
scopes,
username,
assertion);
}

AcquireTokenByRefreshTokenParameterBuilder IByRefreshToken.AcquireTokenByRefreshToken(
IEnumerable<string> scopes,
string refreshToken)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Collections.Generic;

namespace Microsoft.Identity.Client
{
/// <summary>
/// Provides an interface for acquiring tokens using the User Federated Identity Credential (UserFIC) flow.
/// </summary>
#if !SUPPORTS_CONFIDENTIAL_CLIENT
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] // hide confidential client on mobile
#endif
public interface IByUserFederatedIdentityCredential
{
/// <summary>
/// Acquires a token on behalf of a user using a federated identity credential assertion.
/// This uses the <c>user_fic</c> grant type.
/// </summary>
/// <param name="scopes">Scopes requested to access a protected API.</param>
/// <param name="username">The UPN (User Principal Name) of the user, e.g. <c>john.doe@contoso.com</c>.</param>
/// <param name="assertion">
/// The federated identity credential assertion (JWT) for the user.
/// Acquire this token from a Managed Identity or Confidential Client application before calling this method.
/// </param>
/// <returns>A builder enabling you to add optional parameters before executing the token request.</returns>
AcquireTokenByUserFederatedIdentityCredentialParameterBuilder AcquireTokenByUserFederatedIdentityCredential(
IEnumerable<string> scopes,
string username,
string assertion);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace Microsoft.Identity.Client
#if !SUPPORTS_CONFIDENTIAL_CLIENT
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] // hide confidential client on mobile
#endif
public partial interface IConfidentialClientApplication : IClientApplicationBase
public partial interface IConfidentialClientApplication : IClientApplicationBase, IByUserFederatedIdentityCredential
{
/// <summary>
/// Application token cache which holds access tokens for this application. It's maintained
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Identity.Client.ApiConfig.Parameters;
using Microsoft.Identity.Client.OAuth2;
using Microsoft.Identity.Client.Utils;

namespace Microsoft.Identity.Client.Internal.Requests
{
internal class UserFederatedIdentityCredentialRequest : RequestBase
{
private readonly AcquireTokenByUserFederatedIdentityCredentialParameters _userFicParameters;

public UserFederatedIdentityCredentialRequest(
IServiceBundle serviceBundle,
AuthenticationRequestParameters authenticationRequestParameters,
AcquireTokenByUserFederatedIdentityCredentialParameters userFicParameters)
: base(serviceBundle, authenticationRequestParameters, userFicParameters)
{
_userFicParameters = userFicParameters;
}

protected override async Task<AuthenticationResult> ExecuteAsync(CancellationToken cancellationToken)
{
await ResolveAuthorityAsync().ConfigureAwait(false);

var additionalBodyParameters = GetAdditionalBodyParameters(_userFicParameters.Assertion);
MsalTokenResponse msalTokenResponse = await SendTokenRequestAsync(additionalBodyParameters, cancellationToken).ConfigureAwait(false);

return await CacheTokenResponseAndCreateAuthenticationResultAsync(msalTokenResponse, cancellationToken).ConfigureAwait(false);
}

private Dictionary<string, string> GetAdditionalBodyParameters(string assertion)
{
var dict = new Dictionary<string, string>
{
[OAuth2Parameter.GrantType] = OAuth2GrantType.UserFic,
[OAuth2Parameter.Username] = _userFicParameters.Username,
[OAuth2Parameter.UserFederatedIdentityCredential] = assertion
};

ISet<string> unionScope = new HashSet<string>
{
OAuth2Value.ScopeOpenId,
OAuth2Value.ScopeOfflineAccess,
OAuth2Value.ScopeProfile
};

unionScope.UnionWith(AuthenticationRequestParameters.Scope);
dict[OAuth2Parameter.Scope] = unionScope.AsSingleString();
dict[OAuth2Parameter.ClientInfo] = "1";

return dict;
}

protected override KeyValuePair<string, string>? GetCcsHeader(IDictionary<string, string> additionalBodyParameters)
{
return GetCcsUpnHeader(_userFicParameters.Username);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ internal static class OAuth2Parameter
public const string SpaCode = "return_spa_code"; // not a standard OAuth2 param
public const string FmiPath = "fmi_path"; // not a standard OAuth2 param
public const string Attributes = "attributes"; // not a standard OAuth2 param
public const string UserFederatedIdentityCredential = "user_federated_identity_credential"; // user_fic grant type parameter
}

internal static class OAuth2GrantType
Expand All @@ -58,6 +59,7 @@ internal static class OAuth2GrantType
public const string JwtBearer = "urn:ietf:params:oauth:grant-type:jwt-bearer";
public const string Password = "password";
public const string DeviceCode = "device_code";
public const string UserFic = "user_fic";
}

internal static class OAuth2ResponseType
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder.WithForceRefresh(bool forceRefresh) -> Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder.WithSendX5C(bool withSendX5C) -> Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Microsoft.Identity.Client.IByUserFederatedIdentityCredential
Microsoft.Identity.Client.IByUserFederatedIdentityCredential.AcquireTokenByUserFederatedIdentityCredential(System.Collections.Generic.IEnumerable<string> scopes, string username, string assertion) -> Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder.WithForceRefresh(bool forceRefresh) -> Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder.WithSendX5C(bool withSendX5C) -> Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Microsoft.Identity.Client.IByUserFederatedIdentityCredential
Microsoft.Identity.Client.IByUserFederatedIdentityCredential.AcquireTokenByUserFederatedIdentityCredential(System.Collections.Generic.IEnumerable<string> scopes, string username, string assertion) -> Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder.WithForceRefresh(bool forceRefresh) -> Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder.WithSendX5C(bool withSendX5C) -> Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Microsoft.Identity.Client.IByUserFederatedIdentityCredential
Microsoft.Identity.Client.IByUserFederatedIdentityCredential.AcquireTokenByUserFederatedIdentityCredential(System.Collections.Generic.IEnumerable<string> scopes, string username, string assertion) -> Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder.WithForceRefresh(bool forceRefresh) -> Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder.WithSendX5C(bool withSendX5C) -> Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Microsoft.Identity.Client.IByUserFederatedIdentityCredential
Microsoft.Identity.Client.IByUserFederatedIdentityCredential.AcquireTokenByUserFederatedIdentityCredential(System.Collections.Generic.IEnumerable<string> scopes, string username, string assertion) -> Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder.WithForceRefresh(bool forceRefresh) -> Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder.WithSendX5C(bool withSendX5C) -> Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Microsoft.Identity.Client.IByUserFederatedIdentityCredential
Microsoft.Identity.Client.IByUserFederatedIdentityCredential.AcquireTokenByUserFederatedIdentityCredential(System.Collections.Generic.IEnumerable<string> scopes, string username, string assertion) -> Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder.WithForceRefresh(bool forceRefresh) -> Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder.WithSendX5C(bool withSendX5C) -> Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Microsoft.Identity.Client.IByUserFederatedIdentityCredential
Microsoft.Identity.Client.IByUserFederatedIdentityCredential.AcquireTokenByUserFederatedIdentityCredential(System.Collections.Generic.IEnumerable<string> scopes, string username, string assertion) -> Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ public enum ApiIds
InitiateLongRunningObo = 1017,
AcquireTokenInLongRunningObo = 1018,

// UserFIC
AcquireTokenByUserFederatedIdentityCredential = 1019,

// "2002" is reserved for 1p OTEL signal that telemetry is disabled
}

Expand Down
Loading
Loading