// App startup
var cred = new OnBehalfOfCredential(TenantId, ClientId, Secret);
var client = new BlobClient(Uri, cred);
// some web API
using (var _ = UserAssertionContext(CurrentUserContextToken))
{
result = new List<string>();
//do request in the context of CurrentUserContextToken
foreach (BlobItem blob in container.GetBlobs())
{
result.Add(blob.Name);
}
return Ok(result);
}
TokenCredential modifies TokenRequestContext approach (POC)
public interface IModifiesTokenRequestContext
{
TokenRequestContext ModifyTokenRequestContext(TokenRequestContext context);
}
public readonly struct TokenRequestContext
{
public string? Claims { get { throw null; } }
public string? ParentRequestId { get { throw null; } }
public string[] Scopes { get { throw null; } }
public string? UserAssersion { get { throw null; } }
}
namespace Azure.Identity
{
/// <summary>
///
/// </summary>
public class UserAssertionScope : IDisposable
{
internal static AsyncLocal<UserAssertion> _userAssertion = new AsyncLocal<UserAssertion>();
/// <summary>
///
/// </summary>
/// <param name="userAccessToken"></param>
public UserAssertionScope(string userAccessToken)
{
_userAssertion.Value = new UserAssertion(userAccessToken);
}
/// <summary>
///
/// </summary>
public void Dispose()
{
GC.SuppressFinalize(this);
_userAssertion.Value = default;
}
}
}
// In the BearerTokenAuthenticationPolicy
if (credential is IModifiesTokenRequestContext ex)
{
_credentialEx = ex;
}
// <snip>
_credentialEx?.ModifyTokenRequestContext(context);
TokenCredential implementations implement the interface which adds a method to modify the TokenRequestContext. This leverages an AsyncLocal to set the value of a UserAssertion into the TokenRequestContext.
- More explicit support: logic confined to credentials that support it
- A lot of moving parts
- inspect the credential
- call
ModifyTokenRequestContext
on it with the TRC - Use the newly modified TRC in the policy
Same async local pattern as above, but the TokenRequestContext somehow inspects the state of the AsyncLocal value for the user assertion at construction time and sets the UserAssertion value at that time if it is populated.
- Agnostic to credential type
- Does not require inspection of the credential by the policy
- Less explicit: Any TokenRequestContext could be populated with the UserAssertion even for credentials that do not support it
- Need to create factory abstraction for TokenRequestContext construction so that Core doesn't need to know about this specific scenaro
TokenCredential supports caching approach (POC)
Token Credential implementations implement ISupportsTokenCache
which indidcates that a credential will cache the token if possible (no caching should be attempted by a policy or upstream component).
- Cleanest implementation that requires no modification of TokenRequestContext
- Creates a pathway to a more universal caching strategy for credentials which potentially leverages MSAL under the covers or some other caching strategy, including developer supplied implementations.
- Could be a marker interface, but might also support a method or property such as
bool BypassCaching
public interface ISupportsTokenCaching
{
bool BypassCache { get; set; }
}
// logic in the policy
if (_credential is ISupportsTokenCaching c && !c.BypassCache)
{
info = await GetHeaderValueFromCredentialAsync(context, async, message.CancellationToken).ConfigureAwait(false);
return info.HeaderValue;
}