¿Tu script de Python devuelve el mensaje «AADSTS90023: Invalid STS request» al intentar entrar en SharePoint Online? Este artículo explica las causas del error y presenta un procedimiento detallado para autenticarse con flujos modernos en Azure AD y resolver el problema.
Explicación general del problema
El código de error AADSTS90023 surge en Azure Active Directory cuando el servicio de tokens (STS) rechaza la petición de autenticación porque no reconoce el formato, el flujo o el contexto de seguridad empleados. Suele ocurrir al intentar usar el flujo ROPC (Resource Owner Password Credentials) desde un entorno donde:
- Se exige autenticación multifactor (MFA) o se aplican directivas de Conditional Access que bloquean ROPC.
- La organización ha deshabilitado explícitamente el uso de credenciales de usuario/contraseña sin interacción.
- El script se ejecuta en un servicio o VM sin un explorador que permita un desafío de inicio de sesión interactivo.
En consecuencia, la librería office365-rest-python-client
arroja una excepción similar a:
ValueError: Failed to authenticate: AADSTS90023: Invalid STS request
Por qué aparece AADSTS90023
AAD emite este error cuando rechaza parámetros críticos de la solicitud OAuth2. Las causas habituales incluyen:
- Flujo obsoleto ROPC. Requiere usuario y contraseña en texto claro y no admite desafíos MFA.
- Redirección mal formada. Algunos SDK construyen incorrectamente la URL de autorización.
- Parámetros inconsistentes:
scope
,resource
oclient_id
no coinciden con el registro de la app. - Bloqueo por políticas de la organización: Conditional Access, protecciones de riesgo, o directivas de Tenant.
Evaluación del flujo de autenticación
La función acquiretokenfor_user()
de office365-rest-python-client
invoca ROPC. Antes de buscar soluciones, confirma si tu tenant acepta ese flujo:
- Inicia sesión en el portal de Azure AD y abre Azure Active Directory > Security > Conditional Access.
- Revisa si existen políticas que incluyan la condición Grant con Require MFA o Block legacy authentication.
- Si la organización ha deshabilitado ROPC (valor predeterminado en muchos tenants nuevos), ningún script basado en usuario/contraseña funcionará.
Pasos recomendados para resolver el error
Paso | Detalle |
---|---|
Comprueba el flujo de autenticación | acquiretokenfor_user() usa ROPC. Verifica si tu tenant lo permite. Si está bloqueado por MFA o Conditional Access, ROPC fallará siempre. |
Usa MSAL o flujos modernos | Registra una aplicación en Azure AD y utiliza: • Device Code (servidores sin GUI). • Interactive Browser (abre ventana de inicio de sesión). • Client Credential (certificado o secreto) para ejecución sin usuario. |
Otorga permisos correctos | Añade Sites.Read.All, Sites.ReadWrite.All o Sites.FullControl.All como permisos delegados o de aplicación y otorga admin consent. |
Actualiza la librería | Instala la versión más reciente de office365-rest-python-client , que incluye soporte MSAL desde la v 2.6.x. |
Ejemplo mínimo con MSAL | Explora el bloque de código incluido más abajo para autenticación con Client Credential. |
Revisa la URL y permisos de SharePoint | Asegúrate de que la ruta (sites/{nombreSitio} o teams/{equipo} ) existe y la identidad tiene acceso. |
Flujos de autenticación modernos recomendados
Device Code
Ventajoso para servidores Linux sin entorno gráfico. El usuario copia un código mostrado en consola y lo introduce en microsoft.com/devicelogin con MFA.
import msal, requests
app = msal.PublicClientApplication(
"APP\_ID",
authority="[https://login.microsoftonline.com/TENANT\ID](https://login.microsoftonline.com/TENANTID)"
)
flow = app.initiate\device\flow(scopes=\["[https://graph.microsoft.com/Sites.ReadWrite.All](https://graph.microsoft.com/Sites.ReadWrite.All)"])
print(flow\["message"]) # Instrucciones para el usuario
result = app.acquire\token\by\device\flow(flow)
token = result\["access\_token"]
Interactive Browser
Ideal para máquinas de desarrollo. Abre un navegador donde se completan MFA y consentimiento.
result = app.acquiretokeninteractive(
scopes=["https://graph.microsoft.com/Sites.ReadWrite.All"]
)
Client Credential
Para procesos headless o automatizaciones sin usuario presente. Requiere un certificado o secreto de aplicación.
import msal, os, requests
app = msal.ConfidentialClientApplication(
"APP\_ID",
authority="[https://login.microsoftonline.com/TENANT\ID](https://login.microsoftonline.com/TENANTID)",
client\credential=os.getenv("CLIENT\SECRET")
)
scopes = \["[https://graph.microsoft.com/.default](https://graph.microsoft.com/.default)"]
result = app.acquire\token\for\_client(scopes=scopes)
token = result\["access\_token"]
headers = {"Authorization": f"Bearer {token}"}
endpoint = "[https://graph.microsoft.com/v1.0/sites/{site-id}/drive/root/children](https://graph.microsoft.com/v1.0/sites/{site-id}/drive/root/children)"
print(requests.get(endpoint, headers=headers).json())
Registro de la aplicación en Azure AD
Sigue este procedimiento una sola vez:
- Entra en el portal de Azure AD y selecciona App registrations ➔ New registration.
- Asigna un nombre descriptivo y deja la opción Supported account types en Single‑tenant, salvo que requieras multi‑tenant.
- Guarda el Application (client) ID y el Directory (tenant) ID.
- En Certificates & secrets genera un secreto (validez 6‑24 meses) o sube un certificado .pem/pfx.
- Abre API permissions ➔ Add a permission ➔ Microsoft Graph. Elige Application permissions y selecciona: Sites.Read.All o superior. Pulsa Grant admin consent.
Permisos necesarios en SharePoint y Graph
Escenario | Permisos mínimo necesarios |
---|---|
Lectura de documentos | Sites.Read.All |
Carga/edición de archivos | Sites.ReadWrite.All |
Administrar sitio completo | Sites.FullControl.All |
Estos permisos se aplican a nivel de Microsoft Graph; el usuario o la aplicación también deben tener permisos en el propio sitio de SharePoint.
Actualización de la librería office365‑rest‑python‑client
Si prefieres mantenerte en este SDK, actualízalo para aprovechar MSAL interno:
pip install --upgrade office365-rest-python-client
A partir de la versión 2.6.x incluye la clase MSALClientCredential
:
from office365.runtime.auth.client_credential import ClientCredential
from office365.sharepoint.client_context import ClientContext
site\_url = "[https://contoso.sharepoint.com/sites/proyectos](https://contoso.sharepoint.com/sites/proyectos)"
client\id = "APP\ID"
client\secret = "CLIENT\SECRET"
ctx = ClientContext(site\url).with\credentials(ClientCredential(client\id, client\secret))
web = ctx.web.get().execute\_query()
print("Título del sitio:", web.properties\["Title"])
Ejemplo práctico completo con Graph
El siguiente script usa Client Credential y consulta los 100 primeros elementos en la biblioteca raíz de documentos:
#!/usr/bin/env python3
import msal, requests, os, sys, json
TENANT\_ID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
CLIENT\_ID = "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"
CLIENT\SECRET = os.getenv("SP\CLIENT\_SECRET")
SITE\_ID = "contoso.sharepoint.com,1234abcd-5678-90ef-ghij-klmnopqrstuv,abcd1234-5678-90ef-abcd-12345678efgh"
def get\app\token():
app = msal.ConfidentialClientApplication(
CLIENT\_ID,
authority=f"[https://login.microsoftonline.com/{TENANT\ID}](https://login.microsoftonline.com/{TENANTID})",
client\credential=CLIENT\SECRET
)
result = app.acquire\token\for\_client(scopes=\["[https://graph.microsoft.com/.default](https://graph.microsoft.com/.default)"])
if "access\_token" in result:
return result\["access\_token"]
print("Error al obtener token:", result.get("error\_description"))
sys.exit(1)
def list\root\items(token):
endpoint = f"[https://graph.microsoft.com/v1.0/sites/{SITE\ID}/drive/root/children?\$top=100](https://graph.microsoft.com/v1.0/sites/{SITEID}/drive/root/children?$top=100)"
headers = {"Authorization": f"Bearer {token}"}
r = requests.get(endpoint, headers=headers, timeout=15)
r.raise\for\status()
return \[item\["name"] for item in r.json().get("value", \[])]
if name == "main":
access\token = get\app\_token()
for name in list\root\items(access\_token):
print(name)
Comprobaciones adicionales
- Logs de inicio de sesión: en Azure AD ➔ Sign‑in logs filtra por la app. Asegúrate de que el resultado es Success. Un estado Failure con mensaje Invalid STS request confirma un problema de flujo.
- Nombre del sitio: valida que no existan redirecciones teams/. Usa la URL exacta mostrada en la barra del navegador cuando abres el sitio.
- Rol de la cuenta de servicio: una app con permisos de aplicación ignora licencias de usuario, pero SharePoint debe permitir su entrada. Concede acceso mediante App‑only access o añádela como miembro del sitio.
- Proxies y firewalls: puertos 80 y 443 deben estar abiertos hacia login.microsoftonline.com y graph.microsoft.com.
Resumen y conclusiones
El error AADSTS90023 se debe casi siempre a que el flujo ROPC ya no está permitido en entornos que exigen MFA y directivas modernas de seguridad. La transición a MSAL con flujos actualizados —Device Code, Interactive Browser o Client Credential— junto con el registro correcto de la aplicación y la concesión de permisos adecuados, resuelve el problema en la mayoría de los casos. Una vez configurado, tu script de Python podrá conectarse a SharePoint Online de forma estable, segura y acorde con las prácticas recomendadas de Azure AD.