We use cookies to provide you with a great user experience, analyze traffic and serve targeted promotions.   Learn More   Accept
Card image cap

Social Authentication in ASP.NET Core - Handling Multiple Login Providers

ASP.NET Core  • Posted 2 months ago

So far we have seen in details about how we can provide Google and Facebook authentication for a user with each provider having its own configuration and middleware invoked. In general scenarios, one would prefer providing multiple options for user to signin as it can open up multiple sections of audience.

Getting Started with Social Auth - Login Module

Integrating with Facebook

Integrating with Google

To achieve this, we can make little tweaks in the login module we have seen before and make use of it to cater to mutiple providers at once.

First, we'd create a model class which holds the Provider configuration present in the appsettings.json file for multiple providers as below:


public class OidcProviders
{
    public List<OidcProvider> Providers { get; set; }
}

public class OidcProvider
{
    public string Name { get; set; }
    public string ClientId { get; set; }
    public string ClientSecret { get; set; }
}

And the Provider Name can be from any of the above predefined provider types as below:


public static class OidcProviderType
{
    public const string Google = "google";
    public const string Facebook = "facebook";
    public const string Twitter = "twitter";
    public const string Legacy = "legacy";
}

In our application, we shall maintain a list of providers in our appsettings.json as below format:


"Oidc": {
    "providers": [
      {
        "name": "facebook",
        "clientId": "xxxxxxxxxxxxx",
        "clientSecret": "xxxxxxxxxxxxxxx",
        "redirectUrl": "/user/me"
      },
      {
        "name": "google",
        "clientId": "xxxxxxxxxxxxx-xxxxxxxxxxxxxxx.apps.googleusercontent.com",
        "clientSecret": "xxxxxxxxxxxxx",
        "redirectUrl": "/user/me"
      }
    ]
  }

And use these configurations to bind to respective login providers for their authentication middleware. In the Startup class, we'd add up to these for every provider present in the configuration as below:


var oidcProviders = new OidcProviders();
Configuration.Bind("Oidc", oidcProviders);
services.AddSingleton(oidcProviders);

var builder = services.AddAuthentication(options =>
{
    options.DefaultScheme = SocialAuthenticationDefaults.AuthenticationScheme;
    options.DefaultSignInScheme = SocialAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(SocialAuthenticationDefaults.AuthenticationScheme);

// loop through the configured providers
// and register middlewares for known types
foreach (OidcProvider provider in oidcProviders.Providers)
{
    switch (provider.Name)
    {
        case OidcProviderType.Google:
        builder.AddGoogle(options =>
        {
            options.SaveTokens = true;
            options.ClientId = provider.ClientId;
            options.ClientSecret = provider.ClientSecret;
        });
        break;
        case OidcProviderType.Facebook:
        builder.AddFacebook(options =>
        {
            options.SaveTokens = true;
            options.ClientId = provider.ClientId;
            options.ClientSecret = provider.ClientSecret;
        });
        break;
    }
}

Now that this is done, let's modify the Login endpoint for to take on the provider name as below:


public IActionResult ExternalLogin(string returnUrl, string provider = "google")
{
    string authenticationScheme = string.Empty;

    switch (provider)
    {
        case OidcProviderType.Facebook:
            authenticationScheme = FacebookDefaults.AuthenticationScheme;
            break;
        default:
            authenticationScheme = GoogleDefaults.AuthenticationScheme;
            break;
    }

    var auth = new AuthenticationProperties
    {
        RedirectUri = Url.Action(nameof(LoginCallback), new { provider, returnUrl })
    };

    return new ChallengeResult(authenticationScheme, auth);
}

Finishing this off, we'd have our View load the login buttons with dynamic provider links as below:


<div class="row my-lg-5 mx-lg-4">
    <div class="col-lg-12">
        <h5>Sign in using</h5>
    </div>
    @foreach (var provider in providers.Providers)
    {
        switch (provider.Name)
        {
            case "google":
                <div class="col-lg-12 p-2">
                    <a 
                        target="_self" 
                        href="~/user/ExternalLogin?provider=@provider.Name" 
                        class="btn btn-block btn-danger"
                    >
                        <span class="icon-google"></span> Google
                    </a>
                </div>
                break;
            case "facebook":
                <div class="col-lg-12 p-2">
                    <a 
                        target="_self" 
                        href="~/user/ExternalLogin?provider=@provider.Name" 
                        class="btn btn-block btn-primary"
                    >
                        <span class="icon-facebook2"></span> Facebook
                    </a>
                </div>
                break;
        }
    }
</div>

And when we run this application, we can see that our login module runs super fine for whatever provider we click, say google or facebook we can have the user authenticated profile added into our database.

Facebook Flow:

data/Admin/2020/4/app-login.PNG data/Admin/2020/4/fb-redirect.PNG data/Admin/2020/4/profile-capture.PNG

Google Flow:

data/Admin/2020/4/app-login.PNG data/Admin/2020/4/google-redirect.PNG data/Admin/2020/4/profile-google-capture.PNG

The source code for this project is available for clone in GitHub:

https://github.com/referbruv/socialloginsample.git