Search code examples
identityserver4

Add checks in IdentityServer login before redirecting to client app


I have an IdentityServer4 website, used as OpenIdConnect Authority for other websites.

When users try to connect, they enter their login and password, and are redirected to the client website.

I would like to add some verifications before redirecting to the site. I tried to use the EventSink, but I don't know how to use it

    public class IdentityEventSink : IEventSink
    {
        private readonly UserManager<Utilisateur> userManager;
        private readonly IHttpContextAccessor httpContextAccessor;

        public IdentityEventSink(UserManager<Utilisateur> userManager, IHttpContextAccessor httpContextAccessor)
        {
            this.userManager = userManager;
            this.httpContextAccessor = httpContextAccessor;
        }

        public async Task PersistAsync(Event evt)
        {
            switch (evt.Id)
            {
                case EventIds.TokenIssuedSuccess:
                    var user = await userManager.GetUserAsync(httpContextAccessor.HttpContext.User);

                    if (!user.IsAllowedToDoThis)
                    {
                        //TODO : cancel and redirect to specific page
                    }

                    break;
            }

        }
    }

I'm not sure if the EventSink allows to do this or if there's a better way to do it.


Solution

  • When you need to perform your checks synchronously, without interacting with the user, the simplest could be to implement ICustomAuthorizeRequestValidator. For instance in the piece of code below we check that some specific application (client) is not restricted for the tenant of the current user, othervice display an error message instead of the redirect.

    public Task ValidateAsync(CustomAuthorizeRequestValidationContext context)
    {
        var request = context.Result.ValidatedRequest;
        var identity = request.Subject?.Identity;
        var sub = identity?.IsAuthenticated == true ? identity.GetSubjectId() : null;
    
        if (sub != null)
        {                
            var tenantId = _users.GetUserTenant(sub);
            if (tenantId != null)
            {
                var tenantInfo = _tenantService.GetTenantInfoAsync(tenantId).Result;
                var subscribedModules = tenantInfo?.Subscription?.Modules?.ToArray() ?? new string[] { };
    
                if (!subscribedModules.Contains((request.Client as WebAppClient).SubscriptionModule))
                {
                    context.Result.Error =
                    $"Module {(request.Client as WebAppClient).SubscriptionModule} is not in the subscription.";
                    context.Result.IsError = true;                
                }
            }
        }    
        return Task.CompletedTask;
    }
    

    To add your custom validator into DI just add .AddCustomAuthorizeRequestValidator<CustomAuthorizeRequestValidator>() to your services.AddIdentityServer() chain.