I'm having issues with getting Social login to work in our existing ASP.NET MVC website project. The normal customer (our custom DB) login works just fine. For some reason the Challenge method on the IAuthenticationManager is not redirecting to the ExternalLoginCallback Action so that the proper Social login provider can prompt for login. Right now the Challenge method is redirecting back to the AccountController Login Action and the url after the login page loads looks like this:
http://localhost/Account/Login?ReturnUrl=%2fAccount%2fExternalLogin
I have went through multiple tutorials on the ASP.Net site that pertains to the new Identity API. I went through this tutorial first to get an understanding on the code, setup, and to create a Proof of Concept. I then went along with this tutorial to meld the new Identity API into our existing web site and replace our old System.Web.Security.MembershipProvider implementation. Here are some snapshots of the code.
Startup.Auth.cs
public partial class Startup
{
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
//app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.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
{
OnValidateIdentity = SecurityStampValidator
.OnValidateIdentity<ApplicationUserManager, IdentityUser, int>(validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager),
getUserIdCallback: (id) => (Int32.Parse(id.GetUserId())))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Uncomment the following lines to enable logging in with third party login providers
//app.UseMicrosoftAccountAuthentication(
// clientId: "",
// clientSecret: "");
//app.UseTwitterAuthentication(
// consumerKey: "",
// consumerSecret: "");
FacebookAuthenticationOptions fbAuthenticationOptions = new FacebookAuthenticationOptions();
fbAuthenticationOptions.Scope.Add("email");
fbAuthenticationOptions.AppId = "XXXXXX";
fbAuthenticationOptions.AppSecret = "YYYYYYY";
fbAuthenticationOptions.Provider = new FacebookAuthenticationProvider()
{
OnAuthenticated = async context =>
{
context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken));
foreach (var claim in context.User)
{
var claimType = string.Format("urn:facebook:{0}", claim.Key);
string claimValue = claim.Value.ToString();
if (!context.Identity.HasClaim(claimType, claimValue))
context.Identity.AddClaim(new System.Security.Claims.Claim(claimType, claimValue, "XmlSchemaString", "Facebook"));
}
}
};
fbAuthenticationOptions.SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie;
app.UseFacebookAuthentication(fbAuthenticationOptions);
//app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
//{
// ClientId = "",
// ClientSecret = ""
//});
}
}
ChallengeResult class inside of AccountController.cs
private 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;
}
IOwinContext owinContext = context.HttpContext.GetOwinContext();
IAuthenticationManager authenticationManager = owinContext.Authentication;
try
{
authenticationManager.Challenge(properties, LoginProvider);
}
catch (Exception ex)
{
throw;
}
}
}
ExternalLogin in AccountController.cs
public ActionResult ExternalLogin(string provider, string returnUrl)
{
// Request a redirect to the external login provider
return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
}
ExternalLoginCallBack in AccountController.cs
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 user = await UserManager.FindAsync(loginInfo.Login);
if (user != null)
{
await SignInAsync(user, isPersistent: false);
return RedirectToLocal(returnUrl);
}
else
{
// Get the information about the user from the external login provider
var info = await AuthenticationManager.GetExternalLoginInfoAsync();
if (info == null)
{
return View("ExternalLoginFailure");
}
string email = info.ExternalIdentity.Claims.First(c => c.Type == "urn:facebook:email").Value;
string firstName = info.ExternalIdentity.Claims.First(c => c.Type == "urn:facebook:first_name").Value;
string lastName = info.ExternalIdentity.Claims.First(c => c.Type == "urn:facebook:last_name").Value;
// If the user does not have an account, then prompt the user to create an account
RegisterDisplay registerDisplay = new RegisterDisplay
{
Email = email,
Agree = true,
UserName = loginInfo.DefaultUserName,
MailingAddress = new MailingAddress() { FirstName = firstName, LastName = lastName }
};
ViewBag.ReturnUrl = returnUrl;
ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
TempData["RegisterDisplay"] = registerDisplay;
return View("Register", returnUrl);
}
}
This one has me stumped as I'm not seeing any errors being thrown in the debugger. Please let me know if there is any other code that needs to be shown. Any help would be greatly appreciated. Thank you.
In my case, the challenge was simply returnung 401 (Unauthorized)
:
HttpContext.GetOwinContext().Authentication.Challenge("Application")
To get it working, I had to change it to:
HttpContext.GetOwinContext().Authentication.Challenge(DefaultAuthenticationTypes.ApplicationCookie)
Because this is how I configured my Cookie authentication on Startup
:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, // <---- I have to pass the same value as `AuthenticationType` to the `Challenge` method
AuthenticationMode = AuthenticationMode.Passive,
LoginPath = new PathString("/Account/Login"),
Provider = cookieAuthenticationProvider
});
The Challenge
method works only with registered authentication methods and it reconizes them via the configured AuthenticationType
property I you can se above.
Ok a co-worker and I figured out the issue with the Challenge method skipping the ExternalLoginCallback. This was a web.config issue, which I forgot to post with my original question. We needed to modify the authentication mode to be None. It use to be Forms, which was causing the site to hijack the Challenge call.
Original system.web section in the web.config
<system.web>
<httpRuntime targetFramework="4.5" />
<compilation debug="true" targetFramework="4.5" />
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="2880" />
</authentication>
</system.web>
Fixed system.web section in the web.config
<system.web>
<authentication mode="None" />
<compilation debug="true" targetFramework="4.5.1" />
<httpRuntime targetFramework="4.5.1" />
</system.web>
We also had to add a remove child to the system.webServer modules section
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<remove name="FormsAuthenticationModule" />
</modules>
</system.webServer>
Now everything is redirecting as it should.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With