Search code examples
c#asp.net-coreasp.net-web-apiopenid-connectopeniddict

How to support multiple external identity providers with an API-only OpenIddict based authorization server?


I'm trying to implement an authorization server based on OpenIddict which doesn't manage any users locally and delegates authentication to a set of supported external identity providers.

My scenario:

A user needs to log in into a (rich) client desktop application. In that application the user should be able to select one of the supported identity providers (i.e. Google, GitHub, etc.) for authentication. The authorization server shall not deliver a login webpage where the user clicks some "login with external provider" button/link. Instead, the user shall be able to click the corresponding login button in the desktop application which shall trigger a service call to a corresponding API endpoint of the authorization server. The authorization server shall then delegate authentication to the external identity provider the user selected in the desktop application's UI. On the client machine a browser window shall open where the user has to log in. Finally, the authorization server shall return an OpenID Connect access token to the rich client application which it shall then use to call backend services.

Question:

I had a look at several blog posts as well as the projects provided in the openiddict-samples repository. However, they don't show how to cope with multiple external identity providers and they usually have some server-side web application within the authorization server component. However, in my scenario the authorization server should only provide API endpoints but no web UI. I managed to pass the user-selected identity provider type together with the authorization request to the "authorize" API endpoint of the server but I don't know how to let the server leverage this information for redirecting the user to the correct identity provider's login page. Can anyone show me a code example for the authorize API endpoint which shows how to authenticate the user with the corresponding identity provider endpoint or point me to resources where a similar scenario is implemented?

UPDATE: Let me rephrase my question:

What needs to be modified in the Mimban sample application in order to support more external providers? (The client would specify the identity provider it wants to use for authentication via a URI query parameter in its authentication request.)


Solution

  • Here's an example showing how to trigger a challenge pointing to a provider specified by the client:

    // For applications that want to allow the client to select the external authentication provider
    // that will be used to authenticate the user, the identity_provider parameter can be used for that.
    if (!string.IsNullOrEmpty(request.IdentityProvider))
    {
        if (!string.Equals(request.IdentityProvider, Providers.GitHub, StringComparison.Ordinal))
        {
            return Forbid(
                authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
                properties: new AuthenticationProperties(new Dictionary<string, string>
                {
                    [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidRequest,
                    [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
                        "The specified identity provider is not valid."
                }));
        }
    
        var properties = _signInManager.ConfigureExternalAuthenticationProperties(
            provider: request.IdentityProvider,
            redirectUrl: Url.Action("ExternalLoginCallback", "Account", new
            {
                ReturnUrl = Request.PathBase + Request.Path + QueryString.Create(parameters)
            }));
    
        // Note: when only one client is registered in the client options,
        // specifying the issuer URI or the provider name is not required.
        properties.SetString(OpenIddictClientAspNetCoreConstants.Properties.ProviderName, request.IdentityProvider);
    
        // Ask the OpenIddict client middleware to redirect the user agent to the identity provider.
        return Challenge(properties, OpenIddictClientAspNetCoreDefaults.AuthenticationScheme);
    }
    

    You can see it in action here: https://github.com/openiddict/openiddict-core/blob/17693a4105925d3d474b1e7102041799bd2e43eb/sandbox/OpenIddict.Sandbox.AspNetCore.Server/Controllers/AuthorizationController.cs#L98-L127