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
- Öffnen Sie das Microsoft Entra Admin Center und navigiere zu Entra ID > App-Registrierungen > Neue Registrierung.
- Geben Sie einen Namen ein, z. B. „Example Mobile“.
- Wählen Sie Unterstützte Kontotypen. Dies legt die Authority fest, die Sie später verwenden.
- Überspringe die Redirect URI vorerst und klicke auf Registrieren.
- 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/azureals benutzerdefinierte Redirect-URI. Fügecom.example.app://oauth/logoutauch, 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:
- Öffnen Bereitstellen eines API und die Application ID URI festlegen, üblicherweise
api://<client-id>. - Scope mit dem Namen hinzufügen
access_as_user, wer zustimmen kann: Admins und User. - In Manifestfestlegen
"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
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.