Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IdentityServer4 custom AuthorizeInteractionResponseGenerator

Sadly documentation on the implementation of a custom AuthorizeInteractionResponseGenerator in IdentityServer4 is sorely lacking.

I'm trying to implement my own AuthorizeInteractionResponseGenerator because I need a further step of user interaction (after authentication). My scenario is that a single identity (email) can be associated with multiple tenants. So after logon, I need the user to be presented with a list of associated tenants, so that they can choose one.

I have evaluated the source code, and have come up with the the following custom AuthorizeInteractionResponseGenerator:

public class AccountChooserResponseGenerator : AuthorizeInteractionResponseGenerator
    {
        public AccountChooserResponseGenerator(ISystemClock clock, 
            ILogger<AuthorizeInteractionResponseGenerator> logger, 
            IConsentService consent, IProfileService profile) 
            : base(clock, logger, consent, profile)
        {
        }

        public override async Task<InteractionResponse> ProcessInteractionAsync(ValidatedAuthorizeRequest request, ConsentResponse consent = null)
        {
            var response = await base.ProcessInteractionAsync(request, consent);
            if (response.IsConsent || response.IsLogin || response.IsError)
                return response;

            return new InteractionResponse
            {
                RedirectUrl = "/Organization"
            };
        }
    }

It inherits from the base AuthorizeInteractionResponseGenerator built into IdentityServer4, so that the standard Logon and Consent pages can show. This happens, and then the user is correctly redirected to the /Organization url to select an organization (tenant).

But what then? With the lack of documentation and examples, I'm really struggling to figure out the following two questions:

1) How do I now, having selected a Tenant, indicate to my custom AccountChooserResponseGenerator that my interaction is complete, and that the user can now be redirected back to the Client?

Edit:

Answer to 1: To indicate that the interaction is complete, you I have to return an empty new InteractionResponse(). In my case, a check for the existence of the TenantId claim sufficed, as follows:

if (!request.Subject.HasClaim(c=> c.Type == "TenantId" && c.Value != "0"))
                return new InteractionResponse
                {
                    RedirectUrl = "/Organization"
                };

            return new InteractionResponse();

2) And how can I get information about the selected Tenant to be added to the identity token that IdentityServer4 passes back to the Client?

Edit: Answer to 2: In the Controller Action method that gets executed after selecting a Tenant, I called :

 await HttpContext.SignInAsync(User.Claims.Single(r=> r.Type == "sub").Value,
                new System.Security.Claims.Claim("TenantId", tenant.Id.ToString()));

            return Redirect(ReturnUrl);

...which is an IdentityServer4-provided Extension to HttpContext.

like image 968
Shawn de Wet Avatar asked Nov 30 '25 02:11

Shawn de Wet


1 Answers

To implement a custom interaction response generator in Identity Server 4 and add information about the selected tenant to the identity token, you can inherit from the base AuthorizeInteractionResponseGenerator and override the ProcessInteractionAsync method. You can also use the IProfileService interface provided by Identity Server 4 to add the tenant information as a claim to the user's identity before the token is issued.

Steps to implement a custom interaction response generator:

Inherit from the base AuthorizeInteractionResponseGenerator and override the ProcessInteractionAsync method:

// Example implementation of ProcessInteractionAsync
public override async Task<InteractionResponse> ProcessInteractionAsync(ValidatedAuthorizeRequest request, ConsentResponse consent = null)
{
    // Your custom logic here

    // Indicate that the interaction is complete
    return new InteractionResponse();
}

Add information about the selected tenant to the identity token:

// Example implementation of IProfileService
public class CustomProfileService : IProfileService
{
    private readonly UserManager<ApplicationUser> _userManager;

    public CustomProfileService(UserManager<ApplicationUser> userManager)
    {
        _userManager = userManager;
    }

    public async Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        var user = await _userManager.GetUserAsync(context.Subject);
        if (user != null)
        {
            var tenantIdClaim = user.Claims.FirstOrDefault(c => c.Type == "TenantId");
            if (tenantIdClaim != null)
            {
                context.IssuedClaims.Add(new Claim("tenant_id", tenantIdClaim.Value));
            }
        }
    }

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

Register the CustomProfileService with Identity Server 4 in your Startup.cs file:

services.AddTransient<IProfileService, CustomProfileService>();

Tips:

  1. Always call base class methods first when overriding methods from IdentityServer4.
  2. Check the request object for details on the current flow.
  3. Log DEBUG level logs to stdout to trace the flow through your custom logic.
  4. Use the IConsentService and IProfileService injected into your generator to access user profiles and consent data.
  5. Check the Identity Server build instructions for compiling the source, as it makes debugging much easier!
like image 170
user21717839 Avatar answered Dec 04 '25 18:12

user21717839