Skip to content

Commit

Permalink
Update locking and caching for `OpenIdConnectCachingSecurityTokenProv…
Browse files Browse the repository at this point in the history
…ider`
  • Loading branch information
mderriey committed Nov 2, 2024
1 parent 3dc8286 commit e3ca4bd
Showing 1 changed file with 18 additions and 31 deletions.
Original file line number Diff line number Diff line change
@@ -1,30 +1,28 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Owin.Security.Jwt;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.Identity.Web
{
// This class is necessary because the OAuthBearer Middleware does not leverage
// the OpenID Connect metadata endpoint exposed by the STS by default.
internal class OpenIdConnectCachingSecurityTokenProvider : IIssuerSecurityKeyProvider
{
private int _lock = 0;

public ConfigurationManager<OpenIdConnectConfiguration> _configManager;
private string? _issuer;
private IEnumerable<SecurityKey>? _keys;
private readonly string _metadataEndpoint;

private readonly ReaderWriterLockSlim _synclock = new ReaderWriterLockSlim();

public OpenIdConnectCachingSecurityTokenProvider(string metadataEndpoint)
{
_metadataEndpoint = metadataEndpoint;
_configManager = new ConfigurationManager<OpenIdConnectConfiguration>(metadataEndpoint, new OpenIdConnectConfigurationRetriever());

RetrieveMetadata();
Expand All @@ -41,15 +39,7 @@ public string? Issuer
get
{
RetrieveMetadata();
_synclock.EnterReadLock();
try
{
return _issuer;
}
finally
{
_synclock.ExitReadLock();
}
return _issuer;
}
}

Expand All @@ -64,32 +54,29 @@ public IEnumerable<SecurityKey>? SecurityKeys
get
{
RetrieveMetadata();
_synclock.EnterReadLock();
try
{
return _keys;
}
finally
{
_synclock.ExitReadLock();
}
return _keys;
}
}

private void RetrieveMetadata()
{
_synclock.EnterWriteLock();
try
// Try to acquire the lock
//
// Interlocked.Exchange returns the original value of _lock before it was swapped.
// If it's 0, it means it went from 0 to 1, so we did acquire the lock.
// If it's 1, then the lock was already acquired by another thread.
//
// See the example in the Exchange(Int32, Int32) overload: https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.exchange?view=netframework-4.7.2
if (Interlocked.Exchange(ref _lock, 1) == 0)
{
#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits
OpenIdConnectConfiguration config = Task.Run(_configManager.GetConfigurationAsync).Result;
#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits
_issuer = config.Issuer;
_keys = config.SigningKeys;
}
finally
{
_synclock.ExitWriteLock();

// Release the lock
Interlocked.Exchange(ref _lock, 0);
}
}
}
Expand Down

0 comments on commit e3ca4bd

Please sign in to comment.