简体   繁体   中英

.NET CORE API Making Facebook Login Work With Openiddict/Identity

I have one project (Project A) which is a .NET CORE API project using Openiddict with an endpoint of /connect/token to issue JWT tokens using Identity to handle the security etc. This project works great as is.

I have another project (Project B), which is just a very simple project with some HTML that makes requests to the API to get an access token, and get data from the API. This project also works great.

Now the part I cannot wrap my brain around, how do I use Facebook login between these two totally separate projects? I know how to use it if everything is under one roof, and it's really easy, but this scenario has me totally confused since everything is separated. So for starters, who handles the 'ExternalLogin', 'ExternalLoginCallBack' logic (from .NET web template using individual accounts), the API? The HTML project? When connecting with Facebook, what redirect uri should I use (API/HTML project)? Then who should have the below code in their 'Startup.cs' file?

app.UseFacebookAuthentication(new FacebookOptions
{
     AppId = "xxxxxxx",
     AppSecret = "xxxxxxxxx",
     Scope = { "email", "user_friends" },
     Fields = { "name", "email" },
     SaveTokens = true,
});

And finally if this helps here is how I have Project A currently setup:

STARTUP.CS (API)

public void ConfigureServices function: (API)

// add entity framework using the config connection string
            services.AddEntityFrameworkSqlServer()
                .AddDbContext<ApplicationDbContext>(options =>
                     options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

            // add identity
            services.AddIdentity<ApplicationUser, ApplicationRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders();

            // add OpenIddict
            services.AddOpenIddict<ApplicationUser, ApplicationRole, ApplicationDbContext>()
                .DisableHttpsRequirement()
                .EnableTokenEndpoint("/connect/token")
                .AllowPasswordFlow()
                .AllowRefreshTokenFlow()
                .UseJsonWebTokens()
                .AddEphemeralSigningKey();

            services.AddCors();

public void Configure function: (API)

 app.UseJwtBearerAuthentication(new JwtBearerOptions
                {
                    AutomaticAuthenticate = true,
                    AutomaticChallenge = true,
                    RequireHttpsMetadata = false,
                    Audience = "http://localhost:54418/",
                    Authority = "http://localhost:54418/"
                });

Authorization Controller (API)

public class AuthorizationController : Controller
    {
        private OpenIddictUserManager<ApplicationUser> _userManager;

        public AuthorizationController(OpenIddictUserManager<ApplicationUser> userManager)
        {
            _userManager = userManager;
        }

        [HttpPost("~/connect/token")]
        [Produces("application/json")]
        public async Task<IActionResult> Exchange()
        {
            var request = HttpContext.GetOpenIdConnectRequest();

            if (request.IsPasswordGrantType())
            {
                var user = await _userManager.FindByNameAsync(request.Username);
                if (user == null)
                {
                    return BadRequest(new OpenIdConnectResponse
                    {
                        ErrorDescription = "The username or password provided is incorrect"
                    });
                }

                var identity = await _userManager.CreateIdentityAsync(user, request.GetScopes());

                // Add a custom claim that will be persisted
                // in both the access and the identity tokens.
                if (user.Avatar != null)
                {
                    identity.AddClaim("user_avatar", user.Avatar,
                        OpenIdConnectConstants.Destinations.AccessToken,
                        OpenIdConnectConstants.Destinations.IdentityToken);
                }

                if (user.InSiteUserName != null)
                {
                    identity.AddClaim("insite_username", user.InSiteUserName,
                  OpenIdConnectConstants.Destinations.AccessToken,
                  OpenIdConnectConstants.Destinations.IdentityToken);
                }


                identity.AddClaim("hasLoggedIn", user.HasLoggedIn.ToString(),
              OpenIdConnectConstants.Destinations.AccessToken,
              OpenIdConnectConstants.Destinations.IdentityToken);


                // Create a new authentication ticket holding the user identity.
                var ticket = new AuthenticationTicket(
                    new ClaimsPrincipal(identity),
                    new AuthenticationProperties(),
                    OpenIdConnectServerDefaults.AuthenticationScheme);

                ticket.SetResources(request.GetResources());
                ticket.SetScopes(request.GetScopes());

                return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
            }

            return BadRequest(new OpenIdConnectResponse
            {
                Error = OpenIdConnectConstants.Errors.UnsupportedGrantType,
                ErrorDescription = "The specified grant type is not supported."
            });
        }



    }
}

I don't know if it's including anything from Project B since it's pretty basic/bare and relies on the API for everything.

I know this is a loaded and complicated question, and I'm sure I'm not presenting it as fluidly as possible so I apologize in advance for that, like I said before, I'm confused. Thank you!

Now the part I cannot wrap my brain around, how do I use Facebook login between these two totally separate projects? I know how to use it if everything is under one roof, and it's really easy, but this scenario has me totally confused since everything is separated. So for starters, who handles the 'ExternalLogin', 'ExternalLoginCallBack' logic (from .NET web template using individual accounts), the API? The HTML project?

In the recommended case (ie when using an interactive flow like the authorization code flow or the implicit flow ), the authorization server project itself is responsible of handling the external authentication dance, using the social providers you've configured in your ASP.NET Core pipeline.

In theory, the final client application (ie the JS app) doesn't even know that you've decided to use external authentication at the authorization server level, since it's not directly linked to Facebook or Google.

In this case, the redirect_uri configured in the Facebook options must correspond to an endpoint owned by the authorization server application (in your case, it's provided by the Facebook authentication middleware).


If you don't like this approach, there's also a different flow named "assertion grant" , that basically reverses how things are handled: the final client app (the JS app in your case) is directly linked to Facebook - so the redirect_uri must correspond to the JS app - and uses OpenIddict's token endpoint to "exchange" Facebook tokens with tokens issued by your own server, that can be used with your own APIs.

For more information about this flow, please read Exchanging a google idToken for local openId token c# .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM