Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to integrate custom authentication provider into IdentityServer4

Is it possible to somehow extend IdentityServer4 to run custom authentication logic? I have the requirement to validate credentials against a couple of existing custom identity systems and struggle to find an extension point to do so (they use custom protocols). All of these existing systems have the concept on an API key which the client side knows. The IdentityServer job should now be to validate this API key and also extract some existing claims from the system. I imagine to do something like this:

POST /connect/token
    custom_provider_name=my_custom_provider_1&
    custom_provider_api_key=secret_api_key

Then I do my logic to call my_custom_provider_1, validate the API key, get the claims and pass them back to the IdentityServer flow to do the rest.

Is this possible?

like image 252
NKnusperer Avatar asked May 05 '17 16:05

NKnusperer


People also ask

Which external providers are supported by identityserver4?

06. IdentityServer4 External Providers You can find the project here. All Identity Providers are supported using standard protocols like OpenID Connect, OAuth2, SAML2 and WS-Federation. This could be Okta, it could be Auth0, could be proprietary IdP of a client, could be another IdentityServer4.

How do I connect to an external provider using ASP NET identity?

This involves a couple of steps. If you are using ASP.NET Identity, many of the underlying technical details are hidden from you. It is recommended that you also read the Microsoft docs and do the ASP.NET Identity quickstart. The protocol implementation that is needed to talk to an external provider is encapsulated in an authentication handler .

How do I login to the identityserver4 application?

Click the Microsoft button to login. This redirects the user to the Microsoft Account login for the microsoft_id4_damienbod application. After a successful login, the user is redirected to the consent page. Click yes, and the user is redirected back to the IdentityServer4 application.

Can identityserver4 be used as a SSO provider?

As IdentityServer4 is OIDC Identity Provider you can actually set up one IdentityServer4 instance to be an external provider for another IdentityServer4 instance using OIDC middleware. As long as there is a single root node, all Identity Servers connected this way can achieve SSO.


1 Answers

I'm assuming you have control over the clients, and the requests they make, so you can make the appropriate calls to your Identity Server.

It is possible to use custom authentication logic, after all that is what the ResourceOwnerPassword flow is all about: the client passes information to the Connect/token endpoint and you write code to decide what that information means and decide whether this is enough to authenticate that client. You'll definitely be going off the beaten track to do what you want though, because convention says that the information the client passes is a username and a password.

In your Startup.ConfigureServices you will need to add your own implementation of an IResourceOwnerPasswordValidator, kind of like this:

services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();

Then in the ValidateAsync method of that class you can do whatever logic you like to decide whether to set the context.Result to a successful GrantValidationResult, or a failed one. One thing that can help you in that method, is that the ResourceOwnerPasswordValidationContext has access to the raw request. So any custom fields you add into the original call to the connect/token endpoint will be available to you. This is where you could add your custom fields (provider name, api key etc).

Good luck!

EDIT: The above could work, but is really abusing a standard grant/flow. Much better is the approach found by the OP to use the IExtensionGrantValidator interface to roll your own grant type and authentication logic. For example:

Call from client to identity server:

POST /connect/token
grant_type=my_crap_grant&
scope=my_desired_scope&
rhubarb=true&
custard=true&
music=ska

Register your extension grant with DI:

services.AddTransient<IExtensionGrantValidator, MyCrapGrantValidator>();

And implement your grant validator:

public class MyCrapGrantValidator : IExtensionGrantValidator
{
    // your custom grant needs a name, used in the Post to /connect/token
    public string GrantType => "my_crap_grant";

    public async Task ValidateAsync(ExtensionGrantValidationContext context)
    {
        // Get the values for the data you expect to be used for your custom grant type
        var rhubarb = context.Request.Raw.Get("rhubarb");
        var custard = context.Request.Raw.Get("custard");
        var music = context.Request.Raw.Get("music");

        if (string.IsNullOrWhiteSpace(rhubarb)||string.IsNullOrWhiteSpace(custard)||string.IsNullOrWhiteSpace(music)
        {
            // this request doesn't have the data we'd expect for our grant type
            context.Result = new     GrantValidationResult(TokenRequestErrors.InvalidGrant);
            return Task.FromResult(false);
        }

        // Do your logic to work out, based on the data provided, whether 
        // this request is valid or not
        if (bool.Parse(rhubarb) && bool.Parse(custard) && music=="ska")
        {
            // This grant gives access to any client that simply makes a 
            // request with rhubarb and custard both true, and has music 
            // equal to ska. You should do better and involve databases and 
            // other technical things
            var sub = "ThisIsNotGoodSub";
            context.Result = new GrantValidationResult(sub,"my_crap_grant");
            Task.FromResult(0);
        }

        // Otherwise they're unauthorised
        context.Result = new GrantValidationResult(TokenRequestErrors.UnauthorizedClient);
        return Task.FromResult(false);
    }
}
like image 156
Mashton Avatar answered Jan 02 '23 08:01

Mashton