Zum Hauptinhalt springen

Sign-In mit Microsoft Entra ID (Azure AD) in Capacitor

Microsoft Entra ID (Azure AD) Login für Capacitor 8 Apps mit PKCE hinzufügen: App-Registrierung, Redirect URIs, Graph- und API Scopes sowie AADSTS-Fehlerbehebung.

Sign-In mit Microsoft Entra ID (Azure AD) in Capacitor

Um sich mit Microsoft Entra ID (ehemals Azure Active Directory) in einer Capacitor App anzumelden, registrieren Sie eine Public Client App im Entra Admin Center und fügen Sie eine Mobile und Desktop-Anwendungen Redirect-URI wie zum Beispiel com.example.app://oauth/azure, und führt den OpenID Connect Authorization code Flow mit PKCE über den Systembrowser aus. Mit @capgo/capacitor-social-login das bedeutet, ein einziger initialize Call über die Microsoft Identity Platform v2.0 Endpunkte und SocialLogin.login({ provider: 'oauth2', options: { providerId: 'azure' } }).

Dieser Guide deckt die App-Registrierung, die Wahl der richtigen Authority, Graph- und benutzerdefinierte API Scopes, Token-Validierung, Conditional Access, Logout sowie die auftretenden AADSTS-Fehler ab. Er bezieht sich auf Capacitor 8 im Oktober 2026.

Was du benötigst

  • Einen Microsoft Entra Tenant und eine Rolle, die App-Registrierungen erstellen darf (Application Developer oder höher).
  • Eine Capacitor 8 App und deren appId, die du als Redirect-Scheme wiederverwendest.
  • Optional ein Backend API, das Entra-Token akzeptieren soll.

Warum nicht MSAL.js in der WebView?

MSAL.js ist für Browser mit eigener Origin und Popup-Unterstützung gebaut. In einer Capacitor WebView ist die Origin capacitor://localhost unter iOS und https://localhost unter Android werden Popups blockiert, und ein Full-Page-Redirect würde die App aus ihrem eigenen Bundle herausnavigieren. Microsofts Empfehlung für native Apps ist der System-Browser mit PKCE (RFC 8252), genau das, was das Plugin macht:

Plattform Verwendeter Browser Zusätzliches Setup
iOS ASWebAuthenticationSession Keine
Android Chrome Custom Tabs mit androidUseCustomTabs: true Intent-Filter
Web Popup oder Redirect SPA-Redirect-URI

Aktivieren Sie unter Android Custom Tabs. Die standardmäßige WebView kann nicht mit Microsoft Authenticator, Passkeys oder dem Company Portal kommunizieren, auf die Conditional Access oft angewiesen ist.

Schritt 1: App in Entra ID registrieren

  1. Öffnen Sie das Microsoft Entra Admin Center und navigiere zu Entra ID > App-Registrierungen > Neue Registrierung.
  2. Geben Sie einen Namen ein, z. B. „Example Mobile“.
  3. Wählen Sie Unterstützte Kontotypen. Dies legt die Authority fest, die Sie später verwenden.
  4. Überspringe die Redirect URI vorerst und klicke auf Registrieren.
  5. Auf Übersicht, kopiere die Anwendungs-ID (Client-ID) und Verzeichnis- (Mandanten-) ID.

Wähle die Authority

Account-Typ bei der Registrierung Authority-Segment in URLs
Single Tenant (nur deine Org) Deine Tenant ID, zum Beispiel 8f1c...
Jedes Organisationsverzeichnis organizations
Organisationsverzeichnisse plus persönliche Microsoft-Konten common
Nur persönliche Microsoft-Konten consumers

Nutze nach Möglichkeit immer die Tenant ID. Das sorgt für vorhersehbare Issuer und verhindert versehentliche Logins aus anderen Tenants.

Redirect-URIs hinzufügen

