How do you consume extra parameters in OAuth2 Token request within .net WebApi2 application

nak5ive picture nak5ive · Jan 20, 2014 · Viewed 14.8k times · Source

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?

Answer

nak5ive picture nak5ive · Jan 20, 2014

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!