How to do Preemptive authentication using Java 11 HTTP client? - basic-authentication

I am trying to use Java 11 HTTP Client against an authenticated service, using Basic Authentication.
The authentication occurs successfully but it makes an additional round-trip to the server, to understand it should send the authentication data.
Have searched around documentation and code and at some point internally it uses some kind of cache, but I am unable to set the cache value.
Here is my client code:
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://someurl.com"))
.build();
HttpClient client = HttpClient.newBuilder()
.authenticator(new Authenticator() {
#Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("user", "pass".toCharArray());
}
})
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
What I expected was that somehow I could tell the client to preemptively sent the authentication data, not only when the server requests.

The HttpClient behaves in the same way than HttpURLConnection in what preemptive authentication is concerned: for basic authentication it will preemptively send the credentials if it finds them in its cache. However, the cache is populated after the first successful request (or more exactly after the response headers indicating that the authentication was successful are parsed).
If this is not satisfactory for you then a possibility is to handle authentication directly in your code by preemptively inserting the Authorization header in your request, and not setting any Authenticator.

Thanks to #daniel, this is the solution I came up with, adding the header to the HttpRequest and removing Authenticator.
String encodedAuth = Base64.getEncoder()
.encodeToString(("user" + ":" + "pass").getBytes(StandardCharsets.UTF_8));
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://someurl.com"))
.header("Authorization", "Basic " + encodedAuth)
.build();
HttpClient client = HttpClient.newHttpClient();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
Wish the client has some other way to tell to preemptively send authentication data rather than manually creating the Header, but this way it works.

Related

Using Identity Server 3, ClaimsPrinciple null even after successful bearer token authentication

I have a test console app which I'm pointing at a local instance of Identity Server 3 to request an access token. The following code does this and returns my token fine (passing a single scope "scope.test.client").
static TokenResponse GetClientToken(string clientId, string clientSecret, string[] scopes)
{
var uri = new Uri(string.Concat(ID_BASE_URI, ID_URL_TOKEN));
var client = new TokenClient(
uri.AbsoluteUri,
clientId,
clientSecret);
return client.RequestClientCredentialsAsync(string.Join(" ", scopes)).Result;
I then use this token to call an API also running locally. This takes the TokenResponse obtained above and passed it to this method:
static void CallApi(string url, TokenResponse response)
{
try
{
using (var client = new HttpClient())
{
client.SetBearerToken(response.AccessToken);
Console.WriteLine(client.GetStringAsync(url).Result);
}
}
catch (Exception x)
{
Console.WriteLine(string.Format("Exception: {0}", x.Message));
}
}
The API (an ASP.NET WebApi project) uses an Owin Startup class to enforce bearer token authentication for all requests:
appBuilder.Map(baseApiUrl, inner =>
{
inner.UseWebApi(GlobalConfiguration.Configuration);
// Enforce bearer token authentication for all API requests
inner.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = "https://identityserver/core",
ValidationMode = ValidationMode.ValidationEndpoint,
RequiredScopes = new[] { "scope.test.client" }
});
});
It also ensures all API requests are handled by a custom authorize attribute:
GlobalConfiguration.Configuration.Filters.Add(new DefaultApiAuthorizeAttribute());
Debugging this API, the first line in my overridden OnAuthorize method (in DefaultApiAuthorizeAttribute) is this:
var caller = actionContext.RequestContext.Principal as System.Security.Claims.ClaimsPrincipal;
If I break on this line I can see that actionContext.RequestContext.Principal is always null. However, I can see that ((System.Web.Http.Owin.OwinHttpRequestContext)actionContext.RequestContext).Request.Headers contains an Authorization header with the bearer token passed from my console app.
So it would seem that the API project is not authenticating the bearer token. Certainly the Identity Server logs suggest it isn't being hit at all after issuing the initial access token. So I'd appreciate your expert advice about why this might not be happening, or at least some pointers about where to look.
I suspect it might have something to do with SSL. Both sites are hosted locally under self-signed SSL certs, although Identity Server is configured to not require SSL and uses the idsrv3test.pfx development certificate for signing. I do have another test MVC web app which delegates authentication to the same IS3 instance which works fine locally, so I believe my IS3 instance is configured correctly.
You need to call UseIdentityServerBearerTokenAuthentication before you call UseWebApi. When you set up an OWIN Middleware Pipeline, the order is important.
In your case, Web API will be handling your requests before they get sent onto Identity Server (if they get sent on at all).
I imagine a range of possible issues could have the impact I described, but in my case I was able to find the cause by adding a diagnostics log to my consuming API. This led me to discover that the problem was an assembly conflict. The Owin middleware was looking for a Newtonsoft.JSON assembly with version 8.0.0.0 but my consuming API (actually running on top of a CMS intance) was using 7.0.0.0.
For anyone else who wants to find the answer fast, rather than spend hours tweaking configurations, here's the documentation that describes how to add this logging: https://identityserver.github.io/Documentation/docsv2/consuming/diagnostics.html

