/// <summary>
 /// Process ADAL exception and provide common handlers.
 /// </summary>
 /// <param name="serviceUrl"></param>
 /// <param name="clientCredentials"></param>
 /// <param name="userCert"></param>
 /// <param name="clientId"></param>
 /// <param name="redirectUri"></param>
 /// <param name="promptBehavior"></param>
 /// <param name="isOnPrem"></param>
 /// <param name="authority"></param>
 /// <param name="logSink"></param>
 /// <param name="useDefaultCreds"></param>
 /// <param name="adalEx"></param>
 /// <param name="msalAuthClient"></param>
 private async static Task <ExecuteAuthenticationResults> ProcessAdalExecptionAsync(Uri serviceUrl, ClientCredentials clientCredentials, X509Certificate2 userCert, string clientId, Uri redirectUri, PromptBehavior promptBehavior, bool isOnPrem, string authority, object msalAuthClient, DataverseTraceLogger logSink, bool useDefaultCreds, Microsoft.Identity.Client.MsalException adalEx)
 {
     if (adalEx.ErrorCode.Equals("interaction_required", StringComparison.OrdinalIgnoreCase) ||
         adalEx.ErrorCode.Equals("user_password_expired", StringComparison.OrdinalIgnoreCase) ||
         adalEx.ErrorCode.Equals("password_required_for_managed_user", StringComparison.OrdinalIgnoreCase) ||
         adalEx is Microsoft.Identity.Client.MsalUiRequiredException)
     {
         logSink.Log("ERROR REQUESTING TOKEN FROM THE AUTHENTICATION CONTEXT - USER intervention required", TraceEventType.Warning);
         // ADAL wants the User to do something,, determine if we are able to see a user
         if (promptBehavior == PromptBehavior.Always || promptBehavior == PromptBehavior.Auto)
         {
             // Switch to MFA user mode..
             Microsoft.Identity.Client.IAccount user = null;                      //TODO:UPDATE THIS OR REMOVE AS WE DETERMIN HOW TO SOLVE THIS ISSUE IN MSAL //  new Microsoft.Identity.Client.AccountId();
             user = null;
             //user = new UserIdentifier(clientCredentials.UserName.UserName, UserIdentifierType.OptionalDisplayableId);
             return(await ExecuteAuthenticateServiceProcessAsync(serviceUrl, null, userCert, clientId, redirectUri, promptBehavior, isOnPrem, authority, msalAuthClient, logSink, useDefaultCreds : useDefaultCreds, user : user));
         }
         else
         {
             logSink.Log("ERROR REQUESTING TOKEN FROM THE AUTHENTICATION CONTEXT - USER intervention required but not permitted by prompt behavior", TraceEventType.Error, adalEx);
             throw adalEx;
         }
     }
     else
     {
         logSink.Log("ERROR REQUESTING Token FROM THE Authentication context - General ADAL Error", TraceEventType.Error, adalEx);
         throw adalEx;
     }
 }
