Identityserver4: Override the Token authentication

Created on 15 Jul 2017  路  12Comments  路  Source: IdentityServer/IdentityServer4

Hello,

I would like to override the code that checks if the user exists, password is correct under the /connect/token endpoint.

My use case is that my application has a concept of two different user types

  1. Regular Local user with a password

  2. External user, this is a user that is authenticated by an external service

So I do not store any password information about the external user since they are not really authenticating through Identity Server.

Is this possible?

question

Most helpful comment

Thats probably because it is invoking the IProfileService.IsActiveAsync method. You would need to override that in a custom profile service.

All 12 comments

For which grant type?

Sorry, should of included that.

It's for ResourceOwnerPassword.

Interesting,

I followed that and looked at some examples but I am unable to get it to work correctly.

Here is my OwnerPasswordValidator class

public class OwnerPasswordValidator : IResourceOwnerPasswordValidator
    {
        public OwnerPasswordValidator(UserManager<User> um)
        {
            UserManager = um;
        }

        public UserManager<User> UserManager { get; }

        public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
        {
            var user = await UserManager.FindByNameAsync(context.UserName);

            if (user == null)
            {
                context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Username or password is incorrect");
                return;
            }

            var passwordValid = await UserManager.CheckPasswordAsync(user, context.Password);
            if (!passwordValid)
            {
                context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Username or password is incorrect");
                return;

            }

            var roles = await UserManager.GetRolesAsync(user);
            var claims = new List<Claim>
            {
                new Claim(ClaimTypes.Name, user.UserName),
                new Claim(ClaimTypes.Email, user.Email)
            };

            foreach (var role in roles)
            {
                claims.Add(new Claim(ClaimTypes.Role, role));
            }
            context.Result = new GrantValidationResult(user.UserName, "password", claims);
        }
    }

And I've registered this in the ConfigureServices method like the following:

            services.AddIdentityServer()
                .AddTemporarySigningCredential()
                .AddInMemoryPersistedGrants()
                .AddInMemoryIdentityResources(IdentityConfig.GetIdentityResources())
                .AddInMemoryApiResources(IdentityConfig.GetApiResources())
                .AddInMemoryClients(IdentityConfig.GetClients())
                .AddAspNetIdentity<User>()
                .Services.AddTransient<IResourceOwnerPasswordValidator, OwnerPasswordValidator>();

I am now getting the error when the username and password are correct:

{
    "error": "invalid_grant"
}

If I remove the Services.AddTransient<IResourceOwnerPasswordValidator, OwnerPasswordValidator>(); then it now works fine.

check logs/debug?

Very strange

It's somehow stating that the user has been disabled with the above OwnerPasswordValidator class

Logs:

2017-07-18 12:41:22.705 +01:00 [Information] Request starting HTTP/1.1 POST http://localhost:52038/connect/token/ application/x-www-form-urlencoded 91
2017-07-18 12:41:22.709 +01:00 [Information] "Identity.Application" was not authenticated. Failure message: "Unprotect ticket failed"
2017-07-18 12:41:22.709 +01:00 [Debug] CORS request made for path: "/connect/token/" from origin: "chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop" but rejected because invalid CORS path
2017-07-18 12:41:22.709 +01:00 [Debug] Request path "/connect/token/" matched to endpoint type Token
2017-07-18 12:41:22.710 +01:00 [Debug] Mapping found for endpoint: Token, creating handler: "IdentityServer4.Endpoints.TokenEndpoint"
2017-07-18 12:41:22.712 +01:00 [Information] Invoking IdentityServer endpoint: "IdentityServer4.Endpoints.TokenEndpoint" for "/connect/token/"
2017-07-18 12:41:22.712 +01:00 [Debug] Start token request.
2017-07-18 12:41:22.712 +01:00 [Debug] Start client validation
2017-07-18 12:41:22.712 +01:00 [Debug] Start parsing Basic Authentication secret
2017-07-18 12:41:22.712 +01:00 [Debug] Start parsing for secret in post body
2017-07-18 12:41:22.716 +01:00 [Debug] Parser found secret: "PostBodySecretParser"
2017-07-18 12:41:22.717 +01:00 [Debug] Secret id found: "frontend"
2017-07-18 12:41:22.717 +01:00 [Debug] Secret validator success: "HashedSharedSecretValidator"
2017-07-18 12:41:22.717 +01:00 [Debug] Client validation success
2017-07-18 12:41:22.717 +01:00 [Debug] Start token request validation
2017-07-18 12:41:22.717 +01:00 [Debug] Start resource owner password token request validation
2017-07-18 12:41:52.903 +01:00 [Error] User has been disabled: "a"
2017-07-18 12:41:52.903 +01:00 [Error] "{
  \"ClientId\": \"frontend\",
  \"GrantType\": \"password\",
  \"Scopes\": \"api\",
  \"UserName\": \"a\",
  \"Raw\": {
    \"client_id\": \"frontend\",
    \"scope\": \"api\",
    \"client_secret\": \"secret\",
    \"grant_type\": \"password\",
    \"username\": \"a\",
    \"password\": \"***REDACTED***\"
  }
}"
2017-07-18 12:41:52.904 +01:00 [Debug] Connection id ""0HL6DQ63AQ0LP"" completed keep alive response.
2017-07-18 12:41:52.905 +01:00 [Information] Request finished in 30197.945ms 400 application/json

