Skip to content

Commit

Permalink
Merge pull request #217 from DuendeSoftware/joe/dpop-cnf
Browse files Browse the repository at this point in the history
  • Loading branch information
josephdecock authored Oct 3, 2024
2 parents ef083f6 + 038594e commit 960fb64
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 31 deletions.
13 changes: 0 additions & 13 deletions IdentityServer/v6/DPoP/Api/DPoP/DPoPExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,19 +57,6 @@ public static void SetDPoPNonce(this AuthenticationProperties props, string nonc
props.Items["DPoP-Nonce"] = nonce;
}

/// <summary>
/// Create the value of a thumbprint-based cnf claim
/// </summary>
public static string CreateThumbprintCnf(this JsonWebKey jwk)
{
var jkt = jwk.CreateThumbprint();
var values = new Dictionary<string, string>
{
{ JwtClaimTypes.ConfirmationMethods.JwkThumbprint, jkt }
};
return JsonSerializer.Serialize(values);
}

/// <summary>
/// Create the value of a thumbprint
/// </summary>
Expand Down
6 changes: 6 additions & 0 deletions IdentityServer/v6/DPoP/Api/DPoP/DPoPProofValidatonContext.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;

namespace ApiHost;
Expand Down Expand Up @@ -29,4 +30,9 @@ public class DPoPProofValidatonContext
/// The access token
/// </summary>
public string AccessToken { get; set; }

/// <summary>
/// The claims associated with the access token.
/// </summary>
public IEnumerable<Claim> AccessTokenClaims { get; set; } = Enumerable.Empty<Claim>();
}
30 changes: 28 additions & 2 deletions IdentityServer/v6/DPoP/Api/DPoP/DPoPProofValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,10 @@ public async Task<DPoPProofValidatonResult> ValidateAsync(DPoPProofValidatonCont
protected virtual Task ValidateHeaderAsync(DPoPProofValidatonContext context, DPoPProofValidatonResult result)
{
JsonWebToken token;
var handler = new JsonWebTokenHandler();

try
{
var handler = new JsonWebTokenHandler();
token = handler.ReadJsonWebToken(context.ProofToken);
}
catch (Exception ex)
Expand Down Expand Up @@ -161,7 +161,33 @@ protected virtual Task ValidateHeaderAsync(DPoPProofValidatonContext context, DP

result.JsonWebKey = jwkJson;
result.JsonWebKeyThumbprint = jwk.CreateThumbprint();
result.Confirmation = jwk.CreateThumbprintCnf();

var accessToken = handler.ReadJsonWebToken(context.AccessToken);
var cnf = accessToken.Claims.FirstOrDefault(c => c.Type == JwtClaimTypes.Confirmation);
if (cnf == null)
{
result.IsError = true;
result.ErrorDescription = "Missing 'cnf' value.";
return Task.CompletedTask;
}
var json = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(cnf.Value);
if (json == null)
{
result.IsError = true;
result.ErrorDescription = "Invalid 'cnf' value.";
return Task.CompletedTask;
}
if (json.TryGetValue(JwtClaimTypes.ConfirmationMethods.JwkThumbprint, out var jktJson))
{
var accessTokenJkt = jktJson.ToString();
if (accessTokenJkt != result.JsonWebKeyThumbprint)
{
result.IsError = true;
result.ErrorDescription = "Invalid 'cnf' value.";
return Task.CompletedTask;
}
result.Confirmation = cnf.Value;
}

return Task.CompletedTask;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Duende.IdentityServer" Version="6.3.0-rc.1" />
<PackageReference Include="Duende.IdentityServer" Version="6.3.10" />
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />
</ItemGroup>

Expand Down
13 changes: 0 additions & 13 deletions IdentityServer/v7/DPoP/Api/DPoP/DPoPExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,19 +55,6 @@ public static void SetDPoPNonce(this AuthenticationProperties props, string nonc
props.Items["DPoP-Nonce"] = nonce;
}

/// <summary>
/// Create the value of a thumbprint-based cnf claim
/// </summary>
public static string CreateThumbprintCnf(this JsonWebKey jwk)
{
var jkt = jwk.CreateThumbprint();
var values = new Dictionary<string, string>
{
{ JwtClaimTypes.ConfirmationMethods.JwkThumbprint, jkt }
};
return JsonSerializer.Serialize(values);
}

/// <summary>
/// Create the value of a thumbprint
/// </summary>
Expand Down
8 changes: 8 additions & 0 deletions IdentityServer/v7/DPoP/Api/DPoP/DPoPProofValidatonContext.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Security.Claims;

namespace Api;

public class DPoPProofValidatonContext
Expand Down Expand Up @@ -26,4 +28,10 @@ public class DPoPProofValidatonContext
/// The access token
/// </summary>
public required string AccessToken { get; set; }

/// <summary>
/// The claims associated with the access token.
/// </summary>
public IEnumerable<Claim> AccessTokenClaims { get; set; } = Enumerable.Empty<Claim>();

}
30 changes: 28 additions & 2 deletions IdentityServer/v7/DPoP/Api/DPoP/DPoPProofValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,10 @@ public async Task<DPoPProofValidatonResult> ValidateAsync(DPoPProofValidatonCont
protected virtual Task ValidateHeaderAsync(DPoPProofValidatonContext context, DPoPProofValidatonResult result)
{
JsonWebToken token;
var handler = new JsonWebTokenHandler();

try
{
var handler = new JsonWebTokenHandler();
token = handler.ReadJsonWebToken(context.ProofToken);
}
catch (Exception ex)
Expand Down Expand Up @@ -156,7 +156,33 @@ protected virtual Task ValidateHeaderAsync(DPoPProofValidatonContext context, DP

result.JsonWebKey = jwkJson;
result.JsonWebKeyThumbprint = jwk.CreateThumbprint();
result.Confirmation = jwk.CreateThumbprintCnf();

var accessToken = handler.ReadJsonWebToken(context.AccessToken);
var cnf = accessToken.Claims.FirstOrDefault(c => c.Type == JwtClaimTypes.Confirmation);
if (cnf == null)
{
result.IsError = true;
result.ErrorDescription = "Missing 'cnf' value.";
return Task.CompletedTask;
}
var json = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(cnf.Value);
if (json == null)
{
result.IsError = true;
result.ErrorDescription = "Invalid 'cnf' value.";
return Task.CompletedTask;
}
if (json.TryGetValue(JwtClaimTypes.ConfirmationMethods.JwkThumbprint, out var jktJson))
{
var accessTokenJkt = jktJson.ToString();
if (accessTokenJkt != result.JsonWebKeyThumbprint)
{
result.IsError = true;
result.ErrorDescription = "Invalid 'cnf' value.";
return Task.CompletedTask;
}
result.Confirmation = cnf.Value;
}

return Task.CompletedTask;
}
Expand Down

0 comments on commit 960fb64

Please sign in to comment.