Skip to content

Developing an AAD B2C app with MSAL Python

Ray Luo edited this page Oct 4, 2019 · 8 revisions

This page is currently a preview. The actual feature in MSAL Python has NOT been implemented yet.

Use MSAL Python to work with B2C

You can use MSAL Python to sign-in users with social identities, acquire token, customize the sign-in experience, by using Azure AD B2C. AAD B2C is built around the notion of policies. In MSAL Python, specifying a policy translates to providing an authority.

  • When you instantiate the client application, you need to specify the policy in authority, and every usual acquire_token_by_xyz(...) would work with that policy.
  • When you want to use a policy, such as for Edit Profile or Reset Password, you create a PublicClientApplication with the authority containing that policy, and then call the get_authorization_request_url().

Authority for a B2C tenant and policy

The authority to use is https://{tenant_name}.b2clogin.com/{tenant_name}.onmicrosoft.com/{policy} where:

  • tenant_name is the name of the Azure AD B2C tenant, such as "contoso"
  • policy the name of the policy to apply (for instance you might have "b2c_1_susi" for sign-in/sign-up).

You may also configure your own customized domain name, so that your authority would look like https://contoso.com/{tenant_name}.onmicrosoft.com/{policy}

Instantiating the application

When building the application, you need to provide, as usual, the authority, built as above

app = msal.PublicClientApplication(  # Or ConfidentialClientApplication(...)
    "your_client_id",
    authority="https://contoso.b2clogin.com/contoso.onmicrosoft.com/b2c_1_susi",
    ...)

or if you plan to use your customized domain, you will have to let MSAL Python know it is a B2C authority

app = msal.PublicClientApplication(  # Or ConfidentialClientApplication(...)
    "your_client_id",
    authority=msal.Authority(
        "https://contoso.com/contoso.onmicrosoft.com/b2c_1_susi",
        is_b2c=True  # This is the equivalent way of other MSAL's B2CAuthority(...)
        ),
    ...)

Acquiring a token

Acquiring a token for an Azure AD B2C protected API, based on the policy already provided as part of the authority, are exactly the same as what you would do in non-B2C scenario.

For example, if you want to use acquire_token_by_authorization_code(...), do so as usual.

result = app.acquire_token_by_authorization_code(code, scopes, ...)

Same applies when you want to use get_accounts(...) and acquire_token_silent(...)

accounts = app.get_accounts()
if accounts:
    chosen = accounts[0]  # Assuming the end user chose this one
    result = app.acquire_token_silent(config["scope"], account=chosen)

There is no need to filter accounts by policy, because the usage pattern here is essentially "one msal app per policy" (because the B2C policy is designed to behave like isolated authority). If you need to work with a different policy, create a new msal app.

Special case of EditProfile and ResetPassword policies

Some policies (for instance letting the end user edit their profile or reset their password) is sometimes known as "action policy". They customize the user experience, but you do not need their returned token. We recommend do it by calling get_authorization_request_url(...).

app = msal.PublicClientApplication(
    "your_client_id",
    authority="https://contoso.b2clogin.com/contoso.onmicrosoft.com/b2c_1_edit_profile",
    )
uri = app.get_authorization_request_url(
    scopes,  # It can be an empty list
    redirect_uri,  # The user experience will go back to your app here
    )

Resource Owner Password Credentials (ROPC) With B2C

Normal caveats on ROPC flow still applies. Please see this wiki page.

In your AzureAD B2C tenant, create a new user flow and select Sign in using ROPC. This will enable the ROPC policy for your tenant. See Configure the resource owner password credentials flow for more details.

Once you create the msal instance with the authority which contains the ROPC policy, the acquire_token_by_username_password(...) would work as usual.

app = msal.PublicClientApplication(
    "your_client_id",
    authority="https://contoso.b2clogin.com/contoso.onmicrosoft.com/b2c_1_ropc",
    ...
    )
result = app.acquire_token_by_username_password(username, password, scopes)

Limitations of the ROPC flow

  • This only works for local accounts (where you register with B2C using an email or username). This flow does not work if federating to any of the IdPs supported by B2C (Facebook, Google, etc...).

Caching with B2C in MSAL Python

Known issue with Azure B2C:

MSAL Python token cache usage pattern starts with querying all existing accounts by get_accounts(...), which supports a username parameter as filter. That username data is populated by a preferred_username claim inside the ID Token.

By default, that claim is missing in many of the Azure AD B2C scenarios.

The customer impact is that when trying to display the accounts, their username field would be empty. This is generally not an issue if you are using Auth Code flow and dealing with only one account per user. But if you are using ROPC flow and feels you already know the end user's username, accounts = app.get_accounts(username="[email protected]") would still give you an empty result, because "[email protected]" won't match "".

The workaround is to customize your B2C policy to populate and return a preferred_username claim, or simply call your app.get_accounts() without a specific username parameter.

Samples illustrating acquiring tokens interactively with MSAL Python for B2C applications

Sample Platform Description
ToDo All platforms supporting Python A web app showcasing how to use MSAL Python to authenticate users via Azure Active Directory B2C, and access a Web API with the resulting tokens.