Working With OAuth2 and OpenID Connect from a Xamarin Forms Application using IdentityServer3

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 applicationsOpenID 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! :)

 Tweet about this on TwitterShare on LinkedInShare on Facebook

27 Comments

Matt Perry

This is a great article and the sample code being added to the Samples repo is a bonus.

Any thoughts on how to mimic refresh tokens in an Implicit flow on mobile where we don’t have the luxury of frames etc?

Kevin Dockx

Hi Matt,

thanks, glad you liked it :) As you mention, with frames this can quite easily be achieved – a new request to the auth endpoint will get you a new token as long as you are still logged in to IdentityServer (& disable the consent screen). It’s not a refresh token of course, but it’s the “best allowed option”.

Without frames, as in a Xamarin app, I think the best we can do is use a hidden WebView to trigger the flow & read out the tokens on redirect. You could check if the token is about to expire, and if it is, inject a WebView (should share existing cookies automatically), create the URI to the auth endpoint & handle the nav event.

Lai

Hi Kevin,
Awesome article! I’m running the sample through Xamarin Android Player and cannot connect to localhost:44333 to connect to Identity Server running on the same machine. Can you share your process of how you are able to do this?

Cheers

Lai

Steve Degosserie

Hi Kevin,

Nice post. Let me point out that if as a dev, you build both the backend & the app (you have end-to-end control over the solution), and you’re not planning to support any federation scenarios, you could use the Resource Owner Password Flow which allows you to have a native experience for you login page.
Next up: supporting refreshing tokens, refreshing claims, offline login, revoking tokens … this stuff quickly gets quite complex :-)
Cheers
Steve

Kevin Dockx

Hi Steve,

thanks :)

Just a few words on ROPC… that’s indeed the only one that offers a native experience for a login-page, but it’s also the least secure of the bunch. It’s not included in the OIDC spec (ie: you can’t use it with OIDC), which means you miss out on replay attack protection, to just name one disadvantage. I know sometimes the business requires a native login screen, but if you can avoid it: do :) It’s in the OAuth2 spec for legacy/migration reasons, but it gets abused quite a lot.

Steve Degosserie

TLS mitigates replay attacks (OIDC & OAuth _must_ be used on a secure HTTPS channel). For an added level of protection, use cert pinning.

Antonin

Is there any way how you can secure “public” API endpoints? I mean – let’s say I have an app displaying some data. Everyone can see the data but only authenticated users can insert/update/delete – so these endpoints will be protected but Get endpoint will not. However, I do not want to leave it completely unprotected, I’d like only my app have access to it. I can possibly use Client Credentials flow for that but that would mean storing secret in my app, which is not desirable. Are there other options?

Kevin Dockx

There are other options, but the nature of a public endpoint is, well, that it’s public :) Client credentials is, as you said, not desirable, but it’s still better than nothing – but if someone wants access, they’ll be able to get it.

For other options, you have to look at other means of authenticating than requiring a user to provide un/pw. You can achieve that with client certificates, but that will require a means to get that certificate on the device without packaging it in the app itself. That’s an approach that’s often used for intra-company apps. Another approach might be to use the device itself as means of authentication (MAC address, for example) – but that will require you to have a whitelist of allowed MAC addresses on your side.

MichaCo

He, thanks for the great example. I got it running for our custom login flow implementation, which is great.
Question I have is: Why do you have to construct your URL manually, is there any way to make Xamarin.Auth work with identityserver3? (I failed doing that^^)

For logout I would have to call the /connect/endsession endpoint, right? Would maybe a good addition to your example 😉

Johannes

Great work there, I run this on hybrid flow and it works perfectly fine on android.
My problem is, i tested this on WinPhone8.1 (and UWP) too, and the redirect (redirect_uri looks very similar to your example) doesn’t trigger the navigating event so i can’t consume the tokens. Instead on WinPhone nothing happens and on UWP the WebView just shows a blank page.
Did you get the same problems or is it just me?

