Skip to content

Commit

Permalink
Add support for additional claims in DPoP proof payload
Browse files Browse the repository at this point in the history
  • Loading branch information
Gjermund Stensrud committed Dec 6, 2024
1 parent 473cf2c commit 5c427a8
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ protected virtual async Task<bool> SetDPoPProofTokenAsync(HttpRequestMessage req

if (!string.IsNullOrEmpty(token.DPoPJsonWebKey))
{
request.Options.TryGetValue(new HttpRequestOptionsKey<IReadOnlyDictionary<string, string>>("Duende.AccessTokenManagement.DPoPProofAdditionalPayloadClaims"), out var additionalClaims);

// create proof
var proofToken = await _dPoPProofService.CreateProofTokenAsync(new DPoPProofRequest
{
Expand All @@ -135,6 +137,7 @@ protected virtual async Task<bool> SetDPoPProofTokenAsync(HttpRequestMessage req
Method = request.Method.ToString(),
DPoPJsonWebKey = token.DPoPJsonWebKey,
DPoPNonce = dpopNonce,
AdditionalPayloadClaims = additionalClaims,
});

if (proofToken != null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,14 @@ public static string GetDPoPUrl(this HttpRequestMessage request)
{
return request.RequestUri!.Scheme + "://" + request.RequestUri!.Authority + request.RequestUri!.LocalPath;
}

/// <summary>
/// Additional claims that will be added to the DPoP proof payload on generation
/// </summary>
/// <param name="request"></param>
/// <param name="customClaims"></param>
public static void AddDPoPProofAdditionalPayloadClaims(this HttpRequestMessage request, IDictionary<string, string> customClaims)
{
request.Options.TryAdd("Duende.AccessTokenManagement.DPoPProofAdditionalPayloadClaims", customClaims.AsReadOnly());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,14 @@ await _dPoPNonceStore.StoreNonceAsync(new DPoPNonceContext
payload.Add(JwtClaimTypes.Nonce, nonce);
}

if (request.AdditionalPayloadClaims?.Count > 0)
{
foreach (var claim in request.AdditionalPayloadClaims)
{
payload.Add(claim.Key, claim.Value);
}
}

var handler = new JsonWebTokenHandler() { SetDefaultTimesOnTokenCreation = false };
var key = new SigningCredentials(jsonWebKey, jsonWebKey.Alg);
var proofToken = handler.CreateToken(JsonSerializer.Serialize(payload), key, header);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ public class DPoPProofRequest
/// The access token
/// </summary>
public string? AccessToken { get; set; }

/// <summary>
/// Additional claims to add to the DPoP proof payload
/// </summary>
public IReadOnlyDictionary<string, string>? AdditionalPayloadClaims { get; set; }
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,32 @@ public async Task dpop_tokens_should_be_passed_to_api()
proofToken.ShouldNotBeNull();
}

[Fact]
public async Task when_additional_proof_payload_claims_are_defined_they_should_be_included_in_dpop_proof()
{
string? proofToken = null;

ApiHost.ApiInvoked += ctx =>
{
proofToken = ctx.Request.Headers["DPoP"].FirstOrDefault()?.ToString();
};
var client = _clientFactory.CreateClient("test");

var requestMessage = new HttpRequestMessage(HttpMethod.Get, ApiHost.Url("/test"));
requestMessage.AddDPoPProofAdditionalPayloadClaims(new Dictionary<string, string>() {
{ "claim_one", "one" },
{ "claim_two", "two" },
});

var apiResult = await client.SendAsync(requestMessage);

proofToken.ShouldNotBeNull();
var payload = Base64UrlEncoder.Decode(proofToken!.Split('.')[1]);
var values = JsonSerializer.Deserialize<Dictionary<string, object>>(payload);
values!["claim_one"].ToString().ShouldBe("one");
values!["claim_two"].ToString().ShouldBe("two");
}

[Fact]
public async Task when_api_issues_nonce_api_request_should_be_retried_with_new_nonce()
{
Expand Down

0 comments on commit 5c427a8

Please sign in to comment.