Advanced AAD B2C authentication w/ Xamarin Forms

How to do authentication with Azure Active Directory B2C in Xamarin Forms in 2021 and how to implement a custom sign up policy using the Identity Experience Framework, Azure Functions, and custom validation emails.

Philipp Bauknecht
medialesson

--

So why all the effort? Well, AAD B2C has a distinct way of user registration where new users can hit the registration form but need to verify their email addresses before completing sign up. AAD B2C sends out a verification code to the new user that needs to be entered in the registration form. Most other identity providers send out a link instead of the verification code. And this is acutually quiete a big deal: When working with a verification code the app that hosts the registration form needs to keep it’s state so the user can enter the code. In case of a mobile app built with Xamarin Forms this is a real challenge as users may choose to close the app when switching over to their mail app to get the code. So when they return, they have to start over as the sign up state is managed in the Microsoft.Identity.Client library outside of our app. So this can be a dead end!

Luckily it’s possible to customize the entire authentication user experience with the Identity Experience Framework. This allows us to describe user flows (e.g. sign up) using XML policies. So the idea is to send out a custom e-mail for verification including a link to complete the registration process from anywhere instead of a verification code.

Here is the complete sample on Github: https://github.com/GrillPhil/AdvancedAADB2CXamarinForms

But before we start with customizing the sign up flow, let’s start simple by creating and configuring the default way using AAD B2C and Xamarin Forms.

#1 Setup a new Azue Active Directory B2C Tenant

Let’s create a new AAD B2C tenant in the Azure Portal (sorry no ARM support here for automation). BTW: AAD B2C is free for up to 50K monthly active users.

User Flows

The first configuration we need is to create user flows for sign up, sign and password reset. We will later use these flows in our app to enabled these scenarios. So go to User flows, click on “New user flow” and start with Sign in:

Since we haven’t configured any additional identity providers the only option is local accounts using email signin. Every flows allows to choose application claims that are returned in tokens issued by AAD B2C. In our case lets pick Display Name, Email Addresses and the user’s object id:

In total we need 3 user flows to have a complete set for our app:

When creating the sign up user flow make sure to select Display Name and E-Mail Address in the Collect attribute column and Display Name and Email Addresses in the Return claim.

App registration

In order to use AAD B2C for authentication in our app we need to create a new app registration through the Azure Portal (yes no ARM available here either). In the portal choose App registration and click on “New registration”, choose a name and click on “Register”:

In the app registration’s overview page copy the Client Id, we will need it later:

Finally we need to configure a platform to use this app registration for authentication. In our case this is “Mobile and desktop applications”. Make sure to select the proposed Redirect URI that starts with msal followed by the registration’s client id and ends with ://auth. This is the redirect URI that the MSAL aka Microsoft.Identity.Client library will expect:

That’s the minimal configuration needed for AAD B2C, now let’s create a new empty Xamarin Forms app to use it.

#2 Setup an new Xamarin Forms App using AAD B2C Authentication

We will need the Microsoft Authentication Library (MSAL) to implement authentication. Let’s add it to both the Xamarin and the Android project of our solution using NuGet. Just search for

Microsoft.Identity.Client

The Xamarin Forms Part

Our app requires some configuration information so it knows which AAD to connect to. We can store these in a Constants class:

Next we add a LoginPage to the Xamarin app to handle authentication. On this page we need a button to sign up and one to sign in:

To handle all authentication operations we need a PublicClientApplication instance from the MSAL library. We create this instance with information from the constants and make it available as public property in App.xaml.cs so it’s reachable from anywhere in our app. Note how we also introduce a public property UIParent to handle callbacks later:

With the PublicClientApplication in place we can now implement the signup invocation method in the code-behind of the login page:

Note how we use the fluent API part WithB2CAuthority to tell the library which user flow we want to use. The method for sign in looks similar but we don’t need to provide the user flow as sign in is the default user flow we configured in App.xaml.cs when instanciating the PublicClientApplication. On the other side we need to also handle password reset as a seperate user flow:

So now we have all 3 defined user flows implemented in the app. In each case we navigate to our MainPage once authentication has completed and pass through the AuthenticationResult object as parameter. Actually there is one case still missing: Sign in the user silently in the background in case the user has previously signed in when starting the app. Therefore we can override the page’s OnAppearing method:

In the MainPage let’s display the user’s information that is included in the ID token’s claims like name, email and id. Therefore we need to pull this information out of the ClaimsPrincipal object in the AuthenticationResult object which contains an enumartion of claims. Here is a little helper class to do so:

and a simple POCO class to store the user’s information:

With this helper in place let’s implement a UI to display the user information and a logout button in the MainPage:

We can then fill those labels in the code-behind using the AuthHelper:

The platform specific part

The Microsoft.Identity.Client library uses a web view to render the user flows. In case of Android we need to configure this as an activity in the AndroidManifest.xml inside the application node:

Make sure to insert your client id in the data element.

Finally we also need to set the UIParent in the MainActivity constructor and handle the activity result:

That’s it! we have successfully integrated authentication using AAD B2C in a Xamarin Forms app for Android. So let’s see how this looks like:

So sign up, sign in and password change work but if we exit the app after sending the verification code we would have to start over. So this flow is not robust enough for mobile users.

The new sign up user journey

So how could a more robust sign up user experience look like? I scribbled something to illustrate the idea:

The idea is to present a native page in the app. First time users can use the native form to start the sign up process.

Once a user fills the form and clicks on the sign up button the form data is submitted to a Azure Function app that creates an individual invite link. Therefore a id token is created with the provided name and email using a custom AAD B2C user flow built with the Identity Experience Framework. A link to a redeem function in the same Azure Function app is created using the id token as a query parameter.

