B2C: AcquireTokenSilent fails for ADFS, works for local accounts - single-page-application

We have set up AD FS as an identity provider in our B2C login flows. Interactive login works just fine, but whenever we execute acquireTokenSilent with MSAL-JS in our Single Page Applications (SPA), we get an error:
Refused to display 'https://mytenant.b2clogin.com...' in a frame because it set 'X-Frame-Options' to 'deny'.
This only happens for the implicit flow. Applications using the authorization code grant work just fine. Local accounts work with both flows. From reading up on the documentation, this should not happen because I should have a session.
https://learn.microsoft.com/en-us/windows-server/identity/ad-fs/operations/customize-http-security-headers-ad-fs#x-frame-options
Note that non-interactive logins can be performed via iFrame due to prior session level security that has been established.
What can I do to fix this error?

After double checking my policies and the documentation, the error became obvious. For some reason, we had this code in our policy for the SAML technical profile:
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop"/>
So basically the user had a session with B2C, and a session with ADFS, but B2C did not have a session with ADFS.
Everything started working once we used the SamlSSOSessionProvider as indicated in the documentation.
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Saml-idp" />
....
<ClaimsProvider>
<DisplayName>Session Management</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="SM-Saml-idp">
<DisplayName>Session Management Provider</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.SSO.SamlSSOSessionProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="IncludeSessionIndex">false</Item>
<Item Key="RegisterServiceProviders">false</Item>
</Metadata>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
When investigating this a bit more, we discovered that the documentation originally contained the same error, which is how we got the code in the first place. The documentation was fixed one month ago!

Related

RedisSessionStateProvider Elasticache deadlock

we want to share ASP.NET Session state between our apps and services. We chose Elasticache/redis to achieve this. It was going well but we've run into a deadlock scenario.
Here's the deadlock sequence:
user navigates to page served by App 1
App 1 uses RedisSessionStateProvider, successfully fetches the Session in a few milliseconds
App 1 makes an HttpWebRequest to App 2, with the ASP.NET_SessionId cookie attached
App 2 also uses RedisSessionStateProvider, which attempts to fetch the Session from the same redis instance and times-out after ~ 2 minutes
Presumably App 1's RedisSessionStateProvider is holding a (write?) lock on the cache item containing the Session. As you can tell from my parlance, I'm no redis guru...
AFAICT Elasticache gives you no visibility onto situations like this, just performance-y graphs. And RedisSessionStateProvider is closed-source so I can't poke around there.
I also tried to get RedisSessionStateProvider to log (via the loggingClassName parameter) but nothing gets written by either App 1 or App 2 (my Log() method is called though).
To prove that it is the RedisSessionStateProvider deadlocking (rather than our own code deadlocking) I switched App 1 back to using InProc sessions and everything runs fine.
Does anyone have any suggestions? BTW our Session data is to all intents and purposes immutable, so there really is no need for it to be locked.
Many thanks,
Pete
EDIT: the sessionState config as requested. Note that the large operationTimeoutInMilliseconds value is so that we don't get exceptions whilst debugging the app. This will be changed to ~ 5000 in production.
<sessionState mode="Custom" customProvider="RedisSessionProvider">
<providers>
<add name="RedisSessionProvider"
type="Microsoft.Web.Redis.RedisSessionStateProvider"
host = "ec2-184-73-3-249.compute-1.amazonaws.com"
port = "6379"
ssl = "false"
throwOnError = "true"
retryTimeoutInMilliseconds = "2000"
applicationName = "PE"
connectionTimeoutInMilliseconds = "2000"
operationTimeoutInMilliseconds = "1800000"
</providers>
</sessionState>
This is not answer but it is not fitting in comment section.
At start of page asp.net page execution life cycle it calls GetItemExclusive which fetches session from store (in this case redis) and puts a lock on that session so other parallel request cannot modify session while this request is working. This lock has time out that is equivalent of request timeout that you can set using web.config like below.
<configuration>
<system.web>
<httpRuntime executionTimeout="10"/>
</system.web>
</configuration>
Now, page executes and depending on weather anything was modified or not modified in session it calls SetAndReleaseItemExclusive or ReleaseItemExclusive which releases the lock. If this request fails for some reason than it will retry depending on retryTimeoutInMilliseconds value. If retryTimeoutInMilliseconds is very less or same as operationTimeoutInMilliseconds then it might not retry at all. If SetAndReleaseItemExclusive or ReleaseItemExclusive is not completed successfully then basically your session will be locked for complete time of “executionTimeout” that you have set above which is in seconds. All other request will be blocked and won’t be able to access the session while it is locked. Lock will be released automatically when it reaches expiry.
Use web.config properties loggingClassName and loggingMethodName for configuring logging. You can find more details in web.config comments when you upgrade to above package. You can basically provide a public, static method which returns a TextWriter. Session state provider and StackExchange.Redis.StrongName both will use this TextWriter object to log details.
This will help us get more details about problem. Beware that enabling logging will reduce the performance.
Example of using logging:
namespace SSPWebAppLatest3
{
public static class Logger
{
public static TextWriter GetLogger()
{
return File.CreateText("C:\\Logger.txt");
}
}
}
Web.config:
<add name="MySessionStateStore" type="Microsoft.Web.Redis.RedisSessionStateProvider" host="127.0.0.1" accessKey="" ssl="false"
loggingClassName="Logger, SSPWebAppLatest3, Version=1.0.0.0, Culture=neutral ……."
loggingMethodName="GetLogger"/>
Please send me a reproducible test app with which I debug this further. You can also do the same as session state and output cache provider code is open source now. (https://github.com/Azure/aspnet-redis-providers)

Azure Dedicated Cache Role not working in the cloud

I'm sorry there's not a lot to go on with this, but some pointers in the right direction would be greatly appreciated.
I have an Azure Cloud Service with a web role and a dedicated cache worker role. In the web role, I'm using the cache like so in a Web Api controller:
var cacheFactory = new DataCacheFactory();
_cache = cacheFactory.GetDefaultCache();
And in the web.config:
<dataCacheClients>
<dataCacheClient name="default">
<autoDiscover isEnabled="true" identifier="MyProject.Workers.MyCache" />
</dataCacheClient>
It works fine locally using the Azure emulators, but on deploying to Azure, the controller method times out (after about 15 minutes!). The only error message I have is:
ErrorCode:SubStatus:There is a temporary failure. Please retry later. (One or more specified cache servers are unavailable, which could be caused by busy network or servers. For on-premises cache clusters, also verify the following conditions. Ensure that security permission has been granted for this client account, and check that the AppFabric Caching Service is allowed through the firewall on all cache hosts. Also the MaxBufferSize on the server must be greater than or equal to the serialized object size sent from the client.). Additional Information : The client was trying to communicate with the server: net.tcp://MyProject.Workers.MyCache:24233.
EDIT:
Similar lack of success trying to use the web role itself for caching:
<dataCacheClient name="default">
<autoDiscover isEnabled="true" identifier="MyProject.WebRole" />
<localCache isEnabled="true" sync="NotificationBased" objectCount="100000" ttlValue="300" />
<clientNotification pollInterval="60" />
</dataCacheClient>
Simply nothing coming back from the server. It doesn't even time out!

ASP.NET Identity: Authorize attribute with roles doesn't work on Azure

I just published my new ASP.NET MVC web site with Identity and OWIN authorization on Azure. Front-end works great, but got a problem with back-end. I use [Authorize] attribute with my admin controllers to check if user has a required role to access it, like this:
[Authorize(Roles = "Admin")]
On localhost even when using remote Azure SQL database it works fine.
But on Azure, any controller with authorize attribute with roles loading several minutes and then throws:
A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 26 - Error Locating Server/Instance Specified)
Authorize attribute without roles works fine.
Adding this code to the web.config fixes the issue.
<system.webServer>
<modules>
<remove name="RoleManager" />
</modules>
</system.webServer>
I know this is late now but I have a real answer for you. Thought I'd share it anyway as I just wasted a few hours on it myself.
Info found from this post
Didn't really solve the Azure problem, ended up writing my own Authorization attribute. The problem seems to be in User.IsInRole() method, so just avoid it.
I had a similar problem because I was using an old form of Identity: the <rolemanager>. If you are using asp.net identity 2 then you want to have have <rolemanager> disabled (which it is by default - i.e. have no reference to it in your web.config).

