Tuesday, 5 May 2015

Claims based Authentication – Refreshing User Claims

As a scenario for claims based authorization, imagine an organization that allows executives Full control, project managers contribute access and all employees read access to a site.
In a site hosted under a claims aware web application, instead of selecting users or security groups to add to site Owners, Members or visitors group, you could select ‘Executive’, ‘Manager’ or ‘Employee’ claim from People picker.  Any user coming into the site with a corresponding claim in their token would then be granted respective access.
You could implement a Custom Claims Provider that will allow you to augment the User claims token with such additional claims. Down the line, these claims can be used to grant permissions and secure objects in SharePoint. Shown below, a kickass custom CCP allows people picker to show claims based on designation :)
You can then select all users who are Managers and add them to a group or an item with specific permissions
Obviously you need a place to store this kind of mapping to determine the users claim before injecting it into the token. I just use SQL Server, with BCS providing an easy interface for an admin guy to add or remove claims.
So to the problem now:
Once the CCP evaluates who you are based on SQL mapping, it augments your security token with the correct claim (‘Employee’, ‘Manager’ or ‘Executive’). If the mapping is changed in the backend SQL (if you are changed from a Manager to an Executive for instance), the claim still seems to stick to the old value forever and never gets refreshed. This is true even if the user logs out and logs in again.
I was able to leave the machine running for 4 hours, came back and logged into the site and it was still showing my old claim (I use a wee webpart to show the current users claims on the site for quick debugging). I found that short of recycling the STS Application pool or recycling IIS, nothing refreshed claims.
There are a few options to get the token refreshed quicker than a whole nap time.
You could hook into global.asax and use the event raised by SystemAuthenticationModule and get the token refreshed as shown here. This could be done in every request or when user clicks a particular link.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
SessionAuthenticationModule sam = sender as SessionAuthenticationModule;
 
var logonWindow = SPSecurityTokenServiceManager.Local.LogonTokenCacheExpirationWindow;
 
DateTime 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;
While this approach worked, it did not work consistently for me. The event was not raised for every request by SAM, even with a custom link being opened in a new browser window. Effectively, if a user’s claims are changed, this should relate to their privileges on site being either reduced or increased. Relying on SAM events would effectively allow the user to maintain current privileges as long as they keep their current session on, which would be undesirable.
I ended up modifying the SecurityTokenConfigService properties to reduce the lifespan of the token to 1 minute. This gets the token refreshed at least every minute. Not sure how much of a performance hit this carries, but at the moment I cannot find another approach that ensures that the logged in user has correct permissions on the site based on his claims.
Open SharePoint PowerShell (Start -> Programs -> Microsoft SharePoint 2010 Products -> SharePoint 2010 Management Shell) and run the following commands
1
2
3
4
5
6
7
8
9
10
11
12
13
$sts = Get-SPSecurityTokenServiceConfig
 
$sts.UseSessionCookies = $true
 
$sts.WindowsTokenLifetime = (New-TimeSpan -Minutes 2)
 
$sts.ServiceTokenLifetime = (New-TimeSpan -Minutes 2)
 
$sts.LogonTokenCacheExpirationWindow = (New-TimeSpan -Minutes 1)
 
$sts.ServiceTokenCacheExpirationWindow = (New-TimeSpan -Minutes 1)
 
$sts.Update()
This is how my configuration ends up looking after the changes.

No comments:

Post a Comment