Skip to content

Instantly share code, notes, and snippets.

@christothes
Last active June 28, 2021 16:25
Show Gist options
  • Save christothes/15b9903f7da0954aee166166a8fa1919 to your computer and use it in GitHub Desktop.
Save christothes/15b9903f7da0954aee166166a8fa1919 to your computer and use it in GitHub Desktop.

On Behalf Of Credential flow

Sample Usage

// 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.

Pros

  • More explicit support: logic confined to credentials that support it

Cons

  • A lot of moving parts
    • inspect the credential
    • call ModifyTokenRequestContext on it with the TRC
    • Use the newly modified TRC in the policy

Modify TokenRequestContext automatically at construction time approach

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.

Pros

  • Agnostic to credential type
  • Does not require inspection of the credential by the policy

Cons

  • 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).

Pros

  • 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.

Cons

  • 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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment