Saltar al contenido principal

Inicia sesión con Microsoft Entra ID (Azure AD) en Capacitor

Añade inicio de sesión con Microsoft Entra ID (Azure AD) a una app de Capacitor 8 con PKCE: registro de la app, URIs de redirección, scopes de Graph y API, y corrección de errores AADSTS.

Página de inicio de sesión de Microsoft Entra ID lanzada desde una app de Capacitor

Para iniciar sesión con Microsoft Entra ID (anteriormente Azure Active Directory) en una app de Capacitor, registra una app de cliente público en el centro de administración de Entra y añade una Aplicaciones móviles y de escritorio URI de redirección como com.example.app://oauth/azure, y ejecuta el flujo de autorización OpenID Connect code con PKCE a través del navegador del sistema. Con @capgo/capacitor-social-login eso significa una sola initialize llamada a los endpoints de la plataforma de identidad de Microsoft v2.0 y SocialLogin.login({ provider: 'oauth2', options: { providerId: 'azure' } }).

Esta guía cubre el registro de la app, la elección de la autoridad correcta, los scopes de Graph y los personalizados de API, la validación de tokens, el Acceso condicional, el cierre de sesión y los errores AADSTS que verás. Está orientada a Capacitor 8 en octubre de 2026.

Qué necesitas

  • Un tenant de Microsoft Entra y un rol que pueda crear registros de aplicaciones (Application Developer o superior).
  • Una app Capacitor 8 y su appId, que reutilizarás como esquema de redirección.
  • Opcionalmente, un backend API que deba aceptar tokens de Entra.

Por qué no usar MSAL.js dentro del WebView

MSAL.js está diseñado para navegadores que controlan su propio origen y pueden abrir popups. En un WebView de Capacitor, el origen es capacitor://localhost en iOS y https://localhost en Android, los popups están bloqueados y un redirect de página completa sacaría a tu app de su propio bundle. La guía de Microsoft para apps nativas recomienda el navegador del sistema con PKCE (RFC 8252), que es lo que hace el plugin:

Plataforma Navegador utilizado Configuración extra
iOS ASWebAuthenticationSession Ninguno
Android Chrome Custom Tabs con androidUseCustomTabs: true Filtro de intents
Web Popup o redirect URI de redirección de SPA

En Android, activa Custom Tabs. El WebView integrado por defecto no puede comunicarse con Microsoft Authenticator, passkeys ni Company Portal, en los que el Conditional Access suele depender.

Paso 1: Registrar la app en Entra ID

  1. Abre la Centro de administración de Microsoft Entra y ve a Entra ID > Registros de aplicaciones > Nuevo registro.
  2. Introduce un nombre, como «Example Mobile».
  3. Elige Tipos de cuenta admitidos. Esto determina la autoridad que usarás más adelante.
  4. Omite la URI de redirección por ahora y haz clic en Registrarse.
  5. En Resumen, copia el ID de la aplicación (cliente) y ID del directorio (tenant).

Elegir la autoridad

Tipo de cuenta al registrarse Segmento de autoridad en las URLs
Inquilino único (solo tu organización) Tu ID de inquilino, por ejemplo 8f1c...
Cualquier directorio de la organización organizations
Cualquier directorio de la organización y cuentas personales de Microsoft common
Solo cuentas personales de Microsoft consumers

Usa el tenant ID siempre que puedas. Esto genera emisores predecibles y evita inicios de sesión accidentales desde otros tenants.

Añadir URIs de redirección

Ve a Autenticación > Añadir una plataforma:

  • Aplicaciones móviles y de escritorio: añadir com.example.app://oauth/azure como una URI de redirección personalizada. Añade com.example.app://oauth/logout también si quieres una redirección tras el logout.
  • Aplicación de página única (solo si publicas un build web): añade https://app.example.com/auth/callback.

No registres la URI móvil bajo Web. Las redirecciones de la plataforma Web son clientes confidenciales y requieren un secreto en el endpoint de token.

permisos de API

permisos de API already contains Microsoft Graph > User.Read. Añade offline_access, openid, profile y email si no aparecen. Algunos inquilinos bloquean el consentimiento del usuario. En ese caso, un administrador debe hacer clic Conceder consentimiento de administrador, otherwise users see AADSTS65001 or a “Need admin approval” screen.

Exponer un scope para tu propio API (opcional)

Si tu backend debe aceptar el token:

  1. Abrir Exponer un API y establecer la Application ID URI, normalmente api://<client-id>.
  2. Agregar un scope llamado access_as_user, who can consent: Admins and users.
  3. En Manifiesto, establece "requestedAccessTokenVersion": 2 los access tokens utilizan el formato de emisor v2.0.

Paso 2: Instala el plugin y añade el intent filter

bun add @capgo/capacitor-social-login
bunx cap sync

En android/app/src/main/AndroidManifest.xml, dentro de MainActivity:

<intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="com.example.app" android:host="oauth" />
</intent-filter>

