La respuesta corta

Cuando el departamento de IT pide "SSO con SAML" en una app móvil, lo que hay que construir casi nunca es SAML. SAML es un protocolo de navegador: se apoya en redirecciones y en cookies de sesión web, y no encaja en una app nativa. Lo que se implementa es OAuth 2.0 con OpenID Connect y PKCE, y si la organización tiene un IdP que habla SAML, la federación ocurre por detrás, entre proveedores de identidad, sin que la app se entere.

La segunda decisión es más importante que la primera: dónde se pinta la pantalla de login. Si la metes en un webview dentro de tu app, incumples el estándar, te quedas sin SSO y te ganas un "no" en la revisión de seguridad. Y con Microsoft Entra ID, en cuanto haya una política de Acceso Condicional, hace falta además un broker instalado en el dispositivo.

SAML no viaja en una app nativa

SAML 2.0 fue diseñado para el navegador: el flujo depende de POSTs de formulario entre el proveedor de servicio y el de identidad, y de una cookie de sesión que el navegador conserva. Una app nativa no tiene ni lo uno ni lo otro de forma natural.

El patrón correcto es de dos capas:

  1. La app habla OIDC/OAuth 2.0 contra el proveedor de identidad, y recibe tokens.
  2. El proveedor de identidad habla SAML con el IdP corporativo del cliente, si es lo que hay.

Así la app no cambia cuando el cliente migra de ADFS a Entra ID, o de Okta a otro. Es la diferencia entre integrar una vez y reintegrar con cada cliente.

Lo que exige el estándar, literalmente

RFC 8252 —OAuth 2.0 for Native Apps— no deja margen de interpretación:

"native apps MUST NOT use embedded user-agents to perform authorization requests"

Y a continuación:

"native apps MUST use an external user-agent to perform OAuth authorization requests"

La razón que da el propio documento no es teórica: la app que aloja el webview puede leer la credencial completa del usuario, no solo la concesión de OAuth, y puede registrar cada pulsación del formulario de login. Para un revisor de seguridad, un webview de login es exactamente eso — un keylogger con permiso.

En la práctica:

  • iOS: ASWebAuthenticationSession (el sucesor de SFAuthenticationSession), o SFSafariViewController.
  • Android: Custom Tabs, con el navegador por defecto como alternativa.

Y PKCE no es opcional:

"Public native app clients MUST implement the Proof Key for Code Exchange (PKCE) extension to OAuth, and authorization servers MUST support PKCE for such clients"

Sin PKCE, otra app del mismo dispositivo puede interceptar el código de autorización. Es el ataque que PKCE existe para cerrar.

Redirect URIs: solo hay tres formas legales

El estándar admite exactamente tres, y elegir mal es de los errores que se descubren tarde:

TipoEjemploCuándo
Esquema privadocom.empresa.app:/oauth2redirect/proveedorEl más común. Debe ir en notación de dominio invertido
https reclamadohttps://app.empresa.com/oauth2redirect/…Preferido en iOS 9+ y Android 6+: el sistema operativo verifica la propiedad del dominio
Loopbackhttp://127.0.0.1:{puerto}/Escritorio, no móvil

El https reclamado es el que conviene defender ante seguridad: un esquema privado lo puede registrar cualquier otra app del dispositivo; un dominio verificado, no. A cambio exige publicar el fichero de asociación en el dominio del cliente, y eso es una conversación con su equipo de sistemas que hay que abrir pronto.

Y del lado del servidor, el estándar es igual de tajante: el servidor de autorización debe exigir el redirect URI completo registrado, incluida la ruta, y rechazar cualquiera que no coincida exactamente. También no debe asumir que una app nativa puede guardar un secreto. Si tu integración depende de un client_secret embebido en el binario, no tienes un cliente confidencial: tienes un secreto publicado.

Entra ID: el broker es lo que casi nadie presupuesta

Con Microsoft Entra ID y MSAL hay dos caminos: a través de un broker o a través del navegador del sistema.

El broker en Android es un componente que viene dentro de Microsoft Authenticator, Intune Company Portal o Link to Windows. Y aquí está lo que cambia el alcance del proyecto: el broker es lo que permite cumplir Acceso Condicional, que es justo lo que va a pedir el CISO del cliente. En concreto:

  • SSO a nivel de dispositivo, no solo entre tus apps.
  • Acceso Condicional con protección de aplicaciones de Intune.
  • Registro de dispositivo (Workplace Join).
  • Gestión de dispositivos móviles.

