Always receiving 'invalid_client' error wh

2019-03-17 18:23发布

问题:

About a month ago I had a project working perfectly with ASP Identity OAuth. I'd send a POST request to the /Token endpoint with grant_type, username, and password, and all was dandy.

I recently started a new project based off of Visual Studio 2013 RC2's SPA template. It's a bit different than the old template. Authentication is set up to pretty basic defaults,

OAuthOptions = new OAuthAuthorizationServerOptions
{
    TokenEndpointPath = new PathString("/Token"),
    //AuthorizeEndpointPath = new PathString("/Account/Authorize"), 
    Provider = new ApplicationOAuthProvider(PublicClientId),
    AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
    AllowInsecureHttp = true
};

Nothing significant changed from the default template. I can register accounts successfully through a Web API controller method I have implemented;

    // POST: /Account/Register
    [HttpPost]
    [AllowAnonymous]
    public async Task<IHttpActionResult> Register(RegisterBindingModel model)
    {
        if (ModelState.IsValid)
        {
            var user = new TunrUser() { UserName = model.Email, Email = model.Email, DisplayName = model.DisplayName };
            var result = await UserManager.CreateAsync(user, model.Password);
            if (result.Succeeded)
            {
                return Created(new Uri("/api/Users/" + user.Id,UriKind.Relative), user.toViewModel());
            }
            else
            {
                return BadRequest(result.Errors.First());
            }
        }
        return BadRequest(ModelState);
    }

However, no matter what I POST to the /Token endpoint, I always get the same response.

{"error":"invalid_client"}

Normally I pass the following request body

grant_type=password&username=user%40domain.com&password=userpassword

But this results in the same error. This worked in the previous VS2013 SPA template / Identity. What's changed?

Thank you!

回答1:

You have to Override the ValidateClientAuthentication & GrantResourceOwnerCredentials in the OAuthAuthorizationServerProvider.

See example here: http://www.tugberkugurlu.com/archive/simple-oauth-server-implementing-a-simple-oauth-server-with-katana-oauth-authorization-server-components-part-1



回答2:

So it turns out that the new templates don't include a functional implementation of ApplicationOAuthProvider that was present in the older templates.

After watching this build talk, I investigated further and found that a working implementation of ApplicationOAuthProvider is available to check out in this NuGet package! It's very similar to the old implementation.



回答3:

In addition, you can use the ApplicationOAuthProvider class that comes with the WebApi template when Individual User Accounts is chosen as the Security option. However, you'll have to change a couple other things, which I've listed below. I hope it helps.

The ApplicationOAuthProvider class that comes with the WebApi/Individual User Accounts template contains the following method:

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();

        ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);

        if (user == null)
        {
            context.SetError("invalid_grant", "The user name or password is incorrect.");
            return;
        }

        ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
           OAuthDefaults.AuthenticationType);
        ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager,
            CookieAuthenticationDefaults.AuthenticationType);

        AuthenticationProperties properties = CreateProperties(user.UserName);
        AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
        context.Validated(ticket);
        context.Request.Context.Authentication.SignIn(cookiesIdentity);
    }

Copy this to the ApplicationOAuthProvider class in your SPA template project, overwriting the original method. The code user.GenerateUserIdentityAsync method is invalid when copied to the SPA template project because the ApplicationUser class does not allow for the "bearer" authentication type.

Add an overload similar to the following to the ApplicationUser class (find it in the Models\IdentityModels.cs file):

    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager , string authenticationType)
    {
        var userIdentity = await manager.CreateIdentityAsync(this , authenticationType);
        // Add custom user claims here
        return userIdentity;
    }

You should now be able to use /Token endpoint correctly.