Freigeben über


Aktivieren der Authentifizierung in Ihrer eigenen Python-Webanwendung mit Azure Active Directory B2C

Von Bedeutung

Ab dem 1. Mai 2025 steht Azure AD B2C nicht mehr für neue Kunden zur Verfügung. Weitere Informationen finden Sie in unseren HÄUFIG gestellten Fragen.

In diesem Artikel erfahren Sie, wie Sie azure Active Directory B2C (Azure AD B2C)-Authentifizierung in Ihrer eigenen Python-Webanwendung hinzufügen. Mit Azure AD B2C-Benutzerflüssen können Sie sich anmelden, abmelden, Profil aktualisieren und Kennwort zurücksetzen. In diesem Artikel wird die Microsoft Authentication Library (MSAL) für Python verwendet, um das Hinzufügen der Authentifizierung zu Ihrer Python-Webanwendung zu vereinfachen.

Ziel dieses Artikels ist es, die Beispielanwendung zu ersetzen, die Sie in der Konfiguration der Authentifizierung in einer Python-Beispielwebanwendung mithilfe von Azure AD B2C mit Ihrer eigenen Python-Anwendung verwendet haben.

In diesem Artikel wird Python 3.9+ und Flask 2.1 verwendet, um eine einfache Web-App zu erstellen. Die Ansichten der Anwendung verwenden Jinja2-Vorlagen.

Voraussetzungen

Schritt 1: Erstellen des Python-Projekts

  1. Erstellen Sie in Ihrem Dateisystem einen Projektordner für dieses Lernprogramm, wie my-python-web-app.

  2. Verwenden Sie Ihr Terminal, um das Verzeichnis in Ihren Python-App-Ordner zu wechseln, beispielsweise cd my-python-web-app.

  3. Führen Sie den folgenden Befehl aus, um eine virtuelle Umgebung zu erstellen und zu aktivieren, die auf Ihrem aktuellen Interpreter basiert .venv .

    sudo apt-get install python3-venv  # If needed
    python3 -m venv .venv
    source .venv/bin/activate
    
  4. Aktualisieren Sie pip in der virtuellen Umgebung, indem Sie den folgenden Befehl im Terminal ausführen:

    python -m pip install --upgrade pip
    
  5. Um die Flask-Debugfeatures zu aktivieren, wechseln Sie Flask in den development Modus der Entwicklungsumgebung. Weitere Informationen zum Debuggen von Flask-Apps finden Sie in der Flask-Dokumentation.

    export FLASK_ENV=development
    
  6. Öffnen Sie den Projektordner in VS Code, indem Sie den code . Befehl ausführen, oder öffnen Sie VS Code, und wählen Sie den Ordner " Datei>öffnen" aus.

Schritt 2: Installieren von App-Abhängigkeiten

Erstellen Sie unter Ihrem Web App-Stammordner die requirements.txt Datei. Die Anforderungsdatei listet die Pakete auf, die mithilfe der Pip-Installation installiert werden sollen. Fügen Sie der datei requirements.txt den folgenden Inhalt hinzu:

Flask>=2
werkzeug>=2

flask-session>=0.3.2,<0.5
requests>=2,<3
msal>=1.7,<2

Installieren Sie in Ihrem Terminal die Abhängigkeiten, indem Sie die folgenden Befehle ausführen:

python -m pip install -r requirements.txt

Schritt 3: Erstellen von App-UI-Komponenten

Flask ist ein einfaches Python-Framework für Webanwendungen, das die Grundlagen für das URL-Routing und das Seitenrendering bereitstellt. Es verwendet Jinja2 als Vorlagenmodul, um den Inhalt Ihrer App zu rendern. Weitere Informationen finden Sie in der Vorlagen-Designerdokumentation. In diesem Abschnitt fügen Sie die erforderlichen Vorlagen hinzu, die die grundlegenden Funktionen Ihrer Web-App bereitstellen.

Schritt 3.1 Erstellen einer Basisvorlage

