benanderson89
benanderson89

Reputation: 83

Don't issue a token in Identity Server 4 when a user is set to inactive

We've recently implemented the ability to disable users in our application with an "Active" boolean field in the Identity.AspNetUsers table. Logging in to the back office system (an Angular application) is easily handled with an implicit flow -- simply check the field before calling PasswordSignInAsync.

We can't figure a way to stop a token being issued for any mobile devices using the sister application (written in Flutter) that calls the built in ID Server 4 endpoint /connect/token. Likewise, we can't stop the application from requesting, and then receiving, a valid refresh token. We can't hard delete the user as we have hard links to other tables in the database for auditing purposes.

Any help would be massively appreciated.

We're using DotNET Core 3,1.

EDIT: We're using the password grant type.

Upvotes: 1

Views: 1502

Answers (2)

benanderson89
benanderson89

Reputation: 83

When using the password grant with the built in /connect/token endpoint, you implement the interface ICustomTokenRequestValidator and add it as a Transient to the service collection. This has one method, ValidateAsync, and if the user referenced by your request is valid you simply return and the pipeline continues as normal. If your user is not valid you set the Result.IsError property on CustomTokenRequestValidationContext to true, and supply a string to Result.Error before you return so the token is then not issued.

Inject UserManager<T> and IHttpContextAccessor so you can access the username and user store from the method.

Here's an implementation:

    public class CustomTokenRequestValidator : ICustomTokenRequestValidator
    {
        private readonly UserManager<ApplicationUser> _userManager;
        private readonly IHttpContextAccessor _httpContextAccessor;
        private const string errorMessage = "invalid_username_or_password";
        public CustomTokenRequestValidator(
              UserManager<ApplicationUser> userManager
            , IHttpContextAccessor httpContextAccessor)
        {
            _userManager = userManager;
            _httpContextAccessor = httpContextAccessor;
        }
        public async Task ValidateAsync(CustomTokenRequestValidationContext context)
        {
            _httpContextAccessor.HttpContext.Request.Form.TryGetValue("username", out var userOut);
            var u = userOut.ToString();

            if(u != null)
            {
                var user = await _userManager.FindByEmailAsync(u);
                if(user == null || !user.Active)
                {
                    context.Result.IsError = true;
                    context.Result.Error = errorMessage;
                }
            } else
            {
                context.Result.IsError = true;
                context.Result.Error = errorMessage;
            }
            return;
        }
    }

Upvotes: 1

Tore Nestenius
Tore Nestenius

Reputation: 19931

When the client asks for a new access token using the refresh token, then the RefreshTokenService is involved. by Customizing refresh token behavior you can lookup if the user is disabled and then reject thew new access token from being issued. See this page for more details about how to do this.

Alternatively you can in the class that implements IPersistedGrantStore add some code to lookup if the user is disabled and then return

return Task.FromResult<PersistedGrant>(null!);

When blocked.

Upvotes: 1

Related Questions