Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issue Application Id for each kind of Client Application

I'm using Microsoft Owin and ASP.NET WebApi for authentication and authorization process for my client application. Also the authentication server is secured by HTTPS. I've read a few articles about using Microsoft Owin, one of them which I've chosen to implement is: Token Based Authentication using ASP.NET Web API 2, Owin, and Identity

There are some differences between my project and that implementation:

  1. I need to identify my client in case the request is sent by my application on the mobile phone, not any other devices or tools like Fiddler. I think one the options could be sending an application id by each request from the mobile application. But I don't know how and where should I verify the requests in authentication server application. This is really important for registering users:

        [AllowAnonymous]
        [Route("Register")]
        public async Task<IHttpActionResult> Register(UserModel userModel)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
    
            IdentityResult result = await _repo.RegisterUser(userModel);
    
            IHttpActionResult errorResult = GetErrorResult(result);
    
            if (errorResult != null)
            {
                return errorResult;
            }
    
            return Ok();
        }
    

    I don't want to let unreliable devices, i.e. the clients except the mobile application, to call this method.

  2. I need to let anonymous users to buy some products from the website, but I don't know what is the best practice to issue token for anonymous users without doing authentication.
like image 424
Saman Gholami Avatar asked Sep 05 '15 18:09

Saman Gholami


1 Answers

If you want to identify your client and authorize it you can override the method ValidateClientAuthentication.

In Taiseer's example you have linked you will find some code:

public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
    context.Validated();
}

and a note which says:

As you notice this class inherits from class “OAuthAuthorizationServerProvider”, we’ve overridden two methods “ValidateClientAuthentication” and “GrantResourceOwnerCredentials”. The first method is responsible for validating the “Client”, in our case we have only one client so we’ll always return that its validated successfully.

If you want to validate the client you have to put some logic in there.
Normally you would pass a clientId and a clientSecret in the header of your http request, so that you can validate the client's request with some database parameters, for example.

public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
    string clientId = string.Empty;
    string clientSecret = string.Empty;

    if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
    {
        context.TryGetFormCredentials(out clientId, out clientSecret);
    }

    if (context.ClientId == null)
    {
        context.SetError("invalid_client", "Client credentials could not be retrieved through the Authorization header.");
        context.Rejected();

        return;
    }

    try
    {
        // You're going to check the client's credentials on a database.
        if (clientId == "MyApp" && clientSecret == "MySecret")
        {
            context.Validated(clientId);
        }
        else
        {
            // Client could not be validated.
            context.SetError("invalid_client", "Client credentials are invalid.");
            context.Rejected();
        }
    }
    catch (Exception ex)
    {
        string errorMessage = ex.Message;
        context.SetError("server_error");
        context.Rejected();
    }

    return;
}

In the sample above you will try to extract the client credentials sent in the header of your request:

if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
    context.TryGetFormCredentials(out clientId, out clientSecret);
}

and validated them:

// You're going to check the client's credentials on a database.
if (clientId == "MyApp" && clientSecret == "MySecret")
{
    context.Validated(clientId);
}

if the client is sending a wrong request header you need to reject the request:

context.SetError("invalid_client", "Client credentials are invalid.");
context.Rejected();

The method ValidateClientAuthentication is processed before GrantResourceOwnerCredentials. This way you can extend it and pass GrantResourceOwnerCredentials some extra information you might need there.

In one of my applications I've created a class:

class ApplicationClient
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string ClientSecretHash { get; set; }
    public OAuthGrant AllowedGrant { get; set; }
    public DateTimeOffset CreatedOn { get; set; }
}

which I use in ValidateClientAuthentication right after I've checked the clientId and the secret are ok:

if (clientId == "MyApp" && clientSecret == "MySecret")
{
    ApplicationClient client = new ApplicationClient();
    client.Id = clientId;
    client.AllowedGrant = OAuthGrant.ResourceOwner;
    client.ClientSecretHash = new PasswordHasher().HashPassword("MySecret");
    client.Name = "My App";
    client.CreatedOn = DateTimeOffset.UtcNow;

    context.OwinContext.Set<ApplicationClient>("oauth:client", client);

    context.Validated(clientId);
}

As you can see here

context.OwinContext.Set<ApplicationClient>("oauth:client", client);

I am setting a Owin variable which I can read later on. In your GrantResourceOwnerCredentials now you can read that variable in case you need it:

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
    ApplicationClient client = context.OwinContext.Get<ApplicationClient>("oauth:client");
    ...
}

Now, if you want to fetch the bearer token - which you're going to use for all the secure API calls - you need to encode your clientId and clientSecret (base64) and pass it in the header of the request:

An ajax request with jquery would look something like this:

var clientId = "MyApp";
var clientSecret = "MySecret";

var authorizationBasic = $.base64.btoa(clientId + ':' + clientSecret);

$.ajax({
        type: 'POST',
        url: '<your API token validator>',
        data: { username: 'John', password: 'Smith', grant_type: 'password' },
        dataType: "json",
        contentType: 'application/x-www-form-urlencoded; charset=utf-8',
        xhrFields: {
           withCredentials: true
        },
        headers: {
           'Authorization': 'Basic ' + authorizationBasic
        },
        beforeSend: function (xhr) {
        },
        success: function (result) {
        var token = result.access_token;
        },
        error: function (req, status, error) {
            alert(error);
        }
});

As you can see I've also added the username and password - with the grant type - in the body of the request:

data: { username: 'John', password: 'Smith', grant_type: 'password' }

so that the server will be able to validate the client (clientId + clientSecret) and the user (username + password).

If the request is successful you should get back a valid token:

oAuth.Token = result.access_token;

which you can store somewhere for the following requests.

Now you can use this token for all the requests to the api:

$.ajax({
    type: 'GET',
    url: 'myapi/fetchCustomer/001',
    data: { },
    dataType: "json",
    headers: {
    'Authorization': 'Bearer ' + oAuth.Token
    },
    success: function (result) {
    // your customer is in the result.
   },
    error: function (req, status, error) {
    alert(error);
    }
});

Another thing you might want to add to your API during the startup is SuppressDefaultHostAuthentication:

config.SuppressDefaultHostAuthentication();

this is an extension method of HttpConfiguration. Since you're using bearer tokens you want to suppress the standard cookie-based authentication mechanism.

Taiseer has written another series of articles which are worth reading where he explains all these things.

I have created a github repo where you can see how it works.
The Web API is self-hosted and there are two clients: jQuery and Console Application.

like image 168
LeftyX Avatar answered Oct 22 '22 04:10

LeftyX