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
Expand Up @@ -33,6 +33,16 @@ internal AcquireTokenByUserFederatedIdentityCredentialParameterBuilder(
Parameters.Assertion = assertion;
}

internal AcquireTokenByUserFederatedIdentityCredentialParameterBuilder(
IConfidentialClientApplicationExecutor confidentialClientApplicationExecutor,
Guid userObjectId,
string assertion)
: base(confidentialClientApplicationExecutor)
{
Parameters.UserObjectId = userObjectId;
Parameters.Assertion = assertion;
}

internal static AcquireTokenByUserFederatedIdentityCredentialParameterBuilder Create(
IConfidentialClientApplicationExecutor confidentialClientApplicationExecutor,
IEnumerable<string> scopes,
Expand All @@ -54,6 +64,27 @@ internal static AcquireTokenByUserFederatedIdentityCredentialParameterBuilder Cr
.WithScopes(scopes);
}

internal static AcquireTokenByUserFederatedIdentityCredentialParameterBuilder Create(
IConfidentialClientApplicationExecutor confidentialClientApplicationExecutor,
IEnumerable<string> scopes,
Guid userObjectId,
string assertion)
{
if (userObjectId == Guid.Empty)
{
throw new ArgumentException("userObjectId must not be empty.", nameof(userObjectId));
}

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

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

/// <summary>
/// Forces MSAL to refresh the token from the identity provider even if a cached token is available.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,47 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Text;
using Microsoft.Identity.Client.Core;

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

/// <inheritdoc/>
public void LogParameters(ILoggerAdapter logger)
{
if (logger.IsLoggingEnabled(LogLevel.Info))
{
// PII-enabled message: includes actual Username and UserObjectId values
var builder = new StringBuilder();
builder.AppendLine("=== AcquireTokenByUserFederatedIdentityCredentialParameters ===");
builder.AppendLine("SendX5C: " + SendX5C);
builder.AppendLine("ForceRefresh: " + ForceRefresh);
builder.AppendLine("Username: " + Username);
builder.AppendLine("UserObjectId: " + UserObjectId);
builder.AppendLine("Assertion set: " + !string.IsNullOrEmpty(Assertion));

string messageWithPii = builder.ToString();

// Non-PII message: redacts Username and UserObjectId to booleans
builder = new StringBuilder();
builder.AppendLine("=== AcquireTokenByUserFederatedIdentityCredentialParameters ===");
builder.AppendLine("SendX5C: " + SendX5C);
builder.AppendLine("ForceRefresh: " + ForceRefresh);
builder.AppendLine("Username set: " + !string.IsNullOrEmpty(Username));
builder.AppendLine("UserObjectId set: " + UserObjectId.HasValue);
builder.AppendLine("Assertion set: " + !string.IsNullOrEmpty(Assertion));

logger.InfoPii(messageWithPii, builder.ToString());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,19 @@ AcquireTokenByUserFederatedIdentityCredentialParameterBuilder IByUserFederatedId
assertion);
}

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

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

using System;
using System.Collections.Generic;

namespace Microsoft.Identity.Client
Expand All @@ -15,7 +16,7 @@ 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.
/// This uses the <c>user_fic</c> grant type. The user is identified by UPN.
/// </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>
Expand All @@ -28,5 +29,21 @@ AcquireTokenByUserFederatedIdentityCredentialParameterBuilder AcquireTokenByUser
IEnumerable<string> scopes,
string username,
string assertion);

/// <summary>
/// Acquires a token on behalf of a user using a federated identity credential assertion.
/// This uses the <c>user_fic</c> grant type. The user is identified by Object ID (OID).
/// </summary>
/// <param name="scopes">Scopes requested to access a protected API.</param>
/// <param name="userObjectId">The Object ID (OID) of the user in Entra ID. Must not be <see cref="Guid.Empty"/>.</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,
Guid userObjectId,
string assertion);
Comment thread
Avery-Dunn marked this conversation as resolved.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,22 @@ private Dictionary<string, string> GetAdditionalBodyParameters(string assertion)
var dict = new Dictionary<string, string>
{
[OAuth2Parameter.GrantType] = OAuth2GrantType.UserFic,
[OAuth2Parameter.Username] = _userFicParameters.Username,
[OAuth2Parameter.UserFederatedIdentityCredential] = assertion
};

// The user_fic grant identifies the user by either OID (user_id) or UPN (username).
// The parameter builder enforces that exactly one is set via separate constructors,
// so both values cannot be populated simultaneously through the public API.
// OID is checked first because it is immutable and preferred over UPN, which can be renamed.
if (_userFicParameters.UserObjectId.HasValue)
{
dict[OAuth2Parameter.UserId] = _userFicParameters.UserObjectId.Value.ToString("D");
}
else
{
dict[OAuth2Parameter.Username] = _userFicParameters.Username;
}

ISet<string> unionScope = new HashSet<string>
{
OAuth2Value.ScopeOpenId,
Expand All @@ -58,6 +70,21 @@ private Dictionary<string, string> GetAdditionalBodyParameters(string assertion)

protected override KeyValuePair<string, string>? GetCcsHeader(IDictionary<string, string> additionalBodyParameters)
{
// CCS routing hint mirrors the OID/UPN choice above — route by OID when available, UPN otherwise.
if (_userFicParameters.UserObjectId.HasValue)
{
string ccsHint = CoreHelpers.GetCcsClientInfoHint(
_userFicParameters.UserObjectId.Value.ToString("D"),
AuthenticationRequestParameters.Authority.TenantId);

if (!string.IsNullOrEmpty(ccsHint))
{
return new KeyValuePair<string, string>(Constants.CcsRoutingHintHeader, ccsHint);
}

return null;
}

return GetCcsUpnHeader(_userFicParameters.Username);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ internal static class OAuth2Parameter
public const string Attributes = "attributes"; // not a standard OAuth2 param
public const string AttributeTokens = "attribute_tokens"; // not a standard OAuth2 param
public const string UserFederatedIdentityCredential = "user_federated_identity_credential"; // user_fic grant type parameter
public const string UserId = "user_id"; // user_fic grant type parameter (OID-based user identification)
}

internal static class OAuth2GrantType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.IsMtlsPopS
Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.MaxSupportedBindingStrength.get -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.Source.get -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentityCapabilitiesAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities>
Microsoft.Identity.Client.IByUserFederatedIdentityCredential.AcquireTokenByUserFederatedIdentityCredential(System.Collections.Generic.IEnumerable<string> scopes, System.Guid userObjectId, string assertion) -> Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.IsMtlsPopS
Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.MaxSupportedBindingStrength.get -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.Source.get -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentityCapabilitiesAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities>
Microsoft.Identity.Client.IByUserFederatedIdentityCredential.AcquireTokenByUserFederatedIdentityCredential(System.Collections.Generic.IEnumerable<string> scopes, System.Guid userObjectId, string assertion) -> Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.IsMtlsPopS
Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.MaxSupportedBindingStrength.get -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.Source.get -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentityCapabilitiesAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities>
Microsoft.Identity.Client.IByUserFederatedIdentityCredential.AcquireTokenByUserFederatedIdentityCredential(System.Collections.Generic.IEnumerable<string> scopes, System.Guid userObjectId, string assertion) -> Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.IsMtlsPopS
Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.MaxSupportedBindingStrength.get -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.Source.get -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentityCapabilitiesAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities>
Microsoft.Identity.Client.IByUserFederatedIdentityCredential.AcquireTokenByUserFederatedIdentityCredential(System.Collections.Generic.IEnumerable<string> scopes, System.Guid userObjectId, string assertion) -> Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.IsMtlsPopS
Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.MaxSupportedBindingStrength.get -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.Source.get -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentityCapabilitiesAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities>
Microsoft.Identity.Client.IByUserFederatedIdentityCredential.AcquireTokenByUserFederatedIdentityCredential(System.Collections.Generic.IEnumerable<string> scopes, System.Guid userObjectId, string assertion) -> Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.IsMtlsPopS
Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.MaxSupportedBindingStrength.get -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.Source.get -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentityCapabilitiesAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities>
Microsoft.Identity.Client.IByUserFederatedIdentityCredential.AcquireTokenByUserFederatedIdentityCredential(System.Collections.Generic.IEnumerable<string> scopes, System.Guid userObjectId, string assertion) -> Microsoft.Identity.Client.AcquireTokenByUserFederatedIdentityCredentialParameterBuilder
Loading
Loading