Using Envoy as a Backend-for-Frontend (BFF) in Azure with Azure AD B2C

Patrick KearnsPatrick Kearns
3 min read

Envoy is a high-performance, cloud-native proxy designed for microservices architectures. Developed by Lyft, it provides advanced traffic management, observability, security, and load balancing. Envoy is widely used as an API gateway, service mesh proxy, and even as a Backend-for-Frontend (BFF).

Why Use Envoy as a BFF?

A BFF acts as an intermediary between frontend applications and backend services, optimising responses, handling authentication, and reducing the complexity of frontend applications. Using Envoy as a BFF in Azure can:

  • Secure API requests with OAuth2 (including Azure AD B2C authentication)

  • Centralise authentication and authorisation logic

  • Improve performance with caching and rate limiting

  • Simplify backend communication by routing requests across microservices

Setting Up Envoy as a BFF in Azure

We'll walk through configuring Envoy as a BFF in an Azure environment with Azure AD B2C for authentication.

Step 1: Deploy Envoy in Azure

Envoy can be deployed as an Azure Container App, Azure Kubernetes Service (AKS), or Azure Virtual Machine.

For a Container App deployment, push an Envoy image to Azure Container Registry (ACR) and deploy it:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: envoy-bff
spec:
  replicas: 1
  selector:
    matchLabels:
      app: envoy-bff
  template:
    metadata:
      labels:
        app: envoy-bff
    spec:
      containers:
      - name: envoy
        image: domain.azurecr.io/envoy-bff:latest
        ports:
        - containerPort: 9901
        - containerPort: 10000

Step 2: Configure Envoy for API Routing

Create an envoy.yaml configuration file to define:

  • Listeners: Define ports and protocols.

  • Routes: Specify how requests are routed to backend services.

  • AuthN/AuthZ: Enforce authentication via Azure AD B2C.

Here's an example Envoy config to route requests to microservices and validate JWT tokens from Azure AD B2C:

static_resources:
  listeners:
  - name: listener_0
    address:
      socket_address: { address: 0.0.0.0, port_value: 10000 }
    filter_chains:
    - filters:
      - name: envoy.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          codec_type: AUTO
          stat_prefix: ingress_http
          route_config:
            virtual_hosts:
            - name: backend
              domains: ["*"]
              routes:
              - match: { prefix: "/api/orders" }
                route: { cluster: service_orders }
              - match: { prefix: "/api/customers" }
                route: { cluster: service_customers }
          http_filters:
          - name: envoy.filters.http.jwt_authn
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication
              providers:
                azure_b2c:
                  issuer: "https://login.microsoftonline.com/YOUR_TENANT_ID/v2.0"
                  audiences:
                  - "YOUR_CLIENT_ID"
                  remote_jwks:
                    http_uri:
                      uri: "https://login.microsoftonline.com/YOUR_TENANT_ID/discovery/v2.0/keys"
                      cluster: azure_b2c_keys
                  forward: true
  clusters:
  - name: service_orders
    connect_timeout: 5s
    type: STRICT_DNS
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: service_orders
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address: { address: orders.api.internal, port_value: 80 }

  - name: service_customers
    connect_timeout: 5s
    type: STRICT_DNS
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: service_customers
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address: { address: customers.api.internal, port_value: 80 }

  - name: azure_b2c_keys
    connect_timeout: 5s
    type: LOGICAL_DNS
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: azure_b2c_keys
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address: { address: login.microsoftonline.com, port_value: 443 }

Step 3: Deploy and Secure Envoy in Azure

Once Envoy is configured, deploy it to Azure Container Apps or AKS.

For Azure Container Apps, use:

az containerapp create \
  --name envoy-bff \
  --resource-group rg-ci \
  --image domain.azurecr.io/envoy-bff:latest \
  --cpu 0.5 --memory 1.0Gi \
  --ingress external \
  --target-port 10000

Step 4: Configure the Frontend to Use Envoy

Your frontend app should now send API requests to Envoy (e.g., https://bff.domain.net/api/orders), and Envoy will handle JWT validation and routing.

Step 5: Testing the Setup

  1. Login via Azure AD B2C and retrieve a JWT.

  2. Call an API through Envoy:

     curl -X GET "https://bff.domain.net/api/orders" \
       -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
    
  3. Ensure Envoy logs show successful authentication and request forwarding.

Using Envoy as a BFF in Azure with Azure AD B2C enables a secure, scalable, and centralised API gateway. It simplifies authentication, optimises API calls, and improves security. Whether running in Azure Container Apps, AKS, or VMs, this setup provides a powerful way to manage API traffic efficiently.

0
Subscribe to my newsletter

Read articles from Patrick Kearns directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Patrick Kearns
Patrick Kearns