I am working on an API but I get this error whenever I try to connect to it:
Request starting HTTP/1.1 GET https://localhost:5061/api/machine
info: System.Net.Http.HttpClient.IdentityModel.AspNetCore.OAuth2Introspection.BackChannelHttpClientName.LogicalHandler[100]
Start processing HTTP request GET https://localhost:5001/.well-known/openid-configuration
info: System.Net.Http.HttpClient.IdentityModel.AspNetCore.OAuth2Introspection.BackChannelHttpClientName.ClientHandler[100]
Sending HTTP request GET https://localhost:5001/.well-known/openid-configuration
fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
An unhandled exception has occurred while executing the request.
System.InvalidOperationException: Error parsing discovery document from https://localhost:5001: Error connecting to https://localhost:5001/.well-known/openid-configuration. The SSL connection could not be established, see inner exception..
at IdentityModel.AspNetCore.OAuth2Introspection.PostConfigureOAuth2IntrospectionOptions.GetIntrospectionEndpointFromDiscoveryDocument(OAuth2IntrospectionOptions options)
at IdentityModel.AspNetCore.OAuth2Introspection.PostConfigureOAuth2IntrospectionOptions.InitializeIntrospectionClient(OAuth2IntrospectionOptions options)
at IdentityModel.AspNetCore.OAuth2Introspection.OAuth2IntrospectionHandler.LoadClaimsForToken(String token)
at IdentityModel.AspNetCore.OAuth2Introspection.OAuth2IntrospectionHandler.HandleAuthenticateAsync()
at Microsoft.AspNetCore.Authentication.AuthenticationHandler`1.AuthenticateAsync()
at Microsoft.AspNetCore.Authentication.AuthenticationService.AuthenticateAsync(HttpContext context, String scheme)
at Microsoft.AspNetCore.Authentication.AuthenticationHandler`1.AuthenticateAsync()
at Microsoft.AspNetCore.Authentication.AuthenticationService.AuthenticateAsync(HttpContext context, String scheme)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished in 145.1044ms 500 text/plain
I am using IdentityModel.AspNetCore.OAuth2Introspection but there is no option there to configure a Backchannel HTTP Handler like the ones in JwtBearer.
I want to bypass certificate validation in OAuth2Introspection in development, something like the code below:
.AddOAuth2Introspection("introspection", options =>
{
// SOME INTROSPECTION CODES
options.BackchannelHttpHandler = new SocketsHttpHandler
{
SslOptions = new SslClientAuthenticationOptions
{
RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true
}
};
// MORE INTROSPECTION CODES
});
resolved it by using ConfigurePrimaryHttpMessageHandler extension method
services.AddHttpClient(OAuth2IntrospectionDefaults.BackChannelHttpClientName)
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new SocketsHttpHandler
{
SslOptions = new SslClientAuthenticationOptions
{
RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true
}
};
});
Related
I have a Net 5 Blazor server app published on a productive server and used a valid certificate for https requests, which works fine. Newly I added a HubConnection class to support SignalR notifications between web pages. But if I call the web page through an public URL like https://crm.example.com/call, I get the following error, although the same page works fine if I call it through an internal URL like https://10.12.0.151/call:
Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host..
I don't know why it happens, what is the difference between a public and private HTTPS connection? For public connection it uses a valid public certificate and actually works fine if I deactivate the SignalR notification.
Because of the application working just fine if I call the page with an internal URL, seems that all prerequisite for using SignalR are included such as installing WebSocket-Protocol feature on the server and so on.
The following snipet shows the part of code:
try
{
string sHubUrl = NavManager.BaseUri;
sHubUrl = sHubUrl.TrimEnd('/') + "/call";
LogBuilder.LogInfo($"URL in Call.NotificationInit: " + sHubUrl);
hubConnection = new HubConnectionBuilder()
.WithUrl(sHubUrl, options => {
options.UseDefaultCredentials = true;
options.HttpMessageHandlerFactory = (msg) =>
{
if (msg is HttpClientHandler clientHandler)
{
System.Net.ServicePointManager.SecurityProtocol |= System.Net.SecurityProtocolType.Tls11 | System.Net.SecurityProtocolType.Tls12;
// bypass SSL certificate
clientHandler.ServerCertificateCustomValidationCallback +=
(sender, certificate, chain, sslPolicyErrors) => { return true; };
}
return msg;
};
})
.WithAutomaticReconnect()
.Build();
hubConnection.On<string, string>("NewMessage", ReceivedNotification);
await hubConnection.StartAsync();
}
catch (Exception ex)
{
LogBuilder.LogExecption("Exception at Call.NotificationInit");
}
What else should I do? Can anyone help me to solve this problem?
Getting errors when an api is trying to validate a reference token. Our identity server will serve reference tokens only. Why would a signing certificate be required. The error is keyset related.
System.InvalidOperationException: Policy error while contacting the discovery endpoint https://****.net/.well-known/openid-configuration: Keyset is missing
at IdentityModel.AspNetCore.OAuth2Introspection.PostConfigureOAuth2IntrospectionOptions.GetIntrospectionEndpointFromDiscoveryDocument(OAuth2IntrospectionOptions Options)
at IdentityModel.AspNetCore.OAuth2Introspection.PostConfigureOAuth2IntrospectionOptions.InitializeIntrospectionClient(OAuth2IntrospectionOptions Options)
at IdentityModel.AspNetCore.OAuth2Introspection.OAuth2IntrospectionHandler.LoadClaimsForToken(String token)
at IdentityModel.AspNetCore.OAuth2Introspection.OAuth2IntrospectionHandler.HandleAuthenticateAsync()
at Microsoft.AspNetCore.Authentication.AuthenticationHandler`1.AuthenticateAsync()
at Microsoft.AspNetCore.Authentication.AuthenticationService.AuthenticateAsync(HttpContext context, String scheme)
at IdentityServer4.AccessTokenValidation.IdentityServerAuthenticationHandler.HandleAuthenticateAsync()
at Microsoft.AspNetCore.Authentication.AuthenticationHandler`1.AuthenticateAsync()
at Microsoft.AspNetCore.Authentication.AuthenticationService.AuthenticateAsync(HttpContext context, String scheme)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Cors.Infrastructure.CorsMiddleware.Invoke(HttpContext context)
at Ips.Middleware.SerilogMiddleware.Invoke(HttpContext httpContext)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)
You can stop the key information being return from the DiscoveryEndpoint by setting IdentityServerOptions in IdentityServer:
options.Discovery.ShowKeySet = false;
Looking the implementation of PostConfigureOAuth2IntrospectionOptions.InitializeIntrospectionClient:
private async Task<IntrospectionClient> InitializeIntrospectionClient(OAuth2IntrospectionOptions Options)
{
string endpoint;
if (Options.IntrospectionEndpoint.IsPresent())
{
endpoint = Options.IntrospectionEndpoint;
}
else
{
endpoint = await GetIntrospectionEndpointFromDiscoveryDocument(Options).ConfigureAwait(false);
Options.IntrospectionEndpoint = endpoint;
}
IntrospectionClient client;
if (Options.IntrospectionHttpHandler != null)
{
client = new IntrospectionClient(
endpoint,
headerStyle: Options.BasicAuthenticationHeaderStyle,
innerHttpMessageHandler: Options.IntrospectionHttpHandler);
}
else
{
client = new IntrospectionClient(endpoint);
}
client.Timeout = Options.DiscoveryTimeout;
return client;
}
You can avoid the call to GetIntrospectionEndpointFromDiscoveryDocument by setting the IntrospectionEndpoint property on OAuth2IntrospectionOptions
Found the solution. You do not need to make changes to identity. The changes are to the api.
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(
IdentityServerAuthenticationDefaults.AuthenticationScheme,
//Null if you do not want to support jwt bearer tokens
null,
options =>
{
options.Authority = "https://yourIdentityServer.com";
//This is the key
options.DiscoveryPolicy.RequireKeySet = false;
options.ClientId = "xxxx";
options.ClientSecret = "xxxx";
});
It looks like something is validating the discovery document to ensure it’s well formed. You could probably disable this validation by overriding the policy but since you’ll need a signing key for id_tokens anyway you might as well set up the signing and validation credentials.
I have oauth token implemented on server side but upon Invalid token or Token expirey i am getting 200 http status code but in response body i have
{"code":"4XX", "data":{"some":"object"}
When i try to read string in interceptor i get okhttp dispatcher java.lang.illegalstateexception closed because response.body().string() must be called only once.
Also i read from here Refreshing OAuth token using Retrofit without modifying all calls that we can use OkHttp Authenticator class but it works only with 401/407 i havent triedn as i will not get this. Is there any way we can customize Authenticator and proceed our logic inside it.
Thank you
If it possible, try to talk with your server side about response codes. Communication is also a very important skill.
If it inpossible, you can modify response codes manually with reflection, it enables okHttp authentication logic.
public OkHttpClient getOkHttpClient() {
return new OkHttpClient.Builder()
.authenticator((route, response) -> {
System.out.println("it working");
return null;
})
.addNetworkInterceptor(new UnauthorizedCaseParserInterceptor())
.build();
}
public class UnauthorizedCaseParserInterceptor implements Interceptor {
#Override
public Response intercept(#NonNull Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
if (isUnauthorizedResponse(response)) {
try {
Field codeField = response.getClass().getDeclaredField("code");
codeField.setAccessible(true);
codeField.set(response, HttpURLConnection.HTTP_UNAUTHORIZED);
} catch (Exception e) {
return response;
}
}
return response;
}
private boolean isUnauthorizedResponse(Response response) {
//parse response...
}
}
Please use this solution only as a last resort.
I am trying to implement a custom authentication filter in Guice. I receive the token, get the username and realm from the token and then create a Principal. Now I am stuck and I don't know how to set the Principal. It would be nice if I could just set it like this request.setUserPrincipal(principal);, but obviously I can't.
How can I do this?
My doFilter method looks like this:
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
if (authorizationHeader != null && authorizationHeader.length() > 0) {
String token = authorizationHeader.substring("Bearer".length()).trim();
if (token.length() > 0) {
try {
Credentials credentials = securityService.getCredentials(token);
String username = credentials.getUsername();
String realm = credentials.getRealm();
Principal principal = new HttpPrincipal(username, realm);
// request.setUserPrincipal(principal);
LOGGER.info(credentials);
} catch (Exception e) {
LOGGER.error(e);
}
}
}
filterChain.doFilter(servletRequest, servletResponse);
}
The servlet spec section 13.10 says:
The container establishes the caller identity of a request prior to
dispatching the request to the servlet engine. The caller identity
remains unchanged throughout the processing of the request or until
the application sucessfully calls authenticate, login or logout on the
request.
That is the reason why there is no setUserPrincipal.
But there are good news. You can provide your own getUserPrincipal because you can provide your own HttpServletRequest object. Any servlet filter can do it. Look at your code, you are calling the chain method with two parameters: the request and the response. There is no need to pass the same objects that you receive.
The spec even provides you with a helper class: HttpServletRequestWrapper. You just create your own request class as a subclass of the wrapper and override any method that you want, like getUserPrincipal.
I am accessing a web service using WCF. Using WSHttpBinding, Security mode is set Transport (https) and client credential type is Basic. When I try to access the service using the proxy, getting an 401 unauthorized exception.
Here is the Binding
var binding = new WSHttpBinding()
{
UseDefaultWebProxy = true,
Security =
{
Mode = SecurityMode.Transport,
Transport =
{
ClientCredentialType = HttpClientCredentialType.Basic,
},
}
};
Here is the service call
var client = new InternetClient(binding, new EndpointAddress("httpsurl"));
client.ClientCredentials.UserName.UserName = "username";
client.ClientCredentials.UserName.Password = "password";
client.ProcessMessage("somevalue");
When looked into Http headers using Http Analyzer
CONNECT HEADER
(Request-Line):CONNECT somehost.com:443 HTTP/1.1
Host:somehost.com
Proxy-Connection:Keep-Alive
POST HEADER
(Request-Line):POST /Company/1.0 HTTP/1.1
Content-Type:application/soap+xml; charset=utf-8
VsDebuggerCausalityData:uIDPo+voStemjalOv5LtRotFQ7UAAAAAUKLJpa755k6oRwto14BnuE2PDtYKxr9LhfqXFSOo8pEACQAA
Host:somehost.com
Content-Length:898
Expect:100-continue
Connection:Keep-Alive
If you see the header Authorization header is missing
Now my question is why WCF call missing the Authorization header? Am I missing something? . Please ask if you need more information
This is a common problem, but the situation is different from what you think.
It turns out that initially for the 1st request a WCF client that is configured to use HTTP basic authentication will nevertheless send the request without the necessary Authorization header to the server. This is the default behavior of the HttpWebRequest class used by the WCF client.
Normally, the web service server will then return a HTTP 401 Unauthorized response to the WCF client, upon which the latter will resend the message with the Authorization header. This means under normal conditions for HTTP Basic Authentication there will be a a rather useless round trip to the server.
This also explains why the header was missing in your sniffed message. Some Http sniffs possibly don't pass on the 401 response, so the whole exchange gets messed up.
The server round-trip and dependence on the 401 response can be avoided by manually injecting the required Authorization header into every request. See e.g. how to manually inject Authorization header into WCF request
As a slight modification from a previous answer, to support async / await calls, you can actually create a new OperationContext and pass it around on whatever thread you like (as long as it is not shared across concurrent threads as it isn't a thread-safe object)
var client = new MyClient();
client.ClientCredentials.UserName.UserName = "username";
client.ClientCredentials.UserName.Password = "password";
var httpRequestProperty = new HttpRequestMessageProperty();
httpRequestProperty.Headers[HttpRequestHeader.Authorization] = "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes(client.ClientCredentials.UserName.UserName + ":" + client.ClientCredentials.UserName.Password));
var context = new OperationContext(ormClient.InnerChannel);
using (new OperationContextScope(context))
{
context.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = httpRequestProperty;
return await client.SomeMethod();
}
I had the exact same issues. I was able to manually inject the authorization headers by using the following code:
var callcontext = new CAdxCallContext();
callcontext.codeLang = "ENG";
callcontext.poolAlias = "BGRTEST";
var proxy = new CAdxWebServiceXmlCCClient();
proxy.Endpoint.EndpointBehaviors.Add(new CustomEndpoint());
proxy.ClientCredentials.UserName.UserName = "USERNAME"; // Might not benecessary
proxy.ClientCredentials.UserName.Password = "PASSWORD"; // Might not benecessary
string inputXml = "<PARAM>" +
"<GRP ID= \"GRP1\">" +
"<FLD NAME = \"ITMREF\">" + "100001" + "</FLD>" +
"</GRP>" +
"</PARAM>";
CAdxResultXml response;
try
{
response = proxy.run(callcontext, "BGR_SIEPRO", inputXml);
}
catch (TimeoutException timeout)
{
Console.WriteLine(timeout.Message);
// handle the timeout exception.
proxy.Abort();
}
catch (CommunicationException commexception)
{
Console.WriteLine(commexception.Message);
// handle the communication exception.
proxy.Abort();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
proxy.Close();
}
}
public class ClientMessageInspector : IClientMessageInspector
{
public void AfterReceiveReply(ref Message reply, object correlationState)
{
// Nothing Here
Console.Write(reply.ToString());
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
HttpRequestMessageProperty httpRequestProperty = new HttpRequestMessageProperty();
httpRequestProperty.Headers[HttpRequestHeader.Authorization] = "Basic " +
Convert.ToBase64String(Encoding.ASCII.GetBytes("USERNAME" + ":" +
"PASSWORD"));
request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestProperty);
return null;
}
}
public class CustomEndpoint : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
// Nothing here
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.ClientMessageInspectors.Add(new ClientMessageInspector());
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
// Nothing here
}
public void Validate(ServiceEndpoint endpoint)
{
// Nothing here
}
}
Notice the Expect:100-continue in the header. That's the reason for the round trip.
Put this in your web.config and try again:
<system.net>
<settings>
<servicePointManager expect100Continue="false"/>
</settings>
</system.net>
Actually, I was wrong about this question. I did see different behaviour when running HTTP analyzer. While Http anaylzer running, my application crashed after receiving 401 response. When Http analyzer application closed, the above code worked as expected.