-
Notifications
You must be signed in to change notification settings - Fork 45
Allow ClientAssertions with PAR #340
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -59,6 +59,7 @@ public void Configure(string? name, OpenIdConnectOptions options) | |
| options.Events.OnRedirectToIdentityProvider = CreateCallback(options.Events.OnRedirectToIdentityProvider); | ||
| options.Events.OnAuthorizationCodeReceived = CreateCallback(options.Events.OnAuthorizationCodeReceived); | ||
| options.Events.OnTokenValidated = CreateCallback(options.Events.OnTokenValidated); | ||
| options.Events.OnPushAuthorization = CreateCallback(options.Events.OnPushAuthorization); | ||
|
|
||
| options.BackchannelHttpHandler = new AuthorizationServerDPoPHandler(dPoPProofService, dPoPNonceStore, httpContextAccessor, loggerFactory) | ||
| { | ||
|
|
@@ -153,4 +154,40 @@ async Task Callback(TokenValidatedContext context) | |
|
|
||
| return Callback; | ||
| } | ||
|
|
||
| private Func<PushedAuthorizationContext, Task> CreateCallback(Func<PushedAuthorizationContext, Task> inner) | ||
| { | ||
| async Task Callback(PushedAuthorizationContext context) | ||
| { | ||
| await inner.Invoke(context); | ||
|
|
||
| // --- DPoP thumbprint --- | ||
| var dPoPKeyStore = context.HttpContext.RequestServices.GetRequiredService<IDPoPKeyStore>(); | ||
| var key = await dPoPKeyStore.GetKeyAsync(ClientName); | ||
| if (key != null) | ||
| { | ||
| var jkt = dPoPProofService.GetProofKeyThumbprint(key.Value); | ||
| if (jkt != null) | ||
| { | ||
| context.Properties.SetProofKey(key.Value); | ||
| context.ProtocolMessage.Parameters[OidcConstants.AuthorizeRequest.DPoPKeyThumbprint] = | ||
| jkt.ToString(); | ||
| } | ||
| } | ||
|
Comment on lines
+164
to
+176
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we need to add this. We should double check, but the backchannel handler already sets the DPoP header value. DPoP defines two ways to bind the authorization code to the proof key. If you're not using PAR, you have to use the dpop_jkt query string parameter. Probably this is so because custom headers on a redirect can be annoying. So, the spec gives you this easy way to say "here's the thumbprint of my public key". But, if you are using PAR, the backchannel pushed authorization request can easily set headers, including the DPoP header. That header is actually a stronger binding because it conveys both the public key and proof of possession of the private key. The spec also points out that doing it this way means that the client can just always include the proof on all backchannel requests, and needs less special case logic. For your reference, from RFC 9449:
|
||
|
|
||
| // --- Client assertion --- | ||
| var assertion = await clientAssertionService | ||
| .GetClientAssertionAsync(ClientName, ct: context.HttpContext.RequestAborted) | ||
| .ConfigureAwait(false); | ||
|
|
||
| if (assertion != null) | ||
| { | ||
| context.ProtocolMessage.ClientAssertionType = assertion.Type; | ||
| context.ProtocolMessage.ClientAssertion = assertion.Value; | ||
| context.HandleClientAuthentication(); | ||
| } | ||
| } | ||
|
|
||
| return Callback; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't access token management create this proof? I think there's an http handler designed for interaction with the resource server.