Azure App Services Custom Auth (Part 3: client authentication)

Hi there!

How have you been? Happy new year to all of you!

It is time to get to the third (already!) part of these blog posts about Azure App Services Custom Authentication.
We will now be talking about the client-side authentication.

Again, this post is part of a whole:

We have thanks to the first two parts a system that is able to:

  • Create and manage users
  • Generate tokens from user informations
  • Request and validate queries by authentication

Which is already pretty great in my opinion! However you probably want to use the azure app services framework on the client-side with our custom auth and I am here to deliver.


Azure App Services Client

The very first thing we will need is to introduce the client framework of Azure App Services. I did not do this properly before.

I will use the .NET managed version of it in a PCL project (Xamarin Forms).
There might be some slight differences for other platforms but nothing that you should not be able to handle.

We will need to define the MobileServiceClient which is the object we can use later for all our requests. Microsoft made it quite simple for us:

var client = new MobileServiceClient(@"https://mywebsite.azurewebsites.net/");

Remember to use the HTTPS protocol.

I also strongly advice you to use ModernHttpClient which handles a bit better the http requests. To use it with Azure App Services we only need to pass on their handler in the constructor:

var client = new MobileServiceClient(@"https://mywebsite.azurewebsites.net/", 
					new NativeMessageHandler());

Both these nuget packages (Azure App Services Client and ModernHttpClient) have to be embedded on your projects using it and on your specific project (iOS, Android, …) if I am correct.

I advise you to declare your mobile client only once and access it with a container by dependency injection.
For instance, I have defined a ServerManager class and a IServerManager interface in charge of creating, controlling and accessing the instances of MobileServiceClient (I have several client services).
This container is in charge of login and logout my users too.

You can also pass on directly the client with the IMobileServiceClient interface if you do not need a container.

I will consider you have access to a declared mobile services client named _client for the rest of the article.


Authenticate the user

If you followed the previous blog post on custom auth, this step should be pretty straightforward.
You will need a registered user as seen on the first part of this topic (call controller from client).
Just to remind you quickly how it works here are the required steps for the auth:

  • Get the token from user informations
  • Use the token for requests
  • Store the token for future use

All of this will be pretty simple.

Get the token

For this step, you will need to ask the user for his / her informations. You can simply create a screen with email / password fields and a login button.

It is not really relevant here to blog about UI, so I will just give you a method to get the token with this signature:

Task<AuthenticationToken> GetAuthenticationToken(string email, 
												 string hash);

The response we will receive is a classic JSON so we will need the following class to parse it. This is the response we got from the /oauth/token endpoint in the previous post:

public class AuthenticationToken
{
    public string Guid { get; set; }
    public string Access_Token { get; set; }
    public string Token_Type { get; set; }

    [JsonProperty(".expires")]
    public DateTime Expires { get; set; }
}

The implementation is then pretty straightforward, we only need to send the data like we did testing our server:

public async Task<AuthenticationToken> 
		GetAuthenticationToken(string email, string password)
{
	// define request content
    HttpContent content = new StringContent(
    string.Format("username={0}&password={1}&grant_type=password", 
                  email.ToLower(), 
                  password));
                                    
    // set header
	content.Headers.ContentType 
    = new MediaTypeHeaderValue("application/x-www-form-urlencoded");

	// invoke Api
	HttpResponseMessage response 
    	= await _client.InvokeApiAsync("/oauth/token", 
            						   content, 
            						   HttpMethod.Post, 
            						   null, 
            						   null);

	// read and parse token
    string flatToken = await response.Content.ReadAsStringAync();
    return JsonConvert
    		.DeserializeObject<AuthenticationToken>(flatToken);
}

As you can see we use the magic of the framework to do it all!
We are simply using a POST request with the informations (url encoded) to /oauth/token (the endpoint we defined earlier).
Then we get the flat token and deserialize it as an AuthenticationTicket.

Note that emails are not case-sensitive, which is why I registered and authenticated the users with an email.ToLower().

I strongly advise you to use custom exceptions for invalid grant like this:

public async Task<AuthenticationToken> 
		GetAuthenticationToken(string email, string password)
{
	try
    {
    	// all we did before
    }
    catch (MobileServiceInvalidOperationException exception)
    {
    	if (string.Equals(exception.Message, "invalid_grant"))
            throw new InvalidGrantException("Wrong credentails", 
            								exception);
        else
        	throw;
	}
}

Use the token

The underlying idea on the previous article was to fit as best as we could to the existing mechanism to simplify this very step.

The AuthenticationToken we got from the server has all the informations needed for the MobileServiceClient to handle authentication.

We now simply need to pass them as a MobileServiceUser to the client like this:

public async Task Authenticate(string email, string password)
{
	// get the token
	var token = await GetAuthenticationToken(email, password);

	// authenticate: create and use a mobile service user
	MobileServiceUser user = new MobileServiceUser(token.Guid);
	user.MobileServiceAuthenticationToken = token.Access_Token;
    _client.CurrentUser = user;
}

And… that's it.

Yes.

That's it.

