Skip to content

Commit 0b7519d

Browse files
authored
Adding sendX5C api to the acquire token by refresh token and auth code flows for confidential clients to enable SN+I. (#1492)
1 parent 92adf73 commit 0b7519d

File tree

7 files changed

+124
-0
lines changed

7 files changed

+124
-0
lines changed

src/client/Microsoft.Identity.Client/ApiConfig/AcquireTokenByAuthorizationCodeParameterBuilder.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,24 @@ internal override Task<AuthenticationResult> ExecuteInternalAsync(CancellationTo
6666
{
6767
return ConfidentialClientApplicationExecutor.ExecuteAsync(CommonParameters, Parameters, cancellationToken);
6868
}
69+
70+
/// <summary>
71+
/// Specifies if the x5c claim (public key of the certificate) should be sent to the STS.
72+
/// Sending the x5c enables application developers to achieve easy certificate roll-over in Azure AD:
73+
/// this method will send the public certificate to Azure AD along with the token request,
74+
/// so that Azure AD can use it to validate the subject name based on a trusted issuer policy.
75+
/// This saves the application admin from the need to explicitly manage the certificate rollover
76+
/// (either via portal or powershell/CLI operation)
77+
/// </summary>
78+
/// <param name="withSendX5C"><c>true</c> if the x5c should be sent. Otherwise <c>false</c>.
79+
/// The default is <c>false</c></param>
80+
/// <returns>The builder to chain the .With methods</returns>
81+
public AcquireTokenByAuthorizationCodeParameterBuilder WithSendX5C(bool withSendX5C)
82+
{
83+
CommonParameters.AddApiTelemetryFeature(ApiTelemetryFeature.WithSendX5C);
84+
Parameters.SendX5C = withSendX5C;
85+
return this;
86+
}
6987
}
7088
#endif
7189
}

src/client/Microsoft.Identity.Client/ApiConfig/AcquireTokenByRefreshTokenParameterBuilder.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,26 @@ internal override ApiEvent.ApiIds CalculateApiEventId()
5353
{
5454
return ApiEvent.ApiIds.AcquireTokenByRefreshToken;
5555
}
56+
57+
#if !ANDROID_BUILDTIME && !iOS_BUILDTIME && !WINDOWS_APP_BUILDTIME && !MAC_BUILDTIME // Hide on mobile platforms
58+
59+
/// <summary>
60+
/// Specifies if the x5c claim (public key of the certificate) should be sent to the STS.
61+
/// Sending the x5c enables application developers to achieve easy certificate roll-over in Azure AD:
62+
/// this method will send the public certificate to Azure AD along with the token request,
63+
/// so that Azure AD can use it to validate the subject name based on a trusted issuer policy.
64+
/// This saves the application admin from the need to explicitly manage the certificate rollover
65+
/// (either via portal or powershell/CLI operation)
66+
/// </summary>
67+
/// <param name="withSendX5C"><c>true</c> if the x5c should be sent. Otherwise <c>false</c>.
68+
/// The default is <c>false</c></param>
69+
/// <returns>The builder to chain the .With methods</returns>
70+
public AcquireTokenByRefreshTokenParameterBuilder WithSendX5C(bool withSendX5C)
71+
{
72+
CommonParameters.AddApiTelemetryFeature(ApiTelemetryFeature.WithSendX5C);
73+
Parameters.SendX5C = withSendX5C;
74+
return this;
75+
}
76+
#endif
5677
}
5778
}

src/client/Microsoft.Identity.Client/ApiConfig/Executors/ClientApplicationBaseExecutor.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ public async Task<AuthenticationResult> ExecuteAsync(
6464

6565
requestContext.Logger.Info(LogMessages.UsingXScopesForRefreshTokenRequest(commonParameters.Scopes.Count()));
6666

67+
requestParameters.SendX5C = refreshTokenParameters.SendX5C;
68+
6769
var handler = new ByRefreshTokenRequest(ServiceBundle, requestParameters, refreshTokenParameters);
6870
return await handler.RunAsync(CancellationToken.None).ConfigureAwait(false);
6971
}

src/client/Microsoft.Identity.Client/ApiConfig/Executors/ConfidentialClientExecutor.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public async Task<AuthenticationResult> ExecuteAsync(
3333
commonParameters,
3434
requestContext,
3535
_confidentialClientApplication.UserTokenCacheInternal);
36+
requestParams.SendX5C = authorizationCodeParameters.SendX5C;
3637

3738
var handler = new AuthorizationCodeRequest(
3839
ServiceBundle,

src/client/Microsoft.Identity.Client/ApiConfig/Parameters/AcquireTokenByAuthorizationCodeParameters.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ internal class AcquireTokenByAuthorizationCodeParameters : IAcquireTokenParamete
1010
{
1111
public string AuthorizationCode { get; set; }
1212

13+
public bool SendX5C { get; set; }
14+
1315
public void LogParameters(ICoreLogger logger)
1416
{
1517
}

src/client/Microsoft.Identity.Client/ApiConfig/Parameters/AcquireTokenByRefreshTokenParameters.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ internal class AcquireTokenByRefreshTokenParameters : IAcquireTokenParameters
1010
{
1111
public string RefreshToken { get; set; }
1212

13+
public bool SendX5C { get; set; }
14+
1315
public void LogParameters(ICoreLogger logger)
1416
{
1517
}

tests/Microsoft.Identity.Test.Unit.net45/PublicApiTests/ClientCredentialWithCertTest.cs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,84 @@ public async Task JsonWebTokenWithX509PublicCertSendCertificateOnBehalfOfTestAsy
189189
}
190190
}
191191

192+
[TestMethod]
193+
[Description("Test for client assertion with X509 public certificate using Auth code")]
194+
public async Task JsonWebTokenWithX509PublicCertSendCertificateByAuthCodeTestAsync()
195+
{
196+
using (var harness = CreateTestHarness())
197+
{
198+
SetupMocks(harness.HttpManager);
199+
200+
var certificate = new X509Certificate2(
201+
ResourceHelper.GetTestResourceRelativePath("valid_cert.pfx"),
202+
TestConstants.DefaultPassword);
203+
204+
var app = ConfidentialClientApplicationBuilder
205+
.Create(TestConstants.ClientId)
206+
.WithAuthority(new System.Uri(ClientApplicationBase.DefaultAuthority), true)
207+
.WithRedirectUri(TestConstants.RedirectUri)
208+
.WithHttpManager(harness.HttpManager)
209+
.WithCertificate(certificate)
210+
.BuildConcrete();
211+
212+
var appCacheAccess = app.AppTokenCache.RecordAccess();
213+
var userCacheAccess = app.UserTokenCache.RecordAccess();
214+
215+
var userAssertion = new UserAssertion(TestConstants.DefaultAccessToken);
216+
217+
//Check for x5c claim
218+
harness.HttpManager.AddMockHandler(CreateTokenResponseHttpHandlerWithX5CValidation(false));
219+
AuthenticationResult result = await app
220+
.AcquireTokenByAuthorizationCode(TestConstants.s_scope, TestConstants.DefaultAuthorizationCode)
221+
.WithSendX5C(true)
222+
.ExecuteAsync(CancellationToken.None)
223+
.ConfigureAwait(false);
224+
Assert.IsNotNull(result.AccessToken);
225+
226+
appCacheAccess.AssertAccessCounts(0, 0);
227+
userCacheAccess.AssertAccessCounts(0, 1);
228+
}
229+
}
230+
231+
[TestMethod]
232+
[Description("Test for client assertion with X509 public certificate using acquire token by refresh token")]
233+
public async Task JsonWebTokenWithX509PublicCertSendCertificateByRefreshTokenTestAsync()
234+
{
235+
using (var harness = CreateTestHarness())
236+
{
237+
SetupMocks(harness.HttpManager);
238+
239+
var certificate = new X509Certificate2(
240+
ResourceHelper.GetTestResourceRelativePath("valid_cert.pfx"),
241+
TestConstants.DefaultPassword);
242+
243+
var app = ConfidentialClientApplicationBuilder
244+
.Create(TestConstants.ClientId)
245+
.WithAuthority(new System.Uri(ClientApplicationBase.DefaultAuthority), true)
246+
.WithRedirectUri(TestConstants.RedirectUri)
247+
.WithHttpManager(harness.HttpManager)
248+
.WithCertificate(certificate)
249+
.BuildConcrete();
250+
251+
var appCacheAccess = app.AppTokenCache.RecordAccess();
252+
var userCacheAccess = app.UserTokenCache.RecordAccess();
253+
254+
var userAssertion = new UserAssertion(TestConstants.DefaultAccessToken);
255+
256+
//Check for x5c claim
257+
harness.HttpManager.AddMockHandler(CreateTokenResponseHttpHandlerWithX5CValidation(false));
258+
AuthenticationResult result = await ((IByRefreshToken)app)
259+
.AcquireTokenByRefreshToken(TestConstants.s_scope, TestConstants.DefaultAuthorizationCode)
260+
.WithSendX5C(true)
261+
.ExecuteAsync(CancellationToken.None)
262+
.ConfigureAwait(false);
263+
Assert.IsNotNull(result.AccessToken);
264+
265+
appCacheAccess.AssertAccessCounts(0, 0);
266+
userCacheAccess.AssertAccessCounts(0, 1);
267+
}
268+
}
269+
192270
[TestMethod]
193271
[Description("Test for acqureTokenSilent with X509 public certificate using sendCertificate")]
194272
public async Task JsonWebTokenWithX509PublicCertSendCertificateSilentTestAsync()

0 commit comments

Comments
 (0)