I ended up using Jack Jia's answer
var token = new JwtSecurityToken(
issuer,
appId,
claims,
signDate,
expiryDate);
var header = Base64UrlEncoder.Encode(JsonConvert.SerializeObject(new Dictionary<string, string>()
{
{ JwtHeaderParameterNames.Alg, "RS256" },
{ JwtHeaderParameterNames.Kid, "https://myvault.vault.azure.net/keys/mykey/keyid" },
{ JwtHeaderParameterNames.Typ, "JWT" }
}));
var byteData = Encoding.UTF8.GetBytes(header + "." + token.EncodedPayload);
var hasher = new SHA256CryptoServiceProvider();
var digest = hasher.ComputeHash(byteData);
var signature = await _keyVault.SignAsync("https://myvault.vault.azure.net/keys/mykey/keyid", "RS256", digest);
return $"{header}.{token.EncodedPayload}.{Base64UrlEncoder.Encode(signature.Result)}";
I found another solution, which I didn't like as much but it "integrates" better with the JWT libraries.
var token = new JwtSecurityToken(
issuer,
appId,
claims,
signDate,
expiryDate,
new SigningCredentials(new KeyVaultSecurityKey("https://myvault.vault.azure.net/keys/mykey/keyid", new KeyVaultSecurityKey.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback)), "RS256")
{
CryptoProviderFactory = new CryptoProviderFactory() { CustomCryptoProvider = new KeyVaultCryptoProvider() }
});
var handler = new JwtSecurityTokenHandler();
return handler.WriteToken(token);
Turns out that there is a library Microsoft.IdentityModel.KeyVaultExtensions
with extensions to SecurityToken
and ICryptoProvider
which support KeyVault.
My problems with it are
- I can't reuse an existing instance of
KeyVaultClient
with this solution.
- It's blocking (Behind the scenes, it calls
.GetAwaiter().GetResult()
on KeyVaultClient.SignAsync