App Service Auth and Azure AD B2C (Part 2)

EDIT 1/23/2017: Updated token refresh section with simplified instructions and added code snippets.

This post is a continuation of my previous post on App Service Auth and Azure AD B2C, where I demonstrated how you can create a web app that uses Azure AD B2C without writing any code. If you haven’t done so already, be sure to read that post to get proper context for this one.

In a recent service update, we’ve improved our overall support for B2C in Azure App Service. This includes enhancing our existing web app support, but more importantly includes adding full support for leveraging B2C in mobile, API and function apps. More specifically, support has been added for the Easy Auth Token Refresh API. Additionally, the Login API has been updated to support specifying specific B2C policies as arguments. If you’re a Mobile app developer, these improvements will make using Azure AD B2C in your mobile app significantly easier.

This post will go into more details about these improvements, show a few code snippets, and conclude by demonstrating these B2C capabilities in a simple demo web app.

Pick Your Policy

Specific B2C policies can be invoked using the built-in Easy Auth login API for AAD. If you’re not familiar with the Easy Auth login API, it works by allowing you to initiate a server-directed login by sending a GET request to your web, mobile, API, or function app’s built-in {root}/.auth/login/aad endpoint. This endpoint supports the standard sign-in query string parameters for AAD, including the newly added p={policy} query string parameter which allows you to specify a B2C policy by name.

For example, one could use the following HTML in a web app to create hyperlinks that invoke specific B2C policy workflows, such as editing the user profile or resetting the password:

<a href="/.auth/login/aad?p=B2C_1_EditProfile&post_login_redirect_uri=/">Edit Profile</a>
<a href="/.auth/login/aad?p=B2C_1_ResetPassword&post_login_redirect_uri=/">Reset Password</a>

Clicking one of these links will automatically redirect the user to the corresponding B2C policy page and allow the user to edit their user profile or reset their password accordingly.

If you’re writing a native client which uses one of the App Service Mobile Client SDK flavors, you could write something similar in platform-specific code. The example below shows how a mobile client app written in C# can invoke a B2C sign-in policy that requires MFA:

