Azure Functions, Azure AD B2C and Python
Tags: python, security, azure, serverless
Published: 2022-03-04 01:45:30

[Azure App Authentication](https://docs.microsoft.com/en-us/azure/app-service/overview-authentication-authorization) (aka Eazy Auth) has been around for a while and it's a great turnkey solution for implementing authentication for your Azure Web Apps, APIs and Azure Functions.

There are, as with every technology, certain limitations. For example, you can't implement complex logic or do token validation and, in some cases, you need to write some code if you want to interact with the authenticated context. But if you want an ON/OFF solution that can quickly secure your Azure-based apps, then App Authentication is the solution!

In this blog post I'll show you how to:
1. Configure App Authentication with Azure AD B2C
2. How to retrieve and interact with the B2C Access Token
3. All of the above in Python!

## Create a local Azure Function
I'll spare you the trouble of taking you through the steps in creating the boilerplate Azure Function. There is a great [Quickstart](https://docs.microsoft.com/en-us/azure/azure-functions/create-first-function-cli-python?tabs=azure-cli%2Cbash%2Cbrowser) in our official docs

Once you have the Function created, open the `requirements.txt` and add the following package: `python-jose`. This [library](https://pypi.org/project/python-jose/) is excellent for working with JWTs. 

Then, open the `__init__.py` file and update the code to look like below:

```
import logging
from jose import jwt
import azure.functions as func
import json

token=None

def main(req: func.HttpRequest) -> func.HttpResponse:
    
    logging.info('Python HTTP trigger function processed a request.')

    auth = req.headers.get("Authorization", None)
    if not auth:
        return func.HttpResponse(
             "Authentication error: Authorization header is missing",
             status_code=401
        )
    parts = auth.split()

    if parts[0].lower() != "bearer":
        return func.HttpResponse("Authentication error: Authorization header must start with ' Bearer'", 401)
    elif len(parts) == 1:
        return func.HttpResponse("Authentication error: Token not found", 401)
    elif len(parts) > 2:
        return func.HttpResponse("Authentication error: Authorization header must be 'Bearer <token>'", 401)

    token_claims = jwt.get_unverified_claims(parts[1])

    # Do anything else you need here

    return func.HttpResponse(json.dumps(token_claims, indent=4))
``` 

This code checks for an `Authorization` header and then, it either returns an error message if something is wrong, or parses the claims and dumps them back in the HTTP response. Since we're not using Azure AD but Azure AD B2C, the standard [App Authentication](https://docs.microsoft.com/azure/app-service/configure-authentication-oauth-tokens#retrieve-tokens-in-app-code) headers don't apply. The access token will be passed in the **Authorization** header with the expected value of `Bearer <token>`.

Function done!

## Create the B2C application registration
In the Azure AD B2C, head to **App Registrations** and hit the **New Registration** button at the very top. Give it a meaningful name and press **Register**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hrb4fpjyda4w1kkt8a4z.png)

Head over to the **Expose an API** tab and click on the **Set** `Application ID URI` 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uy0386p2qkko4stlwq6t.png)

Final step, add a new scope. Give it a name such as `access_as_user` and the message for the admin consent and click the **Add Scope** button

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6nmaf4v3fifwbuaac0qc.png)

Since this is B2C, you also need a SignUp/SignIn policy. You can follow the steps in this [document](https://docs.microsoft.com/en-us/azure/active-directory-b2c/add-sign-up-and-sign-in-policy?pivots=b2c-user-flow).

There is one more piece of information we need to complete the process in B2C. Head to the **Overview** tab of the App Registration you just created and grab the well known endpoint from the **Endpoints** tab!

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p91z4fib8kmb72wqu9c3.png)

In the end, the information we need to configure our Azure Function Authentication is:

- **ClientID**: your app registration client id
- **TenantID**: your tenant ID
- **The well known URL**: this is for JWT information

## Set up Eazy Auth in the Azure Function App
The App Service Authentication can be set up through the CLI etc, but for the purpose of this blog, we'll use the Azure Portal. In the Azure Function App, navigate to the Authentication Tab and click on the **Add New Authentication** button

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s589rhcu2r8ogq9hfwsq.png)

In the next window, add the following details
- Identity Provider: Microsoft
- App Registration Type: Provide the details of an existing app registration
- Application ID: the API App Registration Client ID
- Issuer URL: The Azure AD B2C Well Known endpoint
- Allowed Token Audiences: your API App Registration Client ID
- Restrict Access: Require Authentication
- Unauthenticated requests: HTTP 401

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/odyk0j5fhu2fwuu862i3.png)

With this information, the Azure Function App will require an Access Token to be passed in the HTTP request in the Authorization Header. If no token is available or the token is invalid, an HTTP 401 will be returned to the client

## Source code
You can find the source code for this blog post on [GitHub](https://github.com/425show/JWTClaimsInPythonAzFunctions)