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 z. B. com.example.app://oauth/azure, und führe den OpenID Connect Authorization code Flow mit PKCE über den Systembrowser aus. Mit @capgo/capacitor-social-login das bedeutet eine initialize Aufruf mit den Microsoft identity platform v2.0-Endpunkten und SocialLogin.login({ provider: 'oauth2', options: { providerId: 'azure' } }).
Dieser Guide behandelt die App-Registrierung, die Wahl der richtigen Authority, Graph- und benutzerdefinierte API-Scopes, Token-Validierung, Conditional Access, Logout und die AADSTS-Fehler, die Sie sehen werden. Er richtet sich an Capacitor 8 im Oktober 2026.
Voraussetzungen
- Ein Microsoft Entra-Tenant und eine Rolle, die App-Registrierungen erstellen kann (Application Developer oder höher).
- Eine Capacitor 8-App und deren
appId, die Sie als Redirect-Schema wiederverwenden werden. - Optional ein Backend-API, das Entra-Tokens akzeptieren soll.
Warum nicht MSAL.js in der WebView?
MSAL.js ist für Browser konzipiert, die ihren eigenen Origin besitzen und Popups öffnen können. In einer Capacitor-WebView ist der Origin capacitor://localhost auf iOS und https://localhost auf Android sind Popups blockiert, und ein Full-Page-Redirect würde Ihre App von ihrem eigenen Bundle weg navigieren. Die Empfehlung von Microsoft für Native Apps ist der Systembrowser mit PKCE (RFC 8252), was genau das tut, was das Plugin macht:
| Plattform | Verwendeter Browser | Extra setup |
|---|---|---|
| iOS | ASWebAuthenticationSession |
Keine |
| Android | Chrome Custom Tabs mit androidUseCustomTabs: true |
Intent-Filter |
| Web | Popup oder Redirect | SPA-Redirect-URI |
Aktiviere auf Android Custom Tabs. Die standardmäßige eingebettete 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
- Öffne die Microsoft Entra Admin Center und gehen Sie zu Entra ID > App-Registrierungen > Neue Registrierung.
- Gib einen Namen ein, z. B. „Example Mobile“.
- Wähle Unterstützte Kontotypen. Damit wird die Authority festgelegt, die du später verwendest.
- Überspringe die Redirect-URI vorerst und klicke auf Registrieren.
- An Übersicht, kopiere die Anwendungs-ID (Client) und Verzeichnis-ID (Mandant).
Wähle die Authority
| Kontotyp bei der Registrierung | Authority-Segment in URLs |
|---|---|
| Single-Tenant (nur deine Org) | Deine Tenant-ID, zum Beispiel 8f1c... |
| Beliebiges Organisationsverzeichnis | organizations |
| Beliebiges Organisationsverzeichnis plus persönliche Microsoft-Konten | common |
| Nur persönliche Microsoft-Konten | consumers |
Nutze die Tenant-ID, wann immer möglich. Das liefert vorhersehbare Issuer und vermeidet versehentliche Anmeldungen aus anderen Tenants.
Redirect-URIs hinzufügen
Gehe zu Authentifizierung > Plattform hinzufügen:
- Mobile und Desktop-Anwendungen: hinzufügen
com.example.app://oauth/azureals benutzerdefinierte Redirect-URI. Fügecom.example.app://oauth/logoutauch, wenn du nach dem Logout weiterleiten möchtest. - Single-Page-Application (nur bei Web-Builds): hinzufügen
https://app.example.com/auth/callback.
Registriere die mobile URI nicht unter WebWeb-Plattform-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 gelistet sind. Manche Tenants blockieren die Benutzereinwilligung. In diesem Fall muss ein Admin Administratorzustimmung erteilen, andernfalls sehen Nutzer AADSTS65001 oder einen „Need admin approval“-Bildschirm.
Scope für dein eigenes API bereitstellen (optional)
Falls dein Backend den Token akzeptieren soll:
- Öffne Einen API bereitstellen und die Application ID URI festlegen, üblicherweise
api://<client-id>. - Einen Scope mit dem Namen hinzufügen
access_as_user, wer zustimmen kann: Admins und Benutzer. - Unter Manifest, setzen
"requestedAccessTokenVersion": 2damit 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
Unter 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>
Behalten android:launchMode="singleTask" auf MainActivity (den Capacitor-Standard). Falls du Google-, Apple- oder Facebook-Login nicht nutzt, deaktiviere sie unter plugins.SocialLogin.providers in einem 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 der auf Graph zeigt /me, ruft das Plugin direkt nach dem Login Graph auf und gibt das Profil in zurück resourceData.
Falls du von Ionic Auth Connect migrierst, SocialLoginAuthConnect hat ein azure Preset, das nur benötigt tenantId, clientId und redirectUrl. Siehe die 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 vorab aus und überspringt den Account-Picker für diesen User.prompt: 'select_account'zeigt immer den Account-Picker an, praktisch für gemeinsam genutzte Geräte.additionalParameters: { domain_hint: 'contoso.com' }leitet User direkt zu ihrem föderierten IdP weiter.
mail ist oft null für Konten ohne Postfach. Weiche 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 in derselben oauth2 Map deines initialize aufrufen und dabei deinen API-Scope statt Graph anfordern:
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 Benutzer hat bereits eine Microsoft-Sitzung, daher läuft der zweite Login normalerweise ohne Prompt durch. 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 jedes Kunden. Validiere den tid Claim gegen die Liste der Tenants, die du akzeptierst, statt gegen einen festen Issuer. Verwende als Key die oid sowie tid, nicht die E-Mail-Adresse.
Schritt 7: Aktualisieren und abmelden
// 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-Tokens für öffentliche Clients gelten bis zu 90 Tage mit gleitendem Fenster und werden bei jeder Nutzung ersetzt. Das Plugin speichert das neue Token. Wenn du Tokens 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 Tokens und öffnet den v2.0-Logout-Endpunkt im Systembrowser. Microsoft zeigt manchmal eine Seite „Du hast dich von deinem Konto abgemeldet“ an, statt auf einen benutzerdefinierten Scheme umzuleiten, sodass der Benutzer sie möglicherweise schließen muss. Wenn du nur einen lokalen Logout brauchst, lass 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 brauchen mehr:
- Compliant-Gerät oder hybrid-joined Gerät erforderlich: Entra muss den Gerätestatus sehen. Auf Android klappt das über Custom Tabs, wenn Company Portal oder Authenticator installiert sind. Auf verwalteten iOS-Geräten übernimmt das Microsoft Enterprise SSO Plug-in, das Ihr MDM ausrollt. Testen Sie das früh mit einer echten Policy.
- Intune App Protection Policies (MAM): diese erfordern das Intune App SDK in der nativen App. OIDC allein erfüllt die Grant-Anforderung „Require app protection policy“ nicht.
Fehlerbehebung
| Fehler | Bedeutung | Lösung |
|---|---|---|
| AADSTS50011 | Redirect-URI stimmt nicht überein | 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 SPAs 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 | Fordere Graph-Scopes und deine API Scopes in separaten Logins an |
| Kein Refresh Token | offline_access fehlt |
Zu Scopes und Berechtigungen hinzufügen |
Das Entra Admin Center Anmeldeprotokolle zeigen den genauen Fehlergrund für jeden Versuch an, und logsEnabled: true gibt in der Plugin-Konfiguration die Authorize-URL und die Token-Response auf dem Gerät aus.
Updates ausliefern
Richtlinienänderungen auf der Entra-Seite erfolgen sofort, aber clientseitige Fixes müssen trotzdem erst auf den Geräten ankommen. TypeScript-Änderungen wie Scopes oder Error Handling können über Capgo Live Updates. Native Änderungen (Intent-Filter, neue Schemes) erfordern einen neuen Store-Build, der Capgo Build kann ohne einen lokalen Mac erstellt werden.