Reputation: 53
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
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.
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
Terminal Output :
Local Output :
I got the below output in the browser.
I successfully signed in to the application by clicking on Login with Azure AD
as shown below.
I uploaded a file to Azure Blob Storage by selecting the container where I wanted it to be stored.
The blob uploaded successfully in the Azure storage.
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.
I successfully signed in and upload a file to Azure Blob storage.
The blob uploaded successfully in the Azure storage.
Upvotes: 1