SharePoint 2016: FBA authentication changes


Disclaimer: The below is a summary of observations made as the result of some reverse-engineering and Source Code review. It's not necessarily to be taken as "official," but does check out according to my testing.

This is post is not about configuring Forms-based Authentication (FBA). There's plenty of other posts out there about that. The way you enable FBA has not really changed in SharePoint 2016. You still make the same changes to the Web Application and Security Token Service (STS) web.config files.

During a recent troubleshooting session, I noticed a change in behavior in FBA in SharePoint 2016 as compared to 2010 and 2013. This post is a summary of what I found.

 

Cookie / Session Refresh

In SharePoint 2016, we added several new methods to the Source Code that change the way an FBA session is handled.

In SharePoint 2010 / 2013, if the user presented a valid FedAuth cookie to the server, but the users logon token (cached server-side) was expired, the FedAuth cookie was rejected, and the user was redirected back to the FBA login page.

In SharePoint 2016, we added functionality to automatically refresh the users FedAuth cookie without forcing them to log in again. I believe this was added to relieve some customer pain points in SharePoint Online (SPO), which we then inherited in SharePoint on-premise.

This may create some unexpected behavior. For example, consider the following scenario:

  • You want your user to be forced to log in again after 1 hour regardless if they've been active in the site or not, so you set FormsTokenLifetime to 1 hour:

 

$sts = Get-SPSecurityTokenServiceConfig

$sts.FormsTokenLifetime = (New-TimeSpan -Hours 1)

$sts.update()

 

  • You log in and leave the browser open for more than 1 hour. You then refresh the page or click on some other link within the site. You expect to be redirected back to the FBA login page, but instead, you find you're still logged in and the page renders without prompt.

     

This happens due to new automatic cookie refresh functionality. If your cookie is still valid, we will automatically refresh your login token and send you a new FedAuth cookie without prompting you to login again.

In order to force the user back to the login page, you would need to change the CookieLifetimeRefreshWindow value within SPSecurityTokenServiceConfig.

However the "correct" value for CookieLifetimeRefreshWindow depends on a couple other values within the security token service config. It must be greater than FormsTokenLifetime and WindowsTokenLifetime, but less than CookieLifetime.

If you try to set it otherwise, it will throw this error:

 

PS C:\Users\josh> $sts.CookieLifetimeRefreshWindow = (New-TimeSpan -hours 1)

Exception setting "CookieLifetimeRefreshWindow": "Specified argument was out of the range of valid
values

Parameter name: value"

 

Also, LogonTokenCacheExpirationWindow must be less than FormsTokenLifetime and WindowsTokenLifetime. The default value is 10 minutes, so that's usually pretty safe, but you may need to adjust that while you're at it.

In order to meet my above hypothetical scenario (user forced to login again after 1 hour of inactivity), you'd want to set all the values to something like this:

 

$sts = Get-SPSecurityTokenServiceConfig

$sts.FormsTokenLifetime = (New-TimeSpan -Hours 1)

$sts.WindowsTokenLifetime = (New-TimeSpan -Hours 1)

$sts.LogonTokenCacheExpirationWindow = (New-TimeSpan -Minutes 1)

$sts.CookieLifetime = (New-TimeSpan -Hours 1 -Minutes 1)

$sts.CookieLifetimeRefreshWindow = (New-TimeSpan -Hours 1)

$sts.update()

iisreset

# Then run IISReset on other boxes in the farm to clear logon tokes from in-memory cache

# You also need to restart the AppFabric Caching service on your Distributed Cache servers to clear the existing logon tokens

# Failure to restart IIS and D-cache may result in inconsistent token refresh behavior

 

Note: If you're still not getting the logout behavior you expect based on the values you set, you may also have to look at the controls on your sites / pages.

There may be controls that keep issuing requests even when you're not active in the browser.

For example, in an out-of-box Team site, there's a NewsFeed web part on the home page. There is JavaScript on the page that issues a client-side object model (CSOM) call every so often to automatically refresh the feed. Those CSOM calls are authenticated and will result in a logon token refresh and a FedAuth cookie reissue.

