Always receiving 'invalid_client' error wh

2019-03-17 18:03发布

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!

3条回答
聊天终结者
2楼-- · 2019-03-17 18:19

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

查看更多
淡お忘
3楼-- · 2019-03-17 18:19

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.

查看更多
淡お忘
4楼-- · 2019-03-17 18:25

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.

查看更多
登录 后发表回答