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?
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;
}
}
}
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.