Gehe zu Authentication > Add a platform:

  • Mobile und Desktop-Anwendungen: hinzufügen com.example.app://oauth/azure als benutzerdefinierte Redirect-URI. Füge com.example.app://oauth/logout auch, wenn du eine Weiterleitung nach dem Logout möchtest.
  • Single-Page-Application (nur bei Web-Builds): füge hinzu https://app.example.com/auth/callback.

Registriere die mobile URI nicht unter Web. Web-Redirects sind Confidential Clients und benötigen ein Secret am Token-Endpoint.

API Berechtigungen

API Berechtigungen enthält bereits Microsoft Graph > User.Read. Füge hinzu offline_access, openid, profile und email , falls sie nicht aufgeführt sind. Manche Tenants blockieren die Benutzereinwilligung. In diesem Fall muss ein Admin auf Admin-Zustimmung erteilen klicken, andernfalls sehen User AADSTS65001 oder einen „Need admin approval“-Screen.

Scope für dein eigenes API bereitstellen (optional)

Falls dein Backend den Token akzeptieren soll:

  1. Öffnen Bereitstellen eines API und die Application ID URI festlegen, üblicherweise api://<client-id>.
  2. Scope mit dem Namen hinzufügen access_as_user, wer zustimmen kann: Admins und User.
  3. In Manifestfestlegen "requestedAccessTokenVersion": 2 damit Access Tokens das v2.0 Issuer-Format nutzen.

Schritt 2: Plugin installieren und Intent Filter hinzufügen

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

In android/app/src/main/AndroidManifest.xml, innerhalb von 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>

Behalte android:launchMode="singleTask" auf MainActivity (dem Capacitor Standard). Falls du kein Google-, Apple- oder Facebook-Login nutzt, deaktiviere sie unter plugins.SocialLogin.providers in capacitor.config.ts um deren SDKs aus dem Build fernzuhalten.

Schritt 3: Den Entra ID Provider initialisieren

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,
      },
    },
  });
}

Mit resourceUrl das auf Graph zeigt /me, ruft das Plugin Graph direkt nach dem Login auf und gibt das Profil in zurück resourceData.

Falls du von Ionic Auth Connect migrierst, SocialLoginAuthConnect hat ein azure Voreinstellung, die nur benötigt tenantId, clientId und redirectUrl. Siehe den Entra ID Integrationsseite.

Schritt 4: Anmelden

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;
  }
}

Nützliche Optionen:

  • loginHint: 'jane@contoso.com' füllt das Konto vor und überspringt die Kontenauswahl für diesen Nutzer.
  • prompt: 'select_account' zeigt immer die Kontenauswahl an, ideal für gemeinsam genutzte Geräte.
  • additionalParameters: { domain_hint: 'contoso.com' } leitet Nutzer direkt zu ihrem föderierten IdP weiter.

mail ist oft null für Konten ohne Postfach. Fallback auf userPrincipalName oder den preferred_username Claim des ID-Tokens.

Schritt 5: Microsoft Graph aufrufen

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();
}

Füge jeden benötigten Graph-Scope hinzu (z. B. Calendars.Read) sowohl in API Berechtigungen als auch im scope String.

Schritt 6: Token für dein eigenes API abrufen

Ein Access Token ist für eine Ressource bestimmt. Um dein Backend aufzurufen, füge einen zweiten Eintrag neben azure im selben oauth2 Map deines initialize ruft statt Graph deinen API Scope auf:

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,
    },
  },
});

Der User hat bereits eine Microsoft-Session, daher erfolgt der zweite Login meist ohne Prompt. Validiere das Token auf dem Server:

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
}

Bei Multitenant-Apps enthält der Issuer die Tenant-ID des jeweiligen Kunden. Validiere den tid Claim gegen die Liste der akzeptierten Tenants statt gegen einen festen Issuer. Wichtige User auf oid plus tid, nicht über die E-Mail.

Schritt 7: Refresh und Logout

// 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' });