Now every query that the client will make will pass on a X-ZUMO-AUTH header, granting access to all methods flagged as [Authorize].
That also include the whole synchronization mechanism and the SQLiteStorage mechanism.

As long as the token is valid everything will work fine.

Store the token

We do not want the user to log in each time (s)he opens the application, which is why we will need to store and re-use the token.

If you followed the explanations on the previous post you will not be surprised when I tell you that the token only has some basic informations, and that its signature is strongly signed. This means we can store the token unsecurely without compromising our security.

However, a fraud could steal the token on the phone and make requests pretending to be the user. While it is very unlikely I advise you to securely store the token as well as the user id (better safe than sorry).

I will post in a next article an implementation for a cross-platform vault, meanwhile we will consider the next interface:

public interface ISecureStorage
{
	void Store(string key, string value);
	string Retrieve(string key);
	void Delete(string key);
    bool Contains(string key);
} 

If you absolutely need it right now I believe an implementation can be found in the XLabs project. Meanwhile you can also store the token and user id in the settings even if it is not the best option.

You can then store the informations easily:

public async Task Authenticate(string email, string password)
{
	// get token
    var token = await GetAuthenticationToken(email, password);

    // authenticate
    // …

    // store settings
    _secureStorage.Store("UserId", token.Guid);
    _secureStorage.Store("Token", token.Access_Token);
}

And you can handle the invalid grant to clean user informations:

public async Task Authenticate(string email, string password)
{
	try
    {
    	// previous code
    }
    catch (InvalidGrantException)
    {
    	_secureStorage.Delete("UserId");
    	_secureStorage.Delete("Token");
        
        // alert user!
    }
}

The last step is to use these informations on the opening of the application to log-in right away.
For instance, it can be done when you create you MobileServiceClient.

It is exactly like before but with the stored informations:

public async Task AutoAuthenticate()
{
    if (_secureStorage.Contains("UserId") 
    	&& _secureStorage.Contains("Token"))
    {
        var id = _secureStorage.Retrieve("UserId");
        var token = _secureStorage.Retrieve("Token");

        var user = new MobileServiceUser(id);
        user.MobileServiceAuthenticationToken = token;
        _client.CurrentUser = user;
    }
}

You can use this method on the start of the application or anywhere you want to log the user automatically.

Now as long as the authentication token is valid this will be enough. But the real issue is when it expires…


Reauthenticate the user after token expiration

There would be two ways to implement it: use a refresh token mechanism or store their credentials.

I used the latter which means that for this part you will absolutely need a secure storage for this mechanism.

Email and password should NEVER be stored without encryption even on the client.

The code is pretty simple: if a user logs successfully we register his / her credentials and the token expiration like this


public async Task Authenticate(string email, string password)
{
    // previous authentication code

    // store settings
    _secureStorage.Store("UserId", token.Guid);
    _secureStorage.Store("Token", token.Access_Token);
    _secureStorage.Store("TokenExpirationDate", 
    					 token.Expires.Ticks.ToString());
    _secureStorage.Store("UserEmail", email);
    _secureStorage.Store("UserPassword", password);
}

I chose to store the expiration date as a string with the date's ticks. If you have a better option you can comment below to offer me better solutions :)

Remember to clean every information we added here when the user enters invalid credentials.

The last step is to check on start if the token is expired and re-authenticate the user if it is.

public async Task AutoAuthenticate()
{
	if (_secureStorage.Contains("UserId") 
	      && _secureStorage.Contains("Token") 
    	  && _secureStorage.Contains("TokenExpirationDate"))
	{
		var expirationDateTicks 
        	= _secureStorage.Retrieve("TokenExpirationDate");
	    DateTime expirationDate 
        	= new DateTime(long.Parse(expirationDateTicks));
    
		if (expirationDate < DateTime.Now)
	    {
        	// you can also check first if email and password exists
	    	var email = _secureStorage.Retrieve("UserEmail");
    		var password = _secureStorage.Retrieve("UserPassword");
            
            await Authenticate(email, password);
    	}
	    else
	    {
	      var id = _secureStorage.Retrieve("UserId");
    	  var token = _secureStorage.Retrieve("Token");

	      var user = new MobileServiceUser(id);
    	  user.MobileServiceAuthenticationToken = token;
	      _client.CurrentUser = user;
    	}
	}
}

And we are done!

We have a system for the client that can authenticate, auto-authenticate on start and re-authenticate when the token is expired.


Finally

Apart from the ISecureStorage implementation, you should have everything you need to use our brand new custom authentication!

As you can see, a single article for both client and server authentication would have been HUGE which is why I splitted it.

If you have any remark, question, suggestion, please use the comment section below! I am always glad to hear from you, even with a comment saying hi.

But wait… you said there would be a forth article? Why? Have we not everything we need like you said?
Well I feel there is still some work to be done. On the next article, we will see how we can integrate our custom auth with the existing auth (Facebook, Twitter, G+, Microsoft, AAD, …) in order to make our server fully transparent regarding the origin of the auth. We will create cross-platform users.

See you soon!

Paul, out!


This post is part of a whole: