Cache database results in WebAPI authentication - asp.net

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";
}

Related

Cannot reconfigure DbContext more than once

I have a .Net Core 2.1 Web API talking to a MySQL database, using the Pomelo provider (version 2.1.0-rc1-final). Because this is a multi-tenant application, the API needs to change databases depending on which tenant is connecting. The front end web app tells the API which tenant is doing the request by including a TenantId header in the HTPP request.
When the API receives an HTTP request from the front end, I have a service in the API's pipeline that reads the TenantId from the request, and then this is used to determine which database the API must connect to.
To connect to various databases, I change the connection string of the DbContext. I do this in the OnConfiguring event:
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
string connectionString = CreateConnectionString();
optionsBuilder.UseMySql(connectionString);
base.OnConfiguring(optionsBuilder);
}
My problem is that this works the first time, but the second time this event fires, when it executes the optionsBuilder.UseMySql(connectionString); line, it throws this exception:
An item with the same key has already been added. Key: Pomelo.EntityFrameworkCore.MySql.Infrastructure.Internal.MySqlOptionsExtension
It's only letting me configure the DbContext once. I need to reconfigure it every time an API action endpoint gets called. Any ideas?
I'm not 100% sure what I did - but it's working now. The process is as follows: I created middleware to identify the tenant making the request (i.e. consuming the API). This is done by reading a "tenantId" that the client app sends in the header of the request, and then saving that tenantId into a global variable (I used HttpContext). Because middleware executes before pretty much most other things, by the time the OnConfiguring event of the dbContext fires, I already know which tenant this is So then, on the OnConfiguring event of the dbContext, I set the connectionString of the dbContext to whatever it should be (depending on the tenantId). I can't post the code in this comment because comments don't allow line breaks and they are too short, but If you want to, send me your email address and I'll email you the code.

Create session with BASIC authentication

