Example #1
0
        public override async Task <AppAuthenticationResult> GetAuthResultAsync(string resource, string authority,
                                                                                CancellationToken cancellationToken = default)
        {
            string errorMessage = string.Empty;

            try
            {
                // If authority is not specified, create it using azureAdInstance and tenant Id. Tenant ID comes from the connection string.
                if (string.IsNullOrWhiteSpace(authority))
                {
                    authority = $"{_azureAdInstance}{_tenantId}";
                }

                ClientCredential clientCredential = new ClientCredential(_clientId, _clientSecret);

                var authResult = await _authenticationContext.AcquireTokenAsync(authority, resource, clientCredential).ConfigureAwait(false);

                var accessToken = authResult?.AccessToken;

                if (accessToken != null)
                {
                    PrincipalUsed.IsAuthenticated = true;
                    PrincipalUsed.TenantId        = AccessToken.TenantIdInfo(accessToken);

                    return(authResult);
                }
            }
            catch (Exception ex)
            {
                errorMessage = ex.Message;
            }

            // The exception will have the connection string, but with the secret redacted.
            throw new AzureServiceTokenProviderException(ConnectionString?.Replace(_clientSecret, "<<Redacted>>"),
                                                         resource, authority, $"{AzureServiceTokenProviderException.GenericErrorMessage} {errorMessage}");
        }
        /// <summary>
        /// Get access token using asymmetric key associated with an Azure AD application.
        /// </summary>
        /// <param name="resource">Resource to access.</param>
        /// <param name="authority">Authority where resource is.</param>
        /// <returns></returns>
        public override async Task <AppAuthenticationResult> GetAuthResultAsync(string resource, string authority,
                                                                                CancellationToken cancellationToken = default)
        {
            // If authority is not specified and tenantId was present in connection string, create it using azureAdInstance and tenantId.
            if (string.IsNullOrWhiteSpace(authority) && !string.IsNullOrWhiteSpace(_tenantId))
            {
                authority = $"{_azureAdInstance}{_tenantId}";
            }

            List <X509Certificate2>     certs = null;
            Dictionary <string, string> exceptionDictionary = new Dictionary <string, string>();

            try
            {
                switch (_certificateIdentifierType)
                {
                case CertificateIdentifierType.KeyVaultCertificateSecretIdentifier:
                    // Get certificate for the given Key Vault secret identifier
                    try
                    {
                        var keyVaultCert = await _keyVaultClient
                                           .GetCertificateAsync(_certificateIdentifier, cancellationToken).ConfigureAwait(false);

                        certs = new List <X509Certificate2>()
                        {
                            keyVaultCert
                        };

                        // If authority is still not specified, create it using azureAdInstance and tenantId. Tenant ID comes from Key Vault access token.
                        if (string.IsNullOrWhiteSpace(authority))
                        {
                            _tenantId = _keyVaultClient.PrincipalUsed.TenantId;
                            authority = $"{_azureAdInstance}{_tenantId}";
                        }
                    }
                    catch (Exception exp)
                    {
                        throw new AzureServiceTokenProviderException(ConnectionString, resource, authority,
                                                                     $"{AzureServiceTokenProviderException.KeyVaultCertificateRetrievalError} {exp.Message}");
                    }
                    break;

                case CertificateIdentifierType.SubjectName:
                case CertificateIdentifierType.Thumbprint:
                    // Get certificates for the given thumbprint or subject name.
                    bool isThumbprint = _certificateIdentifierType == CertificateIdentifierType.Thumbprint;
                    certs = CertificateHelper.GetCertificates(_certificateIdentifier, isThumbprint,
                                                              _storeLocation);

                    if (certs == null || certs.Count == 0)
                    {
                        throw new AzureServiceTokenProviderException(ConnectionString, resource, authority,
                                                                     AzureServiceTokenProviderException.LocalCertificateNotFound);
                    }
                    break;
                }

                Debug.Assert(certs != null, "Probably wrong certificateIdentifierType was used to instantiate this class!");

                // If multiple certs were found, use in order of most recently created.
                // This helps if old cert is rolled over, but not removed.
                // To hold reason why token could not be acquired per cert tried.
                foreach (X509Certificate2 cert in certs.OrderByDescending(p => p.NotBefore))
                {
                    if (!string.IsNullOrEmpty(cert.Thumbprint))
                    {
                        try
                        {
                            ClientAssertionCertificate certCred = new ClientAssertionCertificate(_clientId, cert);

                            var authResult =
                                await _authenticationContext.AcquireTokenAsync(authority, resource, certCred)
                                .ConfigureAwait(false);

                            var accessToken = authResult?.AccessToken;

                            if (accessToken != null)
                            {
                                PrincipalUsed.CertificateThumbprint = cert.Thumbprint;
                                PrincipalUsed.IsAuthenticated       = true;
                                PrincipalUsed.TenantId = AccessToken.TenantIdInfo(accessToken);
                                return(authResult);
                            }
                        }
                        catch (Exception exp)
                        {
                            // If token cannot be acquired using a cert, try the next one
                            exceptionDictionary[cert.Thumbprint] = exp.Message;
                        }
                    }
                }
            }
            finally
            {
                if (certs != null)
                {
                    foreach (var cert in certs)
                    {
#if net452
                        cert.Reset();
#else
                        cert.Dispose();
#endif
                    }
                }
            }

            // Could not acquire access token, throw exception
            string message = $"Tried {certs.Count} certificate(s). {AzureServiceTokenProviderException.GenericErrorMessage}";

            // Include exception details for each cert that was tried
            int count = 1;
            foreach (string thumbprint in exceptionDictionary.Keys)
            {
                message += Environment.NewLine +
                           $"Exception for cert #{count} with thumbprint {thumbprint}: {exceptionDictionary[thumbprint]}";
                count++;
            }

            throw new AzureServiceTokenProviderException(ConnectionString, resource, authority, message);
        }