Entra Refresh-Token für Public Clients halten mit einem Sliding Window bis zu 90 Tage und werden bei jeder Nutzung erneuert. Das Plugin speichert das neue Token. Wenn du Token selbst speicherst, nutze z. B. den Keychain oder Keystore NativeBiometric.setData von @capgo/capacitor-native-biometric, und übergib sie an SocialLogin.refreshToken.

logout löscht lokale Token und öffnet den v2.0 Logout-Endpoint im System-Browser. Microsoft zeigt manchmal eine „Sie wurden abgemeldet“-Seite statt der Weiterleitung zu einem Custom Scheme an, sodass der User diese schließen muss. Wenn du nur einen lokalen Logout benötigst, nutze logoutUrl und übergeben prompt: 'select_account' beim nächsten Login.

Conditional Access und Intune

Browser-basiertes OIDC funktioniert mit MFA, Passwordless und den meisten Conditional Access Policies. Zwei Fälle benötigen mehr:

  • Konformes oder Hybrid-joined Gerät erforderlich: Entra muss den Gerätestatus sehen können. Auf Android geschieht dies über Custom Tabs, sofern der Company Portal oder Authenticator installiert ist. Auf verwalteten iOS-Geräten wird dies durch das Microsoft Enterprise SSO Plug-in bereitgestellt, das via MDM verteilt wird. Teste dies frühzeitig mit einer echten Policy.
  • Intune App Protection Policies (MAM): diese erfordern die Intune App SDK innerhalb der nativen App. OIDC allein erfüllt die Berechtigungen für „Require app protection policy“ nicht.

Fehlerbehebung

Fehler Bedeutung Lösung
AADSTS50011 Redirect-URI-Fehler (Mismatch) Füge die exakte URI unter „Mobile and desktop applications“ hinzu
AADSTS7000218 Token-Request benötigt ein Secret Redirect wurde unter „Web“ registriert. Verschiebe ihn zu „Mobile and desktop“
AADSTS9002326 Cross-origin Token-Redemption nur für SPA erlaubt Web-Build nutzt einen Mobile-Redirect. Registriere die Web-URL unter „Single-page application“
AADSTS700016 App im Verzeichnis nicht gefunden Falscher Tenant in der Authority oder Single-Tenant-App verwendet common
AADSTS50020 Benutzerkonto aus einem anderen Tenant Nutze eine Multitenant-Registrierung oder lade den User als Gast ein
AADSTS65001 Zustimmung nicht erteilt Admin-Zustimmung erteilen oder User-Zustimmung erlauben
AADSTS53003 Durch Conditional Access blockiert Prüfe die Sign-in-Logs (siehe Abschnitt oben)
AADSTS28000 Scopes für mehrere Ressourcen in einem Request Request für Graph und deine API Scopes in separaten Logins
Kein Refresh-Token offline_access fehlt Zu Scopes und Berechtigungen hinzufügen

Das Entra Admin Center Anmelde-Logs zeigen den genauen Fehlergrund für jeden Versuch an, und logsEnabled: true gibt in der Plugin-Config die Authorize-URL und die Token-Response auf dem Gerät aus.

Updates ausrollen

Richtlinienänderungen auf der Entra-Seite erfolgen sofort, aber clientseitige Fixes müssen erst die Geräte erreichen. TypeScript-Änderungen wie Scopes oder Error-Handling können via Capgo Live Updatesversendet werden. Native Änderungen (Intent-Filter, neue Schemes) erfordern einen neuen Store-Build, der Capgo Build kann ohne einen lokalen Mac erstellt werden.

Live-Updates für Capacitor-Apps

Wenn ein Bug im Web-Layer live ist, rollst du den Fix über Capgo aus, statt Tage auf die App-Store-Freigabe zu warten. Nutzer bekommen das Update im Hintergrund, während native Änderungen weiter den normalen Review-Prozess durchlaufen.

Neuestes aus unserem Blog

Capgo bietet dir die besten Insights für eine wirklich professionelle Mobile App.