Eine Basisseitenvorlage in Flask enthält alle freigegebenen Teile einer Reihe von Seiten, einschließlich Verweise auf CSS-Dateien, Skriptdateien usw. Basisvorlagen definieren auch ein oder mehrere Blocktags, die andere Vorlagen, die die Basis erweitern, voraussichtlich außer Kraft setzen. Ein Blocktag wird sowohl durch {% block <name> %} und {% endblock %} in der Basisvorlage als auch in der erweiterten Vorlage abgegrenzt.

Erstellen Sie im Stammordner Ihrer Web-App den templates Ordner. Erstellen Sie im Vorlagenordner eine Datei mit dem Namen base.html, und fügen Sie dann den folgenden Inhalt hinzu:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    {% block metadata %}{% endblock %}

    <title>{% block title %}{% endblock %}</title>
    <!-- Bootstrap CSS file reference -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">
</head>

<body>
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <div class="container-fluid">
            <a class="navbar-brand" href="{{ url_for('index')}}">Python Flask demo</a>
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse"
                data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false"
                aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navbarSupportedContent">
                <ul class="navbar-nav me-auto mb-2 mb-lg-0">
                    <li class="nav-item">
                        <a class="nav-link active" aria-current="page" href="{{ url_for('index')}}">Home</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="{{ url_for('graphcall')}}">Graph API</a>
                    </li>
                </ul>
            </div>
        </div>
    </nav>

    <div class="container body-content">
        <br />
        {% block content %}
        {% endblock %}

        <hr />
        <footer>
            <p>Powered by MSAL Python {{ version }}</p>
        </footer>
    </div>
</body>

</html>

Schritt 3.2 Erstellen der Web-App-Vorlagen

Fügen Sie die folgenden Vorlagen unter dem Vorlagenordner hinzu. Diese Vorlagen erweitern die base.html Vorlage:

  • index.html: die Startseite der Web-App. Die Vorlagen verwenden die folgende Logik: Wenn sich ein Benutzer nicht anmeldet, wird die Anmeldeschaltfläche gerendert. Wenn sich ein Benutzer anmeldet, werden die Ansprüche des Zugriffstokens dargestellt, ein Link zur Bearbeitung des Profils bereitgestellt und eine Graph-API aufgerufen.

    {% extends "base.html" %}
    {% block title %}Home{% endblock %}
    {% block content %}
    
    <h1>Microsoft Identity Python Web App</h1>
    
    {% if user %}
    <h2>Claims:</h2>
    <pre>{{ user |tojson(indent=4) }}</pre>
    
    
    {% if config.get("ENDPOINT") %}
    <li><a href='/graphcall'>Call Microsoft Graph API</a></li>
    {% endif %}
    
    {% if config.get("B2C_PROFILE_AUTHORITY") %}
    <li><a href='{{_build_auth_code_flow(authority=config["B2C_PROFILE_AUTHORITY"])["auth_uri"]}}'>Edit Profile</a></li>
    {% endif %}
    
    <li><a href="/logout">Logout</a></li>
    
    {% else %}
    <li><a href='{{ auth_url }}'>Sign In</a></li>
    {% endif %}
    
    {% endblock %}
    
  • graph.html: Veranschaulicht, wie eine REST-API aufgerufen wird.

    {% extends "base.html" %}
    {% block title %}Graph API{% endblock %}
    {% block content %}
    <a href="javascript:window.history.go(-1)">Back</a>
    <!-- Displayed on top of a potentially large JSON response, so it will remain visible -->
    <h1>Graph API Call Result</h1>
    <pre>{{ result |tojson(indent=4) }}</pre> <!-- Just a generic json viewer -->
    {% endblock %}
    
  • auth_error.html: Behandelt Authentifizierungsfehler.

    {% extends "base.html" %}
    {% block title%}Error{% endblock%}
    
    {% block metadata %}
    {% if config.get("B2C_RESET_PASSWORD_AUTHORITY") and "AADB2C90118" in result.get("error_description") %}
    <!-- See also https://learn.microsoft.com/azure/active-directory-b2c/active-directory-b2c-reference-policies#linking-user-flows -->
    <meta http-equiv="refresh"
      content='0;{{_build_auth_code_flow(authority=config["B2C_RESET_PASSWORD_AUTHORITY"])["auth_uri"]}}'>
    {% endif %}
    {% endblock %}
    
    {% block content %}
    <h2>Login Failure</h2>
    <dl>
      <dt>{{ result.get("error") }}</dt>
      <dd>{{ result.get("error_description") }}</dd>
    </dl>
    
    <a href="{{ url_for('index') }}">Homepage</a>
    {% endblock %}
    

