I have an api specific project in a large .net MVC 5 web solution. I am utilizing the WebApi2 templates out of the box to authenticate a user through the api. Using individual accounts to authenticate, the request body required to get an access token is:
grant_type=password&username={someuser}&password={somepassword}
This works as expected.
However, I need to add a 3rd dimension to the scaffolded method "GrantResourceOwnerCredentials". In addition to checking the username/password, i need to add a device id, which is meant to restrict access from a user account to a specific device. What's not clear is how to add these extra request parameters to the already defined "OAuthGrantResourceOwnerCredentialsContext". This context currently makes room for UserName and Password, but obviously i'll need to include more.
My question is simply, is there a standard way to extend the login requirements for the OWIN OAuth2 token request to include more data? And, how would you reliably do that in a .NET WebApi2 environment?
As it often is the case, I found the answer immediately after submitting the question...
ApplicationOAuthProvider.cs contains the following code out-of-the-box
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
using (UserManager<IdentityUser> userManager = _userManagerFactory())
{
IdentityUser 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 userManager.CreateIdentityAsync(user,
context.Options.AuthenticationType);
ClaimsIdentity cookiesIdentity = await userManager.CreateIdentityAsync(user,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(context.UserName, data["udid"]);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
}
By simply adding
var data = await context.Request.ReadFormAsync();
within the method, you can access all posted variables in the request body and use them as you like. In my case, I placed it immediately after the null-check on the user to perform a more restrictive security check.
Hope this helps someone!