Identity Server 4 refused to frame SignOutIframeUrl - asp.net-mvc

I have IdenityServer4 application (.net core 2.1) and 2 client mvc applications (.net framework 452) using UseOpenIdConnectAuthentication middleware. I am using implicit flow and I am trying to implement Single Log Out via Front-Channel. I am using the Quickstart app and everything is set up to work I just have to return the LoggedOut.cshtml with SignOutIframeUrl present in the Model.
My problem is that even though the signout work and logs me out from both clients I am getting the following errors in the network tab and in the console tab.
I have tried to add Content-Security-Policy header with all kinds of settings in it in the SecurityHeadersAttribute action filter attribute in the IdentityServer but nothing seems to fix the problem.
Few examples:
var csp = "default-src 'self' 'unsafe-inline' http://localhost:*;"
"frame-ancestors 'self' http://localhost:*;" +
"frame-src 'self' http://localhost:*;" +
"child-src 'self' http://localhost:*;";
var csp = "default-src 'self' *.aspnetcdn.com 'unsafe-inline';" +
"frame-src 'self' *";
This is how my FrontChannelLogoutUri action looks like
public void ForceLogOut()
{
AuthenticationManager.SignOut(CookieAuthenticationDefaults.AuthenticationType);
AuthenticationManager.SignOut(OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
I have also tried adding Content-Security-Policy header in the response from ForceLogOut, also didnt work.
My question is how can I fix this error ? There is something(most probably many things) I dont understand, but everywhere I have tried to find info about the Refused to frame.. policy directive problem, people are saying that I have to set appropriate frame-src values.

In IdentityServer options there is a property RequireCspFrameSrcForSignout which you can set to false to stop this happening
If set, will require frame-src CSP headers being emitting on the end
session callback endpoint which renders iframes to clients for
front-channel signout notification. Defaults to true.
https://identityserver4.readthedocs.io/en/latest/reference/options.html

When there is a frame-src violation it is the CSP of the outer frame that prevents the inner frame from being displayed. If it is a frame-ancestors violation the inner frame prevents the outer frame from displaying it. As it seems like you are framing a page from IdentityServer it is most likely the CSP of the other apps that need to add your source of IdentityServer to frame-src.

Not entirely in context, but can be shaped to fit; I just struggled with this for a good hour or two while learning Identity Server 4 in a Dot Net Core 5.0 / React app I am working on. I am using Visual Studio Code for development of the client app (hosted on https://localhost:3000) and Visual Studio for the server-side element (hosted on e.g. https://localhost:41234). There was the obvious cross-site element to this being a problem when used in development, so to rid the error, I used the following:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
//add this method to the Configure method in startup.cs
app.Use(async (context, next) =>
{
context.Response.Headers.Add("Content-Security-Policy", "frame-ancestors https://localhost:3000"); //modifies CSP to allow client url to frame content
await next();
});
...
}

Related

Azure AD B2C ASP.NET redirect loop

We've implemented Azure AD B2C in Umbraco on the front end using Microsofts webapp sample https://github.com/Azure-Samples/active-directory-b2c-dotnet-webapp-and-webapi
Most of the time this is generally working, but after a while everyone starts getting hit by a redirect loop. Restating the website then clears the issue.
It seems to be something causing the .AspNet.Cookies cookie to stop being set when the user is redirected back to the site with an id token.
Any ideas?
For the folks that will run into the same problem and find this question, I wanted to share what caused this in my case and how I resolved it.
The AD B2C App Registration expects to have a RedirectURI. I forgot to put signin-oidc
So changing:
https://localhost:5000
To
https://localhost:5000/signin-oidc
resolved my problem.
This is the default value - /signin-oidc - unless something else is explicitly set.
I had infinite loop issue at logout and it was because of missing support of Razor pages. The default Microsoft.Identity.Web.UI SignOut action uses /Account/SignedOut Razor page as callback url.
var callbackUrl = Url.Page("/Account/SignedOut", pageHandler: null, values: null, protocol: Request.Scheme);
I added Razor support in my Asp.Net core web app and it fixed the issue.
services.AddRazorPages();
and
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
Thanks.
Please ensure that your Reply URL in your application registration matches your Redirect URI in the web.config. Try setting both of these to your main homepage URL to ensure that your app is registered properly. Also make sure that the Application ID and the Client ID are matching and the right tenant is set in your web config. This needs to be the onmicrosoft.com tenant. Also, ensure that your users have the right permissions for the application.
Please follow the instructions in my blog and video to ensure that these are set properly.
https://medium.com/#marilee.turscak/reply-urls-vs-postlogoutredirecturis-in-azure-active-directory-aad-20f57a03267b
https://www.youtube.com/watch?v=A9U1VGyztEM
You can also try deleting the application and republishing it. If none of these things work, it may actually be an issue with the platform itself.
enabled HTTPS only under TLS/SSL settings in web app .
For me, it was because I didn't have the scope defined in my b2c configuration settings, like this:
"Resources": {
"myApi": {
"ResourceUri": "https://localhost:44361",//"https://my.ui.com",
"ResourceScopes": [
"https://myapp.onmicrosoft.com/my-api/Admin.Read.Write" // this was wrong, which caused my looping
]
}
}
I was also getting a logout redirect loop. It would actually log out, but just get stuck in a loop. In my case, the redirect URL I had configured in Azure was fine (I had /signin-oidc).
I followed the guide on adding my own account controller action rather than using the built in 'MicrosoftIdentity/Account/SignOut' (while also adding the 'id_token' validation to secure the logout): https://learn.microsoft.com/en-us/azure/active-directory-b2c/enable-authentication-web-application-options#secure-your-logout-redirect
My startup.cs code is per the documentation, my controller code looks like this (the documentation code is missing 'AuthenticationProperties' variable):
namespace Cosmos.WebPortal.Controllers;
[AllowAnonymous]
[Area("MicrosoftIdentity")]
[Route("[area]/[controller]/[action]")]
public class MyAccountController : Controller
{
[HttpGet("{scheme?}")]
public async Task<IActionResult> SignOutAsync([FromRoute] string scheme)
{
scheme ??= OpenIdConnectDefaults.AuthenticationScheme;
var redirectUrl = Url.Content("~/");
var properties = new AuthenticationProperties { RedirectUri = redirectUrl };
//obtain the id_token
var idToken = await HttpContext.GetTokenAsync("id_token");
//send the id_token value to the authentication middleware
properties.Items["id_token_hint"] = idToken;
return SignOut(properties, CookieAuthenticationDefaults.AuthenticationScheme, scheme);
}
}
So my logout link is now to this controller instead e.g. 'MicrosoftIdentity/MyAccount/SignOut'
That seems to work fine, no infinite loop. A bit frustrating as I don't really understand the cause or difference, but it works.
For me, it was an expired secret/certificate in Azure B2C. It's important to look at the network log to see if any message, thankfully there was message telling me exactly where to look

Breeze JS errors with Content Security Policy

With Breeze JS and a strict Content Security Policy I get the error Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive.
Is there a way to fallback without 'unsafe-eval' the same way AngularJS does with https://docs.angularjs.org/api/ng/directive/ngCsp?
Breeze uses Function(string) in order to make constructor functions for entities that have the same name as the entity. This is purely to make debugging easier, and is not an essential feature.
It should be possible to remove the reliance on Function(string) in the next version of Breeze. In the meantime, you can patch your version using:
function createEmptyCtor(type) {
return function(){};
}
Or minified as in your comment above:
function t(e) { return function(){}; }

SignalR across domains: errors with not allowed by Access-Control-Allow-Origin

Trying to call signalR from another domain, and keep on getting this error:
XMLHttpRequest cannot load
localhost:62150/signalr/negotiate?_=1362242757692. Origin
localhost:4982 is not allowed by Access-Control-Allow-Origin.
This is the code I'm trying to run:
$(function () {
jQuery.support.cors = true;
$.connection.hub.url = 'http://localhost:62150/signalr';
$.connection.hub.start()
.done(function () { alert("Now connected!"); })
.fail(function () { alert("Could not Connect!"); });
});
jquery and jquery.signalr.js are loaded, localhost:62150/signalr/hubs responds with JS, localhost:62150/signalr/hubs/negotiate?_=1362243021215 returns JSON if I run this in browser - so its not a missing script or invalid path issue.
What I've tried:
http://coding4life.wordpress.com/2012/03/30/making-cross-domain-calls-in-signalr/
(setting jQuery.support.cors and $.connection.hub.url)
Adding custom header in web.config with "Access-Control-Allow-Origin" value="*"
(this works only in IIS ? )
Creating an http module that would return this header on every request. Also tried to return actual domain name instead of *.
And combinations of all of the above.
Anyone has any idea what else I can try ?
The serving app is a combination of MVC and WebAPI (don't think it makes any difference).
If I'm trying that code from same domain - it works.
If you are using 1.0 or higher have you enabled cross domain on the server? (it's disabled by default now)
RouteTable.Routes.MapHubs(new HubConfiguration { EnableCrossDomain = true });
After wasting a couple of hours I think it's good to share my experience:
DO NOT add Access-Control-Allow-Origin to your web.config (yes it never sais to add it, but when trying things this is literally the first I did and left it there after a simple jquery cross-domain access test)
RouteTable.Routes.MapHubs( new HubConfiguration() { EnableCrossDomain = true } ); works just fine for classes inheriting Hub
RouteTable.Routes.MapConnection<MyConnection>( "foo", "/foo", new ConnectionConfiguration { EnableCrossDomain = true } ); this works just fine too for classes inheriting PersistentConnection

WIF- ID1014: The signature is not valid. The data may have been tampered with

I've been using WIF to authenticate our new website, the STS is based upon the starter-sts implementation.
To enable this to work correctly on out load balanced environment I've used the following in the global.asax to override the default certificate behaviour.
void onServiceConfigurationCreated(object sender, ServiceConfigurationCreatedEventArgs e)
{
List<CookieTransform> sessionTransforms = new List<CookieTransform>(new CookieTransform[]
{
new DeflateCookieTransform(),
new RsaEncryptionCookieTransform(e.ServiceConfiguration.ServiceCertificate),
new RsaSignatureCookieTransform(e.ServiceConfiguration.ServiceCertificate)
});
SessionSecurityTokenHandler sessionHandler = new SessionSecurityTokenHandler(sessionTransforms.AsReadOnly());
e.ServiceConfiguration.SecurityTokenHandlers.AddOrReplace(sessionHandler);
}
This is all working just find and people have been successfully using the system, however every now and then we get a blast of :
ID1014: The signature is not valid. The data may have been tampered with.
in the event logs, so I switched on WIF tracing and saw the following mentioned in the log.
ID1074: A CryptographicException occurred when attempting to encrypt the cookie using the ProtectedData API (see inner exception for details). If you are using IIS 7.5, this could be due to the loadUserProfile setting on the Application Pool being set to false.
I have a feeling this is leading me down a dark alley as I thought because I'd changed the implementation to use RSA this shouldn't affect me.
Any ideas to help me?
The browser cookies are encrypted with "old" mechanism - DPAPI.
Therefore, when the server tries to decrypt the cookies, it fails - your code use RSA now, not DPAPI.
As a workaround, clear the browser cache, and the application will start running as expected.
I changed the implementation to amend the timeout in the ontokencreated method. This prevents the reissue.
protected override void OnSessionSecurityTokenCreated(Microsoft.IdentityModel.Web.SessionSecurityTokenCreatedEventArgs args)
{
args.SessionToken = FederatedAuthentication.SessionAuthenticationModule.CreateSessionSecurityToken(
args.SessionToken.ClaimsPrincipal,
args.SessionToken.Context,
DateTime.UtcNow,
DateTime.UtcNow.AddDays(365),
true
);
//base.OnSessionSecurityTokenCreated(args);
}
Did you try setting the loadUserProfile option to true? Does the problem still occur?
(Select the Application pool in IIS and then click "Advanced Settings" on the right. "Load User Profile" is in the "Process Model" section).
The intermittent occurrence of your error, combined with the DPAPI exception showing up in your traces suggests to me that you aren't actually overriding the cookie transform, and your service is still using DPAPI.
This might be a long shot, but in your code snippet I noticed your method override "onServiceConfigurationCreated" starts with a lower case o. Such a typo would indeed prevent you from properly overriding default WIF behavior.

IIS Express defaulting to port 44300 for HTTPS when enabling SSL

When you initially set up IIS Express to enable SSL, it defaults the port to 44300. Unfortunately, when I try to access my site in on https://localhost/ it doesn't work unless I use the port number 44300 - https://localhost:44300/.
The links are generated using the following:
<%= Html.ActionLink("Index", "Index", "Home", new { #action = "https://" + Request.Hostname + Url.Action("Index", "Home") }) %>
Although an ugly solution, the #action keyword can override the generated route, but it means that the application would seemingly need to be aware of any non-standard ports (eg 44300).
The problem with that is that I'd be writing something to solve a problem that would only occur in a development environment.
So my question is... How do I change the port to 443 and have IIS Express like it?
Config for my site is below:
<site name="MySite" id="2" serverAutoStart="true">
<application path="/">
<virtualDirectory path="/" physicalPath="C:\Inetpub\MySite" />
</application>
<bindings>
<binding protocol="http" bindingInformation=":80:" />
<binding protocol="https" bindingInformation=":44300:" />
</bindings>
</site>
Many thanks in advance.
Update:
This question has been answered by Divya over on the IIS forums.
This question has been answered by Divya over on the IIS forums.
Once you enable SSL for a website in WebMatrix, it defaults to port 44300 and does all the bindings in the background. I am hoping that you tried to change this port to 443 in the config file. Once that is done and saved, you also need to modify the binding in http.sys. You would need to delete the existing entry for port 44300 and add the entry for port 443.
To do this, you could use httpcfg (WinXp/Win2003) or 'netsh http' (WinVista/Win2K8/Win7).
Here are the commands for netsh:
1) Get the appid and certhash for the existing entry of 44300 (I
assume, you are going to use the same certificate which WebMatrix
installs by default. If you want to change the certificate as well,
get the certificate hash of the certificate from the certificate
store): netsh http show sslcert. In the output search for entry for
port 44300 and copy certhash and appID.
2) Delete the entry for 44300: netsh http delete sslcert ipport=0.0.0.0:44300
3) Add a new entry for port 443 with certhash and appID copied in step
1. netsh http add sslcert ipport=0.0.0.0:443 certhash=<certhash> appid=<appid>
After configuring the entry in http.sys, you need to restart http
service for the changes to take effect.
net stop http
net start http
As noted by others, there are several nice ways of getting your SSL certs.
netsh http show sslcert > output.txt
or (my preferred method):
netsh http show sslcert | clip
Since I have spent much time on this topic , I would like to share my finding. I am reposting segment from my other post minus the code. Some background and explanation:
==========================================
After researching aroud, I was able to solve this issue with IIS Express and an override of the Controller class's OnAuthorization method (Ref#1). I have also gone with the route recommended by Hanselman (Ref#2). However, I was not complete satisfied with these two solutions due to two reasons:
Ref#1's OnAuthorization only works at the action level, not at the controller class level
Ref#2 requires a lot of setup (Win7 SDK for makecert), netsh commands, and, in order to use port 80 and port 443, I need to launch VS2010 as administrator, which I frown upon.
So, I came up with this solution that is quite simplistic with the following conditions:
I want to be able to use the RequireHttps attribute at Controller class or action level
I want MVC to use HTTPS when the RequireHttps attribute is present, and use HTTP if it is absent
I do not want to have to run Visual Studio as administrator
I want to be able to use any HTTP and HTTPS ports that are assigned by IIS Express
I can reuse the self-signed SSL cert of IIS Express, and I do not care if I see the invalid SSL Prompt
=========================================
You can find my solution/code here ==> ASP.NET MVC RequireHttps in Production Only
The port 44300 is sequential: 00 mean that its the first application you have configured as SSL enabled; 01 will be the second one and so on.
Since I also require my website to only work in HTTPS by adding the [RequireHttps] global attribute, I had some trouble debugging. When launched, it was automatically redirecting to https://localhost/
To fix this problem when debugging a web site, I simply create a new RequireHttpsAttribute that specify the port
#if DEBUG
public class RequireHttpsAttribute : System.Web.Mvc.RequireHttpsAttribute
{
protected override void HandleNonHttpsRequest(System.Web.Mvc.AuthorizationContext filterContext)
{
base.HandleNonHttpsRequest(filterContext);
var result = (RedirectResult)filterContext.Result;
var uri = new UriBuilder(result.Url);
uri.Port = 44301;
filterContext.Result = new RedirectResult(uri.ToString());
}
}
#endif
Use this class when debugging only. When deployed to IIS7, you should use Url rewriting to redirect to HTTPS.
Dan answer is right but if you still have problems with configuring IIS Express to serve your website with http and https on standard ports here is nice tutorial that that guide you step by step:
http://www.lansweeper.com/kb/54/How-to-configure-SSL-in-IIS-Express.html
In my case I accidentally deleted IIS Express certificate. I think it is generated the first time you use SSL in Visual Studio (F4 on selected project to get properties window and checking 'SSS Enabled' checkbox). This tutorial guided me how to create certificate and fix it.
Create class
public class RequireSSLAttribute: RequireHttpsAttribute
{
protected override void HandleNonHttpsRequest(AuthorizationContext filterContext)
{
base.HandleNonHttpsRequest(filterContext);
if (filterContext.HttpContext.Request.Url.Host.ToLower().Equals("localhost"))
{
// redirect to HTTPS version of page
string localhostSSLPort = System.Configuration.ConfigurationManager.AppSettings["localhostSSLPort"];
string url = "https://" + filterContext.HttpContext.Request.Url.Host + ":" + localhostSSLPort + filterContext.HttpContext.Request.RawUrl;
filterContext.Result = new RedirectResult(url);
}
}
}
And inside your web config add something like this
<appSettings>
<add key="localhostSSLPort" value="44300"/>
</appSettings>
And then you use it like
[RequireSSL]
public class AdminController : Controller
{
...
}

Resources