MSAL + Graph AcquireTokenForClient Issue - microsoft-graph-api

Intermittent Issue or stops working after a while: SSL error while calling AcquireTokenForClient from .NET Core WEB API.
Works fine in Dev machine. Seeing this issue in server and not sure when it stops. IIS server is running on-prem and calling Azure AD with MSAL.
Code directly used from Microsoft Sample to connect to Graph....
app = ConfidentialClientApplicationBuilder.Create(AuthHelper.ClientId)
.WithTenantId(AuthHelper.Tenant)
.WithClientSecret(AuthHelper.ClientSecret)
.Build();
// a tenant administrator
`string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
AuthenticationResult result = null;
try
{
result = await app.AcquireTokenForClient(scopes)
.ExecuteAsync(System.Threading.CancellationToken.None);
}
Exception:
The SSL connection could not be established, see inner exception.\nTrace
at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)

Related

How to enable an dotnet 6 client app in a linux container to authenticate with user/password against a kestrel server with kerberos

I'm developing a Service in ASP.Net-Core (.net6) that connects to a BusinessCentral OData API.
The Server has SSL/Kerberos enabled and I have a user and password to authenticate with.
I use the AddHttpClient-IServiceCollectionExtension in my Program.cs like:
services.AddHttpClient([name], httpClient =>
{
httpClient.BaseAddress = new Uri(config[BaseUrl]);
}).ConfigurePrimaryHttpMessageHandler(() =>
new HttpClientHandler()
{
Credentials = new NetworkCredential(
config[Username],
config[Password],
config[Domain])
}
);
...and inject the IHttpClientFactory to my client-class:
MyODataClient(IHttpClientFactory factory)
{
_client = factory!.CreateClient([name]);
}
public HttpResponseMessage GetEntity(Entity src)
{
var path = $"{src.Type}('{src.SysId}')?";
var request = new HttpRequestMessage(HttpMethod.Get, path);
var response = _client.SendAsync(request);
return response;
}
So far so good. Everything ist fine and works (with swagger)!
BUT...
When I start this service within a linux Docker container, I get the error:
GSSAPI operation failed with error - Unspecified GSS failure. Minor code may provide more information (Cannot find KDC for realm "[domain]").
Then I tried:
builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNegotiate();
builder.Services.AddAuthorization(options =>
{
options.FallbackPolicy = options.DefaultPolicy;
});
Now Swagger ask me for username and password. It doesn't work but I think this is why I don't passthrough the headers from swagger to BC19... with HeaderPropagation or so (??).
Btw. I need to use a static user. No interaction...
I also read and tried keytab-stuff... But everything I read was >2 years old. Does anybody know, how to deal with it in .net6? It seems to be quite easy but I don't find the two lines of code, that I need to connect the httpClientHandler (credentials) with the AuthenticationBuilder (negotiate)
Maybe this log helps:
Authorization failed. These requirements were not met:
DenyAnonymousAuthorizationRequirement: Requires an authenticated user.
[17:20:26 INF] AuthenticationScheme: Negotiate was challenged.
[17:20:26 INF] Request finished HTTP/1.1 GET https://localhost:49179/Entity?[...]. - - - 401 0 - 28.0138ms
best regards
Oli

EWS Authentication: OAuth with service account

I have below code and working fine when i am running the application from local machine.
But I am getting below exception while running the same application using a service account
// Configure the MSAL client to get tokens
var app = ConfidentialClientApplicationBuilder
.Create(ConfigurationManager.AppSettings["appId"])
.WithAuthority(AzureCloudInstance.AzurePublic, ConfigurationManager.AppSettings["tenantId"])
.WithClientSecret(ConfigurationManager.AppSettings["clientSecret"]).Build();
// The permission scope required for EWS access
var ewsScopes = new string[] { "https://outlook.office.com/.default" };
//Make the toekn request
AuthenticationResult authResult = await app.AcquireTokenForClient(ewsScopes).ExecuteAsync();
Exception : An error occurred while sending the request. at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task
Exception in is on
AuthenticationResult authResult = await app.AcquireTokenForClient(ewsScopes).ExecuteAsync();
Thanks in advance
Regards,
Nidheesh

Securing a Web API with Windows Server 2012 R2 ADFS 3.0 and Katana

I would like to make a MVC Web Application that talks to a Web API application and use ADFS 3.0 (on Windows 2012 R2) for authentication.
I managed to make the MVC Web Application to authenticate using ADFS.
and configured everything as shown in this article by Vittorio Bertocci
http://www.cloudidentity.com/blog/2013/10/25/securing-a-web-api-with-adfs-on-ws2012-r2-got-even-easier/
Now I use the latest pre release of AAL from nuget
Now after authenticating with ADFS from web MVC app, I try to call the webapi
public async Task<String> CallSecuredAPI()
{
string authority = "https://fs.domain.com/adfs";
string resourceURI = "https://{hostheader}/SecuredAPI";
string clientID = "ExternalWebSite1";
string clientReturnURI = "https://{hostheader}/ExternalSite";
AuthenticationContext ac = new AuthenticationContext(authority, false);
AuthenticationResult ar = ac.AcquireToken(resourceURI, clientID, new Uri(clientReturnURI));
string authHeader = ar.CreateAuthorizationHeader();
var client = new HttpClient();
HttpRequestMessage request =
new HttpRequestMessage(HttpMethod.Get, "https://hostheader/SecuredAPI/api/Claims");
request.Headers.TryAddWithoutValidation("Authorization", authHeader);
HttpResponseMessage response = await client.SendAsync(request);
string responseString = await response.Content.ReadAsStringAsync();
return responseString;
}
but I get this error which I think is with the client not being a UI based client or WPF , windows App. Can someone let me know whether I am doing something wrong.
![Error when trying to get Authorization code using AAL][1]
Server Error in '/ExternalSite' Application.
Showing a modal dialog box or form when the application is not running in UserInteractive mode is not a valid operation. Specify the ServiceNotification or DefaultDesktopOnly style to display a notification from a service application.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: Showing a modal dialog box or form when the application is not running in UserInteractive mode is not a valid operation. Specify the ServiceNotification or DefaultDesktopOnly style to display a notification from a service application.
Source Error:
Line 43:
Line 44: AuthenticationContext ac = new AuthenticationContext(authority, false);
Line 45: AuthenticationResult ar = ac.AcquireToken(resourceURI, clientID, new Uri(clientReturnURI));
Line 46:
Line 47: string authHeader = ar.CreateAuthorizationHeader();
Source File: c:\Users\balakrishna.takkalla\Documents\Visual Studio 2013\Projects\ExternalSite\ExternalSite\Controllers\HomeController.cs Line: 45
Stack Trace:
[InvalidOperationException: Showing a modal dialog box or form when the application is not running in UserInteractive mode is not a valid operation. Specify the ServiceNotification or DefaultDesktopOnly style to display a notification from a service application.]
System.Windows.Forms.Form.ShowDialog(IWin32Window owner) +5701502
Microsoft.IdentityModel.Clients.ActiveDirectory.Internal.WindowsFormsWebAuthenticationDialog.ShowBrowser() +18
Microsoft.IdentityModel.Clients.ActiveDirectory.Internal.WindowsFormsWebAuthenticationDialog.OnAuthenticate() +23
Microsoft.IdentityModel.Clients.ActiveDirectory.Internal.WindowsFormsWebAuthenticationDialogBase.AuthenticateAAD(Uri requestUri, Uri callbackUri) +284
Microsoft.IdentityModel.Clients.ActiveDirectory.Internal.InteractiveWebUI.OnAuthenticate() +103
Microsoft.IdentityModel.Clients.ActiveDirectory.OAuth2Request.SendAuthorizeRequest(Authenticator authenticator, String resource, Uri redirectUri, String clientId, String userId, PromptBehavior promptBehavior, String extraQueryParameters, IWebUI webUi, CallState callState) +363
Microsoft.IdentityModel.Clients.ActiveDirectory.<>c__DisplayClass9b.<AcquireAuthorization>b__9a() +111
System.Threading.Tasks.Task.Execute() +110
if I understood correctly: you want to access a Web API from the code-behind of an MVC application.
That topology is possible with Azure Active Directory today, you can see that in action in the sample https://github.com/AzureADSamples/WebApp-WebAPI-OpenIDConnect-DotNet (I am in the process of updating it to the latest ADAL refresh, you can take a peek at the RCUpdate branch to see the work in progress).
However that topology is NOT achievable today from ADFS WS2012 R2. The reason is that an MVC app (and any other web site) is a confidential client, which OAuth2 handles differently from a public client (the WPF app you used as a starting point is a public client). In the scenario you are targeting, to use ADAL for getting a token from a confidential client you would use ADAL's method AcquireTokenByAuthorizationCode (see the sample I mentioned). However ADFS WS2012 R2 is incapable of processing that method. Today the OAuth2 support in ADFS WS2012 R2 is limited to public clients only.
Sorry for bringing bad news! As a mitigation, you might consider federating your ADFS with an AAD tenant: at that point you would be able to do what you want, authenticating as an ADFS user but getting tokens from AAD (which does support the necessary OAuth2 grant).
HTH
V.

request.GetClientCertificate Returns null on Specific Server

I'm using client certificate authentication in an ASP.NET Web API 2 application. I attach the cert to the client like so:
private HttpClient GetHttpClient()
{
HttpClient client = new HttpClient(GetRequestHandler());
client.BaseAddress = new Uri(_apiBaseUrl);
return client;
}
private WebRequestHandler GetRequestHandler()
{
WebRequestHandler wrh = new WebRequestHandler();
X509Certificate cert = GetClientCert();
wrh.ClientCertificates.Add(cert);
return wrh;
}
private X509Certificate GetClientCert()
{
X509Store certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
certStore.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
X509Certificate cert = certStore.Certificates.Find(X509FindType.FindBySubjectName, _certName, false)[0];
certStore.Close();
return cert;
}
When I debug this, I can see the cert object is what I expected. In the Web API on the server, I'm using this:
public class CertAuthHandler : DelegatingHandler
{
private ICertValidator _certValidator;
public CertAuthHandler(ICertValidator certValidator)
{
this._certValidator = certValidator;
}
public CertAuthHandler() : this(new LoggingCertValidator()) { }
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var cert = request.GetClientCertificate();
if (_certValidator.IsValid(cert))
{
return base.SendAsync(request, cancellationToken);
}
return Task<HttpResponseMessage>.Factory.StartNew(() => request.CreateResponse(HttpStatusCode.Unauthorized));
}
}
This works exactly as expected on our TEST server; however on our DEV server the logging that I added for troubleshooting this shows that request.GetClientCertificate() is returning null. I double and triple checked that IIS is set up to accept client certificates; when I change it to require ssl and require client certs it returns a 403. If it matters, the Web API is set up as a sub-application of the main website in both DEV and TEST.
I also had some teammates try the client from their machines to make sure it wasn't something to do with how the cert is installed on my machine (we are using different certs for DEV and TEST); they also get a 401. I've used the same DEV cert to call another API with the same security setup, and it works without issue, so it seems to be server-specific. My question is, what could I look into on our DEV server that might be causing the cert to not come through with the request? I'm not able to find anything related to this in the server's event logs, with the exception of my own custom logging that I added to the API. Any help is greatly appreciated.

facebook login using oauth fails on live server

I'm using OAuthWebSecurity to login with facebook and it is working on localhost. However then deployed to the live server I get the following error message:
The remote server returned an error: (400) Bad Request.
I have checked domain details are correct on facebook.
The sandbox mode is disabled.
I have disabled windows firewall - still get same error.
Responses from facebook have the identical format whether in live or localhost environments.
The date and time of live server is correct.
I've test locally also changing the host file to the live domain - still works locally.
Here's the stack trace:
[WebException: The remote server returned an error: (400) Bad Request.]
System.Net.WebClient.DownloadDataInternal(Uri address, WebRequest& request) +3291120
System.Net.WebClient.DownloadString(Uri address) +207
DotNetOpenAuth.AspNet.Clients.FacebookClient.QueryAccessToken(Uri returnUrl, String authorizationCode) +293
DotNetOpenAuth.AspNet.Clients.OAuth2Client.VerifyAuthentication(HttpContextBase context, Uri returnPageUrl) +167
DotNetOpenAuth.AspNet.OpenAuthSecurityManager.VerifyAuthentication(String returnUrl) +502
Microsoft.Web.WebPages.OAuth.OAuthWebSecurity.VerifyAuthenticationCore(HttpContextBase context, String returnUrl) +231
Any suggestions?
There was a defect open related to this:
https://github.com/DotNetOpenAuth/DotNetOpenAuth/issues/203
but that library is apparently no longer being maintained:
https://github.com/DotNetOpenAuth/DotNetOpenAuth/issues/317#issuecomment-29580565
... although it is still referenced in many Microsoft documents. Investigating that related to another defect.
See also: The remote server returned an error: (400) Bad Request Microsoft.AspNet.Membership.OpenAuth
This is an old one, but I hit a 400 error from Facebook login using dotnetopenoath as it is integrated into MVC 4 and went on a wild goose chase thinking that it was not working because it was no longer supported, when all I needed to do was catch and log the exception which pointed me to the problem for my particular case (app secret proof setting):
catch (WebException exception)
{
using (WebResponse response = exception.Response)
{
HttpWebResponse httpResponse = (HttpWebResponse) response;
m_log.Debug("Error code: " + httpResponse.StatusCode);
using (Stream data = response.GetResponseStream())
{
if (data != null)
{
using (var reader = new StreamReader(data))
{
string text = reader.ReadToEnd();
m_log.Debug(text);
}
}
}
}
}

Resources