I want to write a servlet with which I can create a new session.
The servlet requires the user to authenticate with BASIC authentication and returns the standard session cookie (using HttpServletRequest#getSession(true)). However, if the client uses the received session cookie in his next request instead of BASIC authentication it is not authenticated. The server recognizes the session but it doesn't contain the user information.
I'm using Tomcat and after a bit of debugging the reason is also obvious: the user information (Principal) is added to session upon authentication. However when the first BASIC authentication is taking place no session exists yet as this will be created by the servlet. Does anyone have idea how to solve this problem?
After one night of sleep [1] I believe I have come up with a working solution myself. The following snippet (using JAX-RS, but it shouldn't be too difficult to translate it to plain servlet code) does the trick if the calling client will follow redirects:
public Response getSessionCookie() {
boolean sessionExists = m_servletRequest.getSession(false) != null;
if (sessionExists) {
return Response.noContent().build();
} else {
HttpSession session = m_servletRequest.getSession();
return Response.status(Status.TEMPORARY_REDIRECT)
.header("Location",
m_uriInfo.getAbsolutePathBuilder().matrixParam("jsessionid", session.getId()).build())
.build();
}
}
The first request will create a session and redirect the client to the same address but with the session ID in the URL (which is important). The client will follow the request and send the same BASIC authentication data again but now it will be registered in the existing session. The second invocation of the method above will simply return an empty response with the session cookie that can now be used for subsequent requests.
Note that the session cookie is different for me in the second response but looking at Tomcat code this seems to be deliberate (successful authentication will always create a new session).
[1] Sleep is highly underestimated!

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

Implementing Authorization in a Self Hosted SignalR Server accessed from Web

I'm looking for some guidance on how to implement authorization security for SignalR on a back end service running in a self-hosted (non-IIS) environment, that is called from a Web application. The backend app is basically a monitor that fires SignalR events back to the HTML based client. This all works fine (amazingly well actually).
However, we need to restrict access to the server for authenticated users from the Web site. So basically if a user is authenticated on the Web site, we need to somehow pick up the crendentials (user name is enough) and validation state in the backend app to decide whether to allow the connection as to avoid unauthorized access.
Can anybody point at some strategies or patterns on how to accomplish this sort of auth forwarding?
I am having similar issues here, as in my web app I use a simple cookie authentication system which uses an AoP style approach to check for any controllers with an attribute, then will get the current context (be it from the static HttpContext.Current or from the target invocation object depending on the type of interceptor) and then verify the cookie exists, it contains right data, then finally verify the token with the db or cache etc.
Anyway this approach can also be used for Signalr, although its a bit more long winded and you are using dependency injection. You would basically wrap the hub calls with the desired attribute, then set up your DI/IoC configuration to intercept these calls, then either get the hub instance within your interceptor and get the cookie (or your custom authentication mechanism) from the request, verify it is all valid or not, and if not then throw a new HttpException("403", "Not authenticated"); which should kick the user out and return back before it even hits your hub method, this way you can put the logic in one place (your interceptor, or a class the interceptor consumes) then just wrap any method that needs to use this authentication using your attribute.
I use Ninject and the interception extension, but most major DI frameworks these days have some form of IoC plugin/extensions, such as Autofac, Windsor, Spring etc.
If you were not happy going down the route of introducing DI and/or AOP to your current project, then maybe you could just create a custom hub instance which contains your authentication logic and then just use that in your hubs, so ok you will still be manually calling some authentication logic from within each hub method you want to protect, but its less code, so something like:
public class AuthorisableHub : Hub
{
private ISomeAuthenticationToken GetSomeAuthenticationTokenFromRequest(Request request) // probably a SignalR specific request object
{
// Get your token from the querystring or cookie etc
}
private bool IsAuthenticationTokenValid(ISomeAuthenticationToken token)
{
// Perform some validation, be it simple or db based and return result
}
protected void PerformUserAuthentication()
{
var token = GetSomeAuthenticationTokenFromRequest(Context.Request);
var isRequestValid = IsAuthenticationTokenValid(token);
if(!isRequestValid)
{ throw new HttpException(403, "<Some forbidden message here>"); }
}
}
public class MyFancyPantsHub : AuthorisableHub
{
public void TellAllClientsSomethingSecret(ISecret secret)
{
PerformUserAuthentication();
// Do stuff with the secret as it should have bombed the user out
// before it reaches here if working correctly
}
}
It is not perfect but would work (I think), also I am sure I once read somewhere that Hubs are newly instantiated for each request, and if this is indeed true, you could possibly just put this logic in your constructor if you want to apply the authentication to every action within the hub.
Hope that helps, or gives you ideas... would be interested in knowing how you did solve it in the end.
SignalR does not provide any additional features for authentication. Instead, it is designed to work with the authentication mechanism of your application.
Hubs
You should do authentication as you normally would and then use the Authorize attribute provided by SignalR to enforce the results of the authentication on the Hubs.
The Authorize attribute can be applied to an entire Hub or particular methods in the Hub. Some examples:
[Authorize] – only authenticated users
[Authorize(Roles = "Admin,Manager")] – only authenticated users in the specified .NET roles
[Authorize(Users = "user1,user2")] – only authenticated users with the specified user names
You can also require all Hubs to require authentication by adding the following method in the Application_Start method:
GlobalHost.HubPipeline.RequireAuthentication();
Persistent Connections
You can use the user object in the request to see if the user is authenticated:
request.User.IsAuthenticated

Passing Auth to API calls with Web Service References

I am new to web services. The last time I dealt with SOAP was when I created a bunch of wrapper classes that sent requests and received responses back per some response objects/classes I had created. So I had an object to send certain API requests and likewise a set of objects to hold the response back as an object so I could utilize that 3rd party API.
Then someone came to me and said why not just use the wsdl and a web service. Ok, so today I went and created a "Service Reference". I see that this is what's called a "Proxy Class". You just instantiate an instance of this and then walla you have access to all the methods from the wsdl.
But this leaves me with auth questions. Back when I created my own classes manually, I had a class which exposed properties that I would set then access for things like signature, username, password that got sent along with the Http request that were required by whatever 3rd party API I was using to make API calls.
But then with using a Service Reference, how then would I pass this information just like I had done in my custom classes? For instance I'm going to be working with the PayPal API. It requires you to send a signature and a few other pieces of information like username and password.
// Determins if API call needs to use a session based URI
string requestURI = UseAuthURI == true ? _requestURIAuthBased + aSessionID : _requestURI;
byte[] data = XmlUtil.DocumentToBytes(doc);
// Create the atual Request instance
HttpWebRequest request = CreateWebRequest(requestURI, data.Length);
So how do I pass username, password, signature, etc. when using web service references for each method call? Is it as simple as specifying it as a param to the method or do you use the .Credentials and .URL methods of your proxy class object? It seems to me Credentials means windows credentials but I could be wrong. Is it limited to that or can you use that to specify those required header values that PayPal expects with each method call/API request?
Using Web Service or Web Service Reference

Resources