windows identity foundation / update panel / error 401

i recently created a claim aware web app using wif.
This app contains an update panel that loads dynamically user controls, everything seems to be ok, but sometimes i am getting a 401 error when the app does a async requet for the updatepanel , but the fedauth cookie is still there and with valid lifetime, also the sts session cookie.
I tried to implement sliding sessions but the error seems to be still there
wandering if someone could shed some light here.
Btw, my web.config on the client app, looks like this
<federatedAuthentication>
<wsFederation passiveRedirectEnabled="true" persistentCookiesOnPassiveRedirects="true" issuer="https://stsissuerurl" realm="http://webapp.com" requireHttps="false" />
<cookieHandler requireSsl="false" persistentSessionLifetime="05:00:00" />
</federatedAuthentication>
regards
Have you enabled WIF tracing and seen if any clues there? WIF Tracing

Access Session in WCF service from WebHttpBinding

I'm using WCF service (via WebGet attribute).
I'm trying to access Session from WCF service, but HttpContext.Current is null
I added AspNetCompatibilityRequirements and edited web.config but I still cannot access session.
Is it possible to use WebGet and Session together?
Thank you!
Yes, it is possible. If you edit the web.config:
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
</system.serviceModel>
and add the AspNetCompatiblityRequirements, the HttpContext.Current should be available.
Check everything once again, maybe you've put the attribute in the wrong place (the interface instead of the class?).
A RESTfull service with a session?
See excellent discussion here: Can you help me understand this? "Common REST Mistakes: Sessions are irrelevant"
http://javadialog.blogspot.co.uk/2009/06/common-rest-mistakes.html (point 6)
and
http://www.peej.co.uk/articles/no-sessions.html
Quote from Paul Prescod:
Sessions are irrelevant.
There should be no need for a client to "login" or "start a connection." HTTP authentication is done
automatically on every message. Client applications are consumers of
resources, not services. Therefore there is nothing to log in to!
Let's say that you are booking a flight on a REST web service. You
don't create a new "session" connection to the service. Rather you ask
the "itinerary creator object" to create you a new itinerary. You can
start filling in the blanks but then get some totally different
component elsewhere on the web to fill in some other blanks. There is
no session so there is no problem of migrating session state between
clients. There is also no issue of "session affinity" in the server
(though there are still load balancing issues to continue).

Resources