Schritt 4: Konfigurieren Ihrer Web-App

Erstellen Sie im Stammordner Ihrer Web-App eine Datei mit dem Namen app_config.py. Diese Datei enthält Informationen zu Ihrem Azure AD B2C-Identitätsanbieter. Die Web-App verwendet diese Informationen, um eine Vertrauensstellung mit Azure AD B2C herzustellen, Benutzer anzumelden und abzumelden, Token zu erwerben und zu validieren. Fügen Sie der Datei den folgenden Inhalt hinzu:

import os

b2c_tenant = "fabrikamb2c"
signupsignin_user_flow = "B2C_1_signupsignin1"
editprofile_user_flow = "B2C_1_profileediting1"

resetpassword_user_flow = "B2C_1_passwordreset1"  # Note: Legacy setting.

authority_template = "https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/{user_flow}"

CLIENT_ID = "Enter_the_Application_Id_here" # Application (client) ID of app registration

CLIENT_SECRET = "Enter_the_Client_Secret_Here" # Application secret.

AUTHORITY = authority_template.format(
    tenant=b2c_tenant, user_flow=signupsignin_user_flow)
B2C_PROFILE_AUTHORITY = authority_template.format(
    tenant=b2c_tenant, user_flow=editprofile_user_flow)

B2C_RESET_PASSWORD_AUTHORITY = authority_template.format(
    tenant=b2c_tenant, user_flow=resetpassword_user_flow)

REDIRECT_PATH = "/getAToken"

# This is the API resource endpoint
ENDPOINT = '' # Application ID URI of app registration in Azure portal

# These are the scopes you've exposed in the web API app registration in the Azure portal
SCOPE = []  # Example with two exposed scopes: ["demo.read", "demo.write"]

SESSION_TYPE = "filesystem"  # Specifies the token cache should be stored in server-side session

Aktualisieren Sie den obigen Code mit Ihren Azure AD B2C-Umgebungseinstellungen, wie im Abschnitt Konfigurieren der Beispiel-Webanwendung des Artikels Authentifizierung in einer Beispiel-Python-Webanwendung konfigurieren erläutert.

Schritt 5: Hinzufügen des Web-App-Codes

In diesem Abschnitt fügen Sie die Flask-Ansichtsfunktionen und die MSAL-Bibliotheksauthentifizierungsmethoden hinzu. Fügen Sie unter dem Stammordner Ihres Projekts eine Datei app.py mit dem folgenden Code hinzu:

import uuid
import requests
from flask import Flask, render_template, session, request, redirect, url_for
from flask_session import Session  # https://pythonhosted.org/Flask-Session
import msal
import app_config


app = Flask(__name__)
app.config.from_object(app_config)
Session(app)

# This section is needed for url_for("foo", _external=True) to automatically
# generate http scheme when this sample is running on localhost,
# and to generate https scheme when it is deployed behind reversed proxy.
# See also https://flask.palletsprojects.com/en/1.0.x/deploying/wsgi-standalone/#proxy-setups
from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1)


@app.route("/anonymous")
def anonymous():
    return "anonymous page"

@app.route("/")
def index():
    #if not session.get("user"):
    #    return redirect(url_for("login"))

    if not session.get("user"):
        session["flow"] = _build_auth_code_flow(scopes=app_config.SCOPE)
        return render_template('index.html', auth_url=session["flow"]["auth_uri"], version=msal.__version__)
    else:
        return render_template('index.html', user=session["user"], version=msal.__version__)

@app.route("/login")
def login():
    # Technically we could use empty list [] as scopes to do just sign in,
    # here we choose to also collect end user consent upfront
    session["flow"] = _build_auth_code_flow(scopes=app_config.SCOPE)
    return render_template("login.html", auth_url=session["flow"]["auth_uri"], version=msal.__version__)

