I'm using Asp.Net MVC 5 and utilizing the Microsoft's Identity (v2) implementation for authentication and external (facebook) login.
My code for the external login is like this:
in Startup.Auth.cs I declare the provider:
var facebookAuthenticationOptions = new FacebookAuthenticationOptions();
facebookAuthenticationOptions.Scope.Add("email");
(...)
facebookAuthenticationOptions.AppId = "<my fb app id>";
facebookAuthenticationOptions.AppSecret = "<my fb app secret>";
facebookAuthenticationOptions.Provider = new FacebookAuthenticationProvider()
{
OnAuthenticated = async context =>
{
//Get the access token from FB and store it in the database
context.Identity.AddClaim(
new System.Security.Claims.Claim("FacebookAccessToken",
context.AccessToken));
}
};
app.UseFacebookAuthentication(facebookAuthenticationOptions);
in a Controller function that I post the facebook login form,
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl)
{
returnUrl = !String.IsNullOrEmpty(returnUrl) ? returnUrl : "/";
// Request a redirect to the external login provider
return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
return null;
}
and with this in my base controller (that I derive all my controllers from), I use owin OAuth:
internal class ChallengeResult : HttpUnauthorizedResult
{
public ChallengeResult(string provider, string redirectUri)
: this(provider, redirectUri, null)
{
}
public ChallengeResult(string provider, string redirectUri, string userId)
{
LoginProvider = provider;
RedirectUri = redirectUri;
UserId = userId;
}
public string LoginProvider { get; set; }
public string RedirectUri { get; set; }
public string UserId { get; set; }
public override void ExecuteResult(ControllerContext context)
{
var properties = new AuthenticationProperties { RedirectUri = RedirectUri };
if (UserId != null)
{
properties.Dictionary[XsrfKey] = UserId;
}
context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
}
}
I then get the callback with an external callback function (as is seen that I've declared in the ChallangeResult object above):
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return RedirectToAction("Login");
}
// Sign in the user with this external login provider if the user already has a login
var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);
switch (result)
{
// login logic code here.
// and return some View(""); or RedirerctToAction("somewhere");
}
}
Also noteworthy to add, I'm using StructureMap 3.0 for dependency injection, and, as per the following code it limits the life span cycle of any controller code to the life span of a request.
public void Init(HttpApplication context) {
context.BeginRequest += (sender, e) => StructuremapMvc.StructureMapDependencyScope.CreateNestedContainer();
context.EndRequest += (sender, e) => {
HttpContextLifecycle.DisposeAndClearAll();
StructuremapMvc.StructureMapDependencyScope.DisposeNestedContainer();
};
}
Now, this all works well and does take care of the facebook login for me when I run the application from chrome. But when I use internet explorer (tried 11) or firefox it hangs in a black screen at /{controller}/ExternalLogin and does not do any redirecting.
If this was an issue with the IOC containment of structuremap, I believe it shouldn't have run smoothly on chrome as well. So I've been searching all over the web and couldn't find a solution - or any indication of what is going on. And since I don't get any exceptions or the like, I can't figure out what I'm doing wrong.
To sum up:
- I'm using: ASP.NET MVC 5 + OWIN (v3.0.1) + Identity (v2) + StructureMap 3 + Facebook Login
- This all works well and does the job in chrome
- Doesn't work in IE11 or Firefox
- My question is (1) why? and (2) how can I fix this?
Thanks.