Warm tip: This article is reproduced from stackoverflow.com, please click
azure azure-functions jwt asp.net-core-2.1

Caching public JWT authentication keys in Azure Function

发布于 2020-03-27 15:43:54

I'm having an Azure function .NET core 2.1 with various endpoints.

For Authentication I'm having an external security provider from where I need to retrieve the discovery document (metadata) before being able to validate a request's token.

The document is currently getting loaded every time a single request is made:

var discoveryDocument = await configurationManager.GetConfigurationAsync();

Now I naturally don't want to make this call for every single request due to the fact that the document changes rarely.

On the other hand Azure functions are stateless. I heard of ConfigurationManager.GetConfigurationAsync() is caching the retrieved data somehow, though I couldn't find more information on this. Currently I ended up with a function which gets triggered on startup once, retrieves the document and stores it in the local filesystem. So when a request is made, I'm reading the file again just to avoid another request for getting the public keys.

Any experience on this?

Solution:

I could solve it with a static class as @juunas suggested. For every single function I am re-using the ValidateToken() method. The call for the discovery document is made every once in a while (when IConfigurationManager thinks it needs to be refreshed) because it is getting cached automatically.

class AuthConfig
{
    static readonly IConfigurationManager<OpenIdConnectConfiguration> _configurationManager;

    static AuthConfig()
    {
        _configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(
            "<CONFIGURL>",
            new OpenIdConnectConfigurationRetriever(),
            new HttpDocumentRetriever());
    }

    public static async Task<MyUserEntity> ValidateToken(string token)
    {
        var discoveryDocument = await _configurationManager.GetConfigurationAsync(CancellationToken.None);

        var validationParameters = new TokenValidationParameters
        {
            RequireExpirationTime = true,
            RequireSignedTokens = true,
            ValidateIssuerSigningKey = true,
            IssuerSigningKeys = discoveryDocument.SigningKeys,
            ValidateLifetime = true,
            ValidateAudience = false,
            ValidateIssuer = true,
            ValidIssuer = discoveryDocument.Issuer
        };

        try
        {
            var claimsPrincipal = new JwtSecurityTokenHandler().ValidateToken(token, validationParameters, out var rawValidatedToken);
            var user = ParseMyUserEntity(claimsPrincipal);
            return user;
        }
        catch
        {
            return null;
        }
    }
}
Questioner
ddhille
Viewed
154
juunas 2020-02-01 00:12

You can store the data in a static field (which can be in a separate static class too).

That should work as an in-memory cache on that function instance at least. If the function gets scaled to multiple instances, their caches will be separated.