Example #2
0
        public void LogWriteTest()
        {
            ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
                                                                builder.AddConsole(options =>
            {
                options.IncludeScopes   = true;
                options.TimestampFormat = "hh:mm:ss ";
            }));
            ILogger <ClientTests> Ilogger = loggerFactory.CreateLogger <ClientTests>();

            DataverseTraceLogger logger = new DataverseTraceLogger(Ilogger);

            logger.EnabledInMemoryLogCapture = true;

            logger.Log("TEST INFO MESSAGE");
            logger.Log("TEST WARNING MESSAGE", TraceEventType.Warning);
            logger.Log("TEST VERBOSE MESSAGE", TraceEventType.Verbose);
            logger.Log("TEST ERROR MESSAGE", TraceEventType.Error);
            logger.Log("TEST CRITICAL MESSAGE", TraceEventType.Critical);


            // error throw.
            Microsoft.Rest.HttpOperationException operationException = new Microsoft.Rest.HttpOperationException("HTTPOPEXC");
            HttpResponseMessage Resp500 = new HttpResponseMessage(System.Net.HttpStatusCode.ServiceUnavailable);

            Resp500.Headers.Add("REQ_ID", "39393F77-8F8B-4416-846E-28B4D2AA5667");
            operationException.Response = new Microsoft.Rest.HttpResponseMessageWrapper(Resp500, "{\"error\":{\"code\":\"0x80040203\",\"message\":\"Communication activity cannot have more than one Sender party\",\"@Microsoft.PowerApps.CDS.ErrorDetails.ApiExceptionSourceKey\":\"Plugin/Microsoft.Crm.Common.ObjectModel.PhoneCallService\",\"@Microsoft.PowerApps.CDS.ErrorDetails.ApiStepKey\":\"3ccabb1b-ea3e-db11-86a7-000a3a5473e8\",\"@Microsoft.PowerApps.CDS.ErrorDetails.ApiDepthKey\":\"1\",\"@Microsoft.PowerApps.CDS.ErrorDetails.ApiActivityIdKey\":\"1736f387-e025-4828-a2bb-74ea8ac768a2\",\"@Microsoft.PowerApps.CDS.ErrorDetails.ApiPluginSolutionNameKey\":\"System\",\"@Microsoft.PowerApps.CDS.ErrorDetails.ApiStepSolutionNameKey\":\"System\",\"@Microsoft.PowerApps.CDS.ErrorDetails.ApiExceptionCategory\":\"ClientError\",\"@Microsoft.PowerApps.CDS.ErrorDetails.ApiExceptionMesageName\":\"InvalidArgument\",\"@Microsoft.PowerApps.CDS.ErrorDetails.ApiExceptionHttpStatusCode\":\"400\",\"@Microsoft.PowerApps.CDS.HelpLink\":\"http://go.microsoft.com/fwlink/?LinkID=398563&error=Microsoft.Crm.CrmException%3a80040203&client=platform\",\"@Microsoft.PowerApps.CDS.InnerError.Message\":\"Communication activity cannot have more than one Sender party\"}}");
            logger.Log(operationException);

            Assert.NotNull(logger.LastError);
            Exception exOut = logger.LastException;
        }
Example #3
0
        /// <summary>
        /// Finds the server short name by server uri
        /// </summary>
        /// <param name="serverDisplayName">Name of the Server to find</param>
        /// <returns></returns>
        public string GetServerShortNameByDisplayName(string serverDisplayName)
        {
            try
            {
                if (serverDisplayName.Equals("User Defined Org Detail"))
                {
                    return(null);
                }

                if (_OSDPServers != null)
                {
                    return(_OSDPServers.FirstOrDefault(i => i.DisplayName.Equals(serverDisplayName, StringComparison.CurrentCultureIgnoreCase)).ShortName);
                }
            }
            catch (Exception Ex)
            {
                logger.Log(string.Format(CultureInfo.InvariantCulture, "Failed to find Short Name for {0}", serverDisplayName), System.Diagnostics.TraceEventType.Error, Ex);
            }
            return(null);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="level"></param>
        /// <param name="message"></param>
        /// <param name="containsPii"></param>
        static public void Log(LogLevel level, string message, bool containsPii)
        {
            if (_logEntry == null)
                _logEntry = new DataverseTraceLogger("Microsoft.IdentityModel.Clients.ActiveDirectory"); // set up logging client. 

            if (!EnabledPIILogging.HasValue)
            {
                EnabledPIILogging = true;//Utils.AppSettingsHelper.GetAppSetting("LogADALPII", false);
                _logEntry.Log($"Setting ADAL PII Logging Feature to {EnabledPIILogging.Value}", System.Diagnostics.TraceEventType.Information);
            }

            if (containsPii && !EnabledPIILogging.Value)
            {
                _logEntry.Log($"ADAL LOG EVENT SKIPPED --> PII Logging Disabled.", System.Diagnostics.TraceEventType.Warning);
                return;
            }

            // Add (PII) prefix to messages that have PII in them per AAD Message alert. 
            message = containsPii ? $"(PII){message}" : message; 

            switch (level)
            {
                case LogLevel.Info:
                        _logEntry.Log(message , System.Diagnostics.TraceEventType.Information);
                    break;
                case LogLevel.Verbose:
                        _logEntry.Log(message, System.Diagnostics.TraceEventType.Verbose);
                    break;
                case LogLevel.Warning:
                        _logEntry.Log(message, System.Diagnostics.TraceEventType.Warning);
                    break;
                case LogLevel.Error:
                        _logEntry.Log(message, System.Diagnostics.TraceEventType.Error);
                    break;
                default:
                    break;
            }
        }
        /// <summary>
        /// Get authority and resource for this instance.
        /// </summary>
        /// <param name="targetServiceUrl">URI to query</param>
        /// <param name="logger">Logger to write info too</param>
        /// <param name="clientFactory">HTTP Client factory to use for this request.</param>
        /// <returns></returns>
        private static async Task <AuthRoutingProperties> GetAuthorityFromTargetServiceAsync(IHttpClientFactory clientFactory, Uri targetServiceUrl, DataverseTraceLogger logger)
        {
            AuthRoutingProperties authRoutingProperties = new AuthRoutingProperties();
            var client = clientFactory.CreateClient("DataverseHttpClientFactory");
            var rslt   = await client.GetAsync(targetServiceUrl).ConfigureAwait(false);

            if (rslt.StatusCode == System.Net.HttpStatusCode.NotFound || rslt.StatusCode == System.Net.HttpStatusCode.BadRequest)
            {
                // didnt find endpoint.
                logger.Log($"Failed to get Authority and Resource error. Attempt to Access Endpoint {targetServiceUrl.ToString()} resulted in {rslt.StatusCode}.", TraceEventType.Error);
                return(authRoutingProperties);
            }

            if (rslt.Headers.Contains("WWW-Authenticate"))
            {
                var authenticateHeader = rslt.Headers.GetValues("WWW-Authenticate").FirstOrDefault();
                authenticateHeader = authenticateHeader.Trim();

                // This also checks for cases like "BearerXXXX authorization_uri=...." and "Bearer" and "Bearer "
                if (!authenticateHeader.StartsWith(Bearer, StringComparison.OrdinalIgnoreCase) ||
                    authenticateHeader.Length < Bearer.Length + 2 ||
                    !char.IsWhiteSpace(authenticateHeader[Bearer.Length]))
                {
                    //var ex = new ArgumentException(AdalErrorMessage.InvalidAuthenticateHeaderFormat,
                    //	nameof(authenticateHeader));
                    //CoreLoggerBase.Default.Error(AdalErrorMessage.InvalidAuthenticateHeaderFormat);
                    //CoreLoggerBase.Default.ErrorPii(ex);
                    //throw ex;
                }

                authenticateHeader = authenticateHeader.Substring(Bearer.Length).Trim();

                IDictionary <string, string> authenticateHeaderItems = null;
                try
                {
                    authenticateHeaderItems =
                        EncodingHelper.ParseKeyValueListStrict(authenticateHeader, ',', false, true);
                }
                catch                 //(ArgumentException ex)
                {
                    //var newEx = new ArgumentException(AdalErrorMessage.InvalidAuthenticateHeaderFormat,
                    //	nameof(authenticateHeader), ex);
                    //CoreLoggerBase.Default.Error(AdalErrorMessage.InvalidAuthenticateHeaderFormat);
                    //CoreLoggerBase.Default.ErrorPii(newEx);
                    //throw newEx;
                }

                if (authenticateHeaderItems != null)
                {
                    string param;
                    authenticateHeaderItems.TryGetValue(AuthorityKey, out param);
                    authRoutingProperties.Authority =
                        param.Replace("oauth2/authorize", "")                         // swap out the old oAuth pattern.
                        .Replace("common", "organizations");                          // swap common for organizations because MSAL reasons.
                    authenticateHeaderItems.TryGetValue(ResourceKey, out param);
                    authRoutingProperties.Resource = param;
                }
            }

            return(authRoutingProperties);
        }
        /// <summary>
        ///  Token refresh flow for MSAL User Flows.
        /// </summary>
        /// <param name="publicAppClient">MSAL Client to use.</param>
        /// <param name="scopes">Scopes to send in.</param>
        /// <param name="account"></param>
        /// <param name="promptBehavior">prompting behavior</param>
        /// <param name="clientCredentials">user credential package</param>
        /// <param name="useDefaultCreds">should system default creds be used</param>
        /// <param name="logSink">logger to write logs too.</param>
        /// <returns></returns>
        internal async static Task <AuthenticationResult> ObtainAccessTokenAsync(
            IPublicClientApplication publicAppClient,
            List <string> scopes,
            IAccount account,
            PromptBehavior promptBehavior,
            ClientCredentials clientCredentials,
            bool useDefaultCreds         = false,
            DataverseTraceLogger logSink = null)
        {
            // This works for user Auth flows.
            AuthenticationResult _authenticationResult = null;
            bool clientCredentialsCheck = clientCredentials != null && clientCredentials.UserName != null && !string.IsNullOrEmpty(clientCredentials.UserName.UserName) && !string.IsNullOrEmpty(clientCredentials.UserName.Password);
            // Login user hint
            string loginUserHint = (clientCredentials != null && clientCredentials.UserName != null) ? clientCredentials.UserName.UserName : string.Empty;

            if (publicAppClient != null)
            {
                if (clientCredentialsCheck && !useDefaultCreds && !(promptBehavior == PromptBehavior.Always || promptBehavior == PromptBehavior.SelectAccount))
                {
                    if (account != null)
                    {
                        _authenticationResult = await publicAppClient.AcquireTokenSilent(scopes, account).ExecuteAsync().ConfigureAwait(false);
                    }
                    else
                    {
                        _authenticationResult = await publicAppClient.AcquireTokenByUsernamePassword(scopes, clientCredentials.UserName.UserName, ServiceClient.MakeSecureString(clientCredentials.UserName.Password)).ExecuteAsync().ConfigureAwait(false);
                    }
                }
                else
                {
                    if (useDefaultCreds)
                    {
                        if (!string.IsNullOrEmpty(loginUserHint))
                        {
                            _authenticationResult = await publicAppClient.AcquireTokenByIntegratedWindowsAuth(scopes).WithUsername(loginUserHint).ExecuteAsync();
                        }
                        else
                        {
                            _authenticationResult = await publicAppClient.AcquireTokenByIntegratedWindowsAuth(scopes).ExecuteAsync();
                        }
                    }
                    else
                    {
                        logSink.Log(string.Format("ObtainAccessToken - PROMPT - Behavior: {0}", promptBehavior), TraceEventType.Verbose);
                        Microsoft.Identity.Client.Prompt?userPrompt = null;
                        switch (promptBehavior)
                        {
                        case PromptBehavior.Auto:
                            break;

                        case PromptBehavior.Always:
                            userPrompt = Microsoft.Identity.Client.Prompt.ForceLogin;
                            break;

                        case PromptBehavior.Never:
                        case PromptBehavior.RefreshSession:
                            userPrompt = Microsoft.Identity.Client.Prompt.NoPrompt;
                            break;

                        case PromptBehavior.SelectAccount:
                            userPrompt = Microsoft.Identity.Client.Prompt.SelectAccount;
                            break;

                        default:
                            break;
                        }

                        if (userPrompt != null)
                        {
                            _authenticationResult = await publicAppClient.AcquireTokenInteractive(scopes).WithLoginHint(loginUserHint).WithPrompt(userPrompt.Value).ExecuteAsync();
                        }
                        else
                        {
                            if (account != null)
                            {
                                _authenticationResult = await publicAppClient.AcquireTokenSilent(scopes, account).ExecuteAsync();
                            }
                            else
                            {
                                _authenticationResult = await publicAppClient.AcquireTokenInteractive(scopes).WithLoginHint(loginUserHint).ExecuteAsync();
                            }
                        }
                    }
                }
            }
            else
            {
                // throw here.
            }
            return(_authenticationResult);
        }