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:
- Azure App Services Custom Auth (Part 1: user management)
- Azure App Services Custom Auth (Part 2: server authentication)
- Azure App Services Custom Auth (Part 3: client authentication)
- Azure App Services Custom Auth (Part 4: cross-provider users) — soon
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:
- Azure App Services Custom Auth (Part 1: user management)
- Azure App Services Custom Auth (Part 2: server authentication)
- Azure App Services Custom Auth (Part 3: client authentication)
- Azure App Services Custom Auth (Part 4: cross-provider users) — soon