Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Override the Token authentication #1333

Closed
tidusjar opened this issue Jul 15, 2017 · 11 comments

Comments

Projects
None yet
4 participants
@tidusjar
Copy link

commented Jul 15, 2017

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?

@leastprivilege

This comment has been minimized.

Copy link
Member

commented Jul 18, 2017

For which grant type?

@tidusjar

This comment has been minimized.

Copy link
Author

commented Jul 18, 2017

Sorry, should of included that.

It's for ResourceOwnerPassword.

@leastprivilege

This comment has been minimized.

@tidusjar

This comment has been minimized.

Copy link
Author

commented Jul 18, 2017

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.

@leastprivilege

This comment has been minimized.

Copy link
Member

commented Jul 18, 2017

check logs/debug?

@tidusjar

This comment has been minimized.

Copy link
Author

commented Jul 18, 2017

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
@leastprivilege

This comment has been minimized.

Copy link
Member

commented Jul 18, 2017

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

@tidusjar

This comment has been minimized.

Copy link
Author

commented Jul 18, 2017

That works!

Thank you very much for your help.

@tidusjar tidusjar closed this Jul 18, 2017

@nepodmitljivi

This comment has been minimized.

Copy link

commented Aug 3, 2017

@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\": \"helloworld@yopmail.com\",
  \"Raw\": {
    \"username\": \"helloworld@yopmail.com\",
    \"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"
@IsTypiNOVanS

This comment has been minimized.

Copy link

commented Dec 14, 2017

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;
        }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.