Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Importante
A partir del 1 de mayo de 2025, Azure AD B2C ya no estará disponible para ser adquirido por nuevos clientes. Obtenga más información en nuestras preguntas más frecuentes.
En este artículo, aprenderá a agregar la autenticación de Azure Active Directory B2C (Azure AD B2C) en su propia aplicación web de Python. Permite que los usuarios inicien sesión, cierren sesión, actualicen el perfil y restablezcan la contraseña mediante flujos de usuario de Azure AD B2C. En este artículo se usa la Biblioteca de autenticación de Microsoft (MSAL) para Python para simplificar la adición de autenticación a la aplicación web de Python.
El objetivo de este artículo es sustituir la aplicación de ejemplo que usó en Configuración de la autenticación en una aplicación web de Python de ejemplo mediante Azure AD B2C con su propia aplicación de Python.
En este artículo se usa Python 3.9+ y Flask 2.1 para crear una aplicación web básica. Las vistas de la aplicación usan plantillas de Jinja2.
Prerrequisitos
- Complete los pasos descritos en Configuración de la autenticación en una aplicación web de Python de ejemplo mediante Azure AD B2C. Puede crear flujos de usuario de Azure AD B2C y registrar una aplicación web en Azure Portal.
- Instalación de Python 3.9 o posterior
- Visual Studio Code u otro editor de código
- Instalación de la extensión de Python para Visual Studio Code
Paso 1: Creación del proyecto de Python
En el sistema de archivos, cree una carpeta de proyecto para este tutorial, como
my-python-web-app.En el terminal, cambie el directorio a la carpeta de la aplicación de Python, como
cd my-python-web-app.Ejecute el comando siguiente para crear y activar un entorno virtual denominado
.venven función del intérprete actual.Actualice pip en el entorno virtual mediante la ejecución del comando siguiente en el terminal:
python -m pip install --upgrade pipPara habilitar las características de depuración de Flask, cambie Flask al entorno de desarrollo al modo
development. Para más información sobre la depuración de aplicaciones de Flask, consulte la documentación de Flask.Abra la carpeta del proyecto en VS Code ejecutando el comando
code ., o abriendo VS Code y seleccionando Archivo>Abrir carpeta.
Paso 2: Instalación de dependencias de aplicaciones
En la carpeta raíz de la aplicación web, cree el requirements.txt archivo. El archivo de requisitos enumera los paquetes que se van a instalar mediante pip install. Agregue el siguiente contenido al archivo requirements.txt:
Flask>=2
werkzeug>=2
flask-session>=0.3.2,<0.5
requests>=2,<3
msal>=1.7,<2
En el terminal, instale las dependencias mediante la ejecución de los siguientes comandos:
Paso 3: Compilación de componentes de la interfaz de usuario de la aplicación
Flask es un marco ligero de Python para aplicaciones web que proporciona los conceptos básicos para el enrutamiento de direcciones URL y la representación de páginas. Usa Jinja2 como motor de plantilla para representar el contenido de la aplicación. Para obtener más información, consulte la documentación del diseñador de plantillas. En esta sección, agregará las plantillas necesarias que proporcionan la funcionalidad básica de la aplicación web.
Paso 3.1 Crear una plantilla base
Una plantilla de página base de Flask contiene todas las partes compartidas de un conjunto de páginas, incluidas las referencias a archivos CSS, archivos de script, etc. Las plantillas base también definen una o más etiquetas de bloque que se espera que invaliden otras plantillas que extienden la base. Una etiqueta de bloque se delimita mediante {% block <name> %} y {% endblock %} en la plantilla base y en la plantilla extendida.
En la carpeta raíz de la aplicación web, cree la templates carpeta . En la carpeta templates, cree un archivo denominado base.htmly agregue el contenido siguiente:
<!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>
Paso 3.2 Crear las plantillas de aplicación web
Agregue las siguientes plantillas en la carpeta templates. Estas plantillas amplían la base.html plantilla:
index.html: la página principal de la aplicación web. Las plantillas usan la siguiente lógica: si un usuario no inicia sesión, representa el botón de inicio de sesión. Si un usuario inicia sesión, representa las declaraciones del token de acceso, proporciona un enlace para editar el perfil y llama a la API de Graph.
{% 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: muestra cómo llamar a una API REST.
{% 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: controla los errores de autenticación.
{% 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 %}
Paso 4: Configurar la aplicación web
En la carpeta raíz de la aplicación web, cree un archivo denominado app_config.py. Este archivo contiene información sobre el proveedor de identidades de Azure AD B2C. La aplicación web usa esta información para establecer una relación de confianza con Azure AD B2C, iniciar y cerrar la sesión de los usuarios, adquirir tokens y validarlos. Agregue el siguiente contenido al archivo:
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
Actualice el código anterior con la configuración del entorno de Azure AD B2C, tal como se explica en la sección Configuración de la aplicación web de ejemplo del artículo Configuración de la autenticación en una aplicación web de Python de ejemplo .
Paso 5: Agregar el código de la aplicación web
En esta sección, agregará las funciones de vista de Flask y los métodos de autenticación de la biblioteca MSAL. En la carpeta raíz del proyecto, agregue un archivo denominado app.py con el código siguiente:
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()
Paso 6: Ejecución de la aplicación web
En el Terminal, ejecute la aplicación escribiendo el siguiente comando, que ejecuta el servidor de desarrollo de Flask. El servidor de desarrollo busca app.py de forma predeterminada. A continuación, abra el explorador y vaya a la dirección URL de la aplicación web: http://localhost:5000.
[Opcional] Depura tu aplicación
La característica de depuración le ofrece la oportunidad de pausar un programa en ejecución en una línea de código determinada. Al pausar el programa, puede examinar variables, ejecutar código en el panel Consola de depuración y, de lo contrario, aprovechar las características descritas en Depuración. Para usar el depurador de Visual Studio Code, consulte la documentación de VS Code.
Para cambiar el nombre de host o el número de puerto, use la args matriz del launch.json archivo. En el ejemplo siguiente se muestra cómo configurar el nombre de host en localhost y el número de puerto en 5001. Tenga en cuenta que, si cambia el nombre de host o el número de puerto, debe actualizar el URI de redireccionamiento o la aplicación. Para obtener más información, consulte el paso Registrar una aplicación web .
{
// 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
}
]
}