Um sich mit Auth0 in einer Capacitor App anzumelden, erstelle ein Nativ App in Auth0, registriere eine Custom-Scheme-Callback-URL wie zum Beispiel com.example.app://oauth/auth0, und führe den OpenID Connect Authorization code Flow mit PKCE über einen System-Browser aus. Mit @capgo/capacitor-social-login das ist ein initialize Aufruf mit deinen Auth0-Endpoints und ein SocialLogin.login({ provider: 'oauth2', options: { providerId: 'auth0' } }).
Dieser Guide deckt die Auth0 Dashboard-Einstellungen, den Client code für iOS, Android und Web, API Access Tokens, Refresh Tokens, Logout sowie die gängigsten Fehler ab. Er wurde für Capacitor 8 (Stand Oktober 2026) erstellt.
So funktioniert der Auth0 Flow in Capacitor
Auth0 hostet die Login-Seite (Universal Login). Deine App sieht das Passwort nie. Der Flow sieht so aus:
- Die App generiert einen PKCE Verifier und Challenge und öffnet dann
https://YOUR_TENANT/authorizein einem sicheren Browser. - Der User loggt sich ein (Passwort, Social Login, Passkey, MFA – je nachdem, was du in Auth0 konfiguriert hast).
- Auth0 leitet zur Callback-URL mit einem einmaligen
code. - Die App tauscht den code und den PKCE Verifier bei
/oauth/tokenfür einen ID-Token, einen Access-Token und optional einen Refresh-Token.
Der verwendete Browser hängt von der Plattform ab:
| Plattform | Browser | Callback-Handling |
|---|---|---|
| iOS | ASWebAuthenticationSession |
Vom System gehandelt, keine Info.plist Änderung |
| Android | Standardmäßig Embedded WebView oder Chrome Custom Tabs mit androidUseCustomTabs: true |
Custom Tabs benötigen einen Intent-Filter |
| Web | Popup (Standard) oder Full-Page-Redirect | handleRedirectCallback() auf der Rückkehrseite |
Nutze unter Android Custom Tabs. Sie teilen Cookies mit Chrome, unterstützen Passkeys und Passwort-Manager und folgen RFC 8252 (OAuth für native Apps), was Auth0 erwartet.
Schritt 1: Auth0-App einrichten
- Im Auth0 Dashboard, gehe zu Anwendungen > Anwendungen > Anwendung erstellen.
- Wähle Native und erstelle sie.
- Auf der Einstellungen Tab, beachte die Domain sein (zum Beispiel
example.eu.auth0.com) und die Client-ID. - Unter Credentials, prüfe, ob die Authentifizierungsmethode Keineist. Das ist die Public-Client-Einstellung für PKCE.
Callback- und Logout-URLs
Diese Felder ausfüllen Einstellungen > Application URIs:
| Feld | Wert für Mobile | Wert für Web |
|---|---|---|
| Zulässige Callback-URLs | com.example.app://oauth/auth0 |
https://app.example.com/auth/callback |
| Zulässige Logout-URLs | com.example.app://oauth/logout |
https://app.example.com/ |
| Zulässige Web-Origins | Nicht erforderlich | https://app.example.com |
Mehrere Werte durch Komma trennen. Nutze dein Capacitor appId als Scheme. Ein Reverse-Domain-Scheme verhindert Kollisionen mit anderen Apps. Falls du auch einen Web-Build bereitstellst, erstellen viele Teams eine separate Single-Page-Application in Auth0, damit sich die Web- und Mobile-Einstellungen nicht vermischen.
Refresh-Tokens
Falls die User angemeldet bleiben sollen:
- Unter Einstellungen > Refresh Token Rotation, aktiviere die Rotation. Jeder Refresh liefert einen neuen Refresh Token und invalidiert den alten.
- Lege eine absolute sowie eine Inaktivitäts-Lifetime fest, die zu deinem Produkt passt.
- Falls du deine eigene API aufrufst, öffne Applications > APIs > deine API > Settings und aktiviere Offline-Zugriff erlauben.
- Fordere den
offline_accessScope in der App.
API Audience
Wenn dein Backend Auth0-Access-Tokens validiert, erstelle eine API unter Anwendungen > APIs mit einem Identifier wie https://api.example.com. Übergib diesen Identifier als audience. Ohne ihn ist das Access-Token nur für Auth0s /userinfo Endpoint gültig.
Schritt 2: Plugin installieren und konfigurieren
bun add @capgo/capacitor-social-login
bunx cap sync
Wenn Auth0 dein einziger Provider ist, deaktiviere die gebündelten Social SDKs, um die Binary klein zu halten:
// capacitor.config.ts
plugins: {
SocialLogin: {
providers: { google: false, facebook: false, apple: false, twitter: false },
},
},
Die generische OAuth2-Engine ist nicht von diesen SDKs abhängig.
Android Intent-Filter für Custom Tabs
Füge das Callback-Schema zu MainActivity in android/app/src/main/AndroidManifest.xml:
<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>
Das Capacitor Template setzt bereits android:launchMode="singleTask" auf MainActivity, das für den Redirect benötigt wird. Behalte es bei.
Schritt 3: Auth0-Provider initialisieren
import { Capacitor } from '@capacitor/core';
import { SocialLogin } from '@capgo/capacitor-social-login';
const AUTH0_DOMAIN = 'https://example.eu.auth0.com';
const isWeb = Capacitor.getPlatform() === 'web';
export async function initAuth() {
await SocialLogin.initialize({
oauth2: {
auth0: {
appId: 'YOUR_AUTH0_CLIENT_ID',
authorizationBaseUrl: `${AUTH0_DOMAIN}/authorize`,
accessTokenEndpoint: `${AUTH0_DOMAIN}/oauth/token`,
resourceUrl: `${AUTH0_DOMAIN}/userinfo`,
redirectUrl: isWeb
? 'https://app.example.com/auth/callback'
: 'com.example.app://oauth/auth0',
scope: 'openid profile email offline_access',
pkceEnabled: true,
additionalParameters: {
audience: 'https://api.example.com',
},
logoutUrl: `${AUTH0_DOMAIN}/oidc/logout`,
postLogoutRedirectUrl: isWeb
? 'https://app.example.com/'
: 'com.example.app://oauth/logout',
androidUseCustomTabs: true,
},
},
});
}
Du kannst die expliziten Endpunkte durch issuerUrl: AUTH0_DOMAINersetzen. Das Plugin liest dann /.well-known/openid-configuration. Explizite Werte haben Vorrang vor automatisch erkannten, was bei der Nutzung einer Auth0 Custom Domain hilft.
Wenn du von Ionic Auth Connect migrierst, exportiert das Plugin auch SocialLoginAuthConnect mit einem auth0 Voreinstellung, die übernimmt domain, clientId, redirectUrl und audience. Siehe die Auth Connect Migrationsguide.
Schritt 4: Anmelden
import { SocialLogin } from '@capgo/capacitor-social-login';
export async function signInWithAuth0() {
try {
const { result } = await SocialLogin.login({
provider: 'oauth2',
options: { providerId: 'auth0' },
});
return {
idToken: result.idToken,
accessToken: result.accessToken?.token,
expiresAt: result.accessToken?.expires,
refreshToken: result.refreshToken,
user: result.resourceData, // JSON from /userinfo
};
} catch (error: any) {
if (error?.code === 'USER_CANCELLED') return null;
throw error;
}
}
Nützliche Login-Optionen:
prompt: 'login'erzwingt die Login-Seite, selbst wenn Auth0 noch ein Session-Cookie hat.loginHint: 'user@example.com'füllt die E-Mail vor.additionalParameters: { connection: 'google-oauth2' }überspringt Universal Login und nutzt direkt eine Verbindung.additionalParameters: { screen_hint: 'signup' }öffnet den Registrierungsbildschirm.
Web-Redirect-Callback
Im Web benötigt der Standard-Popup-Flow keine Extras. Wenn du flow: 'redirect'nutzt, lies das Ergebnis auf der Callback-Seite aus:
const result = await SocialLogin.handleRedirectCallback();
if (result?.provider === 'oauth2') {
console.log(result.result.idToken);
}
handleRedirectCallback funktioniert nur im Web und wird auf iOS und Android abgelehnt.
Schritt 5: ID-Token lesen und API aufrufen
Dekodieren Sie den ID-Token nur clientseitig zur Anzeige:
const { claims } = await SocialLogin.decodeIdToken({ idToken: result.idToken! });
console.log(claims.email, claims.name, claims.sub);
decodeIdToken prüft nicht die Signatur. Senden Sie für alles Sicherheitsrelevante den Access-Token an Ihr API und validieren Sie ihn dort:
import { createRemoteJWKSet, jwtVerify } from 'jose';
const jwks = createRemoteJWKSet(new URL('https://example.eu.auth0.com/.well-known/jwks.json'));
export async function verifyAuth0AccessToken(token: string) {
const { payload } = await jwtVerify(token, jwks, {
issuer: 'https://example.eu.auth0.com/', // Auth0 issuers end with a slash
audience: 'https://api.example.com',
});
return payload; // payload.sub is the Auth0 user ID
}
Schritt 6: Access-Token erneuern
import { SocialLogin } from '@capgo/capacitor-social-login';
export async function getValidAccessToken() {
const { isLoggedIn } = await SocialLogin.isLoggedIn({ provider: 'oauth2', providerId: 'auth0' });
if (!isLoggedIn) {
// access token expired: try the stored refresh token
try {
await SocialLogin.refresh({ provider: 'oauth2', options: { providerId: 'auth0' } });
} catch {
// refresh token expired, revoked or already rotated: sign in again
const loginResult = await signInWithAuth0();
if (!loginResult) throw new Error('Sign-in cancelled');
}
}
const { accessToken } = await SocialLogin.getAuthorizationCode({
provider: 'oauth2',
providerId: 'auth0',
});
return accessToken;
}
refresh wird abgelehnt, wenn der Refresh-Token abgelaufen oder widerrufen wurde. Bei aktivierter Rotation schlägt auch ein bereits verwendeter Refresh-Token fehl; leiten Sie den User daher bei einer Ablehnung immer zum Login zurück.
refresh verwendet den beim Login gespeicherten Refresh-Token des Plugins. Wenn Sie den Refresh-Token selbst verwalten, rufen Sie SocialLogin.refreshToken({ provider: 'oauth2', providerId: 'auth0', refreshToken })auf, was die vollständige neue Token-Response zurückgibt. Speichern Sie bei aktivierter Rotation den neuen Refresh-Token jedes Mal.
Das Plugin speichert seine Token-Kopie im app-privaten Speicher. Falls Ihr Security-Review Keychain oder Keystore verlangt, speichern Sie den Refresh-Token mit NativeBiometric.setData({ key, value }) von @capgo/capacitor-native-biometric und übergeben Sie ihn an refreshToken dich selbst. Unsere Anleitung zur sicheren Token-Speicherung vergleicht die Optionen.
Schritt 7: Abmelden
await SocialLogin.logout({ provider: 'oauth2', providerId: 'auth0' });
Das Plugin löscht die gespeicherten Token und öffnet dann logoutUrl mit id_token_hint und post_logout_redirect_uri damit Auth0 das Session-Cookie löscht. Der /oidc/logout Endpoint versteht diese Parameter; das Redirect-Ziel muss in den Allowed Logout URLs eingetragen sein.
Auf iOS und Android öffnet sich die End-Session-Seite im Systembrowser, wodurch der User die App kurzzeitig verlässt. Wenn du nur einen lokalen Logout möchtest, lass logoutUrl und übergeben prompt: 'login' beim nächsten Login, damit eine gespeicherte Auth0-Session den Nutzer nicht sofort wieder automatisch anmeldet. Einstellen von iosPrefersEphemeralSession: true verhindert zudem geteilte Cookies unter iOS, allerdings ohne SSO zwischen den Apps.
Fehlerbehebung
| Fehler | Ursache | Lösung |
|---|---|---|
Callback URL mismatch |
Redirect-URL nicht in den erlaubten Callback-URLs | Kopiere den exakten Wert von code ins Dashboard; prüfe Scheme und Host |
Unauthorized oder invalid_client unter /oauth/token |
App-Typ ist nicht Native oder Auth-Methode ist nicht None | Als Native neu erstellen, Token Endpoint Auth auf None setzen |
Nein refreshToken im Ergebnis |
offline_access fehlt oder API erlaubt keinen Offline-Zugriff |
Scope hinzufügen, Allow Offline Access aktivieren und Rotation einschalten |
| Access Token kann nicht von API verifiziert werden | Nein audience gesendet |
Füge audience in additionalParameters |
| App öffnet sich nach Login unter Android nicht erneut | Intent-Filter-Scheme oder Host stimmt nicht überein redirectUrl |
Abgleichen com.example.app und oauth genau |
OAuth2 provider "auth0" not configured |
login aufgerufen vor initialize oder Key-Mismatch |
Await initialize beim Start dieselbe verwenden providerId |
| Logout-Seite bleibt offen | post_logout_redirect_uri nicht in den Allowed Logout URLs |
Hinzufügen oder lokalen Logout nutzen |
| Web CORS-Fehler beim Token-Exchange | Origin nicht in Allowed Web Origins | Web-Origin hinzufügen oder eine SPA-Anwendung für das Web verwenden |
Setzen Sie logsEnabled: true in der Provider-Konfiguration, um die generierten URLs und Details zum Token-Austausch während des Debuggings auszugeben.
Auth-Fixes ohne Store-Release ausrollen
Das Ändern von Scopes, der Audience oder das Error-Handling erfolgt nur in TypeScript. Mit Capgo Live Updates kannst du diese Fixes noch am selben Tag an die User ausrollen. Native Änderungen wie ein neuer Intent Filter erfordern weiterhin ein neues Binary, was Capgo Build in der Cloud erzeugen kann.