Friday, 23 June 2017

Making a POST request to IdentityServer's token endpoint from a Xamarin client

I recently had to implement a hybrid authorisation flow from a Xamarin client to IdentityServer 4. This involved making a browser request to IdentityServer’s authorize endpoint to retrieve an authorisation code, and then making a REST request to IdentityServer’s token endpoint, exchanging the authorisation code for an access token.

However, I found the documentation on the token endpoint to be slightly lacking. It states that the token endpoint requires a POST request, along with a series of URL query parameters:
POST /connect/token client_id=client1& client_secret=secret& grant_type=authorization_code& code=hdh922& redirect_uri=https://myapp.com/callback
Every attempt at making this request failed. On top of that I wasn’t keen on sending a client secret potentially in the clear. After some digging through the IdentityServer code I was able to construct a POST request that worked for me, which I verified using Advanced REST Client. My POST request was:
POST /connect/token grant_type=authorization_code& code=hdh922& redirect_uri=https://myapp.com/callback
The value of the code parameter is the authorisation code retrieved from IdentityServer’s authorize endpoint, and the value of the redirect_uri parameter is the callback URL registered with IdentityServer. Note that this isn’t the only way of making the POST request to the token endpoint, as IdentityServer permits several variations.

So what’s happened to the client_id and client_secret query parameters listed in the first POST request? While IdentityServer can accept them as query parameters, I was unhappy about sending my client secret as a query parameter. Luckily, IdentityServer also permits both client_id and client_secret to be sent in the Authorization header of the POST request, encoded as a basic authentication value, which is what I did. For more information about basic authentication, see Specifying Basic Authentication in a Web Request.

Translating this into code produces the GetTokenAsync method, which makes the request using the PostAsync method of the RequestProvider class:
public async Task<UserToken> GetTokenAsync(string code) { string data = string.Format("grant_type=authorization_code&code={0}&redirect_uri={1}", code, WebUtility.UrlEncode(GlobalSetting.Instance.IdentityCallback)); var token = await _requestProvider.PostAsync<UserToken>(GlobalSetting.Instance.TokenEndpoint, data, GlobalSetting.Instance.ClientId, GlobalSetting.Instance.ClientSecret); return token; }
The PostAsync method in the RequestProvider class is shown in the following code example:
public async Task<TResult> PostAsync<TResult>(string uri, string data, string clientId, string clientSecret) { HttpClient httpClient = CreateHttpClient(string.Empty); if (!string.IsNullOrWhiteSpace(clientId) && !string.IsNullOrWhiteSpace(clientSecret)) { AddBasicAuthenticationHeader(httpClient, clientId, clientSecret); } var content = new StringContent(data); content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"); HttpResponseMessage response = await httpClient.PostAsync(uri, content); await HandleResponse(response); string serialized = await response.Content.ReadAsStringAsync(); TResult result = await Task.Run(() => JsonConvert.DeserializeObject<TResult>(serialized, _serializerSettings)); return result; }
After creating the HttpClient instance, the PostAsync method calls the AddBasicAuthenticationHeader method, which is shown in the following code example:
private void AddBasicAuthenticationHeader(HttpClient httpClient, string clientId, string clientSecret) { if (httpClient == null) return; if (string.IsNullOrWhiteSpace(clientId) || string.IsNullOrWhiteSpace(clientSecret)) return; httpClient.DefaultRequestHeaders.Authorization = new BasicAuthenticationHeaderValue(clientId, clientSecret); }
The AddBasicAuthenticationHeader method adds the Authorization header to the POST request, with its value being the clientId and clientSecret values encoded using basic authentication.

Back in the PostAsync method, the other piece of the puzzle that was missing from the IdentityServer documentation is that the ContentType of the POST request must be set to application/x-www-form-urlencoded. I suspect this is the missing piece of the puzzle that caused the original request to fail.

To see the full application from which I’ve taken this code, along with more content about using IdentityServer from a Xamarin client, see Enterprise Application Patterns using Xamarin.Forms.

Monday, 5 June 2017

Enterprise Application Patterns using Xamarin.Forms

For the last few months I've been working on a Xamarin.Forms guide, about building cross-platform enterprise apps. The first edition of the guide is now published as an eBook, and can be downloaded here, or by clicking the image below.


What's it all about?

The eBook provides guidance on building cross-platform enterprise apps using Xamarin.Forms. It focuses on providing architectural guidance for developing adaptable, maintainable, and testable Xamarin.Forms enterprise apps. Guidance is provided on how to implement MVVM, dependency injection, navigation, validation, and configuration management, while maintaining loose coupling. In addition, there's also guidance on performing authentication and authorization with IdentityServer, accessing data from containerized microservices, and unit testing.

The guide comes with source code for the eShopOnContainers mobile app, and source code for the eShopOnContainers reference app. The eShopOnContainers mobile app is a cross-platform enterprise app developed using Xamarin.Forms, which connects to a series of containerized microservices known as the eShopOnContainers reference app.

Who's it intended for?

The guide is aimed at readers who are already familiar with Xamarin.Forms. For a detailed introduction to Xamarin.Forms, see the Xamarin.Forms documentation on the Xamarin Developer Center, and Creating Mobile Apps with Xamarin.Forms.

The guide is complementary to .NET Microservices: Architecture for Containerized .NET Applications, which focuses on developing and deploying containerized microservices.

What's in the sample application?

The guide includes a sample application, eShopOnContainers, that's an online store that includes the following functionality:
  • Authenticating and authorizing against a backend service.
  • Browsing a catalog of shirts, coffee mugs, and other marketing items.
  • Filtering the catalog.
  • Ordering items from the catalog.
  • Viewing the user's order history.
  • Configuration of settings.
The following diagram provides a high-level overview of the architecture of the sample application:
The sample application ships with three client apps:
  • An MVC application developed with ASP.NET Core.
  • A Single Page Application (SPA) developed with Angular 2 and Typescript. This approach for web applications avoids performing a round-trip to the server with each operation.
  • A mobile app developed with Xamarin.Forms, which supports iOS, Android, and the Universal Windows Platform (UWP).
For information about the web applications, see Architecting and Developing Modern Web Applications with ASP.NET Core and Microsoft Azure.

The sample application includes the following backend services:
  • An identity microservice, which uses ASP.NET Core Identity and IdentityServer.
  • A catalog microservice, which is a data-driven create, read, update, delete (CRUD) service that consumes an SQL Server database using EntityFramework Core.
  • An ordering microservice, which is a domain-driven service that uses domain-driven design patterns.
  •  A basket microservice, which is a data-driven CRUD service that uses Redis Cache.
These backend services are implemented as microservices using ASP.NET Core MVC, and are deployed as unique containers within a single Docker host. Collectively, these backend services are referred to as the eShopOnContainers reference application. Client apps communicate with the backend services through a Representational State Transfer (REST) web interface. For information about the implementation of the backend services, see .NET Microservices: Architecture for Containerized .NET Applications.

What's the mobile app?

The guide focuses on building cross-platform enterprise apps using Xamarin.Forms, and uses the eShopOnContainers mobile app as an example. The following screenshots show the pages from the eShopOnContainers mobile app that provide the functionality outlined earlier:


The mobile app consumes the backend services provided by the eShopOnContainers reference application. However, it can be configured to consume data from mock services for those who wish to avoid deploying the backend services.

The eShopOnContainers mobile app exercises the following Xamarin.Forms functionality:
  • XAML
  • Controls
  • Bindings
  • Converters
  • Styles
  • Animations
  • Commands
  • Behaviors
  • Triggers
  • Effects
  • Custom Renderers
  • MessagingCenter
  • Custom Controls
In addition, unit tests are provided for some of the classes in the eShopOnContainers mobile app. For more information about this functionality, see the Xamarin.Forms documentation on the Xamarin Developer Center, and Creating Mobile Apps with Xamarin.Forms.

Where can I get help and provide feedback?

This project has a community site, on which you can post questions, and provide feedback. The community site is located at https://github.com/dotnet-architecture/eShopOnContainers. Alternatively, feedback about the eBook can be emailed to [email protected].