Dissecting the Web API Individual Accounts Template–Part 2: Local Accounts

In the last post I gave an overview of the security setup of the Individual Accounts template. I recommend reading that first. Also Brock has some great background content – here and here.

Disclaimer: I would have designed the API surface differently. But this template is consistent with the feature set of the MVC individual accounts template and the SPA template (which is basically the Web API template with a JS client).

In this post I want to focus on the local username/password account features of the template. They are divided into two parts – the account controller has features like creating an account and changing a password. Authentication on the other hand is done in the OAuth2 authorization server. Let’s start at the beginning and create an account.

To create an account you post to the /api/account/register endpoint – that maps to AccountController.Register which allows anonymous access which makes sense.

The client code could look like this:

private async Task Register(string userName, string password)

{

    var client = new HttpClient { BaseAddress = _baseAddress };

 

    var data = new Dictionary<string, string>

    {

        { “UserName”, userName },

        { “Password”, password },

        { “ConfirmPassword”, password }

    };

 

    var response = await client.PostAsync(

        “api/account/register”,

        new FormUrlEncodedContent(data));

    response.EnsureSuccessStatusCode();

}

The account controller then uses the ASP.NET identity framework to create a new user in the local database (after making sure the password and confirmPassword are the same – just in case your client framework is not powerful enough to compare two strings…;)

IdentityResult result = await UserManager.CreateAsync(user, model.Password);

IHttpActionResult errorResult = GetErrorResult(result);

 

if (errorResult != null)

{

    return errorResult;

}

 

return Ok();

In a real application you might want to do email verification and other validation steps – but that’s out of scope for the sample.

From that point on, the client can request a token for the user using the built-in OAuth2 resource owner flow endpoint which listens on /token. For more background on that feature, see here.

The client code using OAuth2Client could look like this:

private async Task<TokenResponse> RequestToken(
 
string userName, string
password)

{

    var client = new OAuth2Client(
     
new Uri(_baseAddress.AbsoluteUri + “/token”
));

    return await client.RequestResourceOwnerPasswordAsync(userName, password);

}

The template wires up a so called provider (ApplicationOAuthProvider.cs) to the authorization server middleware. The relevant method in there is called GrantResourceOwnerCredentials which is responsible for validating the user credentials and creating the authentication ticket which gets turned into an access token.

var user = await userManager.FindAsync(context.UserName, context.Password);

 

if (user == null)

{

    context.SetError(
     
“invalid_grant”, “The user name or password is incorrect.”);

    return;

}

 

var oAuthIdentity = await userManager.CreateIdentityAsync(user,

    context.Options.AuthenticationType);

var properties = CreateProperties(user.UserName);

var ticket = new AuthenticationTicket(oAuthIdentity, properties);

context.Validated(ticket);

Note: This method does a little more than just validating credentials and creating a token. It also sends the username back as part of the OAuth2 response (as an extra parameter). My guess is that this is just a sample how to directly return personalization related information back to the client without forcing an extra round trip to the API. Since those values are not signed, you shouldn’t base any security related decisions on them (compare to the OpenID Connect identity token).

2nd note: The method also sets an application cookie. I don’t see a good reason for that.

With the access token, the client is now allowed to call other account management related operations on the account controller (e.g. userInfo, changePassword or setPassword) or any other controllers decorated with the [Authorize] attribute, e.g.:

private async Task<string> GetUserInfo(string token)

{

    var client = new HttpClient { BaseAddress = _baseAddress };

    client.SetBearerToken(token);

 

    var response = await client.GetStringAsync(“api/account/userInfo”);

    return response;

}

This entry was posted in ASP.NET, Katana, OAuth, OWIN, WebAPI. Bookmark the permalink.

11 Responses to Dissecting the Web API Individual Accounts Template–Part 2: Local Accounts

  1. Pingback: Dissecting the Web API Individual Accounts Template–Part 3: External Accounts | www.leastprivilege.com

  2. With recent versions of Web API, you need to send an email address when registering the credentials. You need to send this email address as the username when getting a token.

    This is not a JWT token that is returned. Is this a published format? What is the token signed with?

    — Marcel

  3. What if we host our web api in Azure with multiple instances? Am I right that we would need to override OAuthAuthorizationServerOptions.AccessTokenProvider to store tokens in shared cache, for example?

    • You don’t cache tokens on the server side – that’s the client’s job.

      • Thank you Dominick. Currently we are using SimpleSecurityTokenHandler class from Thinktecture.Identity library, but in simple way, without refresh token. The way we use SimpleSecurityTokenHandler, we issue the token, store it in shared cache on server (with some expiration), and then when client submitted the token back, we validated that token exists, this was our validation. Are you saying we are doing it wrong?

        And now I want to support refresh token, and also was thinking about moving to template you are describing from Visual Studio. I thought that for validating the token we issued before we would still need to store it. Or, is it done differently here, using some signing mechanisms?

        Sorry for my lack of knowledge, I’m trying to understand how it all works, or better should work properly. And it changes so fast, with OWIN, ASP.Identity. Cannot find comprehensive source of information.

Leave a comment