Userimpersonate from backend to an Azure Blob Storage using MSAL's OnBehalfOf method

Syed Jafer KSyed Jafer K
2 min read

Assumptions:

  1. All access permissions are given.

Problem Faced:

We were developing an image based application, where frontend needs to load lot of images. Backend will serve the blob urls, so that from frontend; we will access these images from Azure Blob Storage using user impersonation.

But due to this, frontend needs to send 100s of API calls to the blob and user impersonate them. Due to this, browser will overflow with memory. As a side-effect, browser will be non-responsive and some hanging will occur.

In order to resolve this, we planned to shift the load to backend api. But i am struck with the following query,

Is it possible to user impersonate from backend client app to the blob storage. (i.e) Without any technical user how is it possible ?

Solution

As plan the backend flow will be,

From frontend, an authentication token will be passed in headers while accessing the backend api (FastApi). We can parse the token from the header,

from fastapi import FastAPI, Request

app = FastAPI()


@app.get("/")
async def root(request: Request):
    token = request.headers.get("authorization", "").replace("Bearer", "").replace(" ", "")
    .
    . # your logic
    .
    .
    return {"message": "Hello World"}

With the token from the frontend, we can perform onTheBehalfOf Authentication using MSAL library.

import msal, os
import requests

credential = msal.ConfidentialClientApplication(
                "<app-client-id>",
                authority="https://login.microsoftonline.com/<tenant-id>",
                client_credential="<app-secret>")

response = credential.acquire_token_on_behalf_of(
            user_assertion=token , # Token value from the auth header.
            scopes=["https://storage.azure.com/user_impersonation"]
        )

headers = {'Authorization': res['token_type'] + ' ' + res['access_token']}
url = '<blob-url>'
res = requests.get(url, headers=headers, timeout=5, verify=False)
base64content = str(base64.b64encode(res.content))

With the above snippet, we can get the url and convert them to base64 string.

So the final code will be,

from fastapi import FastAPI, Request
import os
import msal

app = FastAPI()

def get_blob_and_convert_to_base64(token, url):
    client_id = os.environ.get("CLIENT_ID")
    authority_url = os.environ.get("AUTHORITY_URL")
    client_secret = os.environ.get("CLIENT_SECRET")
    credential = msal.ConfidentialClientApplication(
                client_id=client_id,
                authority=authority_url,
                client_credential=client_secret)
    response = credential.acquire_token_on_behalf_of(
            user_assertion=token , # Token value from the auth header.
            scopes=["https://storage.azure.com/user_impersonation"]
        )
    headers = {'Authorization': response ['token_type'] + ' ' + response ['access_token']}
    res = requests.get(url, headers=headers, timeout=5, verify=False)
    base64content = str(base64.b64encode(res.content))
    return base64content

@app.get("/")
async def root(request: Request):
    token = request.headers.get("authorization", "").replace("Bearer", "").replace(" ", "")
    url = request.dict().get('url')
    base64res = get_blob_and_convert_to_base64(token, url) 
    return {url: base64res}
1
Subscribe to my newsletter

Read articles from Syed Jafer K directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Syed Jafer K
Syed Jafer K