Server sessions (IsReference mode) for Thinktecture Id.Srv. 3 - thinktecture-ident-server

With ref. to brockallen.com/2013/02/21/server-side-session-token-caching-in-wif-and-thinktecture-identitymodel
I want to use server side sessions with Id.Srv. 3 and have created an implementation of ITokenCacheRepository using a Redis Cache as the session store.
Apart from the implementation, I have added the following code in Global.asax of the Id.Srv. project:
protected void Application_Start(object sender, EventArgs e)
{
PassiveSessionConfiguration.ConfigureSessionCache(new SessionManagerCache(sessionManager));
}
public override void Init()
{
PassiveModuleConfiguration.CacheSessionsOnServer();
}
where SessionManagerCache is the ITokenCacheRepository implementation and sessionManager is a Redis Cache used inside that.
I also have these configSections in web.Config -
<section name="system.identityModel" ...
<section name="system.identityModel.services" ...
and also the system.identityModel section -
<system.identityModel>
<identityConfiguration>
<claimsAuthorizationManager type="Identity.TestServer.AuthorizationManager, Identity.TestServer" />
</identityConfiguration>
</system.identityModel>
where AuthorizationManager is a ClaimsAuthorizationManager derivation.
Upon running, from within CacheSessionsOnServer(), this exception is being thrown -
throw new ArgumentException("SessionAuthenticationModule is null");
I have checked the value of FederatedAuthentication.SessionAuthenticationModule in Init() and this is always null.
This comment says Id.Srv. uses SAM to track user login sessions -
https://github.com/IdentityModel/Thinktecture.IdentityModel.45/issues/118#issuecomment-24202385
However, upon checking Id.Srv. code, this is true for Id.Srv. 2.
In Id.Srv. 3, cookie auth. seems to have been used -
https://github.com/IdentityServer/IdentityServer3/blob/master/source/Core/Configuration/AppBuilderExtensions/ConfigureCookieAuthenticationExtension.cs#L49
On CookieAuthenticationOptions, there is an option to set SessionStore to make use of a custom session store. However there seems to be no option to set SessionStore for Id.Srv. as these are local variables being created.
Can anybody please suggest, how to resolve the above error. Am I missing something obvious?
Can Thinktecture.IdentityModel be used for Server sessions with Id.Srv. 3?
If not, what are the alternatives to implement server sessions (IsReference mode) for login sessions using Id.Srv. 3?
Any help would be really appreciated.

For anyone stumbling upon this, Brock Allen has clarified :
"Since IdSvr 3 does not use the SAM, you can't use this feature from IdentityModel. IdSvr3, since it's OWIN based, uses the new cookie authentication middleware for the authentication sessions. These cookies tend to be much smaller than the older SAM cookies, thus there's less need for the cache."

Related

Serilog using EnrichDiagnosticContext with additional properties not being logged in SignalR Hub

I have recently implemented Serilog logging into my ASP.NET Core/.NET5 web app that uses SignalR. I'm using the Elasticsearch sink and everything is largely working as expected. I decided to add some additional HttpContext properties to be logged on each request, so I went down the road of extending the call to UseSerilogRequestLogging() in StartUp.cs as to enrich the diagnostic context with a couple of extra properties (mainly because this seemed like the simplest way to do it):
app.UseSerilogRequestLogging(options =>
{
options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
{
diagnosticContext.Set("HttpRequestClientIP", httpContext.Connection.RemoteIpAddress);
diagnosticContext.Set("UserName", httpContext.User?.Identity?.Name == null ? "(anonymous)" : httpContext.User.Identity.Name);
};
});
At first, this seemed to work as expected until I noticed it wasn't always working. I really want the extra properties logged on all log records written, and it seems to work fine on log records that are written automatically by Serilog when typical HTTP GETs, HTTP POSTs, etc. occur... But in my Signalr Hub class, I have a couple of places where I'm manually writing my own log records like Logger.Log(LogLevel.Information, "whatever.."), but these extra properties are simply not there on these records.
What am I missing here? Is it something about this being in a Signalr Hub that makes them unavailable? Or perhaps there's something I'm doing wrong with my Logger.Log() calls?
Any ideas would be appreciated.
Thanks-
It's not gonna to work with signalR.
Behind the screen, app.UseSerilogRequestLogging make use of a middleware in the request pipeline, that call RequestLoggingMiddleware as what you can see in detail here.
SignalR use the first Http request to setting to connection up to websocket, which won't goes through the pipeline at all. Therefore, doesn't have anything to do with RequestLoggingMiddleware, which you are using to logging out the request.
I finally ended up going with a couple of custom Enrichers. I did experiment briefly with middleware vs enrichers and they both seem to work as expected. Both always added the additional properties to all log entries. I'm still not quite sure I understand why the DiagnosticContext option behaves the way it does, unless it is simply due to the logging in question being in a SignalR hub as #Gordon Khanh Ng. posted. If that were the root of the problem though, you wouldn't think the enrichers or middleware would work either.