Why isn't OAuth2 client refreshing expired access_token?

I've got a client app configured with #EnableOAuth2Sso and #EnableZuulProxy, and a resource server (separate app) configured with #EnableOAuth2Resource. I can see that the client correctly authenticates to the resource server with Authorization: Bearer {access_token here}, but when once the access token expires, the proxied resource server request fails permanently.
[Edited]
I've modified my resource server by providing a custom RemoteTokenServices bean that uses OpenAM's /tokeninfo endpoint to decide whether an access_token remains valid. (The Spring-provided RemoteTokenServices bean attempts to POST, which gets a 405 from OpenAM). When I detect the access_token is invalid, I throw InvalidTokenException from my.spring.oauth2.OpenAMRemoteTokenServices#loadAuthentication. Now, my resource server is (I think correctly) sending HTTP 401 on the response to the client, in the case where the access_token has expired.
Still, the client is not attempting to refresh the token.
Maybe my mental model is wrong. I expect the client, in the case of expired access_token, to automatically use the refresh_token to obtain a new one. I don't know whether I think it should proactively refresh an access_token (within some epsilon before expiry time), or wait for a downstream request to fail and try refreshing then. But my client appears to be doing neither, and I can't tell why not.
As stated in this git issue: https://github.com/spring-guides/tut-spring-security-and-angular-js/issues/140, the problem might be related to the fact that with versions 1.4 and above of spring boot the Zuul filter that handles the downstream of access tokens to services (org.springframework.cloud.security.oauth2.proxy.OAuth2TokenRelayFilter) is missing a bean of type OAuth2RestTemplate, which is used by the filter itself to automatically handle the refresh_token grant when access tokens expire.
I had the same issue and I solved it by adding in a configuration class the following bean:
#Configuration
public class ZuulConfiguration {
#Bean
protected OAuth2RestTemplate oauth2RestTemplate(OAuth2ProtectedResourceDetails resource,
OAuth2ClientContext context) {
return new OAuth2RestTemplate(resource, context);
}
}

Passing connection token is secure or hacky from signalr js client side to hub function

i read this article http://www.asp.net/signalr/overview/security/introduction-to-security#connectiontoken
JS Client
$.connection.hub.qs = { "token" : tokenValue };
$.connection.hub.start().done(function() { /* ... */ });
.NET Client
var connection = new HubConnection("http://foo/",
new Dictionary<string, string>
{
{ "token", tokenValue }
});
Inside a Hub you can access the community name through the Context:
Context.QueryString["token"]
Setting Headers on the .NET Client
var connection = new HubConnection("http://foo/");
connection.Headers.Add("token", tokenValue);
i notice that we can pass some token value from client side to hub function as query string.....if i pass the anything as query string is not safe. so tell me best way to pass token value in secured way from client to hub function as a result no one can hack/change or reuse that token value.
one guy said SignalR uses encryption and a digital signature to protect the connection token..
so please tell me is it true that signalr first encrypt token value and then pass from client side to hub?
suggest me how one can pass token value to hub in secure way. thanks
You can use headers in the javascript client like this for example:
$.signalR.ajaxDefaults.headers = { Authorization: "Bearer " + yourToken };
Now not hacky and is a global setting you can do once at startup or successful auth response! Enjoy!
Now only if I can find a way to override this on a per-request basis so I can get a user emulation sandbox working for my users in administrative roles...
When the client initiates a connection the server creates and encrypts a connection token and sends it to the client. From this point on the client has to send the connection token each time it sends a request to the server. The server verifies the connection token when it receives a request. If you are asking how to prevent from using the same token by multiple clients then I think you need to look at authentication.

