Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Re-calculate claims for SharePoint 2010 user while the user is still logged in

I am not a SharePoint expert by any means, and I've been having a really hard time finding the right information on this. Help, please!

I need a way to cause a claim token established with a call to SPFederationAuthenticationModule.SetPrincipalAndWriteSessionToken to recalculate the claims on the token without logging out the current user. Is there any way to do this?

Some background on why I'm asking this:

We use a custom role & membership provider for authN/Z on our custom SharePoint 2010 web application. Without getting into the details of why (which are complex) the role provider creates dynamically generated role names for a user based on the state of the user in the main app database; these roles represent the permissions for the user and are used inside SharePoint to determine the user's access to sites and site collections in the app.

There are ways within our app for the user to change their permissions, effectively adding new roles through the role provider, granting the user additional access within the app. The problem we are running into is that the Claims based auth which we are forced to use in SP2010 precomputes permissions at login and encodes those permissions in the session token -- effectively forcing us to ask the user to log out and log back in before they can obtain their new permissions. This is creating all sorts of usability issues, hence my question.

Is there some way to programmatically recompute the session token without logging out the user?

Or are we barking up the wrong tree? In my normal happy ASP.NET land I'd use Forms Auth, which computes authorization at every request rather than at login. Unfortunately, that doesn't appear to be an option in SP2010, and I'm rather stuck with SharePoint at the moment. Is there some other action we can pursue?

like image 476
Randolpho Avatar asked Nov 09 '11 19:11

Randolpho


1 Answers

Ok, after much research prompted by @Yahia's links, I discovered the answer to my problems in a blog post. Worked like a charm. I'll give an overview below, in case the link ever breaks:

Windows Identify Foundation's SessionAuthenticationModule, which is used as SharePoint 2010 auth framework, has a nifty little event that you can hook called SessionSecurityTokenReceived, which is called at the beginning of every request. By hooking this event, I can force SharePoint to request an authorization renewal without forcing the user to re-enter credentials.

The code is straightforward and sweet:

var sam = sender as SessionAuthenticationModule;
var logonWindow = SPSecurityTokenServiceManager.Local.LogonTokenCacheExpirationWindow;
var newValidTo = DateTime.UtcNow.Add(logonWindow);
e.SessionToken = sam.CreateSessionSecurityToken(
    e.SessionToken.ClaimsPrincipal,
    e.SessionToken.Context,
    e.SessionToken.ValidFrom,
    newValidTo,
    e.SessionToken.IsPersistent);
e.ReissueCookie = true;

I've tested it, and the approach definitely works, but there are drawbacks:

  1. The call to CreateSessionToken needs existing data from the current session token, which it appears I can only get from the event. That means this approach can only be done in the context of a new request. Since the event fires for every request and I only want to refresh auth in certain circumstances, I'm forced to test every request for a prompt to refresh auth. The simplest approach is to create a magic URL, such as "/refreshToken?redirectUrl={url}" that you test for in the event handler. Simply redirect to this URL when auth has been updated and the token needs to be refreshed.
  2. Hooking the event is problematic -- you can't do it statically because the module doesn't exist when static constructors are built. Ultimately, you're best off doing it in a custom HttpModule or global.asax -- both require updating resources that can't be touched via a .WSP file deployment (web.config or the global.asax file), so either approach causes deployment issues, at least for the first deployment. I chose to go the global.asax route.

In all, a satisfying hack. Got the job done, with a minimal workaround.

like image 96
Randolpho Avatar answered Oct 26 '22 11:10

Randolpho