< Summary

Class:Azure.Identity.SharedTokenCacheCredential
Assembly:Azure.Identity
File(s):C:\Git\azure-sdk-for-net\sdk\identity\Azure.Identity\src\SharedTokenCacheCredential.cs
Covered lines:51
Uncovered lines:4
Coverable lines:55
Total lines:179
Line coverage:92.7% (51 of 55)
Covered branches:33
Total branches:38
Branch coverage:86.8% (33 of 38)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
.cctor()-100%100%
.ctor()-100%100%
.ctor(...)-0%0%
.ctor(...)-0%100%
.ctor(...)-100%100%
.ctor(...)-100%100%
GetToken(...)-100%100%
GetTokenAsync()-100%100%
GetTokenImplAsync()-100%100%
GetAccountAsync()-100%91.67%
GetCredentialUnavailableMessage(...)-100%100%

File(s)

C:\Git\azure-sdk-for-net\sdk\identity\Azure.Identity\src\SharedTokenCacheCredential.cs

#LineLine coverage
 1// Copyright (c) Microsoft Corporation. All rights reserved.
 2// Licensed under the MIT License.
 3
 4using Azure.Core;
 5using Microsoft.Identity.Client;
 6using System;
 7using System.Threading;
 8using System.Threading.Tasks;
 9using System.Linq;
 10using System.Collections.Generic;
 11using System.Globalization;
 12using Azure.Core.Pipeline;
 13
 14namespace Azure.Identity
 15{
 16    /// <summary>
 17    /// Authenticates using tokens in the local cache shared between Microsoft applications.
 18    /// </summary>
 19    public class SharedTokenCacheCredential : TokenCredential
 20    {
 21        internal const string NoAccountsInCacheMessage = "SharedTokenCacheCredential authentication unavailable. No acco
 22        internal const string MultipleAccountsInCacheMessage = "SharedTokenCacheCredential authentication unavailable. M
 23        internal const string NoMatchingAccountsInCacheMessage = "SharedTokenCacheCredential authentication unavailable.
 24        internal const string MultipleMatchingAccountsInCacheMessage = "SharedTokenCacheCredential authentication unavai
 25
 226        private static readonly ITokenCacheOptions s_DefaultCacheOptions = new SharedTokenCacheCredentialOptions();
 27        private readonly MsalPublicClient _client;
 28        private readonly CredentialPipeline _pipeline;
 29        private readonly string _tenantId;
 30        private readonly string _username;
 31        private readonly AuthenticationRecord _record;
 32        private readonly Lazy<Task<IAccount>> _account;
 33        /// <summary>
 34        /// Creates a new <see cref="SharedTokenCacheCredential"/> which will authenticate users signed in through devel
 35        /// </summary>
 36        public SharedTokenCacheCredential()
 8037            : this(null, null, null, null, null)
 38        {
 39
 8040        }
 41
 42        /// <summary>
 43        /// Creates a new <see cref="SharedTokenCacheCredential"/> which will authenticate users signed in through devel
 44        /// </summary>
 45        /// <param name="options">The client options for the newly created <see cref="SharedTokenCacheCredential"/></par
 46        public SharedTokenCacheCredential(SharedTokenCacheCredentialOptions options)
 047            : this(options?.TenantId, options?.Username, options, null, null)
 48        {
 049        }
 50
 51        /// <summary>
 52        /// Creates a new <see cref="SharedTokenCacheCredential"/> which will authenticate users signed in through devel
 53        /// </summary>
 54        /// <param name="username">The username of the user to authenticate</param>
 55        /// <param name="options">The client options for the newly created <see cref="SharedTokenCacheCredential"/></par
 56        public SharedTokenCacheCredential(string username, TokenCredentialOptions options = default)
 057            : this(null, username, options, null, null)
 58        {
 059        }
 60
 61        internal SharedTokenCacheCredential(string tenantId, string username, TokenCredentialOptions options, Credential
 662            : this(tenantId, username, options, pipeline, null)
 63        {
 664        }
 65
 16666        internal SharedTokenCacheCredential(string tenantId, string username, TokenCredentialOptions options, Credential
 67        {
 16668            _tenantId = tenantId;
 69
 16670            _username = username;
 71
 16672            _record = (options as SharedTokenCacheCredentialOptions)?.AuthenticationRecord;
 73
 16674            _pipeline = pipeline ?? CredentialPipeline.GetInstance(options);
 75
 16676            _client = client ?? new MsalPublicClient(_pipeline, tenantId, Constants.DeveloperSignOnClientId, null, (opti
 77
 16678            _account = new Lazy<Task<IAccount>>(GetAccountAsync);
 16679        }
 80
 81        /// <summary>
 82        /// Obtains an <see cref="AccessToken"/> token for a user account silently if the user has already authenticated
 83        /// </summary>
 84        /// <param name="requestContext">The details of the authentication request.</param>
 85        /// <param name="cancellationToken">A <see cref="CancellationToken"/> controlling the request lifetime</param>
 86        /// <returns>An <see cref="AccessToken"/> which can be used to authenticate service client calls</returns>
 87        public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken = d
 88        {
 4089            return GetTokenImplAsync(false, requestContext, cancellationToken).EnsureCompleted();
 90        }
 91
 92        /// <summary>
 93        /// Obtains an <see cref="AccessToken"/> token for a user account silently if the user has already authenticated
 94        /// </summary>
 95        /// <param name="requestContext">The details of the authentication request.</param>
 96        /// <param name="cancellationToken">A <see cref="CancellationToken"/> controlling the request lifetime</param>
 97        /// <returns>An <see cref="AccessToken"/> which can be used to authenticate service client calls</returns>
 98        public override async ValueTask<AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken
 99        {
 40100            return await GetTokenImplAsync(true, requestContext, cancellationToken).ConfigureAwait(false);
 14101        }
 102
 103        private async ValueTask<AccessToken> GetTokenImplAsync(bool async, TokenRequestContext requestContext, Cancellat
 104        {
 80105            using CredentialDiagnosticScope scope = _pipeline.StartGetTokenScope("SharedTokenCacheCredential.GetToken", 
 106
 107            try
 108            {
 80109                IAccount account = async
 80110                    ? await _account.Value.ConfigureAwait(false)
 80111#pragma warning disable AZC0102 // Do not use GetAwaiter().GetResult(). Use the TaskExtensions.EnsureCompleted() extensi
 80112                    : _account.Value.GetAwaiter().GetResult();
 113#pragma warning restore AZC0102 // Do not use GetAwaiter().GetResult(). Use the TaskExtensions.EnsureCompleted() extensi
 114
 115
 36116                AuthenticationResult result = await _client.AcquireTokenSilentAsync(requestContext.Scopes, account, asyn
 117
 28118                return scope.Succeeded(new AccessToken(result.AccessToken, result.ExpiresOn));
 119            }
 4120            catch (MsalUiRequiredException)
 121            {
 4122                throw scope.FailWrapAndThrow(new CredentialUnavailableException($"{nameof(SharedTokenCacheCredential)} a
 123            }
 48124            catch (Exception e)
 125            {
 48126                throw scope.FailWrapAndThrow(e);
 127            }
 28128        }
 129
 130        private async Task<IAccount> GetAccountAsync()
 131        {
 80132            if (_record != null)
 133            {
 4134                return new AuthenticationAccount(_record);
 135            }
 136
 76137            List<IAccount> accounts = (await _client.GetAccountsAsync().ConfigureAwait(false)).ToList();
 138
 139            // filter the accounts to those matching the specified user and tenant
 76140            List<IAccount> filteredAccounts = accounts.Where(a =>
 76141                // if _username is specified it must match the account
 200142                (string.IsNullOrEmpty(_username) || string.Compare(a.Username, _username, StringComparison.OrdinalIgnore
 200143                &&
 200144                //if _tenantId is specified it must match the account
 200145                (string.IsNullOrEmpty(_tenantId) || string.Compare(a.HomeAccountId?.TenantId, _tenantId, StringCompariso
 76146            ).ToList();
 147
 76148            if (filteredAccounts.Count != 1)
 149            {
 44150                throw new CredentialUnavailableException(GetCredentialUnavailableMessage(accounts, filteredAccounts));
 151            }
 152
 32153            return filteredAccounts.First();
 36154        }
 155
 156        private string GetCredentialUnavailableMessage(List<IAccount> accounts, List<IAccount> filteredAccounts)
 157        {
 44158            if (accounts.Count == 0)
 159            {
 16160                return NoAccountsInCacheMessage;
 161            }
 162
 28163            if (string.IsNullOrEmpty(_username) && string.IsNullOrEmpty(_tenantId))
 164            {
 4165                return string.Format(CultureInfo.InvariantCulture, MultipleAccountsInCacheMessage);
 166            }
 167
 24168            var usernameStr = string.IsNullOrEmpty(_username) ? string.Empty : $" username: {_username}";
 24169            var tenantIdStr = string.IsNullOrEmpty(_tenantId) ? string.Empty : $" tenantId: {_tenantId}";
 170
 24171            if (filteredAccounts.Count == 0)
 172            {
 12173                return string.Format(CultureInfo.InvariantCulture, NoMatchingAccountsInCacheMessage, usernameStr, tenant
 174            }
 175
 12176            return string.Format(CultureInfo.InvariantCulture, MultipleMatchingAccountsInCacheMessage, usernameStr, tena
 177        }
 178    }
 179}