Cache database results in WebAPI authentication

I am creating a RESTful web service using ASP.NET WebApi. I am requiring that all incoming requests go over SSL because I will be using Basic HTTP Authentication. The client is responsible for including the credentials in the request header and the web service will authenticate the client on each request.
The actual authentication requires making a database call. Is there a way to cache the database results (the username and password) so that I don't have to make a database call for every incoming request that occurs in a short period of time?
So when a request comes in the web service will look for the username/password combo in the cache. If it is found it will process the request. If it isn't found, it will make the database call to authenticate and then add the username/password to the cache.
The easiest cache that I can think of to use would be using System.Web.Caching. Below is an extremely simplified version of adding a value to the current cache and then retrieving it for processing. I would not recommend using this code as is.
// GET api/values/5
[HttpGet]
public string GetValue(int id)
{
HttpContext.Current.Cache.Insert("2", "test1");
var value = Convert.ToString(HttpContext.Current.Cache.Get(id.ToString()));
return !string.IsNullOrEmpty(value) ? value : "nothing found";
}

SignalR wth gzip compression

Having some problem developing a SignalR client for a Hub hosted in asp.net website with gzip compression enabled. Since we are using IIS compression, the response from SignalR also gets compressed, but, the client does not understand the response and we get a Json parsing error on the client side.
SignalR internally uses HttpWebRequest to make make http requests and HttpWebRequest can be configured to automatically decompress the response using AutomaticDecompression property. So, if somehow I can get hold of the HttpWebRequest object used by SignalR to make the request, I should be able to set the enable automatic decompression.
I thought I should be able to get access to the HttpWebRequest by providing HubConnection.Start with my custom implementation of IHttpClient, IHttpClient.GetAsync takes a prepareRequest action which I thought should give me access to the HttpWebRequest, but, HttpHelper.GetAsync wraps the HttpWebRequest with HttpWebRequestWrapper before passing to prepareRequest and HttpWebRequestWrapper does not provide access to HttpWebRequest.
HttpHelper class is internal so can't use it as well, so, I am not exactly sure how to enable automatic decompression with SignalR.
I can expose the HttpWebRequest in HttpWebRequestWrapper, but, would prefer a simpler solution if one exists. Any thougths?
I am using SignalR version 0.5.1.10822
My auto decompression HttpClient:
public class HttpClientWithAutoDecompression : IHttpClient
{
readonly DefaultHttpClient _httpClient = new DefaultHttpClient();
private readonly DecompressionMethods _decompressionMethods;
public HttpClientWithAutoDecompression(DecompressionMethods decompressionMethods)
{
_decompressionMethods = decompressionMethods;
}
public Task<IResponse> GetAsync(string url, Action<IRequest> prepareRequest)
{
Task<IResponse> task = _httpClient.GetAsync(url,
request =>
{
[ERROR: request is actually HttpRequestWrapper and
does not expose HttpWebRequest]** ]
var httpWebRequest = (HttpWebRequest) request;
httpWebRequest.AutomaticDecompression = _decompressionMethods;
prepareRequest(request);
});
return task.ContinueWith(response =>
{
Log.Debug(this, "Response: {0}", response.Result.ReadAsString());
return response.Result;
});
}
....
}
To the best of my knowledge GZip encoding and streaming do not mix. In the case of the forever frame transport the client wouldn't be able to decode any on the streaming content until the entire response, or at least a significant block of data, is received (due to the way the data is decoded). In the case of web sockets there is not support for encoding of any kind at this time, although there is apparently an extension to the specification for per message encoding being worked on.
That said, if you wanted to attempt to provide support for the LongPolling transport, the only way I can see this being possible is to provide your own SignalR IHttpClient implementation. You can see right now that the DefaultHttpClient class uses HttpHelper::GetAsync which creates the HttpWebRequest internally and you can never get your hands on that because you only have access to the IRequest which is HttpWebRequestWrapper at that point.
By creating your own IHttpClient you can take over the initial instantiation of the HttpWebRequest, set the AutomaticDecompression and then wrap that up yourself with the HttpWebRequestWrapper.

Resources