Identity Server 4 User delegation


Please Share

.net core

Do you have an Identity server 4 project? Are you looking into delegating user permissions? Have you seen how Google Service accounts work and need to work out something similar in Identity server 4. Well this tutorial should help.

We have a customer who needed to be able to access their data from a service. This meant that they would be running without user interaction, and they would need the permissions of a specific user.

What is user delegation?



The following statement was shamelessly stolen from wikipeda

Delegation is the process of a computer user handing over their authentication credentials to another user. In role-based access control models, delegation of authority involves delegating roles that a user can assume or the set of permissions that he can acquire, to other users.

So delegation is a way of logging into a system and being given access of a different user than the one you have logged in as. Sounds like a pretty scary security prospect doesn’t it. How can you be sure that John wants Jane to be logging in as him? The way i have worked it out John is going to have to create a client with access to login as him. He will then be able to give this to Jane enabling her to use this client in her code.

Ideally Jane should be able to create a new service account user which she could add to her company as a user and then its access that way but that will be part two of this system.

How to achieve user dedication with Identity server 4?

I began my research in this by looking into the available grant types with in identity server. The are as follows

  • Implicit – Used for client sided aplications (JavaScript, angular …)
  • Authorization code – used with Hybrid (Oauth2)
  • Hybrid – Used for server sided applications. (Java, .Net, php)
  • Client credentials – Used for server to server communication, tokens are always requested on behalf of a client, not a user
  • Resource owner password – In general a bad idea.
  • Refresh tokens – used for offline_access returns refresh token. Used by Hybrid and authorization code.
  • Extension grants – Extension grants allow extending the token endpoint with new grant types.

Resource owner password

As a emergency solution we implemented this a few months ago. We needed to get the system up and running on a moments notice. This solution is not idea because we have had to store the password in the project. The user we are impersonating can not change their password without the project being updated. The rest of this tutorial will be about how I altered the system to use this new method.

Client credentials

In the beginning I had hoped to use client credentials however there is one caveat to client credentials

tokens are always requested on behalf of a client, not a user

The token that is returned does not contain any subject claims, because it is not a user it is a client. This did not work with our api which is designed around the user.

Extension grants

OAuth 2.0 defines standard grant types for the token endpoint, such as password, authorization_code and refresh_token. Extension grants are a way to add support for non-standard token issuance scenarios like token translation, delegation, or custom credentials. See more Extension Grants

So Extension grants are Identity server 4 solution to delegation. It didn’t take me long to get a basic version of the example from the link above working. However it did not mention how to delegate the permissions to another user. So I am going to go though with you how I achieved that.

Create Client

The following code creates a new client. client.deligate.jane a property is set on this client called subject with the jans user id, which is 1.

return new List
            {
                new Client
                {
                    ClientId = "client.deligate.jane",
                    AllowedGrantTypes = new List() { "serviceaccount" } ,

                    ClientSecrets = 
                    {
                        new Secret("secret".Sha256())
                    },
                    AllowedScopes = { "testapi" },
                    Properties = new Dictionary()
                    {
                        {"subject","1"}  // the user we are going to allow to be deligated.
                    }
                }

You will be able to find the full project on Github at the end.

Validating the client

To use a custom grant type your identity server will need to be configured with a custom ExtensionGrantValidator. There are comments inline but what it does basically is verify that the client contains a subject property as well as the request and that they have the same id. I decided to use this to be sure that the service account requesting access knows which user it should be using. I may in the future set this to a JWT token which can be validated by the server but for now this simple solution works.

public class ServiceAccountGrantValidator : IExtensionGrantValidator
    {
        private readonly ITokenValidator _validator;

        public ServiceAccountGrantValidator(ITokenValidator validator)
        {
            _validator = validator;
        }

        public string GrantType => "serviceaccount";

        async Task IExtensionGrantValidator.ValidateAsync(ExtensionGrantValidationContext context)
        {
            // The clinet must be setup with the subject property.
            var prop = context.Request.Client.Properties.FirstOrDefault(a => a.Key.Equals(ServiceAccountRequiredProperty.Subject));
            if (prop.Key == null)
            {
                context.Result = new GrantValidationResult(TokenRequestErrors.InvalidClient);
                return;
            }

            // Grab the subject from the request.
            var subject = context.Request.Raw.Get(ServiceAccountRequiredProperty.Subject);
            if (prop.Value != subject)
            {
                context.Result = new GrantValidationResult(TokenRequestErrors.InvalidRequest);
                return;
            }

            if (!long.TryParse(subject, out var subjectid))
            {
                context.Result = new GrantValidationResult(TokenRequestErrors.InvalidRequest);
                return;
            }

            context.Result = new GrantValidationResult(subject, GrantType);
        }
    }
}


Your identity server will need to be configured to use this file. You will need to add the following like to your services.AddIdentityServer() in startup.cs

.AddExtensionGrantValidator()

Ideas for improvement

Service account client should be a user in our system. With google the service accounts it self is a user service accounts have their own Google drive account for example which you can upload to. What I would like to do would be create a user at the same time as the server account is client is created. The users login would be client.deligate.jane@mycompany.com then the user who created the service account would be able to share information with the sudu and would be able to add this user to his company in the system and control its access that way. This is the ideal situation but not something I have the power to create at this time.

Currently I am requiring that the client know the subject id that was set up in the client properties. I decided to do this as an extra security step, you need to know the secret and the id to use it. Currently I am sending this value as a string. I had an idea about sending a JWT token which could be encrypted rather than sending this value back and forth in clear text. An idea for another day

Conclusion

Setting up identity server 4 to allow for server to server user delegation is quite easy. You need to create a custom IExtensionGrantValidator which you can use to validate your client and return the credentials of the user you require. The project to go along with this can be found her Daimto.IdentityServer4.Samples

Please Share


Linda Lawton

About Linda Lawton

My name is Linda Lawton I have more than 20 years experience working as an application developer and a database expert. I have also been working with Google APIs since 2012 and I have been contributing to the Google .Net client library since 2013. In 2013 I became a a Google Developer Experts for Google Analytics.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.