Chris W
Chris W

Reputation: 53

How to use Azure App Service Auth in web app to control blob access

Hoping someone can help me with some gaps in my understanding of how to use the azure app service authentication component. I'm trying to write a simple web app (in this case python/flask) that allows a user to login, chose a container and upload a blob. The containers available should be constrained by the signed in users access

What I currently have

My web app is published to Azure and running. The BlobServiceClient is being constructed using DefaultAzureCredential() and so to do this I've enabled system assigned identity in the web app and granted it blob access. At this point, only users assigned to the enterprise app can sign in (great) but the list of containers available and the upload itself are being performed as the managed identity (not what I'd like)

token_credential = DefaultAzureCredential()
blob_service_client = BlobServiceClient(account_url, credential=token_credential)

def list_containers_prefix(blob_service_client: BlobServiceClient):
    container_name_list = []
    containers = blob_service_client.list_containers(name_starts_with='project')
    for container in containers:
        container_name = container['name']
        container_name_list.append(container_name)
    return container_name_list

I'm confused as to what I need to do to get to my end goal. I've seen a couple of roughly similar scenarios (function apps) using two app registrations and user_impersonation but I didn't fully understand the reasoning or logic. I've also wondered whether I can use AppServiceAuthSession cookie or /.auth/me to build the credential for the blob client but before I figure out how to do that via flask Id like some help validating the correct approach. Maybe I should just use standard MSAL libraries instead? I was hoping the Azure auth service would make things easier.

Any guidance much appreciated

Thanks

Upvotes: 0

Views: 100

Answers (1)

Dasari Kamali
Dasari Kamali

Reputation: 3528

I created a sample Flask web app using the CustomTokenCredential to allow a User to log in, choose a container, and upload a blob. It worked fine for me both locally and on an Azure Web App.

app.py :

from flask import Flask, jsonify, request, redirect, url_for, session
import msal
from azure_blob import upload_blob

app = Flask(__name__)
app.secret_key = "<secretkey>" 

redirect_uri = "http://localhost:5000/gettoken"

client_id = '<clientID>' 
client_secret = '<clientSecret>'  
tenant_id = '<tenantID>' 
authority = f"https://login.microsoftonline.com/{tenant_id}"

app_msal = msal.ConfidentialClientApplication(
    client_id,
    authority=authority,
    client_credential=client_secret
)

@app.route('/')
def home():
    return 'Welcome to the Flask Azure Blob Storage API! <a href="/login">Login with Azure AD</a>'

@app.route('/login')
def login():
    auth_url = app_msal.get_authorization_request_url(
        scopes=["https://storage.azure.com/.default"],
        redirect_uri=redirect_uri
    )
    return redirect(auth_url)

@app.route('/gettoken')
def get_token():
    code = request.args.get('code')
    if code:
        result = app_msal.acquire_token_by_authorization_code(
            code,
            scopes=["https://storage.azure.com/.default"],
            redirect_uri=redirect_uri
        )
        if "access_token" in result:
            session['token'] = result['access_token']
            return redirect(url_for('upload'))  
    return "Authentication failed"

@app.route('/upload', methods=['GET', 'POST'])
def upload():
    if request.method == 'GET':
        return '''
            <h1>Upload Blob</h1>
            <form method="POST" enctype="multipart/form-data">
                <label for="container_name">Container Name:</label>
                <input type="text" name="container_name" required><br><br>
                <label for="file">Select File:</label>
                <input type="file" name="file" required><br><br>
                <input type="submit" value="Upload">
            </form>
        '''
    
    if request.method == 'POST':
        try:
            token = session.get('token')
            if not token:
                return redirect(url_for('login'))
            container_name = request.form.get('container_name')
            file = request.files.get('file')

            if container_name and file:
                upload_blob(container_name, file, token) 
                return jsonify({"message": "Blob uploaded successfully"})
            else:
                return jsonify({"error": "Container name or file missing"}), 400
        except Exception as e:
            return jsonify({'error': str(e)}), 500

if __name__ == "__main__":
    app.run(debug=True)

azure_blob.py :

from azure.core.credentials import AccessToken
from azure.storage.blob import BlobServiceClient

def upload_blob(container_name, file, token):
    '''Upload a blob to the selected container'''

    storage_account_name = "kamstrweb"  
    class CustomTokenCredential:
        def __init__(self, token):
            self.token = token
        
        def get_token(self, *scopes):
            return AccessToken(self.token, expires_on=9999999999) 

    token_credential = CustomTokenCredential(token)
    blob_service_client = BlobServiceClient(
        account_url=f"https://{storage_account_name}.blob.core.windows.net", 
        credential=token_credential
    )

    container_client = blob_service_client.get_container_client(container_name)
    blob_client = container_client.get_blob_client(file.filename)
    blob_client.upload_blob(file.read(), overwrite=True)

requirements.txt :

flask
msal
azure-storage-blob

I have added the Storage Blob Data Contributor role to the User in the Azure Storage.

enter image description here

I have added the below URIs in the Service principle's Authentication under Web.

http://localhost:5000/gettoken
https://xxxxxxxxxxx.canadacentral-01.azurewebsites.net/gettoken

enter image description here

Terminal Output :

enter image description here

Local Output :

I got the below output in the browser.

enter image description here

I successfully signed in to the application by clicking on Login with Azure AD as shown below.

enter image description here

I uploaded a file to Azure Blob Storage by selecting the container where I wanted it to be stored.

enter image description here

enter image description here

The blob uploaded successfully in the Azure storage.

enter image description here

Note : Make sure to replace the below Azure Web App URI in the redirect_uri in the app.py code.

redirect_uri = "https://xxxxxxxxxxx.canadacentral-01.azurewebsites.net/gettoken"

Azure Web App Output :

I got below output in the Azure Web App.

enter image description here

I successfully signed in and upload a file to Azure Blob storage.

enter image description here

enter image description here

The blob uploaded successfully in the Azure storage.

enter image description here

Upvotes: 1

Related Questions