Recently a few people asked me on Twitter if OAuth2/OpenID Connect, using IdentityServer as STS, can be used from a Xamarin application, and if yes, how that should be done. So I promised to create a sample app – for the first one, I used Xamarin Forms (iOS, Android, WinPhone).
The sample comes in two flavours. One is integrated with the IdentityServer3 samples project. If you’ve ever used those samples, this is the project you need: it integrates with the WebHost/SelfHost from that same samples project. It can be found at IdentityServer3.Samples at GitHub, more specifically at /Xamarin.
The other sample is a standalone sample: it’s one solution that contains Xamarin Forms clients (Android, iOS, WinPhone), IdentityServer & an API. You can find it at my XamarinFormsOIDCSample GitHub repo.
If you want to dive straight into the code, feel free – but for those of you who aren’t familiar with OAuth2 / OpenID Connect (OIDC), here’s a short crash course.
OAuth2 / OpenID Connect Crash Course
Let’s start with the definitions: OAuth2 is an open protocol to allow secure authorization in a simple and standard method from web, mobile and desktop applications. OpenID Connect is a simple identity layer on top of the OAuth2 protocol.
In a way, OAuth2 is a great starter protocol to build upon – which is exactly what OpenID Connect does. OIDC is stricter than the OAuth2 protocol, which, thanks to that strictness, opens it up for other scenarios – like authentication. These days most applications are using OIDC rather than OAuth2, because they either require signing in to a client application or identity-related information, both of which are provided by OIDC.
But I’m running ahead of myself.
Why do we all of a sudden need all these tokens? “Good old” (opinions differ :-)) Forms Authentication might work when you’re building a standard n-tier ASP.NET application, or even a service-based application that runs in the same context. But these days, most modern applications are API-based, with an API that’s completely separated from whatever client that consumes it (this is actually one of the fundamental constraints imposed by a REST-based architecture).
The client application that consumes that API might be an ASP.NET MVC application, consuming the API from the server, but it might also be a mobile application, or a JavaScript-based application (Angular, for example) – in which case the API is consumed from a client device. Forms Authentication obviously isn’t suited for those scenarios. But… sending over a username/password combo on each HttpRequest to your API really isn’t something you want to do.
Enter (*queue drum roll*): the age of token-based security.
Tokens represent consent, for example: consent, granted by the user, to the client application, to access an API – typically through scopes, in OAuth2 lingo.
Your client application should –somehow– get a token. That token can then be used on the client, or can be passed to the API, used at that level to authorize access. A while ago, people would write “token services” for that. Typically: a call to a self-created /login-endpoint, that would accept your username/password and return a JSON Web Token.
But just sending your username/password to a self-created action on an API and getting a token in return isn’t a good fit for a lot of applications – nor a good idea. It still requires you to share your username/password with that API – which might be ok for your own, trusted application (although I’m not a big fan), but it’s a very bad idea for third-party applications, including other applications that might live in your company. Moreover, once we start creating endpoints like these ourselves, we are essentially reinventing the wheel. We have to implement expiration ourselves. We have to implement token validation. We have to implement logging out. We might want to implement reference token support. We’ll need to separate out identity & access, and so on. In short, mistakes are almost inevitable.
Enter the OAuth2 protocol. The OAuth2 protocol defines a way to securely get a specific type of token (“achieve authorization”) – an access token. It does this through one of the different flows/grants as defined in the RFC. It’s your responsibility to choose the correct flow, depending on the type of application you’re building.
This type of token is used to authorize access to that API. It’s of course still the API that chooses whether or not the consent the access token contains is sufficient to allow access. Your client application should thus get an access token from a security token service (like IdentityServer) through a specific grant/flow, and pass that access token on each call to the API.
But OAuth2 access tokens should not be used for authentication – the protocol is not strict enough to securely allow that. OIDC however, being built on top of OAuth2, defines a new type of token: an identity token. And that can be used for authentication.
The archetypical example is an ASP.NET MVC application that calls an API through an HttpClient instance. You use the identity token to sign in to the ASP.NET MVC application, and you use the access token to access the API.
In this case, that client application is a Xamarin client. But… there is no real use for signing in to such a client. Mobile clients (Xamarin, native iOS, Android, Windows Phone, …) are deployed to a phone – an untrusted device. We cannot not serve up code depending on the user’s identity: the code is already on the client device. The same principle holds true for JavaScript-based clients, like an Angular client. This is contrary to what we can do with a server-side technology, say: an ASP.NET MVC application. In those types of applications, we can effectively block access to parts of the code – they’ll never get executed, the results will never get served up to your browser. For mobile clients, we typically need access tokens more than we need identity tokens.
Yet, identity tokens are also used for these types of clients to get identity-related information from the token (alternatively, you can also use the UserInfo endpoint). So, even though we’re not really signing in to the client as we would in an ASP.NET MVC client, OpenID Connect does make sense as it allows access to identity-related information, either directly contained in an identity token, or by using an access token containing identity scopes to access the UserInfo endpoint. Next to that – maybe even more important –, OIDC is stricter than OAuth2 (as previously mentioned), which means it adds additional security features (nonces, for example) – I’d advise to always use OIDC, even if you don’t need identity tokens.
Now, how does IdentityServer fit into this story? If you read through the RFC’s, you’ll notice there’s a lot of implementation that has to happen at server level. This is not something you typically want to do yourself: Dominick Baier and Brock Allen have done this for us. IdentityServer implements the OAuth2 & OIDC standards, amongst others. What we, as developers of the client applications & API’s have to handle is the client-side part of the standards & ensuring our API can work with those tokens.
There’s a great walkthrough on how to set up IdentityServer in the documentation, in case you haven’t done that before. The walkthrough also contains an example on how to protect your API.
Phew, so far for that crash course – I’ve cut a few corners here and there, but this is the gist of it. On to the sample application.
The Xamarin Forms Sample Application
As we know by now, it’s important to use the correct flow for the type of application you’re going to build. Not using the correct flow might open you up to risks you don’t want; for example: a client credentials flow, which relies on a client secret, should not be used from applications that run on the client: they have no means of safely storing that secret, rendering the secret essentially useless.
The advised flow for a (native) mobile application is the Implicit flow, according to the current spec, although you can also use the Authorization Code flow or Hybrid flow. There’s quite a bit of discussion on this going on currently, and this stance might change in the (near) future (probably favouring Hybrid flow). As with all things related to security: the work is never really finished (which is, incidently, pretty good news for my job security ;-)).
Anyway, I’ve implemented this using the Implicit flow, but the other flows would be implemented in a likewise manner.
This is a redirection-based flow, so we’ll need to use a WebView in our Xamarin Forms application. The general idea is that we’ll use that WebView to navigate to the authorization endpoint at Identity Server level, passing in the scopes & response types we need. On the LoginView.xaml page, there is thus a hidden WebView:
<Button Text="Get id_token"
x:Name="btnGetIdToken"/>
<Button Text="Get access_token"
Grid.Row="1"
x:Name="btnGetAccessToken"/>
<Button Text="Get id_token and access_token"
Grid.Row="2"
x:Name="btnGetIdTokenAndAccessToken"/>
<ScrollView Grid.Row="3" >
<Label x:Name="txtResult"/>
</ScrollView>
<WebView x:Name="wvLogin"
Grid.RowSpan="4"
IsVisible="False"/>
(code is from the sample in IdentityServer3.Samples project, but the standalone project contains likewise code)
As we’re working with OIDC, we must include a nonce in that request. To protect against CSRF attacks, we should include a CSRF token – which is essentially a random, non-guessable value that will be bounced back to the client. Once that’s bounced back, we have to check that it matches the value we sent in the initial request.
So, first thing to do: create the URI to the authorization endpoint at IdentityServer level. I’ve used the IdentityModel package for this, which helps a lot with creating the correct URI’s & parsing the results. The StartFlow method contains the code to create the URI & navigate the WebView to that URI.
public void StartFlow(string responseType, string scope)
{
var authorizeRequest =
new AuthorizeRequest("https://localhost:44333/core/connect/authorize");
// dictionary with values for the authorize request
var dic = new Dictionary<string, string>();
dic.Add("client_id", "implicitclient");
dic.Add("response_type", responseType);
dic.Add("scope", scope);
dic.Add("redirect_uri", "https://xamarin-oidc-sample/redirect");
dic.Add("nonce", Guid.NewGuid().ToString("N"));
// add CSRF token to protect against cross-site request forgery attacks.
_currentCSRFToken = Guid.NewGuid().ToString("N");
dic.Add("state", _currentCSRFToken);
var authorizeUri = authorizeRequest.Create(dic);
wvLogin.Source = authorizeUri;
wvLogin.IsVisible = true;
}
The buttons in the sample call that method, passing in response_type & scopes. For example, this piece of code will ask for an identity token & an access token:
private void GetIdTokenAndAccessToken(object sender, EventArgs e)
{
// when asking both, we can ask for identity-related scopes
// as well as resource scopes
StartFlow("id_token token", "openid profile read write");
}
Once the WebView’s URI is set to the URI we just created, this will show you IdentityServer’s login page, where you’ll have to provide your credentials (alice/alice for the IdentityServer3.Samples sample, Kevin/secret for the standalone sample).
Up next is parsing the tokens once we are redirected. These tokens are included in the URI, and we can parse them by catching the Navigated-event of the WebView and checking if the URI starts with the redirection URI we passed in when creating the authorization URI. AuthorizeResponse is also part of the IdentityModel package. Don’t forget to check the CSRF token to protect against CSRF attacks.
private void WvLogin_Navigating(object sender, WebNavigatingEventArgs e)
{
if (e.Url.Contains("https://xamarin-oidc-sample/redirect"))
{
wvLogin.IsVisible = false;
// parse response
_authResponse = new AuthorizeResponse(e.Url);
// CSRF check
var state = _authResponse.Values["state"];
if (state != _currentCSRFToken)
{
txtResult.Text = "CSRF token doesn't match";
return;
}
string decodedTokens = "";
// decode tokens
if (!string.IsNullOrWhiteSpace(_authResponse.IdentityToken))
{
decodedTokens += "Identity token \r\n";
decodedTokens += DecodeToken(_authResponse.IdentityToken) + "\r\n";
}
if (!string.IsNullOrWhiteSpace(_authResponse.AccessToken))
{
decodedTokens += "Access token \r\n";
decodedTokens += DecodeToken(_authResponse.AccessToken);
}
txtResult.Text = decodedTokens;
}
}
And that’s actually all there is to it. From this moment on, you’ve got the token(s) you asked for, containing the demanded scopes. You can use the identity token for identity-related information, and you can use the access token to access your API.
What’s Next?
I’ve put a few more things on my to-do-list. One is creating a sample for regular Xamarin. And next on the list is creating the Xamarin versions of Dominick’s UwpOidcClient. So there’s definitely more coming up!
In the meantime, if you want to learn more about OAuth2 / OpenID Connect, for Angular and ASP.NET applications combined with IdentityServer, feel free to check my latest Pluralsight course: OAuth2 and OpenID Connect Strategies for Angular and ASP.NET (if you don’t have an account yet, you can always start with a free trial).
That’s it for now, happy coding! 🙂