IdentityServer: How to pass custom parameters to Login Page?

When working with Identity Server, more sophisticated applications usually require specific context in the process of generating of the token.
For example, imagine the user wants to log-on in the client application. This will cause process of generating of the token inside of IdentityServer.
Usually token will be extended with custom claims, which can be used to authorize the access to application specific features.

Identity Server Service

Adding of custom specific claims can be done in IdentityServer inside of the Profile Service, which implements IProfileService interface.

Service (CustomClaimsService) can be registered as shown below:

var builder = services.AddIdentityServer()
               .AddInMemoryIdentityResources(Config.Ids)
               .AddInMemoryApiResources(Config.Apis)
               .AddInMemoryClients(Config.Clients)
               .AddTestUsers(TestUsers.Users)
               .AddProfileService<CustomClaimsService>();

Following code shows a simplified implementation of the custom profile service.

public class CustomClaimsService : IProfileService
{
       public Task GetProfileDataAsync(ProfileDataRequestContext context)
       {
           var myContext = context.Subject.Claims.
                        FirstOrDefault(c => c.Type == "myContext");
           if (myContext != null)
           {
               // todo:
               // Connect to external system, 
               // read roles and add them to claim list.
           }
           
           return Task.FromResult(0);
       }

       public Task IsActiveAsync(IsActiveContext context)
       {
           
           return Task.FromResult(0);
       }

The custom claim 'myContext' will appear in the profile service only if it was previous added to the list of so called 'additional claims' (called additional claims in the QuickStart application). Please go to ExternalController.CallbackMethod and append following code:

  [HttpGet]
  public async Task<IActionResult> Callback()
  {
      . . .
      var returnUrl = result.Properties.Items["returnUrl"] ?? "~/";

      var context = await _interaction.GetAuthorizationContextAsync(myContext);

      additionalLocalClaims.Add(new Claim("myContext",        
      context.Parameters["myContext"]));
      
      . . .
  }

This code makes sure that myContext parameter is added as a local claim, which will appear in your Profile Service implementation in the list claim list context.Subject.Claims.

Client Application

To make this happen, the client application (in my case ASP.NET Core) should be started in the wanted context (in this sample myContext).
For example

https://localhost/home/index/?myContext=123

This means, the user is entering the application in context '123'. The requirement is to propagate this value to the service, so the service can make usage of it. By default client application will not send any user specific parameter to the service. You need to register a hook OnRedirectToIdentityProvider, which will be invoked on authentication request.

  services.AddAuthentication(options =>
            {
                options.DefaultScheme = "Cookies";
                options.DefaultChallengeScheme = "oidc";
            })
            .AddCookie("Cookies")
            .AddOpenIdConnect("oidc", options =>
            {
                //options.Configuration.RequestParameterSupported
                options.Authority = "http://localhost:5000";
                options.RequireHttpsMetadata = false;
                options.ClientId = "mvc";
                options.ClientSecret = "secret";
                options.ResponseType = "code";
                options.Prompt = "login";
                options.SaveTokens = true;
                options.Events = new OpenIdConnectEvents
                {
                    OnRedirectToIdentityProvider = context =>
                    {
                        context.ProtocolMessage.SetParameter("myContext", 
                        context.Request.Query["myContext"]);

                        return Task.FromResult(0);
                    }
                };
            });

Recap

To send a custom parameter to Identity Server you should:

  1. Register OnRedirectToIdentityProvider hook in the client application (In a case of ASP.MVC Core).This function will insert a parameter of your choice in the replyUrl.
  2. Assuming that your parameter (in this example 'myContext') is obtained from the request, application should be started with specified context. For example: https://localhost/home/index/?myContext=123
  3. Extend the callback method in ExternallController of the service to insert your parameter as an additional local claim.
  4. Once the parameter is added as a local claim, it can be accessed within your custom implementation of the Profile Service.

comments powered by Disqus