Skip to content
This repository was archived by the owner on Sep 8, 2025. It is now read-only.

Commit dced1ba

Browse files
committed
Set client credential style to PostBody only if client assertion is set
Client secrets are not recommended in the post body by RFC 6749. We should use post body (at least by default) only for client assertions. Also added tests of this behavior.
1 parent ddddd7d commit dced1ba

File tree

2 files changed

+83
-1
lines changed

2 files changed

+83
-1
lines changed

src/OidcClient/AuthorizeClient.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,15 @@ private async Task<PushedAuthorizationResponse> PushAuthorizationRequestAsync(st
141141
ClientSecret = _options.ClientSecret,
142142
ClientAssertion = await _options.GetClientAssertionAsync(),
143143

144-
ClientCredentialStyle = ClientCredentialStyle.PostBody,
145144

146145
Parameters = CreateAuthorizeParameters(state, codeChallenge, frontChannelParameters),
147146
};
148147

148+
if(par.ClientAssertion?.Value != null)
149+
{
150+
par.ClientCredentialStyle = ClientCredentialStyle.PostBody;
151+
}
152+
149153
return await http.PushAuthorizationAsync(par);
150154
}
151155

test/OidcClient.Tests/CodeFlowResponseTests.cs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using System.Threading.Tasks;
1717
using IdentityModel.Client;
1818
using Xunit;
19+
using System.Web;
1920

2021
namespace IdentityModel.OidcClient.Tests
2122
{
@@ -486,5 +487,82 @@ public async Task Malformed_identity_token_on_token_response_should_fail()
486487
result.IsError.Should().BeTrue();
487488
result.Error.Should().Contain("invalid_jwt");
488489
}
490+
491+
[Fact]
492+
public async Task Authorize_should_push_parameters_when_PAR_is_enabled()
493+
{
494+
// Configure the client for PAR, authenticating with a client secret
495+
_options.ClientSecret = "secret";
496+
_options.ProviderInformation.PushedAuthorizationRequestEndpoint = "https://this-is-set-so-par-will-be-used";
497+
var client = new OidcClient(_options);
498+
499+
// Mock the response from the par endpoint
500+
var requestUri = "mocked_request_uri";
501+
var parResponse = new Dictionary<string, string>
502+
{
503+
{ "request_uri", requestUri }
504+
};
505+
var backChannelHandler = new NetworkHandler(JsonSerializer.Serialize(parResponse), HttpStatusCode.OK);
506+
_options.BackchannelHandler = backChannelHandler;
507+
508+
// Prepare the login to cause the backchannel PAR request
509+
var state = await client.PrepareLoginAsync();
510+
511+
// Validate that the resulting PAR state is correct
512+
var startUrl = new Uri(state.StartUrl);
513+
var startUrlQueryParams = HttpUtility.ParseQueryString(startUrl.Query);
514+
startUrlQueryParams.Should().HaveCount(2);
515+
startUrlQueryParams.GetValues("client_id").Single().Should().Be("client");
516+
startUrlQueryParams.GetValues("request_uri").Single().Should().Be(requestUri);
517+
518+
// Validate that the client authentication during the PAR request was correct
519+
var request = backChannelHandler.Request;
520+
request.Headers.Authorization.Should().NotBeNull();
521+
request.Headers.Authorization.Scheme.Should().Be("Basic");
522+
request.Headers.Authorization.Parameter.Should()
523+
.Be(BasicAuthenticationOAuthHeaderValue.EncodeCredential("client", "secret"));
524+
}
525+
526+
[Fact]
527+
public async Task Par_request_should_include_client_assertion_in_body()
528+
{
529+
// Configure the client for PAR, authenticating with a client assertion
530+
var clientAssertion = "mocked_client_assertion";
531+
var clientAssertionType = "mocked_assertion_type";
532+
_options.ClientAssertion = new ClientAssertion
533+
{
534+
Type = clientAssertionType,
535+
Value = clientAssertion
536+
};
537+
_options.ProviderInformation.PushedAuthorizationRequestEndpoint = "https://this-is-set-so-par-will-be-used";
538+
var client = new OidcClient(_options);
539+
540+
// Mock the response from the par endpoint
541+
var requestUri = "mocked_request_uri";
542+
var parResponse = new Dictionary<string, string>
543+
{
544+
{ "request_uri", requestUri }
545+
};
546+
var backChannelHandler = new NetworkHandler(JsonSerializer.Serialize(parResponse), HttpStatusCode.OK);
547+
_options.BackchannelHandler = backChannelHandler;
548+
549+
// Prepare the login to cause the backchannel PAR request
550+
var state = await client.PrepareLoginAsync();
551+
552+
// Validate that the resulting PAR state is correct
553+
var startUrl = new Uri(state.StartUrl);
554+
var startUrlQueryParams = HttpUtility.ParseQueryString(startUrl.Query);
555+
startUrlQueryParams.Should().HaveCount(2);
556+
startUrlQueryParams.GetValues("client_id").Single().Should().Be("client");
557+
startUrlQueryParams.GetValues("request_uri").Single().Should().Be(requestUri);
558+
559+
// Validate that the client authentication during the PAR request was correct
560+
var parRequest = backChannelHandler.Request;
561+
var parContent = await parRequest.Content.ReadAsStringAsync();
562+
var parParams = HttpUtility.ParseQueryString(parContent);
563+
parParams.GetValues("client_assertion").Single().Should().Be(clientAssertion);
564+
parParams.GetValues("client_assertion_type").Single().Should().Be(clientAssertionType);
565+
parRequest.Headers.Authorization.Should().BeNull();
566+
}
489567
}
490568
}

0 commit comments

Comments
 (0)