Всегда получать ошибку "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! Это очень похоже на старую реализацию.
Ответ 2
Вы должны переопределить ValidateClientAuthentication и GrantResourceOwnerCredentials в OAuthAuthorizationServerProvider.
См. пример здесь:
http://www.tugberkugurlu.com/archive/simple-oauth-server-implementing-a-simple-oauth-server-with-katana-oauth-authorization-server-components-part-1
Ответ 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
правильно.