The Function App then sends the invite link in a custom email to the email address provided by the user.

When the user clicks on the invite link the invoked function creates a link to a second custom AAD B2C user flow with the id token as a parameter. This link is then returned as a redirect result and presents a form of AAD B2C to complete the registration. The email and is already filled in the form and the email is validaded. So the users can complete the form by choosing and confirming a password. Finally after submitting the registration form the user is redirected to a website to show registration completion and a link back to the app.

This solution builds on the sample provided here: https://github.com/azure-ad-b2c/samples/tree/master/policies/invite-via-email

Solution architecture

Here is the new registration flow as described above:

To implement this solution we need some additional components:

  • AAD B2C Custom Policy (= User Flow) to generate an invite id token
  • AAD B2C Custom Policy (= User Flow) to generate a redeem url to complete the registration process
  • Azure Function App with functions to invite (generate id token, link and send as email) and redeem (forward to redeem policy)
  • Website to show registration result

#3 Custom policies

In order to define the custom policy we need to define some base policies to build on top of. Those base policies are pretty long, I won’t explain them in detail. Basically they define the general setup required for our specific flows. The base policies require 2 policy keys that can be created in the Azure Portal. Select Identity Experience Framework > Policy keys to add one key for signing with these details:

And then a second key for encryption with these details:

With the keys in place we can configure the base policy that defines the core flows and properties. Make sure to insert your tenant name in line 7 and 9:

Next a localization policy defines required strings. Make sure to insert your tenant name in live 7, 9 and 12:

One more to go for some extensions. Make sure to again insert your tenant name in lines 7, 9 and 12:

Now comes our actual custom policies. First let’s start with the policy to create an id token to include in our invite link. Insert your tenant name in lines 3, 5 and 8:

The second policy is to redeem this invite and present a form for the user to complete the registration process by entering and confirming a password. Again insert your tenant name in lines 3, 5 and 155:

That’s a lot of policy going on here but we made it! Make sure to upload all policy files in the order we created them in the Azure Portal in the blade Identity Experience Framework:

With the policies deployed we can implement the functions…

#4 Handle invites and redeem codes

In a Azure Function app we will need to create two functions using the HttpTrigger: Invite and Redeem.

Also we need to provide some environment variables for configuration. To test locally we can put them in local.settings.json:

Replace the placeholders with your values (check your Xamarin Constants for reference). As you can see there are also two redirect urls: RedirectUrl and FinalUrl. The first one is used to redirect after successful generation of the invite id token and the second one is invoked after successful completion of the sign up when using the redeem url. We need to add both to our application registration we created previously and also enable Access and ID tokens for implicit flows:

Next let’s create a authentication helper class in the function app that’s responsible for interaction with AAD B2C and add a method to request a id token via the invite user flow and then return a link that contains this token as a query parameter:

The second method will then create a link to the redeem user flow using this id token:

Invite Function

The invite function handles a post request from our xamarin app that contains email and name in the request body. Then it shall call our auth helper’s GenerateLinkAsync method and finally send this link in a mail to the user. To handle the logistics of sending emails let’s introduce a mail helper class:

Now it’s time for the function:

Obivously there is a lot of room for improvement to make a nicer email, but this is just a tech demo ;-)

Redeem Function

The redeem functions is called from the email’s link we just send, extracts the id token from the query parameters, creates the actual user flow link and returns it as a RedirectResult effectively forwarding the caller to this flow:

#5 Registration completed experience

After completing the redeem user flow ths users will be redirected to the url we specified as FinalUrl. In my case this will be http://localhost:3000 so I can test e2e on my local machine. AAD B2C will also pass a id token as query parameter when calling this url. So it would be nice to have a simple website that greets the user, informs that the registration worked and recommends signing in the xamarin app.

I choose to do this with a simple static website. To develop this locally let’s create a new node project and use live-server for local testing:

The live-server needs to have some minimal setup:

And then some minimalistic html to greet the user. Note how we can use the library jwt-decode to parse the incoming id token and fill in the user’s name:

Caution: jwt-decode does NOT verify the token, it just parses it! So this is simply for showing the user’s name, this is not suitable for actual authentication.

#6 Integration with Xamarin Forms app

With all moving pieces in place we can now adopt our app’s UI to the new sign in process. To call our function app locally we need to add a network security configuration to instruct the android emulator. Let’s create a new file network_security_config.xml in the folder Resources/xml in the android project:

The address 10.0.2.2 is typically the host’s ip address. to apply this configuration we need to add it in the AndroidManifest.xml:

The function endpoint to create the invitation belongs in our Constancs class:

Now it’s time to tweak our login page by introducing a native form for sign up and a nice loading indicator:

And of course we need to update the ButtonSignup_Clicked method to invoke the invite function:

InviteRequest is just a POCO class:

That’s it! Now let’s take this monster for a test ride…

Works really well right? To further improve the UX we can now freely style the content and appreance of the invitation email and also the registration success page. We might want to add link to open the app from the registration success page. Also it’s easy to replace html and css to style the actual sign up and sign in webviews rendered by AAD B2C.

Summary

Congratulations if you made it to the summary, this was a long post. I think this sample demonstrates well how extensible and flexible AAD B2C is. You have full control not only over look and feel but more important full control over the actual flow for authentication and thus can create truly tailored user experiences while still have Microsoft and Azue do the heavy lifting in the backend.

--

--

Philipp Bauknecht
medialesson

CEO @ medialesson. Microsoft Regional Director & MVP Windows Development. Father of identical twins. Passionate about great User Interfaces, NYC & Steaks