ADFS OWIN authentication not invoked in local IIS - asp.net-mvc

I am trying to determine how to get an MVC application set up to authenticate against an on-premise ADFS server. I am using the Azure AD sample found at the Github Azure Samples and altered it as described in Vittorio Bertocci's blog to use my app's RealmId and my organization's ADFS metadata endpoint. When I run the sample using IIS Express, the OWIN middleware is invoked and I get the redirect to the ADFS login screen.
However, The app I am working on needs to run in IIS so I am trying to configure my sample app in local IIS. When I run the sample in local IIS, there is no redirect to ADFS and the windows (Kerberos) identity is returned instead. How do I make sure that the OWIN middleware is invoked by IIS? The app pool is running in Integrated v4.0 mode.
This is the code in my Startup.Auth.cs:
private static string realm = ConfigurationManager.AppSettings["ida:Wtrealm"];
private static string metadata = ConfigurationManager.AppSettings["ida:ADFSMetadata"];
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseWsFederationAuthentication(
new WsFederationAuthenticationOptions
{
Wtrealm = realm,
MetadataAddress = metadata,
Notifications = new WsFederationAuthenticationNotifications
{
AuthenticationFailed = context =>
{
context.HandleResponse();
context.Response.Redirect("Home/Error?message=" + context.Exception.Message);
return Task.FromResult(0);
}
}
});
}

What is the Authentication tab in IIS set to for your application?
Have you got Forms or Windows Authentication enabled?
What happens if the only item enabled is Anonymous?

Related

Owin Facebook Login failure on MVC Azure Production site ONLY