HttpContext.Session in Blazor Server Application

I am trying to use HttpContext.Session in my ASP.NET Core Blazor Server application (as described in this MS Doc, I mean: all correctly set up in startup)
Here is the code part when I try to set a value:
var session = _contextAccessor.HttpContext?.Session;
if (session != null && session.IsAvailable)
{
session.Set(key, data);
await session.CommitAsync();
}
When this code called in Razor component's OnAfterRenderAsync the session.Set throws following exception:
The session cannot be established after the response has started.
I (probably) understand the message, but this renders the Session infrastructure pretty unusable: the application needs to access its state in every phase of the execution...
Question
Should I forget completely the DistributedSession infrastructure, and go for Cookies, or Browser SessionStorage? ...or is there a workaround here still utilizing HttpContext.Session? I would not want to just drop the distributed session infra for a way lower level implementation...
(just for the record: Browser's Session Storage is NOT across tabs, which is a pain)
Blazor is fundamentally incompatible with the concept of traditional server-side sessions, especially in the client-side or WebAssembly hosting model where there is no server-side to begin with. Even in the "server-side" hosting model, though, communication with the server is over websockets. There's only one initial request. Server-side sessions require a cookie which must be sent to the client when the session is established, which means the only point you could do that is on the first load. Afterwards, there's no further requests, and thus no opportunity to establish a session.
The docs give guidance on how to maintain state in a Blazor app. For the closest thing to traditional server-side sessions, you're looking at using the browser's sessionStorage.
Note: I know this answer is a little old, but I use sessions with WebSockets just fine, and I wanted to share my findings.
Answer
I think this Session.Set() error that you're describing is a bug, since Session.Get() works just fine even after the response has started, but Session.Set() doesn't. Regardless, the workaround (or "hack" if you will) includes making a throwaway call to Session.Set() to "prime" the session for future writing. Just find a line of code in your application where you KNOW the response hasn't sent, and insert a throwaway call to Session.Set() there. Then you will be able to make subsequent calls to Session.Set() with no error, including ones after the response has started, inside your OnInitializedAsync() method. You can check if the response is started by checking the property HttpContext.Response.HasStarted.
Try adding this app.Use() snippet into your Startup.cs Configure() method. Try to ensure the line is placed somewhere before app.UseRouting():
...
...
app.UseHttpsRedirection();
app.UseStaticFiles();
//begin Set() hack
app.Use(async delegate (HttpContext Context, Func<Task> Next)
{
//this throwaway session variable will "prime" the Set() method
//to allow it to be called after the response has started
var TempKey = Guid.NewGuid().ToString(); //create a random key
Context.Session.Set(TempKey, Array.Empty<byte>()); //set the throwaway session variable
Context.Session.Remove(TempKey); //remove the throwaway session variable
await Next(); //continue on with the request
});
//end Set() hack
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
...
...
Background Info
The info I can share here is not Blazor specific, but will help you pinpoint what's happening in your setup, as I've come across the same error myself. The error occurs when BOTH of the following criteria are met simultaneously:
Criteria 1. A request is sent to the server with no session cookie, or the included session cookie is invalid/expired.
Criteria 2. The request in Criteria 1 makes a call to Session.Set() after the response has started. In other words, if the property HttpContext.Response.HasStarted is true, and Session.Set() is called, the exception will be thrown.
Important: If Criteria 1 is not met, then calling Session.Set() after the response has started will NOT cause the error.
That is why the error only seems to happen upon first load of a page--it's because often in first loads, there is no session cookie that the server can use (or the one that was provided is invalid or too old), and the server has to spin up a new session data store (I don't know why it has to spin up a new one for Set(), that's why I say I think this is a bug). If the server has to spin up a new session data store, it does so upon the first call to Session.Set(), and new session data stores cannot be spun up after the response has started. On the other hand, if the session cookie provided was a valid one, then no new data store needs to be spun up, and thus you can call Session.Set() anytime you want, including after the response has started.
What you need to do, is make a preliminary call to Session.Set() before the response gets started, so that the session data store gets spun up, and then your call to Session.Set() won't cause the error.
SessionStorege has more space than cookies.
Syncing (two ways!) the sessionStorage is impossible correctly
I think you are thinking that if it is on the browser, how can you access that in C#? Please see some examples. It actually read from the browser and transfers (use) on the server side.
sessionstorage and localstorage in blazor are encrypted. We do not need to do extra for encryption. The same applies for serialization.

Unexpected "Cache entry must specify a value for Size when SizeLimit is set" message in AspNetCore 3

So this all worked fine before updating to AspNetCore 3 today.
I am using a memory cache with dependency injection (IMemoryCache cache).
I add it to my middleware with services.AddMemoryCache();
and do NOT set a size, but I still end up with the error message:
Cache entry must specify a value for Size when SizeLimit is set.
When I inspect the instance of MemoryCache and it does indeed have a size of 10240 set (see image).
The problem is I've been looking for a hour and I have no clue where this was set. Nowhere in my code do I have SizeLimit or 10240 anywhere - including config files.
It seems to have started when I switched to using app.UseEndpoints instead of app.UseMvc() - but I've made so many changes I'm not sure.
Where could this possibly be set that is elluding me.?
I managed to stop this exception from being thrown by removing the call to AddEntityFrameworkSqlServer() from my ConfigureServices() method in Startup.cs:
public class Startup
{
...
public void ConfigureServices(IServiceCollection services)
{
...
services
.AddEntityFrameworkSqlServer() // <-- Removed this
.AddDbContext<MyContext>(options =>
options.UseSqlServer(...)
)
...
}
...
}
Apparently calling AddEntityFrameworkSqlServer() is no longer needed in EF Core 3:
Calling this method is no longer necessary when building most applications, including those that use dependency injection in ASP.NET or elsewhere.
Thanks to #Simon_Weaver for his clue about EF Core!

Two Chrome sessions on the same machine - one will connect to our Azure website, the other "unable to connect to SQL Server database"

We have a problem with an Azure website that intermittently fails with this error:
[SqlException (0x80131904): A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 26 - Error Locating Server/Instance Specified)]
and
HttpException (0x80004005): Unable to connect to SQL Server database.
By intermittent I mean that you can start a new browser session and it'll be OK again, then later in the day it'll fail.
There's lots of advice online for this error but it all involves setting up your connectionstring correctly or fixing roleManager or membership in the web.config. None of these solutions would seem to be compatible with intermittent errors on our site (ie. if our connectionstring or web.config were incorrect presumably the site would always fail).
It may be relevant that we had an existing site foo.azurewebsites.net and codebase and we switched to bar.azurewebsites.net and substantially changed the codebase (though starting with the same original files). We've also added some simple Role admin code. Is it possible that because of caching the new site is sometimes trying to connect to the old site's database (now gone)?
But we've had one user laboriously help us out, deleting from his cache anything which was relating to the "old" site ... which fixed his problem ... but next day the problem returned for him.
Update
Recently I was sat here with 2 side by side Chrome browser sessions (different user logins), hitting the site again and again. One session was getting 100% error, the other 0% errors. But I can't reproduce it now. No errors at all for me. But users still saying they're getting a tremendous error rate of 80% to 90% of the time.
Update
It's down again this morning (for one browser session), however many times I try to refresh. A different browser window/identity I fired up alongside it is fine.
Update
Perhaps I have the same problem asked here. Deleting cookies seems to fix it in my case, just as Mark Heath documents. Currently trying the answer Mark posted himself there to see if it helps my situation too.
Presuming you are using Entity Framework
Warning: this works only in EF6+. If you are in EF5 and have this problem, consider updating - it is easy.
If you have intermittent database connection problems in Azure, you should implement retry policy. You can do it via SqlAzureExecutionStrategy. This is described here in detail: http://msdn.microsoft.com/en-us/data/dn456835.aspx
Here is how to enable this:
public class MyConfiguration : DbConfiguration
{
public MyConfiguration()
{
SetExecutionStrategy("System.Data.SqlClient", () => new SqlAzureExecutionStrategy());
}
}
and then you'll need to decorate your DbContext with configuration attribute:
[DbConfigurationType(typeof(MyConfiguration))]
public class MyDbContext : DbContext
{
// blah
}
If you have manually initiated transactions, you'll need to disable retry-policy. For that you'll need to change MyConfiguration to look like this:
public class MyConfiguration : DbConfiguration
{
public MyConfiguration()
{
this.SetExecutionStrategy("System.Data.SqlClient", () => SuspendExecutionStrategy
? (IDbExecutionStrategy)new DefaultExecutionStrategy()
: new SqlAzureExecutionStrategy(1, TimeSpan.FromSeconds(30)));
}
public static bool SuspendExecutionStrategy
{
get
{
return (bool?)CallContext.LogicalGetData("SuspendExecutionStrategy") ?? false;
}
set
{
CallContext.LogicalSetData("SuspendExecutionStrategy", value);
}
}
}
And wrap your transaction calls like this:
MyConfiguration.SuspendExecutionStrategy = true;
// start transaction
// do transaction stuff here
// commit/rollback transaction
MyConfiguration.SuspendExecutionStrategy = false;
Code shamelessly stolen from here: http://msdn.microsoft.com/sv-se/data/dn307226.aspx

You must call "WebSecurity.InitializeDatabaseConnection" ... But I already have

I've read through a number of SO questions on this topic, but all seem to be dealing with where you should put this call.
My problem is different: I have already done the WebSecurity.InitializeDatabaseConnection() call, and have set breakpoints so I know it's been executed. But I still get the invalid operation exception saying I have to call it.
Unlike most of the other questions which are encoutering it in an MVC controller action, I'm encountering it an HttpModule I have written to accomplish Authentication for a REST WebAPI controller. The Init call contains the WebSecurity.InitializeDatabaseConnection call. The OnAuthenticationRequest method then extracts username and password information from the request's Authorization header, and calls the ValidateUser method of the SimpleMembershipProvider. This is where I get the exception
You must call the WebSecurity.InitializeDatabaseConnection method
before you call any other method of theWebSecurityclass.
So
a) why am I getting this exception when I have already fulfilled the conditions not to get it.
b) what can be done about it?
After hours of facing the same problem and setting many many breakpoints I found a solution for my environment, I hope others can benefit from it:
inside Folder App_Start
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
var attr=new InitializeSimpleMembershipAttribute();
// here is the important part
attr.OnActionExecuting(new ActionExecutingContext());
filters.Add(attr);
}
}
Background: In my app I have 2 views:
/Account/Register: this is part of the standard asp.mvc 4 templates and has the attribute [AllowAnonymous]
/Task: this is the "Index"-View of a todo-list, The TaskController has the attribute [Authorize(Roles="Administrator")]
I always had the problem in the following sitation:
debugging session in browser
successfull login
open the view "/Task" which requires authorization
switch to visual studio and keep the browser open
stop debugging
change some code inside a controller (setting some whitespace is enough)
start debugging session
press refresh in browser
At this point the method "OnActionExecuting" inside the class InitializeSimpleMembershipAttribute was not yet called (determined by setting breakpoints)!
But when I opened a View which doesn't require Authorization (i.e. /Account/Register) the Method "OnActionExecuting" was called. After this I could call /Task without errors.
So the solution was to make a "pseudocall" for "OnActionExecuting" on application-startup.
You should not need to init the database connection every time your HttpModule is called. Just do it once at application start up. Here is an article that explains how to use SimpleMembership for authentication/authorization of Web API calls. There is also a link to source code for the example project.