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
- Anonymous
July 11, 2018
Excellent! - Anonymous
January 03, 2019
The comment has been removed- Anonymous
January 07, 2019
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.- Anonymous
January 07, 2019
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 becauseif 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 andon the signout page click on "Go back to site" link and repeat these steps until you get that exception. I was able to reproduce thatin Chrome and IE.Hope this helps.
- Anonymous
- Anonymous