App.MobileClient.LoginAsync(
  MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory,
  new Dictionary<string, string>
  {
    { "p", "B2C_1_SignInWithMFA" }
  }

In this case, the user will presented with a native web view dialog which allows the end user to sign in using MFA (phone authentication, etc.). The primary thing to keep in mind about this experience is that these B2C policies can be easily invoked with just a few simple lines of client-side code. No platform-specific middleware or server-side code is required.

One important note of caution: the claims associated with the signed-in user will be updated every time you invoke a B2C policy. If your app depends on the presence of specific claims, make sure they are explicitly configured in each of your B2C policies.

Refreshing Tokens (Optional)

Easy Auth also has a built-in API for refreshing both provider-specific OAuth tokens and the app-specific authentication tokens. It works similar to the login API, in that it requires a GET request to the app’s built-in {root}/.auth/refresh endpoint.  More information on token refresh (and our token management story all-up) can be found in my earlier App Service Token Store blog post.

This token refresh support also extends to Azure AD B2C apps and is completely optional. However, leveraging token refresh is very important if you’re building a native app to ensure a smooth user experience. In order to set this up, you will need to do the following:

Create an app key for your B2C application

Creating app keys can be done in the Azure management portal for B2C.

Generating an App Key in the B2C Management Portal
Generating an App Key in the B2C Management Portal

Make a note of the app key that gets auto-generated by the portal. We’ll need it to configure Easy Auth in the next step.

Update the Easy Auth Settings

Easy Auth doesn’t require an app key by default and instead relies on the OpenID Connect Implicit Flow to implement secure logins. In order to get refresh tokens, however, we need to switch to the Hybrid Flow (Don’t worry if you don’t understand what these mean, Easy Auth will take care of the protocol details for you).

To make this protocol switch, you need to update the App Service Auth settings for your app with the key from the previous step. This can be done in the Advanced tab in the Azure Active Directory portal.

Setting the B2C app key as the Easy Auth client secret.
Setting the B2C app key as the Easy Auth client secret.

Now that you’ve enabled the hybrid flow, your app needs to start requesting refresh tokens from B2C. This can be done by updating the login parameters in your app code to include the offline_access scope.

Updated HTML Example

<a href="/.auth/login/aad?p=B2C_1_EditProfile&post_login_redirect_uri=/&scope=openid+offline_access">Edit Profile</a>
<a href="/.auth/login/aad?p=B2C_1_ResetPassword&post_login_redirect_uri=/&scope=openid+offline_access">Reset Password</a>

Updated Mobile App SDK C# Example

App.MobileClient.LoginAsync(
  MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory,
  new Dictionary<string, string>
  {
    { "p", "B2C_1_SignInWithMFA" },
    { "scope", "openid+offline_access" }
  }

Once this is done, all future logins should result in refresh tokens in the app’s built-in token store. You can then write client code which invokes the {root}/.auth/refresh API (or use the corresponding Mobile Client SDK method) to periodically refresh these tokens, which allows your app to function for longer periods of time without requiring a re-auth.

Demo App

To demonstrate all of this, I’ve created a single-page application (aka a SPA app) written using HTML, jQuery, and Bootstrap. It’s a trivial app written by someone who is clearly not a UI designer (me) and demonstrates the various patterns described in this blog post.  You can browse to it here and play around with it (I promise not to give out your information if you decide to provide it), or simply copy the source code and host it yourself in your own App Service web app. Note that I theoretically could have also built this using the iOS, Android, UWP/Xamarin, or one of the other mobile SDKs that are provided, but it was simpler for me to build a plain-old HTML web app. 🙂

The important thing to keep in mind is that there is absolutely no auth code in this sample (in fact, no backend code at all). All of this is implemented on the client and in the App Service platform with the help of Azure AD B2C. No SDKs required.

When you first visit the demo page, you will be given two login options. One is a standard email-based login and the other is an MFA login which requires you to register a phone number for phone authentication.

Starting page when unauthenticated
Demo app starting page when unauthenticated

The phone authentication provided by B2C allows you to do phone calls or SMS in my case. You can enter the provided code to complete the authentication.

Phone authentication
Phone authentication when using Azure AD B2C

Once signed-in, you will see a few B2C policy actions that you can invoke as well as a set of user claims displayed on the page. There’s also a sign-out button which uses Easy Auth’s built-in Logout API to clear the session.

Policy Actions
This B2C demo app supports updating the user profile and resetting the password used for logging in.
B2C Claims
This B2C policy is configured to return the object ID, postal code, name and email address.

Note that one of the claims, https://schemas.microsoft.com/claims/authnclassreference, contains the name of the B2C policy that the user logged-in with. Your application code can take advantage of this if, for example, you want to grant special permissions to users who log in using a higher-privilege B2C policy.

Again, it’s a very simple app to quickly demonstrate some of the powerful capabilities of the App Service “Easy Auth” platform when combined with Azure AD B2C.  My hope is that this is enough to give you some ideas about how you can leverage the Azure AD B2C platform in your own App Service apps.

Following Up

Have a technical questions about Azure App Service and/or Azure AD B2C? Head over to StackOverflow.com and tag your questions with azure-app-service and/or azure-ad-b2c accordingly. We monitor posts with these tags and are happy to help.

Author: cgillum

Principal Software Engineer at Microsoft.

44 thoughts on “App Service Auth and Azure AD B2C (Part 2)”

  1. Hi Chris,

    I’m every excited to see this update. Have been try to applied this to our development but getting 2 issues:

    1. With custom Policy, alway get “Bad Request” result for cases with on “/.auth/login/aad?p=CUSTOM_POLICY” after login success and redirect from AD B2C.

    2. If use p=DEFAULT_POLICY (default Policy that we config on Easy Auth Metadata Endpoint), got redirect to website url: “/.auth/login/aad/DEFAULT_POLICY#token=” instead of “/.auth/login/aad/done#token”

    There is a question, do we need to update Azure App Service -> Authentication -> Azure Active Director Advanced config. Currently we are still using Metadata Endpoint of specific Policy? The default Policy that I mention above is the Policy that inlcuded in this Metadata Endpoint.

    Can you please give some advise to solve them? Your demo is working well so I believe it is possible to move ahead.

    Thanks,
    Tri

    1. Thanks Tri for the info. I’ll take a look at custom policies to see if I can reproduce the issues you’re seeing. In the meantime, if you enable Application Logging for your web app in the portal, then you might be able do identify the reason for the “Bad Request” that you’re seeing. BTW, what kind of client are you using (browser or mobile app)?

  2. Thanks Chris, I got the details issue of “Bad Request”: “JWT validation failed: IDX10205: Issuer validation failed”, the Issuers are different between 2 Policies so could not login.

    Now I know why it happens, please check these Metadata Endpoints:
    Default_Policy (config in EasyAuth): https://login.microsoftonline.com/ryanngbeta.onmicrosoft.com/v2.0/.well-known/openid-configuration?p=B2C_1_SignIn

    Custom_Policy:
    https://login.microsoftonline.com/ryanngbeta.onmicrosoft.com/v2.0/.well-known/openid-configuration?p=B2C_1_SignInSignUp

    They have different “issuer”, that contains Policy Name (example: https://login.microsoftonline.com/tfp/25fcc346-bbe2-42a5-9f00-e635b527b490/b2c_1_signinsignup/v2.0/), while in your demo project, the Issuer are the same for all Policy (https://login.microsoftonline.com/1b31c2c6-a181-47bc-90fb-a74ae9e12668/v2.0/)

    How could we create AD B2C Tenant with that config? I just tried to create new AD B2C in Production-Scale (U.S) but got the Endpoint content, previously I used Preview.

    Regards,
    Tri

    1. Thanks Tri for these details! By chance are you in the private preview of B2C? I spoke with the B2C team and the behavior you’re seeing is not expected. App Service Easy Auth does not (yet) support multiple issuers, which is why you’re seeing these failures.

    2. Hi Chris,
      This new demo Tenant (ryanngbeta) was just created for testing, Production-Scale and U.S location as suggestion in G.A Announcement article.
      So as your demo, to get it worked we must have the same Issuer in Metadata for all Policies of one Tenant, since in case it contains Policy’s Name so always different, can you just try to create new Production-Scale and check Metadata of new Policy?
      Currently we bring AD B2C to Azure App Service with Bearer authentication scheme, support multiple Issuers by fetching Meta Endpoint of different Policies, working well but we love to make it easier with Easy Auth.
      Many Thanks,

  3. Thanks Chris. Helpful. Point of clarification – when using the EasyAuth feature the identifier for each users comes in the form of CID:… Whereas using AAD B2C returns the object identifier claim OID as the persistent user id. Since in this case we’re sorta using both… what is the best practice? For example if we need to persist data in a DB with user id… is the SID user id more stable to persist. Or would the OID claim be a better mechanism.

    1. Hi Mark. At this point I would recommend using the oid as the stable identifier to persist in your DB.

  4. Hi Chris,

    Great post BTW, thank you so much. I am trying to do a poc for a SPA application talking to its web API using easy auth and AD B2C. I am following the example and I am very close get it to work. The SPA main page is served from the same site that has the web api. The problem I am trying to figure out is how to be able to serve the SPA page without triggering the login flow. How you setup the web app easy auth to allow the display of the page? I imagine you have easy auth on , B2C configured, but the action is set to “Allow request (no action)”. Is this correct?

    1. Hi Ricky. Yes you are correct. Setting “Allow request (no action)” is the way to serve up the page to unauthenticated clients. Sorry for not making that clear!

    2. Hi Chris,

      Thank you so much for the prompt response! Thank you again for a great post series.

      Ricky

  5. Chris – I’m failing to see how your app demos getting the /.auth/refresh to work. I’ve authed into your app and have tried to hit the /.auth/refresh endpoint both in the browser and through jquery and i’m getting a 403 forbidden error in both. Also i don’t see any refresh token in the /.auth/me endpoint.

    Can you clarify how to trigger the token refresh?

    1. Mark, thanks for pointing this out and very sorry about the confusion. An older version of the demo app code had been mistakenly uploaded to the server, and that version did not work with token refresh. I’ve corrected this and you should now be able to see token refresh working correctly.

    2. Thanks for the response Chris. I’m still not seeing any refresh token and the .auth/refresh still shows an error. Any ideas?

    3. Chris – I think I may have uncovered the issue… it looks like it might be tied to the policy. If I just login I get the errors just described. However, I noticed if I edit my profile and return I do, in fact, see the refresh token in the .auth/me endpoint. Presumably .auth/me endpoint is tied to the editing policy vs the sign in policy.

      This actually ties into another question / error, I’ve been experienced. IF (I don’t know) the EasyAuth is tied to one policy… is it possible to support the ability to sign up, sign in, AND edit profile with EasyAuth?

      Thanks for all the support on this!

      -mark

    4. Hmmm…I’m glad that we’re moving in the right direction, but Easy Auth should not be tied to any specific profile. The intent is to support the ability to use multiple policies like you described. The way that the sample app has been put together, all of them should result in a refresh token. One thing to try – can you open a new in-private/incognito browser window and try logging-in again? This would help to rule-out any unexpected caching on the browser.

    5. Hi Mark,
      I managed to get the /.auth/refresh to work by adding “scope=openid offline_access” to additionalLoginParams property in .authSetting

      Hope this helps.

    6. Thanks Chris….

      Yes the fix is now working. Huzzzzaaaaa!
      “additionalLoginParams”: [
      “response_type=code id_token”,
      “scope=openid offline_access”
      ],

  6. Hi! Thanks for this article. Early in the post you show:

    App.MobileClient.LoginAsync(
    MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory,
    new Dictionary
    {
    { “p”, “B2C_1_SignInWithMFA” }
    }

    First, I had to use a JObject instead of a Dictionary – but if I call it with one of my own signup policies, nothing happens. No webview, no errors. Nothing. Have you tried this with the mobile sdk? I can get it to work with the MSAL library, but not the client lib for xamarin.

    1. When you use JObject instead of a dictionary with the mobile SDK, you’re actually using a completely different login flow known as the client-directed login flow. Unfortunately this is not supported for B2C at this time.

  7. Hi Chris,
    In your demo site’s sign in page, when I click ‘Forgot your password’, an error page returns with the message ‘You do not have permission to view this directory or page.’.
    I tried creating my own mobile app using your example, everything works well, except the reset password page returns the same error as your demo site.

    Could there be a missing step?

    Anyway, your blogs on B2C has been a great learning experience for me.

    1. Glad to hear you’re finding these posts helpful. I just double-checked the “Forgot your password” link and it seems to be working for me. I’ve just now enabled logging, so try it again if you have a chance and I should be able to see what the issue is. If you’re hosting your own version, you can also enable Application Logging to get more details about the failure.

    2. I tried again and got the same error. Here are my steps.
      1. https://easyauth-b2c.azurewebsites.net/
      2. Click Sign-in to go to the Microsoft login page
      3. Click Forgot your password link from the login page, and the error message appears.

      I will also check the application logging when I return to office tomorrow.

    3. I followed up with the B2C team and this error message is actually “by design”: the “Forgot my password” link returns an error to the application and it’s up to the application to react accordingly. This will require some special handling in Easy Auth which we don’t have support for quite yet. Thanks for bringing this to my attention.

    4. Can I assume that at this point, we should add our own link to /.auth/login/aad?p=&post_login_redirect_uri=/ for the user to reset the password?

      Any timeline for Easy Auth to support this?

      Thanks for your prompt response, and I look forward to more enhancements for B2C.

    5. When “Forgot my password” link returns an error to the application, I can’t seem to catch it from my application, because the error message “You do not have permission to view this directory or page.” appears in the page from Microsoft.

    6. Right – this is the main problem. Your suggestion about following the post_login_redirect_uri query string parameter after an error sounds like a good one, and I’ll look into implementing this as a simple solution. In the meantime, one option you have is to override the host-level error handling behavior to take control in this situation. It leverages the custom errors feature of IIS. Here is an example web.config snippet which I’ve tested which allows you to return your own HTML (MyPage.html) when this error is encountered:

      <configuration>
        <system.webServer>
          <httpErrors defaultResponseMode="File" errorMode="Custom" >
            <clear />
            <error statusCode="401" subStatusCode="73" path="MyPage.html" />
          </httpErrors>
        <system.webServer>
      </configuration>
      

      This is a general purpose solution which works with all web apps. It’s a pretty handy feature on it’s own and I recommend folks take advantage of it to ensure a smooth experience for end-users, even when things go wrong.

  8. Hi Chris,

    Thanks for the great articles. Is your demo app on Git Hub? I’d like to get a copy of the source and play around with it in our own app service.

    -Steve

    1. Thanks Steve. It’s not on GitHub currently, but you’re welcome to download the source directly (right-click –> View Source). There is no server-side code that you need to worry about in this sample. The main requirements are that you configure that app as described and set the “Action to take when request is not authenticated” to “Allow request (no action)” in the Azure portal. Putting samples on GitHub is a good idea though. I’ll look into it.

  9. Hi Chris,
    I encountered this problem (not sure if it is related to App Service with authentication).
    Here is my configuration.
    1. App Service requires authentication
    2. ‘Action to take when request is not authenticated’ set to Allow request (no action)
    3. Custom API GET permission set to Anonymous access

    When my app called the custom API without authentication, the following error appears.
    Application has thrown an uncaught exception and is terminated:
    SyntaxError: Unexpected token {
    at exports.runInThisContext (vm.js:53:16)
    at Module._compile (module.js:414:25)
    at Object.Module._extensions..js (module.js:442:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:311:12)
    at Module.require (module.js:366:17)
    at require (module.js:385:17)
    at Object. (D:\home\site\wwwroot\app.js:18:15)
    at Module._compile (module.js:435:26)
    at Object.Module._extensions..js (module.js:442:10)

    1. Looks like this error will occur even for authenticated custom API.
      Yesterday, every call to custom API was successful. Today, we encountered this error for every calls.
      Could it be Microsoft is making some changes to the App Service side?

    2. I just found out that it is my code mistake. One of the custom API had an error, and it causes app.js to fail, which has impact on all other custom APIs.

  10. Hi Chris,

    I am working with a customer planning their cloud migration strategy. We identified a web application to be used for one of the PaaS migration pilots. The difficulty with this application is that it is used by both sales associates (corporate users) in the company AD, and external customers (accounts in SQL server). It uses identity server to federate both IdPs. We want to get rid of identity server and use their new Azure AD tenant for corporate users and Azure AD B2C for the external users. Is there any way we could configure Easy Auth with two AAD providers? Can we modify the authSettings somehow to achieve this? I know B2C is planning to add Azure AD as another identity provider in the future, is easy auth planning to do something similar? Any help on how to accomplish this will be great!

    Thank you so much in advance…Ricky

    1. Hi Ricky. Unfortunately we made a design decision to re-use our existing AAD auth infrastructure to support B2C logins, which means you can configure either AAD for corporate users or AAD B2C for consumers, but not both. 🙁

  11. Was there a change in the past couple days? I’m noticing a ton of weirdness on several apps. My previous auth flow with the refresh_token that WAS working is not throwing a BAD request error (token failed) in the logs.

    1. Mark, some minor bug fixes have been made in App Service, but I’m not seeing any issues in my test app, which also uses token refresh. I’ll follow up with you over email for more information.

  12. I just realized the call to the .auth/refresh is working do the presence of the cookie in the jquery Ajax call. For native clients that probably not going to work and further frequently the id_token would expire so you’d need to post the refresh_token directly. Are there any examples of post the refresh_token to the active directory token endpoint or perhaps to the /.auth/refresh endpoint if that even possible.

    1. Mark, native clients can use the issued authentication token to access the /.auth/refresh endpoint. This is what the Mobile Client SDK RefreshUser API uses internally.

    2. Perhaps i’m misunderstanding, but you need a refresh token after the authentication token has expired. But if the authentication token has expired… how can you used against the refresh endpoint? And if you can use an expired authentication token to the refresh endpoint, it seems as though the client never uses the refresh token directly, so it seems odd that its available in the .auth/me endpoint.

      In any case: the use case i’m trying to solve it getting a new authentication token after it has expired. I know that’s the purpose of the refresh token but its unclear how it can be used with easyauth. I would think there would be something along the lines of posting the refresh token back to the .auth/refresh endpoint after the authentication has expired and can no longer be used.

  13. I restarted my PC, and the problem goes away. Not sure why it happened.

    I have a few more queries on B2C road map.
    1. Currently, the sign-in policy does not allow UI customization for sign in page. Is this on the road map?

    2. The sign-up and sign-in policy (unified) does not support ‘enforce change password on next login’. Is this on the road map?

  14. Hi Chris,
    After I block a user from sign in (via Azure AD portal), the user seems to be able continue to call /.auth/me and continue to refresh token successfully. Is there a way to stop the user from continuing the app (once the user has signed in and we block the user from sign in after that) ?

    Another question is how do we logout a user? Do we call an URL to logout the user?

    Thanks in advance.

    1. In browser scenarios, you can log out users by navigating them to the built-in /.auth/logout endpoint which takes an optional post_logout_redirect_uri query string parameter. Logging out delete any session cookies, which should prevent them from calling the /.auth/me and /.auth/refresh APIs.

Leave a Reply