Cómo solucionar el error AADSTS90023 al conectar Python con SharePoint Online

¿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.

Índice

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 o client_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:

  1. Inicia sesión en el portal de Azure AD y abre Azure Active Directory > Security > Conditional Access.
  2. Revisa si existen políticas que incluyan la condición Grant con Require MFA o Block legacy authentication.
  3. 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

PasoDetalle
Comprueba el flujo de autenticaciónacquiretokenfor_user() usa ROPC. Verifica si tu tenant lo permite. Si está bloqueado por MFA o Conditional Access, ROPC fallará siempre.
Usa MSAL o flujos modernosRegistra 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 correctosAñ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íaInstala 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 MSALExplora el bloque de código incluido más abajo para autenticación con Client Credential.
Revisa la URL y permisos de SharePointAsegú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:

  1. Entra en el portal de Azure AD y selecciona App registrationsNew registration.
  2. Asigna un nombre descriptivo y deja la opción Supported account types en Single‑tenant, salvo que requieras multi‑tenant.
  3. Guarda el Application (client) ID y el Directory (tenant) ID.
  4. En Certificates & secrets genera un secreto (validez 6‑24 meses) o sube un certificado .pem/pfx.
  5. Abre API permissionsAdd a permissionMicrosoft Graph. Elige Application permissions y selecciona: Sites.Read.All o superior. Pulsa Grant admin consent.

Permisos necesarios en SharePoint y Graph

EscenarioPermisos mínimo necesarios
Lectura de documentosSites.Read.All
Carga/edición de archivosSites.ReadWrite.All
Administrar sitio completoSites.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.

Índice