@app.route(app_config.REDIRECT_PATH)  # Its absolute URL must match your app's redirect_uri set in AAD
def authorized():
    try:
        cache = _load_cache()
        result = _build_msal_app(cache=cache).acquire_token_by_auth_code_flow(
            session.get("flow", {}), request.args)
        if "error" in result:
            return render_template("auth_error.html", result=result)
        session["user"] = result.get("id_token_claims")
        _save_cache(cache)
    except ValueError:  # Usually caused by CSRF
        pass  # Simply ignore them
    return redirect(url_for("index"))

@app.route("/logout")
def logout():
    session.clear()  # Wipe out user and its token cache from session
    return redirect(  # Also logout from your tenant's web session
        app_config.AUTHORITY + "/oauth2/v2.0/logout" +
        "?post_logout_redirect_uri=" + url_for("index", _external=True))

@app.route("/graphcall")
def graphcall():
    token = _get_token_from_cache(app_config.SCOPE)
    if not token:
        return redirect(url_for("login"))
    graph_data = requests.get(  # Use token to call downstream service
        app_config.ENDPOINT,
        headers={'Authorization': 'Bearer ' + token['access_token']},
        ).json()
    return render_template('graph.html', result=graph_data)


def _load_cache():
    cache = msal.SerializableTokenCache()
    if session.get("token_cache"):
        cache.deserialize(session["token_cache"])
    return cache

def _save_cache(cache):
    if cache.has_state_changed:
        session["token_cache"] = cache.serialize()

def _build_msal_app(cache=None, authority=None):
    return msal.ConfidentialClientApplication(
        app_config.CLIENT_ID, authority=authority or app_config.AUTHORITY,
        client_credential=app_config.CLIENT_SECRET, token_cache=cache)

def _build_auth_code_flow(authority=None, scopes=None):
    return _build_msal_app(authority=authority).initiate_auth_code_flow(
        scopes or [],
        redirect_uri=url_for("authorized", _external=True))

def _get_token_from_cache(scope=None):
    cache = _load_cache()  # This web app maintains one cache per session
    cca = _build_msal_app(cache=cache)
    accounts = cca.get_accounts()
    if accounts:  # So all account(s) belong to the current signed-in user
        result = cca.acquire_token_silent(scope, account=accounts[0])
        _save_cache(cache)
        return result

app.jinja_env.globals.update(_build_auth_code_flow=_build_auth_code_flow)  # Used in template

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

Schritt 6: Ausführen ihrer Web-App

Führen Sie die App im Terminal aus, indem Sie den folgenden Befehl eingeben, der den Flask-Entwicklungsserver ausführt. Der Entwicklungsserver sucht standardmäßig nach app.py. Öffnen Sie dann Ihren Browser, und navigieren Sie zur Web-App-URL: http://localhost:5000.

python -m flask run --host localhost --port 5000

[Optional] Debuggen Ihrer App

Das Debugging-Feature bietet Ihnen die Möglichkeit, ein ausgeführtes Programm in einer bestimmten Codezeile anzuhalten. Wenn Sie das Programm anhalten, können Sie Variablen untersuchen, Code im Bereich "Debugkonsole" ausführen und andernfalls die im Debugging beschriebenen Features nutzen. Informationen zur Verwendung des Visual Studio Code-Debuggers finden Sie in der VS Code-Dokumentation.

Verwenden Sie das Array der args Datei, um den Hostnamen und/oder die launch.json Portnummer zu ändern. Im folgenden Beispiel wird veranschaulicht, wie Sie den Hostnamen für localhost und die Portnummer konfigurieren.5001 Wenn Sie den Hostnamen oder die Portnummer ändern, müssen Sie den Umleitungs-URI oder Ihre Anwendung aktualisieren. Weitere Informationen finden Sie im Schritt "Registrieren einer Webanwendung" .

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: Flask",
            "type": "python",
            "request": "launch",
            "module": "flask",
            "env": {
                "FLASK_APP": "app.py",
                "FLASK_ENV": "development"
            },
            "args": [
                "run",
                "--host=localhost",
                "--port=5001"
            ],
            "jinja": true,
            "justMyCode": true
        }
    ]
}