Всегда получать ошибку "invalid_client" при отправке POST на конечную точку Token с помощью ASP Identity 2

Примерно месяц назад у меня был проект, прекрасно работающий с ASP Identity OAuth. Я отправил запрос POST в конечную точку /Token с параметром grant_type, именем пользователя и паролем, и все было dandy.

Недавно я начал новый проект, основанный на шаблоне SPA-студии Visual Studio 2013 RC2. Это немного отличается от старого шаблона. Аутентификация настроена на довольно простые значения по умолчанию,

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

Ничего существенного не изменилось из шаблона по умолчанию. Я могу успешно зарегистрировать учетные записи с помощью метода контроллера веб-API, который я реализовал;

    // 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);
    }

Однако, независимо от того, что я отправил на конечную точку /Token, я всегда получаю тот же ответ.

{"error":"invalid_client"}

Обычно я передаю следующий тело запроса

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

Но это приводит к той же ошибке. Это работало в предыдущем шаблоне SPA/Identity VS2013. Что изменилось?

Спасибо!

Ответы

Ответ 1

Итак, оказывается, что новые шаблоны не включают функциональную реализацию ApplicationOAuthProvider, которая присутствовала в старых шаблонах.

После просмотра этого обсуждения по расписанию, я исследовал дальше и обнаружил, что рабочая реализация ApplicationOAuthProvider доступна для проверки в этот пакет NuGet! Это очень похоже на старую реализацию.

Ответ 3

Кроме того, вы можете использовать класс ApplicationOAuthProvider, который поставляется с шаблоном WebApi, когда в качестве параметра безопасности выбраны отдельные учетные записи пользователей. Однако вам придется изменить пару других вещей, которые я привел ниже. Надеюсь, это поможет.

Класс ApplicationOAuthProvider, который поставляется с шаблоном WebApi/Individual User Accounts, содержит следующий метод:

    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);
    }

Скопируйте это в класс ApplicationOAuthProvider в проект вашего шаблона SPA, перезаписав оригинальный метод. Метод code user.GenerateUserIdentityAsync недействителен при копировании в проект шаблона SPA, потому что класс ApplicationUser не допускает тип аутентификации "на предъявителя".

Добавьте в класс ApplicationUser перегрузку, похожую на следующую: (найдите ее в файле Models\IdentityModels.cs):

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

Теперь вы можете использовать конечную точку /Token правильно.