This can be problematic in situations where you want the user to be forced to log in again after a certain amount of time regardless of how active the user has been.

As long as authenticated requests are made within your cookie lifetime, your session will keep getting refreshed.  That's called a "sliding session".  It's by-design for FBA, and I'm not sure there's any way to disable it.

You can run Fiddler to see if this is happening.  Just log in and let the browser sit there for a few minutes.  See if any new requests pop up in Fiddler, for example, requests to _vti_bin/client.svc/ProcessQuery.

 

 

Cookie Persistence

This is not necessarily "new" for SharePoint 2016, but it came up as part of my investigation, so I thought I'd share some things that I haven't seen documented elsewhere.

First, lets talk about some basics. There's two options for cookie persistence:

Session = The cookie (FedAuth) is stored in your browser memory. If you close the browser, it's gone. When you open a new browser, you will be forced to log in again.

Persistent = The FedAuth cookie is written to disk on your client machine. You can close your browser (or even reboot) all you want, when you hit the site again, the cookie will be presented to the server, and you will be authenticated again without logging in. This cookie can also be shared across other applications. For example, Excel can use this same cookie when authenticating to SharePoint. That's not possible with a Session cookie. In that case, every application needs to authenticate separately.

 

Your cookie type is controlled using the UseSessionCookies property of the SPSecurityTokenServiceConfig. The default value is "false".

UseSessionCookies = True = Session Cookie

UseSessionCookies = False = Persistent Cookie

 

There's also the "Sign me in automatically" check box on the out-of-box login form. If you use a custom login form, you may or may not have this functionality.

If "Sign me in automatically" is not selected, you will get a Session cookie regardless of what the UseSessionCookies property is set to.

Here I have it set to False, which should mean a Persistent cookie:

 

But if I don't select the checkbox…

… I will get a Session cookie, which you can see in a Fiddler trace if you base-64 decode the FedAuth cookie.

Find the FedAuth cookie in the client request (top-right pane), right-click on it and choose "Send to TextWizard":

 

Choose "From Base64" in the "Transform" drop-down, and delete the "FedAuth=" part in the upper pane.

 

Now you should be able to see the decoded FedAuth cookie, and the persistence setting.

 

Session Cookie:

False = Session Cookie

 

No Expiration Set.

In the Fiddler trace, find where the server set the FedAuth cookie. You'll notice that a Session cookie does not specify a client-side expiration:

 

FedAuth=77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48U1A+MCMuZnxtZW1iZXJ8am9zaCwwIy5mfG1lbWJlcnxqb3NoLDEzMTc1Nzk5NzU1OTI1NzgzNCxGYWxzZSxXRS9taXdWalRrOWdQR2ZJUkh6Y1RGZFlUTjNJYXBhK01BZ3o4K2dKZEQrTWVJV0hsVm1jTU9JbTQ1OUNLejdaMndPSnh2Uk13YmNYQjNyOGlXd2xVdE5yNWVLOVdZZlBsb0xIZkpTZnBIbUNXOHRwTW9FWTBCMzQvL3RoYzIxZmQ0Sm40Zzc3RFFhR0ZnUUdnY0tGaUJlSzdvRHBlakNLcVhCUnFlZXhkL1VSWDlUdUE1dlc5Nmh4eVh2SXlJY1VHbi81blB3WnE0ei9KV2RZM3YwYXpES293cy9SaHJhL1ZMSnA2UVd0cVhhQjIwYWJjRy9uaUlhelZtUERpOUg3dy9yNHcveC82ZjVLQk45MXVCQTZJeHVYNzFKaDV5TVhzYkRZQnhScnNYVUQ1MW15VjNoRWc3aDNSdm82THpxMmEzYTJiZ01BTVFRNjc5YzFTMDF1Z0E9PSxodHRwOi8vajE2ZmJhLzwvU1A+; path=/; HttpOnly

 

 

Persistent Cookie:

True = Persistent cookie

 

Expiration is set.

In the Fiddler trace, find where the server set the FedAuth cookie. You'll notice that a Persistent cookie does specify a client-side expiration:

FedAuth=77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48U1A+MCMuZnxtZW1iZXJ8am9zaCwwIy5mfG1lbWJlcnxqb3NoLDEzMTc1ODAwNTIwODQyNDYzMSxUcnVlLGUxci8ycDJ2dkg3bk9UaUNkZUMvRS91em1heEdOOWNNRG1kak1Pb01YZkRIZncrL2ViTDJKcXhTNGExNUtpUGZpSEk3SlBaV1dISmV4NUhzSTZ4eWhqQnVHMGdvSFhOc0J3UEkrblk0MkI5YlU4N1ZEaVBrVXpMNk4zTlhNdC9RaHFMbC9SY0NON0k3NTV4NGtGZ3VZMXhSUUxhS0NiU0RtVmtycUhTYmI4VUVjM0ZEZUFPSFNheHM3ZHRyMmNENEdEOXh6RlFnZGJkMFMzMXBYM1NZYW85bTVSQnVXYWtac0xjUVZqTVl6cDFKaTg3OW9ZOTJKKzVqM0xkcXEvLzdjdUxyY1JTdlJITmtMKzJBSE9HNzV5cVRyL3ZoNUZLSDNRaEhnd01kcWxtcmFYLytMUjQyMTZ3K09BaHQrTUFFYUx0b2lvQkpVV2JobzZRNnZQQXRkUT09LGh0dHA6Ly9qMTZmYmEvPC9TUD4=; expires=Wed, 11-Jul-2018 16:35:20 GMT; path=/; HttpOnly


Comments (4)

  1. Arash R says:

    Thanks for sharing this information, I used your solution to force the user to login but here is the issue that I have,
    Our application uses Windows and Forms authentication and when I use windows account to login, I get this exception intermittently:

    [InvalidOperationException: Operation is not valid due to the current state of the object.]
    Microsoft.SharePoint.Administration.Claims.SPClaimUserKeyUtility.GetUserKeyString(String value) +377
    Microsoft.SharePoint.Utilities.SPUtility.GetFullUserKeyFromLoginName(String userNameSuffix) +178
    Microsoft.SharePoint.ApplicationRuntime.SPHeaderManager.AddIsapiHeaders(HttpContext context, String encodedUrl, NameValueCollection headers) +850
    Microsoft.SharePoint.ApplicationRuntime.SPRequestModule.PreRequestExecuteAppHandler(Object oSender, EventArgs ea) +424
    System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +139
    System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step) +195
    System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +88

    After looking at Sharepoint log file I saw this message:
    The value is neither a claim user name nor a claim user name suffix. Value: ‘…’.

    Another way to reproduce this issue is login with windows account and sign out and login again and repeat these steps until you get that exception.
    one solution was to remove Forms Auth but the issue with that is the people picker won’t show the Forms Auth users anymore.

    Here are my questions:
    1- Is there any way to fix this issue?
    2- Can we update the widows token before expiration time programmatically?
    3- Is there any way to use CookieLifetime for Forms Authentication users only?
    4- Is there any way to show Forms Auth users in poeple picker without enabling the Forms auth?

    I appreciate your help.

    1. SPJR says:

      There’s not enough information here to determine why that exception occurs. It quite possible that it’s not related to any of the token lifetime values discussed here. I can only recommended opening a support case with Microsoft for a thorough investigation. However, I’ll answer the questions:
      1. Almost certainly.
      2. Possibly, but I don’t see the point, and doing so would likely create a number of other session management issues.
      3. No.
      4. By implementing a custom claims provider, you can get People Picker to do just about anything you want. However, if you disable Forms auth, then Forms auth users cannot log in, which would seem to be the main point of having Forms auth in the first place.

      1. Arash R says:

        Thank you for your response, would you please let me know what information is necessary to track down this issue?

        The exception is happening occasionally and only for windows users and the reason that I think this issue might be related to CookieLifetime is because
        if I change the token values back to default values we don’t get that exception.

        As I mentioned the only way to reproduce this issue regardless of tokens lifetime is to login with windows account then sign out and
        on the signout page click on “Go back to site” link and repeat these steps until you get that exception. I was able to reproduce that
        in Chrome and IE.

        Hope this helps.

Skip to main content