intangible

If some one wants expert view regarding blogging then i suggest him/her to pay a quick visit this web site,
Keep up the good work.

ΚΑΛΛΥΝΤΙΚΑ

Thank you for the good writeup. It actually was a entertainment account it.
Glance advanced to far introduced agreeable from you!
However, how could we keep in touch?

Fred Besterwitch

Kevin, Superb post.
I cannot get the web view to display the login form in IOS or Android emulators. If I copy the URI(authorizeUri) and paste in chrome the login Form Comes up.

Any pointers, TIA.

Fred

Norman Neubauer

Is your identity server using SSL? If the certificate is not trusted, you can run into issues. For development, you could try setting it to unsecured. There are workarounds for handling/ignoring SSL certificate errors as well, (e.g. for Xamarin.Android adding

System.Net.ServicePointManager.ServerCertificateValidationCallback += (o, certificate, chain, errors) => true;

before the certificate is requested, like in MainActivity.OnCreate).

Κιμωλος

It’s awesome to go to see this web page and reading the views of all friends on the topic of this paragraph, while I am also keen of getting
knowledge.

amcik

constantly i used to read smaller posts that also clear their
motive, and that is also happening with this paragraph which I am
reading now.

zack

Hi Kevin,

I am trying to run the application but Iam getting blank page

Norman Neubauer

I’ve been trying to build a little sandbox for IdentityServer3 and a mix of clients (API, web, Android mobile app). Would you be able to provide points as to how to extend this example to allow me to browse another website secured with OIDC after the initial authorization occurs? Is this a scenario were one might use multi hop delegation? (e.g. https://github.com/IdentityServer/IdentityServer3.Samples/tree/master/source/Multi Hop Delegation (ActAsCustomGrant))

(PS, I’ve also posted the same question on your Pluralsight OIDC course)

Norman Neubauer

Scratch the comment regarding multi-hop delegation. Basically, it’s a matter of providing single sign on across the API (as the example is already doing) as well as another website.

Norman Neubauer

OK, I managed to get this working… now I’m really confused about sign out. If I render a page with a sign out link (just calls the Owin infrastructure SignOut() method, or alternatively, idsrv/identity/logout) everything is hunky dory (cookies cleared, signed out at client and Identity server). This method seems cludgy though in terms of an otherwise native mobile app.

However, the only way I can think of doing this via the app is by calling the endsession endpoint directly, passing in the id token hint and post logout redirect uri. Looking at the logs, this is invoking a different client flow (implicit versus hybrid) and the subject is lost (auth cookie problem?) Additionally, as the id_token (used in calling endsession) is short lived, how do we ensure this is not expired? Do we need to reauthorize each time?

Norman Neubauer

OK, got it… putting an Authorize tag on my Sign Out method will ensure we’re properly authorized, plus using a hidden WebView whose source URI is only set once the log out button clicked (as per one of your comments) seems to be the way to go.

Greg

Thank you for this article. I’m developing using FreshMVVM Xamarin Forms with iOS, Android and a UWP app. I had to install IdentityModel v2.0 for iOS/Android and 1.13 for UWP because v2.0 it breaks the UWP project . Anyway, Im using the code above and using my own localhost uri and when Im debugging my app, VS2015 throws this error at me.

“Exception thrown at 0x5D460863 in RTApp.exe: 0xC0000005: Access violation writing location 0x00000000.”
With an option click OK or Continue.

I click continue and it prompts me again a few dozen times and then I get to your 3 buttons. And clicking the btnGetIdToken loads my login form and after entering my credentials it loops a few times through the WvLogin_Navigating event and then hits my landing page.
Any thoughts on what may be happening?

Thanks in advance!

Cesar De la Torre

Hey , is IdentityServer4 working from Xamarin.Forms apps or Xamarin apps? – I just could find an example and info related to IdentityServer3 and Xamarin.Forms..
Thanks,
Cesar.

Comments are closed.