/// <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; } }
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; }
/// <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); }