Thats probably because it is invoking the IProfileService.IsActiveAsync method. You would need to override that in a custom profile service.

That works!

Thank you very much for your help.

@tidusjar may I ask what have you done to make it work?
I got this far :)


public class ProfileService : IProfileService
    {

        public Task GetProfileDataAsync(ProfileDataRequestContext context)
        {
            return Task.FromResult(0);
        }

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

@leastprivilege what is it that the GetProfileDataAsync needs to return?

This is my log

2017-08-04 01:09:21.602 +02:00 [Information] Invoking IdentityServer endpoint: "IdentityServer4.Endpoints.TokenEndpoint" for "/connect/token"
2017-08-04 01:09:26.862 +02:00 [Information] Token request validation success
"{
  \"ClientId\": \"resourceOwnerClient\",
  \"ClientName\": \"my browser app\",
  \"GrantType\": \"password\",
  \"Scopes\": \"myScope\",
  \"UserName\": \"[email protected]\",
  \"Raw\": {
    \"username\": \"[email protected]\",
    \"password\": \"***REDACTED***\",
    \"grant_type\": \"password\",
    \"client_id\": \"resourceOwnerClient\",
    \"client_secret\": \"mSecret\",
    \"scope\": \"myScope\"
  }
}"
2017-08-04 01:09:29.293 +02:00 [Information] Invoking IdentityServer endpoint: "IdentityServer4.Endpoints.DiscoveryEndpoint" for "/.well-known/openid-configuration"
2017-08-04 01:09:29.376 +02:00 [Information] Invoking IdentityServer endpoint: "IdentityServer4.Endpoints.DiscoveryEndpoint" for "/.well-known/openid-configuration/jwks"
2017-08-04 01:09:32.499 +02:00 [Warning] CorsPolicyService did not allow origin: "http://localhost:8100"
2017-08-04 01:09:32.507 +02:00 [Information] Invoking IdentityServer endpoint: "IdentityServer4.Endpoints.RevocationEndpoint" for "/connect/revocation"
2017-08-04 01:09:32.524 +02:00 [Information] No matching token found
2017-08-04 01:09:34.458 +02:00 [Warning] CorsPolicyService did not allow origin: "http://localhost:8100"
2017-08-04 01:09:34.488 +02:00 [Information] Invoking IdentityServer endpoint: "IdentityServer4.Endpoints.RevocationEndpoint" for "/connect/revocation"
2017-08-04 01:09:34.495 +02:00 [Information] No matching token found
2017-08-04 01:09:34.686 +02:00 [Warning] CorsPolicyService did not allow origin: "http://localhost:8100"
2017-08-04 01:09:34.737 +02:00 [Information] Invoking IdentityServer endpoint: "IdentityServer4.Endpoints.RevocationEndpoint" for "/connect/revocation"
2017-08-04 01:09:34.751 +02:00 [Information] No matching token found
2017-08-04 01:10:43.375 +02:00 [Warning] CorsPolicyService did not allow origin: "http://localhost:8100"

maybe you have to check your user into this function IsActiveAsync

public Task IsActiveAsync(IsActiveContext context)
        {
            _logger.LogDebug("IsActive called from: {caller}", context.Caller);

            var user = _userStore.FindUserBySubjectId(context.Subject.GetSubjectId());
            context.IsActive = user?.IsActive == true;

            return Task.CompletedTask;
        }

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings