This post is a similar to my previous post on Check Permissions, except here, we'll be talking about Forms-Based Authentication (FBA).
With Forms-Based Authentication, all of the same concepts about non-interactively determining a users permissions based off of their external token still apply, so I would recommend you read through that post and the linked posts as well.
A main concept to keep in mind is that external token problems can appear to be "intermittent" or "random". That's because the external token is cached for 24 hours (by default). When the user logs in interactively, they refresh their own external token. Anything that must determine user permissions within the next 24 hours will just read the existing token. Non-interactively determining permissions after the token has expired is when things get interesting.
Like in the Windows Auth scenario, with FBA, there are times that certain processes must be able to determine a users permission non-interactively. This is done by reading the users existing external token. If the external token has expired, that process must refresh it. Examples include:
- The "Check Permissions" function – needs to determine group membership (role claims) to determine your effective permission on the site.
- Alerts – The timer service must make sure you still have permission to the items it wants to send you alert emails about.
- Workflows – Depending on what your workflow does, it must be able to determine your permission to the item the workflow is running on.
So what's different about FBA?
Lots of things. First off, users and groups are not resolved via the same .NET APIs, so unfortunately, pretty much none of the "possible causes" from my Windows auth post apply here. Users are resolved via the Membership Provider and groups are resolved via the Role Manager specified in your web.config files.
Remember setting up FBA, where you had to insert Membership and RoleManger chunks of XML into your web.configs to get it to work?
Those same chunks of XML (and the DLLs they reference) are used to resolve the user and determine group membership in the above-listed non-interactive scenarios.
Here's the big take-away:
The process (w3wp.exe, owstimer.exe, powershell.exe, etc) that is attempting to non-interactively determine user permissions must be able to load your FBA membership provider and role manager.
The "Check Permissions" function is run from the UI. Since you had to define your membership provider and role manager in the web apps web.config file in order for your FBA users to log in, you're already covered here.
Alerts and workflows run within the SharePoint Timer Service (owstimer.exe). Therefore, you must define your membership provider and role manager within the owstimer.exe.config file.
For SharePoint 2016, that file is located here: C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\BIN\
For SharePoint 2013, it would be: C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\BIN\
This guidance hasn't really changed since SharePoint 2007. See this MSDN article.
If you have a PowerShell script that must determine user permissions, then PowerShell.exe must also load your membership provider and role manager.
An example would be a script that calls $item.DoesUserHavePermissions()
If your PowerShell script has not loaded your FBA role manager, it will fail to determine user permission and the following error will be thrown in your ULS logs:
03/18/2018 15:19:19.80 PowerShell.exe (0x1930) 0x03B4 SharePoint Foundation Claims Authentication g7m8 Medium SPClaimsAuthRoleProvider.GetRolesForUserBestEffort() failed to load groups for '0#.f|fbamember|User1': System.Configuration.Provider.ProviderException: The Role Manager feature has not been enabled. at System.Web.Security.Roles.get_Providers() at Microsoft.SharePoint.Administration.Claims.SPClaimsAuthRoleProvider.GetRolesForUserBestEffort(String username). 3eb1052c-a396-0000-194b-b13e96a3d301
You had to define your FBA role manager in the web.config for w3wp.exe processes. You did it in owstimer.exe.config for owstimer.exe (the timer service). Can you guess what you must do for PowerShell?
That's right, you must create a PowerShell.exe.config file (by default one does not exist) within "C:\Windows\System32\WindowsPowerShell\v1.0\" and specify your membership provider and role manager there.
<?xml version="1.0" encoding="utf-8" ?>
<!--Your Membership and role provider stuff here-->
Note: If you're using a custom or 3rd party membership provider and role manager, those DLLs must either be in the Global Assembly Cache (GAC), or copied to the directories that owstimer.exe and powershell.exe run from. If you have this all in place, but non-interactive external token refresh is still not working, it's possible that the custom / 3rd party role manager was not written correctly. It may be missing the methods necessary for this to work. Only an in-depth review of the source code would confirm this.