< Summary

Class:Azure.Identity.DeviceCodeCredential
Assembly:Azure.Identity
File(s):C:\Git\azure-sdk-for-net\sdk\identity\Azure.Identity\src\DeviceCodeCredential.cs
Covered lines:35
Uncovered lines:19
Coverable lines:54
Total lines:227
Line coverage:64.8% (35 of 54)
Covered branches:13
Total branches:24
Branch coverage:54.1% (13 of 24)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
.ctor()-100%100%
.ctor(...)-100%100%
.ctor(...)-100%100%
.ctor(...)-100%50%
.ctor(...)-100%100%
.ctor(...)-100%75%
Authenticate(...)-0%0%
AuthenticateAsync()-0%0%
Authenticate(...)-0%100%
AuthenticateAsync()-0%100%
GetToken(...)-100%100%
GetTokenAsync()-100%100%
AuthenticateImplAsync()-0%100%
GetTokenImplAsync()-69.23%75%
GetTokenViaDeviceCodeAsync()-100%100%
DeviceCodeCallback(...)-100%100%

File(s)

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

#LineLine coverage
 1// Copyright (c) Microsoft Corporation. All rights reserved.
 2// Licensed under the MIT License.
 3
 4using Azure.Core;
 5using Azure.Core.Pipeline;
 6using Microsoft.Identity.Client;
 7using System;
 8using System.Threading;
 9using System.Threading.Tasks;
 10
 11namespace Azure.Identity
 12{
 13    /// <summary>
 14    /// A <see cref="TokenCredential"/> implementation which authenticates a user using the device code flow, and provid
 15    /// For more information on the device code authentication flow see https://github.com/AzureAD/microsoft-authenticat
 16    /// </summary>
 17    public class DeviceCodeCredential : TokenCredential
 18    {
 19        private readonly MsalPublicClient _client = null;
 20        private readonly CredentialPipeline _pipeline;
 21        private AuthenticationRecord _record = null;
 22        private readonly string _clientId;
 23        private readonly Func<DeviceCodeInfo, CancellationToken, Task> _deviceCodeCallback;
 24        private bool _disableAutomaticAuthentication = false;
 25        private const string AuthenticationRequiredMessage = "Interactive authentication is needed to acquire token. Cal
 26        private const string NoDefaultScopeMessage = "Authenticating in this environment requires specifying a TokenRequ
 27
 28        /// <summary>
 29        /// Protected constructor for mocking
 30        /// </summary>
 3231        protected DeviceCodeCredential()
 32        {
 33
 3234        }
 35
 36        /// <summary>
 37        /// Creates a new DeviceCodeCredential with the specified options, which will authenticate users with the specif
 38        /// </summary>
 39        /// <param name="deviceCodeCallback">The callback to be executed to display the device code to the user</param>
 40        /// <param name="clientId">The client id of the application to which the users will authenticate</param>
 41        /// <param name="options">The client options for the newly created DeviceCodeCredential</param>
 42        public DeviceCodeCredential(Func<DeviceCodeInfo, CancellationToken, Task> deviceCodeCallback, string clientId, T
 1643            : this(deviceCodeCallback, null, clientId, options, null)
 44        {
 45
 1646        }
 47
 48        /// <summary>
 49        /// Creates a new DeviceCodeCredential with the specified options, which will authenticate users with the specif
 50        /// </summary>
 51        /// <param name="deviceCodeCallback">The callback to be executed to display the device code to the user</param>
 52        /// <param name="tenantId">The tenant id of the application to which users will authenticate.  This can be null 
 53        /// <param name="clientId">The client id of the application to which the users will authenticate</param>
 54        /// <param name="options">The client options for the newly created DeviceCodeCredential</param>
 55        public DeviceCodeCredential(Func<DeviceCodeInfo, CancellationToken, Task> deviceCodeCallback, string tenantId, s
 456            : this(deviceCodeCallback, tenantId, clientId, options, null)
 57        {
 458        }
 59
 60        /// <summary>
 61        ///  Creates a new DeviceCodeCredential with the specified options, which will authenticate users using the devi
 62        /// </summary>
 63        /// <param name="deviceCodeCallback">The callback to be executed to display the device code to the user.</param>
 64        /// <param name="options">The client options for the newly created <see cref="DeviceCodeCredential"/>.</param>
 65        internal DeviceCodeCredential(Func<DeviceCodeInfo, CancellationToken, Task> deviceCodeCallback, DeviceCodeCreden
 466            : this(deviceCodeCallback, options?.TenantId, options?.ClientId, options, null)
 67        {
 468            _disableAutomaticAuthentication = options?.DisableAutomaticAuthentication ?? false;
 469            _record = options?.AuthenticationRecord;
 470        }
 71
 72        internal DeviceCodeCredential(Func<DeviceCodeInfo, CancellationToken, Task> deviceCodeCallback, string tenantId,
 2473            : this(deviceCodeCallback, tenantId, clientId, options, pipeline, null)
 74        {
 2475        }
 76
 3277        internal DeviceCodeCredential(Func<DeviceCodeInfo, CancellationToken, Task> deviceCodeCallback, string tenantId,
 78        {
 3279            _clientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
 80
 3281            _deviceCodeCallback = deviceCodeCallback ?? throw new ArgumentNullException(nameof(deviceCodeCallback));
 82
 3283            _pipeline = pipeline ?? CredentialPipeline.GetInstance(options);
 84
 3285            _client = client ?? new MsalPublicClient(_pipeline, tenantId, clientId, AzureAuthorityHosts.GetDeviceCodeRed
 3286        }
 87
 88        /// <summary>
 89        /// Interactively authenticates a user via the default browser.
 90        /// </summary>
 91        /// <param name="cancellationToken">A <see cref="CancellationToken"/> controlling the request lifetime.</param>
 92        /// <returns>The result of the authentication request, containing the acquired <see cref="AccessToken"/>, and th
 93        internal virtual AuthenticationRecord Authenticate(CancellationToken cancellationToken = default)
 94        {
 95            // get the default scope for the authority, throw if no default scope exists
 096            string defaultScope = AzureAuthorityHosts.GetDefaultScope(_pipeline.AuthorityHost) ?? throw new CredentialUn
 97
 098            return Authenticate(new TokenRequestContext(new string[] { defaultScope }), cancellationToken);
 99        }
 100
 101        /// <summary>
 102        /// Interactively authenticates a user via the default browser.
 103        /// </summary>
 104        /// <param name="cancellationToken">A <see cref="CancellationToken"/> controlling the request lifetime.</param>
 105        /// <returns>The <see cref="AuthenticationRecord"/> which can be used to silently authenticate the account on fu
 106        internal virtual async Task<AuthenticationRecord> AuthenticateAsync(CancellationToken cancellationToken = defaul
 107        {
 108            // get the default scope for the authority, throw if no default scope exists
 0109            string defaultScope = AzureAuthorityHosts.GetDefaultScope(_pipeline.AuthorityHost) ?? throw new CredentialUn
 110
 0111            return await AuthenticateAsync(new TokenRequestContext(new string[] { defaultScope }), cancellationToken).Co
 0112        }
 113
 114        /// <summary>
 115        /// Interactively authenticates a user via the default browser.
 116        /// </summary>
 117        /// <param name="cancellationToken">A <see cref="CancellationToken"/> controlling the request lifetime.</param>
 118        /// <param name="requestContext">The details of the authentication request.</param>
 119        /// <returns>The <see cref="AuthenticationRecord"/> of the authenticated account.</returns>
 120        internal virtual AuthenticationRecord Authenticate(TokenRequestContext requestContext, CancellationToken cancell
 121        {
 0122            return AuthenticateImplAsync(false, requestContext, cancellationToken).EnsureCompleted();
 123        }
 124
 125        /// <summary>
 126        /// Interactively authenticates a user via the default browser.
 127        /// </summary>
 128        /// <param name="cancellationToken">A <see cref="CancellationToken"/> controlling the request lifetime.</param>
 129        /// <param name="requestContext">The details of the authentication request.</param>
 130        /// <returns>The <see cref="AuthenticationRecord"/> of the authenticated account.</returns>
 131        internal virtual async Task<AuthenticationRecord> AuthenticateAsync(TokenRequestContext requestContext, Cancella
 132        {
 0133            return await AuthenticateImplAsync(true, requestContext, cancellationToken).ConfigureAwait(false);
 0134        }
 135
 136        /// <summary>
 137        /// Obtains a token for a user account, authenticating them through the device code authentication flow. This me
 138        /// </summary>
 139        /// <param name="requestContext">The details of the authentication request.</param>
 140        /// <param name="cancellationToken">A <see cref="CancellationToken"/> controlling the request lifetime.</param>
 141        /// <returns>An <see cref="AccessToken"/> which can be used to authenticate service client calls.</returns>
 142        public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken = d
 143        {
 16144            return GetTokenImplAsync(false, requestContext, cancellationToken).EnsureCompleted();
 145        }
 146
 147        /// <summary>
 148        /// Obtains a token for a user account, authenticating them through the device code authentication flow. This me
 149        /// </summary>
 150        /// <param name="requestContext">The details of the authentication request.</param>
 151        /// <param name="cancellationToken">A <see cref="CancellationToken"/> controlling the request lifetime.</param>
 152        /// <returns>An <see cref="AccessToken"/> which can be used to authenticate service client calls.</returns>
 153        public override async ValueTask<AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken
 154        {
 16155            return await GetTokenImplAsync(true, requestContext, cancellationToken).ConfigureAwait(false);
 6156        }
 157
 158        private async Task<AuthenticationRecord> AuthenticateImplAsync(bool async, TokenRequestContext requestContext, C
 159        {
 0160            using CredentialDiagnosticScope scope = _pipeline.StartGetTokenScope($"{nameof(DeviceCodeCredential)}.{nameo
 161
 162            try
 163            {
 0164                AccessToken token = await GetTokenViaDeviceCodeAsync(requestContext.Scopes, async, cancellationToken).Co
 165
 0166                scope.Succeeded(token);
 167
 0168                return _record;
 169            }
 0170            catch (Exception e)
 171            {
 0172                throw scope.FailWrapAndThrow(e);
 173            }
 0174        }
 175
 176        private async ValueTask<AccessToken> GetTokenImplAsync(bool async, TokenRequestContext requestContext, Cancellat
 177        {
 32178            using CredentialDiagnosticScope scope = _pipeline.StartGetTokenScope($"{nameof(DeviceCodeCredential)}.{nameo
 179
 180            try
 181            {
 32182                Exception inner = null;
 183
 32184                if (_record != null)
 185                {
 186                    try
 187                    {
 0188                        AuthenticationResult result = await _client.AcquireTokenSilentAsync(requestContext.Scopes, (Auth
 189
 0190                        return scope.Succeeded(new AccessToken(result.AccessToken, result.ExpiresOn));
 191                    }
 192                    catch (MsalUiRequiredException e)
 193                    {
 0194                        inner = e;
 0195                    }
 196                }
 197
 32198                if (_disableAutomaticAuthentication)
 199                {
 4200                    throw new AuthenticationRequiredException(AuthenticationRequiredMessage, requestContext, inner);
 201                }
 202
 28203                return scope.Succeeded(await GetTokenViaDeviceCodeAsync(requestContext.Scopes, async, cancellationToken)
 204            }
 20205            catch (Exception e)
 206            {
 20207                throw scope.FailWrapAndThrow(e);
 208            }
 12209        }
 210
 211        private async Task<AccessToken> GetTokenViaDeviceCodeAsync(string[] scopes, bool async, CancellationToken cancel
 212        {
 48213            AuthenticationResult result = await _client.AcquireTokenWithDeviceCodeAsync(scopes, code => DeviceCodeCallba
 214
 12215            _record = new AuthenticationRecord(result, _clientId);
 216
 12217            return new AccessToken(result.AccessToken, result.ExpiresOn);
 12218        }
 219
 220        private Task DeviceCodeCallback(DeviceCodeResult deviceCode, CancellationToken cancellationToken)
 221        {
 20222            return _deviceCodeCallback(new DeviceCodeInfo(deviceCode), cancellationToken);
 223        }
 224
 225
 226    }
 227}