Skip to content

Commit

Permalink
Merge pull request #45 from dlcs/feature/support_hs256
Browse files Browse the repository at this point in the history
Support HS256 signed keys
  • Loading branch information
donaldgray authored Aug 13, 2024
2 parents 15f493d + 8bc36e6 commit 7cf6d18
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,7 @@ public async Task GetDlcsRolesForCode_PassesIdTokenToTokenHandler()

// Assert
A.CallTo(() => jwtTokenHandler.GetClaimsFromToken(idToken, jwksUri, "https://dlcs-dev.uk.auth0.com/", "test-id",
A<CancellationToken>._))
.MustHaveHappened();
"test-secret", A<CancellationToken>._)).MustHaveHappened();
}

[Fact]
Expand All @@ -172,7 +171,7 @@ public async Task GetDlcsRolesForCode_ReturnsEmptyRoles_IfNoMappedClaims()
ClaimType = claimType,
};
A.CallTo(() => jwtTokenHandler.GetClaimsFromToken(idToken, A<Uri>._, A<string>._, A<string>._,
A<CancellationToken>._))
A<string>._, A<CancellationToken>._))
.Returns(new ClaimsPrincipal(new ClaimsIdentity()));

var httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK);
Expand Down Expand Up @@ -207,7 +206,7 @@ public async Task GetDlcsRolesForCode_ReturnsMappedClaims()
}
};
A.CallTo(() => jwtTokenHandler.GetClaimsFromToken(idToken, A<Uri>._, A<string>._, A<string>._,
A<CancellationToken>._))
A<string>._, A<CancellationToken>._))
.Returns(new ClaimsPrincipal(new ClaimsIdentity(new []{new Claim(claimType, "foobar")})));

var httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public async Task<IReadOnlyCollection<string>> GetDlcsRolesForCode(OidcConfigura

var claimsPrincipal =
await jwtTokenHandler.GetClaimsFromToken(auth0Token.IdToken, GetJwksUri(oidcConfiguration),
issuer, audience, cancellationToken);
issuer, audience, oidcConfiguration.ClientSecret, cancellationToken);
if (claimsPrincipal == null) return Array.Empty<string>();

var dlcsRoles = claimsConverter.GetDlcsRolesFromClaims(claimsPrincipal, oidcConfiguration);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using IIIFAuth2.API.Settings;
using LazyCache;
using Microsoft.Extensions.Caching.Memory;
Expand All @@ -17,10 +18,11 @@ public interface IJwtTokenHandler
/// <param name="jwksUri">Path where jwks can be found</param>
/// <param name="issuer">Valid "iss" value</param>
/// <param name="audience">Valid "aud" value</param>
/// <param name="clientSecret">ClientSecret, if known. Used for symmetric validation</param>
/// <param name="cancellationToken">Current cancellation token</param>
/// <returns><see cref="ClaimsPrincipal"/> if jwt is valid, else null</returns>
Task<ClaimsPrincipal?> GetClaimsFromToken(string jwtToken, Uri jwksUri, string issuer, string audience,
CancellationToken cancellationToken);
string? clientSecret, CancellationToken cancellationToken);
}

public class JwtTokenHandler : IJwtTokenHandler
Expand All @@ -41,17 +43,16 @@ public JwtTokenHandler(HttpClient httpClient, IAppCache appCache, IOptions<AuthS

/// <inheritdoc />
public async Task<ClaimsPrincipal?> GetClaimsFromToken(string jwtToken, Uri jwksUri, string issuer,
string audience, CancellationToken cancellationToken)
string audience, string? clientSecret, CancellationToken cancellationToken)
{
try
{
var jwks = await GetWebKeySetForDomain(jwksUri, cancellationToken);

var issuerSigningKeys = await GetSigningKeys(jwksUri, clientSecret, cancellationToken);
var tokenHandler = new JwtSecurityTokenHandler();
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKeys = jwks.GetSigningKeys(),
IssuerSigningKeys = issuerSigningKeys,
ValidateIssuer = true,
ValidIssuer = issuer,
ValidateAudience = true,
Expand All @@ -75,6 +76,22 @@ public JwtTokenHandler(HttpClient httpClient, IAppCache appCache, IOptions<AuthS
return null;
}

private async Task<IList<SecurityKey>> GetSigningKeys(Uri jwksUri, string? clientSecret, CancellationToken
cancellationToken)
{
// jwks used for "alg": "RS256"
var jwks = await GetWebKeySetForDomain(jwksUri, cancellationToken);
var issuerSigningKeys = jwks.GetSigningKeys();

if (!string.IsNullOrWhiteSpace(clientSecret))
{
// client-secret for "alg": "HS256"
issuerSigningKeys.Add(new SymmetricSecurityKey(Encoding.ASCII.GetBytes(clientSecret)));
}

return issuerSigningKeys;
}

private async Task<JsonWebKeySet> GetWebKeySetForDomain(Uri jwksPath, CancellationToken cancellationToken)
{
var cacheKey = $"{jwksPath}:jwks";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ private async Task EnsureSecrets(OidcConfiguration configuration, Guid accessSer
var secretsJson = await secretsManagerCache.GetSecretString(secretName);
var secret = JsonSerializer.Deserialize<Secrets>(secretsJson, Options);
configuration.ClientSecret = secret?.ClientSecret ?? string.Empty;

if (string.IsNullOrWhiteSpace(configuration.ClientSecret))
{
logger.LogWarning("Fetched secretsmanager secret {SecretName} for {AccessService} but got no value",
secretName, accessServiceId);
}
}
catch (Exception ex)
{
Expand Down

0 comments on commit 7cf6d18

Please sign in to comment.