2

I have an ASP.NET Core 2.x project with the following configuration:

services
  .AddAuthentication(options => options.DefaultScheme = CookieAuthenticaitonDefaults.AuthenticationScheme)
  .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
  .AddFacebook(ConfigureFacebook);

Predictably, when I call from one of my actions:

return Challenge(new AuthenticationProperties { RedirectUri = "/test" }, "Facebook");

... Then, I get navigated through the Facebook OAuth sequence. When I find my way back to my app, HttpContext.User.Identity is populated with the relevant details:

  • User.Identity.Name - The Facebook user name.
  • User.Identity.AuthenticationType - The string "Facebook".
  • User.Identity.IsAuthenticated - true.

This is all well and as is expected. However, if I add to my application configuration the following

services.AddIdentity<MyUserType, MyRoleType>()
  .AddEntityFrameworkStores<MyDbContext>();

Suddenly, the OAuth flow ends in User.Identity being anonymous without anything else changing. If we drill into IdentityServiceCollectionExtensions.cs, we find:

options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme; options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme; options.DefaultSignInScheme = IdentityConstants.ExternalScheme;

among other things...

What is going on here? Why is Identity interfering with the Cookie process, and what is the correct way to get the User returned from an OAuth provider?

2 Answers 2

2

For combining OAuth and Asp.Net Core Identity, you need to configure the facebookOptions.SignInScheme with CookieAuthenticationDefaults.AuthenticationScheme.

Try code below:

services
    .AddAuthentication(options => options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddFacebook(facebookOptions =>
    {
        facebookOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        facebookOptions.AppId = "xx";
        facebookOptions.AppSecret = "xxx";
    });
Sign up to request clarification or add additional context in comments.

2 Comments

Unfortunately, that doesn't do the trick. On the return trip back from Facebook (or another provider), the User.Identity is anonymous.
To add to my other remarks, FacebookOptions.SignInScheme falls back to AuthenticationOptions.DefaultSignInScheme and AuthenticationOptions.DefaultScheme, so adding that line is irrelevant.
1

When combining ASP.NET Identity and OAuth, certain considerations need to be made:

Configuring the services:

Adding AddCookie(CookieAuthenticationDefaults.AuthenticationScheme) is no longer necessary because Identity adds its own cookie handlers.

Getting the external user as a ClaimsPrincipal:

If you want the external user to be populated under HttpContext.User, do the following:

.AddFacebook(options => {
    options.SignInScheme = IdentityConstants.ApplicationScheme;
})

After being redirected to the RedirectUri in your challenge's AuthenticationProperties, your HttpContext.User will be populated.

Getting the external user as ExternalLoginInfo:

This is preferred if you need to know things about the user such as:

  1. From what provider did they come?
  2. What is their unique key on the provider?

Your services should be configured like so:

services.AddAuthentication()
    .AddFacebook(options =>
    {
        options.AppId = "";
        options.AppSecret = "";
    });

services.AddIdentity<IdentityUser, IdentityRole>()
    .AddEntityFrameworkStores<MyDbContext>();

In your login controller, inject SignInManager<TUser> in:

public DefaultController(SIgnInManager<IdentityUser> signInManager)

And in your challenge action, use ConfigureExternalAuthenticationProperties to get the challenge properties:

public IActionResult LoginExternal() {
    var props = SignInManager.ConfigureExternalAuthenticationProperties("Facebook", "/");
    return Challenge(props, "Facebook");
}

In your return action, use GetExternalLoginInfoAsync to get the external details about the user:

public async Task<IActionResult> LoginCallback() {
    var loginInfo = await SignInManager.GetExternalLoginInfoAsync();
    // This object will tell you everything you need to know about the incoming user.
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.