The short version:
I have an MVC5 website app deployed as an Azure cloud service web role. Using Owin for a login flow. The Owin Facebook integration works fine when testing the site on localhost, but on the production server GetExternalLoginInfoAsync() is returning null in the callback from signin-facebook.
Some details:
Have all the latest Owin Nuget packages (4.0.1 other than Identity.Owin 2.2.2 and Owin (startup components) 1.0).
The app uses basic Owin cookie authentication (traditional logins work fine).
We are not using the default Owin SigninManager, or UserManager as per the MVC WebApplication template. (Failure occurs well before reaching any of that code, in theory!)
Testing using Chrome on Windows 10.
It works perfectly on localhost (both debug and release), directly running the web project from visual studio (no cloud service involvement).
Am using the same Facebook test user for both localhost and production.
On the Facebook end, everything looks normal and the app is added to the user's list of apps, with all the permissions. It's not a facebook rejection.
Went so far as to remove Application Insights (as that is something different in production), but it did not affect the problem.
Looking at DevTools in Chrome:
Cache control looks normal for everything, all no-cache.
The .AspNet.Correlation.Facebook cookie is set and is being returned (same value) to the signin-facebook endpoint on completion from Facebook. Note that there is no other Owin related cookie.
Re. Deployment:
The Azure cloud service consists of the single web role.
Azure cloud service osFamily="5" osVersion="*"
Deployment is done directly from Visual Studio to the staging slot (right click publish).
I typically perform an interactive login to the staging slot, and when things are warmed up I use the Azure portal to swap the slots. Testing of the Facebook login is only on the production slot (not staging).
Here is a code fragment showing the processing (ChallengeResult is per the current WebApplication template code). The Controller is marked as [OutputCache(Location = OutputCacheLocation.None)] and is not marked for [Authorize]:
// POST: /Membership/ExternalSignupDispatch (They clicked to login with Facebook, Google, etc.)
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ExternalSignupDispatch(string provider, string community = Communities.Dating)
{
// Request a redirect to the external login provider
return new ChallengeResult(provider, Url.Action("ExternalSignupCallback1", "Membership", new { community }));
}
// GET: /Membership/ExternalSignupCallback
public async Task<ActionResult> ExternalSignupCallback1(string community = Communities.Dating)
{
var info = await AuthenticationManager.GetExternalLoginInfoAsync();
if (info == null) // Unsuccessful login
{
The silent failure of Owin/Facebook on the production server is maddening. If only there were an error enumeration... or an exception... but alas
Any ideas are greatly appreciated!
So. It turns out that Owin will currently fail in mysterious manners if there is no pre-existing ASP.NET_SessionID cookie present. Without it, the signin-facebook function does not remove the .Aspnet.Correlation.Facebook cookie, and does not set the .Aspnet.ExternalCookie cookie. The non-presence of the Session ID cookie somehow prevents the required cookie processing from taking place. All this sets the stage for intermittent silent failures, depending on the client's cookie status.
The workaround is to store a fake Session variable when generating the form with the Facebook login, forcing creation of the SessionID cookie prior to any Owin logins.
Note we are using the SystemWebCookieManager (in the hopes of avoiding such cookie issues). It appears that things are still evolving.
For reference, here is the cookie setup in our ConfigureAuth function:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
CookieManager = new SystemWebCookieManager(),
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
ExpireTimeSpan = TimeSpan.FromMinutes(Params.LoginExpiryMinutes),
SlidingExpiration = true,
LoginPath = new PathString("/Login"),
Provider = new CookieAuthenticationProvider // Used to allow returning 401 Unauthorized status to API calls, instead of 302 redirect
{
OnApplyRedirect = ctx =>
{
if (!IsAjaxRequest(ctx.Request))
{
ctx.Response.Redirect(ctx.RedirectUri);
}
}
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

Context.User is null in self-hosted SignalR hub when called from IIS-hosted MVC app

I'm have an IIS hosted MVC 5 app that uses Asp.Net Identity and OWIN for authentication via .AspNet.ApplicationCookie. From one of its views, I make calls to long-running methods on a self-hosted SignalR hub (running on the same server) via a SignalR JS client. These calls all work as expected. I now wish to decorate my hub with [Authorize(Roles = "Administrator")]. This has proved problematic. Setting a breakpoint in a hub method reveals that the Context.User is null, even though the .AspNet.ApplicationCookie is clearly in the Context.RequestCookies.
Here is the bootstrap for the hub (self-hosted in a windows service):
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
map.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie
});
var hubConfiguration = new HubConfiguration();
map.RunSignalR(hubConfiguration);
});
Here is the auth config for the web app (hosted in IIS):
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(UserAccountContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
Question 1: Is the use of [Authorize] possible in the scenario described above? If so, how?
Question 2: Would it be better to just merge the self-hosted hub into the IIS hosted app? If so, are there any issues with long-running hub methods under IIS?
Update 1
I've tried adding TicketDataFormat = new TicketDataFormat(new MachineKeyDataProtector("ASP.NET Identity")) to the CookieAuthenticationOptions on my hub config, but that didn't help. Sure seems like this should be easier than it is.
I ended up moving my self-hosted hub into my ASP.Net application and it worked just fine. This seemed easier and more maintainable than implementing the workaround in this SO question, OWIN Self-Host CookieAuthentication & Legacy .NET 4.0 Application / FormsAuthenticationTicket

Azure ACS with ADFS Server

We have a scenario where we have a single application that will be accessed from our organization and also accessed from a outside organization. We will be hosting this web application in azure. I am using MVC 5 with the Owin WSFederation Middleware. I can connect to my Local ADFS Server and it works as expected.
app.UseWsFederationAuthentication(
new WsFederationAuthenticationOptions
{
Wtrealm = "https://localhost:44321/",
MetadataAddress = "https://sso2.xxxxx.com/FederationMetadata/2007-06/FederationMetadata.xml"
});
When I use ACS as the Main STS and set up our ADFS server as an IDP, it routes to the correct ADFS login page, but once I authenticate I get this error
ID4037: The key needed to verify the signature could not be resolved from the following security key identifier 'SecurityKeyIdentifier
(
IsReadOnly = False,
Count = 1,
Clause[0] = X509RawDataKeyIdentifierClause(RawData = MIIC4DCCAc...'. Ensure that the SecurityTokenResolver is populated with the required key.
app.UseWsFederationAuthentication(
new WsFederationAuthenticationOptions
{
Wtrealm = "https://localhost:44321/",
MetadataAddress = "https://xxxxxxxx.accesscontrol.windows.net/FederationMetadata/2007-06/FederationMetadata.xml"
});
I feel like this is an issue because the Federated metadata contains the key for the signature and since the owin middleware only has the metadata from the ACS the signer can't be determined.
Thoughts?
Is the realm configured in ACS?
If I was implementing this, then i would probably use ADFS instead of ACS for my Home Realm Discovery because ACS is on it's way out.
I would configure Azure as an additional Claims provider in ADFS and only have my application using ADFS.
You also get a little more control around what the HRD pages look like.

Asp .Net Identity 2 OAuth via Google+ error in production server

I am getting the problem when I use Asp .Net Identity 2 connect Google+. Currently, my website connect Google+ via OAuth API 2 and use Claim Identity store some information of users. I was setup fine in running localhost with https. But the problem appear when I deploy my website into production server. After use accept to provide permission for my website to get his information, Google+ callback my website with url /signin-google. And after that, ASP .Net MVC automatically redirect to ExternalCallBack action but it is get error ?error=access_denied. I have capture the network and see information below:
In url callback signin-google:
signin-google?state=nNABsQBmwoPILh1mViOUIqzDcxQIS3HVZx2jtrSYCwd-ifMn4bDgBV1H1qdewFZx5Lz1c35ZZEpUem9jDTUrKlzWDuV-MwTQ3Tesx66PEjWdQQHo0QPJHX_bRMHqgN-Ad1whLs4iUyUSCH39oeTvYg3Cx6O0_v7Sc5GaUujHgr6xW1jw8EImhWJgnFGXgkAjD5hOtr7RoYO23xJyw0AIyuWnyx1gInJndWKvL-eqWPD9BtRaNe3nhWF5NGEG_2Ir&code=4/OEwsZCeeDPKrN5Dls3Uu-Q0wacdMqlhdbb8B1P__8X8.MmSgAQ_-cmIRgrKXntQAax20FxCmlQI
Asp.Net.Collrelation.Google nXgdr60bDh6tfivnvc6NA6ubz1K9zwjOqgrBBQgsitE
In URL callback external-login:
external-login-callback?error=access_denied
Hope you help me.
*Edit:
This is my code in Startup.Config.cs
var googleOpt = new GoogleOAuth2AuthenticationOptions()
{
ClientId = "xxxxxxxxpm0il.apps.googleusercontent.com",
ClientSecret = "xxxxxxxx",
Provider = new GoogleOAuth2AuthenticationProvider()
{
OnAuthenticated = (context) =>
{
context.Identity.AddClaim(new System.Security.Claims.Claim("GoogleAccessToken", context.AccessToken));
return Task.FromResult(o);
}
}
};
googleOpt.Scope.Add("https://www.googleapis.com/auth/plus.login");
googleOpt.Scope.Add("https://www.googleapis.com/auth/plus.profile.emails.read");
googleOpt.SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie;
app.UseGoogleAuthentication(googleOpt);
And in my account controller I use default code of Asp .Net MVC 5.2
I had the same issue and had to enable the Google+ API in the Google Developer Console.
If this is turned off, it will not work. As soon as I turned it on, everything was fine.

Unable to authorize WEB API requests from a site in different project

I have, what I think is a very common solution pattern, in Visual Studio:
ASP.NET MVC project that has a number angular-backed views ASP.NET
WEB API v2 project that serves that data to angularjs
I'm using OWIN with auth stored in application cookie approach
MVC and WEB API projects have this config:
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
LogoutPath = new PathString("/Account/Logout"),
CookieDomain = CloudConfigurationManager.GetSetting("AuthCookieDomain"),
CookieSecure = CookieSecureOption.Always,
CookiePath = "/",
CookieHttpOnly = false,
ExpireTimeSpan = TimeSpan.FromDays(365),
SlidingExpiration = true, });
// Use a cookie to temporarily store information about a user logging in with a third party login provider app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
Web API asks for every call to be authenticated:
config.Filters.Add(new AuthorizeAttribute());
Cors is enabled in and Cors for OWIN is installed in Web API project.
Both projects are running under https:// localhost but with different ports at the moment
If I login into the MVC site thru browser and do a GET on a web api method, all is good. However, if the site does the request, it gets an UNauthorized.
Any suggestions?

Resources