Mantén android:launchMode="singleTask" en MainActivity (el valor predeterminado de Capacitor). Si no usas inicio de sesión con Google, Apple o Facebook, desactívalos en plugins.SocialLogin.providers en capacitor.config.ts para mantener sus SDKs fuera del build.

Paso 3: Inicializar el proveedor de Entra ID

import { Capacitor } from '@capacitor/core';
import { SocialLogin } from '@capgo/capacitor-social-login';

const TENANT = 'YOUR_TENANT_ID'; // or organizations / common
const AUTHORITY = `https://login.microsoftonline.com/${TENANT}/oauth2/v2.0`;
const isWeb = Capacitor.getPlatform() === 'web';

export async function initAuth() {
  await SocialLogin.initialize({
    oauth2: {
      azure: {
        appId: 'YOUR_CLIENT_ID',
        authorizationBaseUrl: `${AUTHORITY}/authorize`,
        accessTokenEndpoint: `${AUTHORITY}/token`,
        redirectUrl: isWeb
          ? 'https://app.example.com/auth/callback'
          : 'com.example.app://oauth/azure',
        scope: 'openid profile email offline_access User.Read',
        pkceEnabled: true,
        resourceUrl: 'https://graph.microsoft.com/v1.0/me',
        logoutUrl: `${AUTHORITY}/logout`,
        postLogoutRedirectUrl: isWeb ? 'https://app.example.com/' : 'com.example.app://oauth/logout',
        androidUseCustomTabs: true,
      },
    },
  });
}

Con resourceUrl apuntando a Graph /me, el plugin llama a Graph justo después del inicio de sesión y devuelve el perfil en resourceData.

Si estás migrando desde Ionic Auth Connect, SocialLoginAuthConnect tiene un azure preset que solo necesita tenantId, clientId y redirectUrl. Consulta la página de integración de Entra ID.

Paso 4: Iniciar sesión

import { SocialLogin } from '@capgo/capacitor-social-login';

export async function signInWithMicrosoft() {
  try {
    const { result } = await SocialLogin.login({
      provider: 'oauth2',
      options: { providerId: 'azure' },
    });

    const me = result.resourceData as {
      id: string;
      displayName?: string;
      mail?: string | null;
      userPrincipalName?: string;
    } | null;

    return {
      idToken: result.idToken,
      graphToken: result.accessToken?.token,
      refreshToken: result.refreshToken,
      user: me,
    };
  } catch (error: any) {
    if (error?.code === 'USER_CANCELLED') return null;
    throw error;
  }
}

Opciones útiles:

  • loginHint: 'jane@contoso.com' precarga la cuenta y omite el selector de cuentas para ese usuario.
  • prompt: 'select_account' muestra siempre el selector de cuentas, útil para dispositivos compartidos.
  • additionalParameters: { domain_hint: 'contoso.com' } envía a los usuarios directamente a su IdP federado.

mail es a menudo null para cuentas sin buzón. Usa como alternativa userPrincipalName o el preferred_username claim del ID token.

Paso 5: Llamar a Microsoft Graph

export async function getMyPhotoBlob(graphToken: string) {
  const res = await fetch('https://graph.microsoft.com/v1.0/me/photo/$value', {
    headers: { Authorization: `Bearer ${graphToken}` },
  });
  if (res.status === 404) return null; // no photo set
  if (!res.ok) throw new Error(`Graph error ${res.status}`);
  return res.blob();
}

Añade cada scope de Graph que necesites (por ejemplo Calendars.Read) tanto en permisos de API como en el scope string.

Paso 6: Obtener un token para tu propio API

Un access token apunta a un recurso. Para llamar a tu backend, añade una segunda entrada junto a azure en el mismo oauth2 mapa de tu initialize llamada, solicitando tu ámbito API en lugar de Graph:

await SocialLogin.initialize({
  oauth2: {
    azure: graphConfig, // the object from step 3, moved into a const
    azureApi: {
      appId: 'YOUR_CLIENT_ID',
      authorizationBaseUrl: `${AUTHORITY}/authorize`,
      accessTokenEndpoint: `${AUTHORITY}/token`,
      redirectUrl: 'com.example.app://oauth/azure-api', // register this URI too
      scope: 'openid profile offline_access api://YOUR_CLIENT_ID/access_as_user',
      pkceEnabled: true,
      androidUseCustomTabs: true,
    },
  },
});

El usuario ya tiene una sesión de Microsoft, por lo que el segundo inicio de sesión suele completarse sin un prompt. Valida el token en el servidor:

import { createRemoteJWKSet, jwtVerify } from 'jose';

const TENANT_ID = 'YOUR_TENANT_ID';
const jwks = createRemoteJWKSet(
  new URL(`https://login.microsoftonline.com/${TENANT_ID}/discovery/v2.0/keys`),
);