Si el recurso está protegido por una política que exige dispositivo registrado, inscrito en MDM o con protección de apps, y no hay broker instalado, MSAL interrumpe el primer acquireToken() interactivo y guía al usuario a instalarlo. Es decir: el primer día de piloto, tus usuarios se encuentran instalando una segunda app. Eso se avisa antes, no durante.

Integrar con broker en Android tiene requisitos concretos. El redirect URI cambia de forma:

msauth://<nombre.del.paquete>/<firma-en-base64url>

La firma se saca de la clave de firmado con keytool, y se registra en el portal de Entra. Y hay que declararlo en la configuración de MSAL:

"redirect_uri": "<tu-redirect-de-broker>",
"broker_redirect_uri_registered": true

Dos avisos operativos que ahorran una tarde de depuración:

  • Si aparece MsalClientException con código BROKER_BIND_FAILURE, o el usuario tiene que desactivar la optimización de batería de Authenticator y Company Portal, o hay que concederle el permiso READ_CONTACTS — MSAL usa el servicio vinculado primero y AccountManager como alternativa.
  • Para verificar que el broker está funcionando de verdad: en los ajustes del dispositivo debe aparecer una cuenta nueva de tipo "Work account". Si no está, no estás pasando por el broker aunque parezca que sí.

Instalar o desinstalar un broker tiene efectos colaterales: al instalarlo, las peticiones interactivas pasan a él y el estado de SSO previo de MSAL no se hereda; al desinstalar el activo, se van la cuenta y los tokens del dispositivo.

La contradicción que conviene entender

Aquí hay un matiz que se malinterpreta a menudo. La documentación de MSAL para Android recomienda WEBVIEW como agente de autorización — y acabamos de citar un estándar que prohíbe los user-agents embebidos.

No se contradicen tanto como parece, pero la diferencia importa:

  • MSAL con broker utiliza WebView y aun así da SSO, porque el estado de sesión vive en el broker y no en tu app. Ese estado, eso sí, no se extiende a apps que no usen MSAL.
  • MSAL sin broker y con WEBVIEW: los tokens se quedan en el sandbox de tu app, y no hay SSO ni entre tus apps ni con la web. Es la configuración que parece funcionar en la demo y decepciona en producción.
  • MSAL sin broker y con BROWSER: se comparte el almacén de cookies del navegador del sistema, que es lo que permite SSO con la web y con otras apps.

Traducción para la decisión de arquitectura: si el cliente tiene Acceso Condicional, ve por broker. Si no lo tiene y quieres SSO real, ve por navegador del sistema. WEBVIEW sin broker es la única combinación que no deberías elegir a propósito, y es la que sale por defecto cuando nadie ha leído esto.

Lo que rompe el SSO sin que nadie se entere

  • Un webview propio "solo para la pantalla de login". Adiós SSO, y suspenso en la auditoría.
  • Un navegador no probado. MSAL aplica una heurística de selección y cae a WebView si el navegador por defecto no está en su lista de probados. En algunos dispositivos, el SSO depende literalmente de qué navegador tenga puesto el usuario.
  • Cambiar la clave de firmado sin actualizar el redirect. La firma va dentro del redirect URI del broker: si se refirma la app, deja de autenticar.
  • Un client_secret en el binario. Lo encuentra cualquiera con strings.

Antes de prometer SSO en una propuesta

Cinco preguntas, y las cinco son del cliente, no tuyas:

  1. ¿Qué proveedor de identidad hay y quién lo administra?
  2. ¿Hay políticas de Acceso Condicional? ¿Cuáles y sobre qué recursos?
  3. ¿Los dispositivos están gestionados por MDM, y con cuál?
  4. ¿Podéis publicar el fichero de asociación de dominio para usar https reclamado?
  5. ¿Quién registra la aplicación en el portal de identidad — vosotros o nosotros?

Sin esas cinco respuestas, "sí, hacemos SSO" es una estimación a ciegas. Con ellas, es una tarde de configuración.

Si estás en ese punto —una app corporativa que tiene que pasar por el equipo de seguridad del cliente antes de existir— es exactamente lo que ordenamos en la fase técnica de un proyecto de Flutter para empresas, y lo que revisamos cuando hacemos de CTO as a Service en un pliego ajeno.


Fuentes primarias, consultadas el 14 de agosto de 2026: RFC 8252 — OAuth 2.0 for Native Apps y la documentación de Microsoft sobre SSO entre apps en Android con MSAL.