|   |  | 1 |  | // Copyright (c) Microsoft Corporation. All rights reserved. | 
|   |  | 2 |  | // Licensed under the MIT License. | 
|   |  | 3 |  |  | 
|   |  | 4 |  | using Azure.Core; | 
|   |  | 5 |  | using Microsoft.Identity.Client; | 
|   |  | 6 |  | using System; | 
|   |  | 7 |  | using System.Security; | 
|   |  | 8 |  | using System.Threading; | 
|   |  | 9 |  | using System.Threading.Tasks; | 
|   |  | 10 |  | using Azure.Core.Pipeline; | 
|   |  | 11 |  |  | 
|   |  | 12 |  | namespace Azure.Identity | 
|   |  | 13 |  | { | 
|   |  | 14 |  |     /// <summary> | 
|   |  | 15 |  |     ///  Enables authentication to Azure Active Directory using a user's username and password. If the user has MFA enab | 
|   |  | 16 |  |     ///  credential will fail to get a token throwing an <see cref="AuthenticationFailedException"/>. Also, this credent | 
|   |  | 17 |  |     ///  trust and is not recommended outside of prototyping when more secure credentials can be used. | 
|   |  | 18 |  |     /// </summary> | 
|   |  | 19 |  |     public class UsernamePasswordCredential : TokenCredential | 
|   |  | 20 |  |     { | 
|   |  | 21 |  |         private const string NoDefaultScopeMessage = "Authenticating in this environment requires specifying a TokenRequ | 
|   |  | 22 |  |  | 
|   |  | 23 |  |         private readonly string _clientId; | 
|   |  | 24 |  |         private readonly MsalPublicClient _client; | 
|   |  | 25 |  |         private readonly CredentialPipeline _pipeline; | 
|   |  | 26 |  |         private readonly string _username; | 
|   |  | 27 |  |         private readonly SecureString _password; | 
|   |  | 28 |  |         private AuthenticationRecord _record; | 
|   |  | 29 |  |  | 
|   |  | 30 |  |  | 
|   |  | 31 |  |         /// <summary> | 
|   |  | 32 |  |         /// Protected constructor for mocking | 
|   |  | 33 |  |         /// </summary> | 
|   | 16 | 34 |  |         protected UsernamePasswordCredential() | 
|   |  | 35 |  |         { | 
|   |  | 36 |  |  | 
|   | 16 | 37 |  |         } | 
|   |  | 38 |  |  | 
|   |  | 39 |  |         /// <summary> | 
|   |  | 40 |  |         /// Creates an instance of the <see cref="UsernamePasswordCredential"/> with the details needed to authenticate  | 
|   |  | 41 |  |         /// and password. | 
|   |  | 42 |  |         /// </summary> | 
|   |  | 43 |  |         /// <param name="username">The user account's username, also known as UPN.</param> | 
|   |  | 44 |  |         /// <param name="password">The user account's password.</param> | 
|   |  | 45 |  |         /// <param name="tenantId">The Azure Active Directory tenant (directory) ID or name.</param> | 
|   |  | 46 |  |         /// <param name="clientId">The client (application) ID of an App Registration in the tenant.</param> | 
|   |  | 47 |  |         public UsernamePasswordCredential(string username, string password, string tenantId, string clientId) | 
|   | 0 | 48 |  |             : this(username, password, tenantId, clientId, (TokenCredentialOptions)null) | 
|   |  | 49 |  |         { | 
|   |  | 50 |  |  | 
|   | 0 | 51 |  |         } | 
|   |  | 52 |  |  | 
|   |  | 53 |  |         /// <summary> | 
|   |  | 54 |  |         /// Creates an instance of the <see cref="UsernamePasswordCredential"/> with the details needed to authenticate  | 
|   |  | 55 |  |         /// and password. | 
|   |  | 56 |  |         /// </summary> | 
|   |  | 57 |  |         /// <param name="username">The user account's user name, UPN.</param> | 
|   |  | 58 |  |         /// <param name="password">The user account's password.</param> | 
|   |  | 59 |  |         /// <param name="tenantId">The Azure Active Directory tenant (directory) ID or name.</param> | 
|   |  | 60 |  |         /// <param name="clientId">The client (application) ID of an App Registration in the tenant.</param> | 
|   |  | 61 |  |         /// <param name="options">The client options for the newly created UsernamePasswordCredential</param> | 
|   |  | 62 |  |         public UsernamePasswordCredential(string username, string password, string tenantId, string clientId, TokenCrede | 
|   | 12 | 63 |  |             : this(username, password, tenantId, clientId, options, null, null) | 
|   |  | 64 |  |         { | 
|   | 12 | 65 |  |         } | 
|   |  | 66 |  |  | 
|   |  | 67 |  |         /// <summary> | 
|   |  | 68 |  |         /// Creates an instance of the <see cref="UsernamePasswordCredential"/> with the details needed to authenticate  | 
|   |  | 69 |  |         /// and password. | 
|   |  | 70 |  |         /// </summary> | 
|   |  | 71 |  |         /// <param name="username">The user account's user name, UPN.</param> | 
|   |  | 72 |  |         /// <param name="password">The user account's password.</param> | 
|   |  | 73 |  |         /// <param name="tenantId">The Azure Active Directory tenant (directory) ID or name.</param> | 
|   |  | 74 |  |         /// <param name="clientId">The client (application) ID of an App Registration in the tenant.</param> | 
|   |  | 75 |  |         /// <param name="options">The client options for the newly created UsernamePasswordCredential</param> | 
|   |  | 76 |  |         internal UsernamePasswordCredential(string username, string password, string tenantId, string clientId, Username | 
|   | 0 | 77 |  |             : this(username, password, tenantId, clientId, options, null, null) | 
|   |  | 78 |  |         { | 
|   | 0 | 79 |  |         } | 
|   |  | 80 |  |  | 
|   | 16 | 81 |  |         internal UsernamePasswordCredential(string username, string password, string tenantId, string clientId, TokenCre | 
|   |  | 82 |  |         { | 
|   | 16 | 83 |  |             _username = username ?? throw new ArgumentNullException(nameof(username)); | 
|   |  | 84 |  |  | 
|   | 16 | 85 |  |             _password = (password != null) ? password.ToSecureString() : throw new ArgumentNullException(nameof(password | 
|   |  | 86 |  |  | 
|   | 16 | 87 |  |             _clientId = clientId ?? throw new ArgumentNullException(nameof(clientId)); | 
|   |  | 88 |  |  | 
|   | 0 | 89 |  |             if (tenantId == null) throw new ArgumentNullException(nameof(tenantId)); | 
|   |  | 90 |  |  | 
|   | 16 | 91 |  |             _pipeline = pipeline ?? CredentialPipeline.GetInstance(options); | 
|   |  | 92 |  |  | 
|   | 16 | 93 |  |             _client = client ?? new MsalPublicClient(_pipeline, tenantId, clientId, null, options as ITokenCacheOptions) | 
|   | 16 | 94 |  |         } | 
|   |  | 95 |  |  | 
|   |  | 96 |  |         /// <summary> | 
|   |  | 97 |  |         /// Authenticates the user using the specified username and password. | 
|   |  | 98 |  |         /// </summary> | 
|   |  | 99 |  |         /// <param name="cancellationToken">A <see cref="CancellationToken"/> controlling the request lifetime.</param> | 
|   |  | 100 |  |         /// <returns>The <see cref="AuthenticationRecord"/> of the authenticated account.</returns> | 
|   |  | 101 |  |         internal virtual AuthenticationRecord Authenticate(CancellationToken cancellationToken = default) | 
|   |  | 102 |  |         { | 
|   |  | 103 |  |             // get the default scope for the authority, throw if no default scope exists | 
|   | 2 | 104 |  |             string defaultScope = AzureAuthorityHosts.GetDefaultScope(_pipeline.AuthorityHost) ?? throw new CredentialUn | 
|   |  | 105 |  |  | 
|   | 2 | 106 |  |             return Authenticate(new TokenRequestContext(new string[] { defaultScope }), cancellationToken); | 
|   |  | 107 |  |         } | 
|   |  | 108 |  |  | 
|   |  | 109 |  |         /// <summary> | 
|   |  | 110 |  |         /// Authenticates the user using the specified username and password. | 
|   |  | 111 |  |         /// </summary> | 
|   |  | 112 |  |         /// <param name="cancellationToken">A <see cref="CancellationToken"/> controlling the request lifetime.</param> | 
|   |  | 113 |  |         /// <returns>The <see cref="AuthenticationRecord"/> of the authenticated account.</returns> | 
|   |  | 114 |  |         internal virtual async Task<AuthenticationRecord> AuthenticateAsync(CancellationToken cancellationToken = defaul | 
|   |  | 115 |  |         { | 
|   |  | 116 |  |             // get the default scope for the authority, throw if no default scope exists | 
|   | 2 | 117 |  |             string defaultScope = AzureAuthorityHosts.GetDefaultScope(_pipeline.AuthorityHost) ?? throw new CredentialUn | 
|   |  | 118 |  |  | 
|   | 2 | 119 |  |             return await AuthenticateAsync(new TokenRequestContext(new string[] { defaultScope }), cancellationToken).Co | 
|   | 2 | 120 |  |         } | 
|   |  | 121 |  |  | 
|   |  | 122 |  |         /// <summary> | 
|   |  | 123 |  |         /// Authenticates the user using the specified username and password. | 
|   |  | 124 |  |         /// </summary> | 
|   |  | 125 |  |         /// <param name="cancellationToken">A <see cref="CancellationToken"/> controlling the request lifetime.</param> | 
|   |  | 126 |  |         /// <param name="requestContext">The details of the authentication request.</param> | 
|   |  | 127 |  |         /// <returns>The <see cref="AuthenticationRecord"/> of the authenticated account.</returns> | 
|   |  | 128 |  |         internal virtual AuthenticationRecord Authenticate(TokenRequestContext requestContext, CancellationToken cancell | 
|   |  | 129 |  |         { | 
|   | 4 | 130 |  |             return AuthenticateImplAsync(false, requestContext, cancellationToken).EnsureCompleted(); | 
|   |  | 131 |  |         } | 
|   |  | 132 |  |  | 
|   |  | 133 |  |         /// <summary> | 
|   |  | 134 |  |         /// Authenticates the user using the specified username and password. | 
|   |  | 135 |  |         /// </summary> | 
|   |  | 136 |  |         /// <param name="cancellationToken">A <see cref="CancellationToken"/> controlling the request lifetime.</param> | 
|   |  | 137 |  |         /// <param name="requestContext">The details of the authentication request.</param> | 
|   |  | 138 |  |         /// <returns>The <see cref="AuthenticationRecord"/> of the authenticated account.</returns> | 
|   |  | 139 |  |         internal virtual async Task<AuthenticationRecord> AuthenticateAsync(TokenRequestContext requestContext, Cancella | 
|   |  | 140 |  |         { | 
|   | 4 | 141 |  |             return await AuthenticateImplAsync(true, requestContext, cancellationToken).ConfigureAwait(false); | 
|   | 4 | 142 |  |         } | 
|   |  | 143 |  |  | 
|   |  | 144 |  |         /// <summary> | 
|   |  | 145 |  |         /// Obtains a token for a user account, authenticating them using the given username and password.  Note: This w | 
|   |  | 146 |  |         /// an <see cref="AuthenticationFailedException"/> if the specified user account has MFA enabled. This method is | 
|   |  | 147 |  |         /// </summary> | 
|   |  | 148 |  |         /// <param name="requestContext">The details of the authentication request.</param> | 
|   |  | 149 |  |         /// <param name="cancellationToken">A <see cref="CancellationToken"/> controlling the request lifetime.</param> | 
|   |  | 150 |  |         /// <returns>An <see cref="AccessToken"/> which can be used to authenticate service client calls.</returns> | 
|   |  | 151 |  |         public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken = d | 
|   |  | 152 |  |         { | 
|   | 4 | 153 |  |             return GetTokenImplAsync(false, requestContext, cancellationToken).EnsureCompleted(); | 
|   |  | 154 |  |         } | 
|   |  | 155 |  |  | 
|   |  | 156 |  |         /// <summary> | 
|   |  | 157 |  |         /// Obtains a token for a user account, authenticating them using the given username and password.  Note: This w | 
|   |  | 158 |  |         /// an <see cref="AuthenticationFailedException"/> if the specified user account has MFA enabled. This method is | 
|   |  | 159 |  |         /// </summary> | 
|   |  | 160 |  |         /// <param name="requestContext">The details of the authentication request.</param> | 
|   |  | 161 |  |         /// <param name="cancellationToken">A <see cref="CancellationToken"/> controlling the request lifetime.</param> | 
|   |  | 162 |  |         /// <returns>An <see cref="AccessToken"/> which can be used to authenticate service client calls.</returns> | 
|   |  | 163 |  |         public override async ValueTask<AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken | 
|   |  | 164 |  |         { | 
|   | 4 | 165 |  |             return await GetTokenImplAsync(true, requestContext, cancellationToken).ConfigureAwait(false); | 
|   | 2 | 166 |  |         } | 
|   |  | 167 |  |  | 
|   |  | 168 |  |         private async Task<AuthenticationRecord> AuthenticateImplAsync(bool async, TokenRequestContext requestContext, C | 
|   |  | 169 |  |         { | 
|   | 8 | 170 |  |             using CredentialDiagnosticScope scope = _pipeline.StartGetTokenScope($"{nameof(UsernamePasswordCredential)}. | 
|   |  | 171 |  |  | 
|   |  | 172 |  |             try | 
|   |  | 173 |  |             { | 
|   | 8 | 174 |  |                 scope.Succeeded(await GetTokenImplAsync(async, requestContext, cancellationToken).ConfigureAwait(false)) | 
|   |  | 175 |  |  | 
|   | 8 | 176 |  |                 return _record; | 
|   |  | 177 |  |             } | 
|   | 0 | 178 |  |             catch (Exception e) | 
|   |  | 179 |  |             { | 
|   | 0 | 180 |  |                 throw scope.FailWrapAndThrow(e); | 
|   |  | 181 |  |             } | 
|   | 8 | 182 |  |         } | 
|   |  | 183 |  |  | 
|   |  | 184 |  |         private async Task<AccessToken> GetTokenImplAsync(bool async, TokenRequestContext requestContext, CancellationTo | 
|   |  | 185 |  |         { | 
|   | 16 | 186 |  |             using CredentialDiagnosticScope scope = _pipeline.StartGetTokenScope("UsernamePasswordCredential.GetToken",  | 
|   |  | 187 |  |  | 
|   |  | 188 |  |             try | 
|   |  | 189 |  |             { | 
|   | 16 | 190 |  |                 AuthenticationResult result = await _client | 
|   | 16 | 191 |  |                     .AcquireTokenByUsernamePasswordAsync(requestContext.Scopes, _username, _password, async, cancellatio | 
|   | 16 | 192 |  |                     .ConfigureAwait(false); | 
|   |  | 193 |  |  | 
|   | 12 | 194 |  |                 _record = new AuthenticationRecord(result, _clientId); | 
|   |  | 195 |  |  | 
|   | 12 | 196 |  |                 return scope.Succeeded(new AccessToken(result.AccessToken, result.ExpiresOn)); | 
|   |  | 197 |  |             } | 
|   | 4 | 198 |  |             catch (Exception e) | 
|   |  | 199 |  |             { | 
|   | 4 | 200 |  |                 throw scope.FailWrapAndThrow(e); | 
|   |  | 201 |  |             } | 
|   | 12 | 202 |  |         } | 
|   |  | 203 |  |     } | 
|   |  | 204 |  | } |