export async function verifyEntraToken(token: string) {
  const { payload } = await jwtVerify(token, jwks, {
    issuer: `https://login.microsoftonline.com/${TENANT_ID}/v2.0`,
    audience: 'YOUR_CLIENT_ID', // v2 tokens use the client ID as aud
  });
  if (!String(payload.scp ?? '').split(' ').includes('access_as_user')) {
    throw new Error('Missing scope');
  }
  return payload; // payload.oid is the stable user object ID
}

Para apps multitenant, el issuer contiene el tenant ID de cada cliente. Valida el tid claim contra la lista de tenants que aceptas, en lugar de un issuer fijo. Clave a los usuarios por oid además de tid, no por email.

Paso 7: Refresh y cierre de sesión

// Get a valid Graph token, refreshing it when it has expired
export async function getGraphToken() {
  const { isLoggedIn } = await SocialLogin.isLoggedIn({ provider: 'oauth2', providerId: 'azure' });
  if (!isLoggedIn) {
    try {
      await SocialLogin.refresh({ provider: 'oauth2', options: { providerId: 'azure' } });
    } catch {
      // no session, or the refresh token expired or was revoked
      const loginResult = await signInWithMicrosoft();
      if (!loginResult) throw new Error('Sign-in cancelled');
    }
  }
  const { accessToken } = await SocialLogin.getAuthorizationCode({ provider: 'oauth2', providerId: 'azure' });
  return accessToken;
}

// Sign out
await SocialLogin.logout({ provider: 'oauth2', providerId: 'azure' });

Los tokens de refresco de Entra para clientes públicos duran hasta 90 días con una ventana deslizante y se reemplazan en cada uso. El plugin almacena el nuevo. Si almacenas los tokens tú mismo, usa Keychain o Keystore, por ejemplo NativeBiometric.setData de los @capgo/capacitor-native-biometric, y pásalos a SocialLogin.refreshToken.

logout borra los tokens locales y abre el endpoint de cierre de sesión v2.0 en el navegador del sistema. A veces Microsoft muestra una página de «Has cerrado sesión en tu cuenta» en lugar de redirigir a un esquema personalizado, por lo que el usuario puede tener que cerrarla. Si solo necesitas un cierre de sesión local, elimina logoutUrl y pasar prompt: 'select_account' en el próximo inicio de sesión.

Acceso condicional e Intune

El OIDC basado en navegador funciona con MFA, passwordless y la mayoría de las políticas de Conditional Access. Dos casos requieren más:

  • Requerir un dispositivo compatible o con unión híbrida: Entra debe ver el estado del dispositivo. En Android, esto funciona mediante Custom Tabs si Company Portal o Authenticator están instalados. En dispositivos iOS gestionados, lo proporciona el plug-in Microsoft Enterprise SSO que despliega tu MDM. Pruébalo cuanto antes con una política real.
  • Intune app protection policies (MAM): esto requiere la SDK de Intune App dentro de la app nativa. OIDC por sí solo no cumple con los permisos de "Require app protection policy".

Solución de problemas

Error Significado Solución
AADSTS50011 Coincidencia incorrecta de URI de redirección Añade la URI exacta en Aplicaciones móviles y de escritorio
AADSTS7000218 La solicitud de token requiere un secreto La redirección estaba registrada en Web. Muévela a Móvil y escritorio
AADSTS9002326 Redención de tokens cross-origin solo permitida para SPA El build web usa una redirección móvil. Registra la URL web bajo Single-page application
AADSTS700016 App no encontrada en el directorio Tenant incorrecto en la authority, o app de un solo tenant usada con common
AADSTS50020 Cuenta de usuario de otro tenant Usa un registro multitenant, o invita al usuario como invitado
AADSTS65001 Consentimiento no otorgado Grant admin consent or allow user consent
AADSTS53003 Bloqueado por Acceso Condicional Revisa los logs de inicio de sesión, ver la sección anterior
AADSTS28000 Scopes para más de un recurso en una sola solicitud Solicita Graph y tus scopes de API en inicios de sesión separados
Sin refresh token offline_access falta Añádelo a scopes y permisos

El centro de administración de Entra Logs de inicio de sesión muestran la razón exacta del fallo en cada intento, y logsEnabled: true en la configuración del plugin imprime la URL de autorización y la respuesta del token en el dispositivo.

Publicar actualizaciones

Los cambios de política en el lado de Entra son instantáneos, pero las correcciones en el cliente aún deben llegar a los dispositivos. Los cambios en TypeScript, como scopes o manejo de errores, se pueden publicar a través de actualizaciones Live Update de Capgo. Los cambios nativos (intent filters, nuevos schemes) requieren un nuevo build de la tienda, que Build de Capgo puede generar sin una Mac local.

Live updates para apps Capacitor

Cuando un bug de la capa web está en producción, publica el fix a través de Capgo en lugar de esperar días a la aprobación de la app store. Los usuarios reciben la actualización en segundo plano mientras los cambios nativos siguen el flujo de revisión habitual.

Lo último de nuestro blog

Capgo te da la información clave que necesitas para crear una app móvil realmente profesional.