CapacitorアプリでMicrosoft Entra ID (旧Azure Active Directory) を使ってサインインするには、Entra管理センターでパブリッククライアントアプリを登録し、追加します モバイルアプリとデスクトップアプリ redirect URI として、 com.example.app://oauth/azure、システムブラウザ経由で PKCE を使用した OpenID Connect 認可 code フローを実行します。 @capgo/capacitor-social-login つまり1つ initialize Microsoft identity platform v2.0 エンドポイントへの呼び出しと SocialLogin.login({ provider: 'oauth2', options: { providerId: 'azure' } }).
本ガイドでは、アプリ登録、適切な authority の選択、Graph およびカスタム API スコープ、トークン検証、Conditional Access、ログアウト、および発生しうる AADSTS エラーについて説明します。対象は 2026年10月の Capacitor 8 です。
必要なもの
- Microsoft Entra テナントと、アプリ登録を作成できるロール(Application Developer 以上)。
- Capacitor 8 アプリとその
appId、これをリダイレクトスキームとして再利用します。 - 必要に応じて、Entra トークンを受け取るバックエンド API。
WebView内でMSAL.jsを使わない理由
MSAL.js は、オリジンを所有しポップアップを開けるブラウザ向けに設計されています。Capacitor WebView ではオリジンは capacitor://localhost (iOS)と https://localhost Android ではポップアップがブロックされ、フルページリダイレクトはアプリを自身の bundle から離れてしまいます。ネイティブアプリ向けの Microsoft のガイダンスは、PKCE(RFC 8252)付きシステムブラウザであり、プラグインもこれを実装しています:
| プラットフォーム | 使用ブラウザ | 追加セットアップ |
|---|---|---|
| iOSと | ASWebAuthenticationSession |
なし |
| Androidでは | Chrome Custom Tabs と androidUseCustomTabs: true |
Intent フィルター |
| Web | ポップアップまたはリダイレクト | SPA リダイレクト URI |
Android では Custom Tabs をオンにしてください。デフォルトの埋め込み WebView は、Conditional Access がしばしば依存する Microsoft Authenticator、パスキー、Company Portal と連携できません。
ステップ1: Entra IDにアプリを登録する
- 次を開きます: Microsoft Entra 管理センター に移動し Entra ID > アプリの登録 > 新規登録.
- 「Example Mobile」などの名前を入力します。
- 選ぶ サポートされているアカウントの種類。これにより、後で使用される authority が決まります。
- リダイレクト URI は一旦スキップし、 新規登録.
- オンラインに 概要、コピー アプリケーション (クライアント) ID と ディレクトリ (テナント) ID.
authorityを選択
| 登録時のアカウントタイプ | URL内のAuthorityセグメント |
|---|---|
| シングルテナント (自社組織のみ) | テナントID、例 8f1c... |
| 任意の組織ディレクトリ | organizations |
| 任意の組織ディレクトリおよび個人用Microsoftアカウント | common |
| 個人用 Microsoft アカウントのみ | consumers |
可能な限りテナント ID を使用してください。発行元が予測可能になり、他のテナントからの誤ったサインインを防げます。
リダイレクトURIを追加する
以下に移動します: Authentication > Add a platform:
- モバイルアプリとデスクトップアプリ:追加
com.example.app://oauth/azureをカスタムリダイレクト URI として追加します。com.example.app://oauth/logoutログアウト後のリダイレクトが必要な場合は、こちらにも追加してください。 - シングルページアプリケーション (Webビルドを配信する場合のみ):追加
https://app.example.com/auth/callback.
モバイルURIは以下に登録しないでください: Web。Webプラットフォームのリダイレクトは Confidential Client であり、トークンエンドポイントでシークレットが必要です。
API の権限
API の権限 は既に含まれています Microsoft Graph > User.Read. 追加 offline_access, openid, profile と email 一覧にない場合。一部のテナントではユーザーの同意がブロックされます。その場合、管理者は 「管理者の同意を与える」をクリックする必要があります。そうしないと、ユーザーには AADSTS65001 エラーや「管理者の承認が必要です」画面が表示されます。
独自のAPI用のスコープを公開する(任意)
バックエンドでトークンを受け入れる場合:
- 開く APIを公開し、 Application ID URIを設定します(通常は以下)。
api://<client-id>. - 次の名前でスコープを追加:
access_as_user、同意できるユーザー:管理者とユーザー。 - では マニフェスト、設定する
"requestedAccessTokenVersion": 2これにより、アクセストークンがv2.0発行者形式になります。
ステップ2: プラグインをインストールしてintent filterを追加
bun add @capgo/capacitor-social-login
bunx cap sync
では android/app/src/main/AndroidManifest.xml、内側 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>
保持 android:launchMode="singleTask" で MainActivity (Capacitor のデフォルト)。Google、Apple、Facebook ログインを使わない場合は、以下で無効にしてください。 plugins.SocialLogin.providers における capacitor.config.ts SDK がビルドに含まれないようにするためです。
ステップ3: 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,
},
},
});
}
を使用すると、 resourceUrl Graph を指す /me、プラグインはログイン直後に Graph を呼び出し、プロファイルをその中に含めて返します resourceData.
Ionic Auth Connect から移行する場合、 SocialLoginAuthConnect があります。 azure だけで済むプリセット tenantId, clientId と redirectUrl。参照: Entra ID 連携ページ.
ステップ 4: サインイン
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;
}
}
便利なオプション:
loginHint: 'jane@contoso.com'そのユーザーのアカウントを事前入力し、アカウント選択画面をスキップします。prompt: 'select_account'常にアカウント選択画面を表示します。共有デバイスを利用する場合に便利です。additionalParameters: { domain_hint: 'contoso.com' }ユーザーを直接フェデレーション済みの IdP へリダイレクトします。
mail であることが多いです。 null メールボックスのないアカウント向けです。フォールバック先: userPrincipalName または preferred_username IDトークンのクレームです。
ステップ5: 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();
}
必要な各Graphスコープを追加します(例: Calendars.Read) を両方に API の権限 および scope 文字列に。
ステップ6: 独自のAPI用のトークンを取得する
アクセストークンは1つのリソースを対象とします。バックエンドを呼び出すには、 azure の隣に同じ oauth2 の initialize 呼び出し内で、Graphの代わりにAPIスコープを要求する2番目のエントリを追加します:
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,
},
},
});
ユーザーはすでにMicrosoftセッションを持っているため、2回目のログインは通常プロンプトなしで完了します。サーバーでトークンを検証します:
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
}
マルチテナントアプリの場合、issuer には各顧客のテナント ID が含まれます。 tid 固定の issuer ではなく、許可するテナントのリストと claim を照合してください。ユーザーは oid と tidで識別し、メールではなく
ステップ 7: リフレッシュとサインアウト
// 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 のパブリッククライアント向けリフレッシュトークンは、スライディングウィンドウで最大90日間有効で、使用のたびに置き換えられます。プラグインが新しいトークンを保存します。トークンを自分で保存する場合は、たとえば Keychain や Keystore を使用してください。 NativeBiometric.setData と @capgo/capacitor-native-biometric、それらを渡します SocialLogin.refreshToken.
logout はローカルトークンをクリアし、システムブラウザで v2.0 ログアウトエンドポイントを開きます。Microsoft はカスタムスキームへのリダイレクトの代わりに「アカウントからサインアウトしました」ページを表示することがあるため、ユーザーがそれを閉じる必要がある場合があります。ローカルログアウトのみ必要な場合は、 logoutUrl 渡します prompt: 'select_account' 次回のログイン時に削除してください。
Conditional Access と Intune
ブラウザベースの OIDC は、MFA、パスワードレス、ほとんどの Conditional Access ポリシーで動作します。次の2つのケースでは追加対応が必要です:
- 準拠デバイスまたはハイブリッド参加デバイスを要求:Entra がデバイス状態を認識できる必要があります。Android では、Company Portal または Authenticator がインストールされていれば Custom Tabs 経由で動作します。管理対象の iOS デバイスでは、MDM がデプロイした Microsoft Enterprise SSO プラグインがこれを提供します。実際のポリシーを使って早めにテストしてください。
- Intune アプリ保護ポリシー (MAM):これらにはネイティブアプリ内の Intune App SDK が必要です。OIDC だけでは「アプリ保護ポリシーを要求する」許可コントロールを満たしません。
トラブルシューティング
| エラー | 意味 | 解決策 |
|---|---|---|
| AADSTS50011 | リダイレクト URI の不一致 | 「モバイル アプリとデスクトップ アプリ」に正確な URI を追加してください |
| AADSTS7000218 | トークンリクエストにはシークレットが必要です | リダイレクト URI が「Web」に登録されています。「モバイル アプリとデスクトップ アプリ」に移動してください |
| AADSTS9002326 | クロスオリジンでのトークン交換は SPA でのみ許可されています | Web ビルドがモバイル用のリダイレクト URI を使用しています。Web URL を「シングルページ アプリケーション」に登録してください |
| AADSTS700016 | ディレクトリにアプリが見つかりません | Authority のテナントが間違っているか、シングルテナント アプリが併用されています common |
| AADSTS50020 | 別テナントのユーザーアカウントです | マルチテナント登録を使用するか、ユーザーをゲストとして招待してください |
| AADSTS65001 | 同意が付与されていません | 管理者の同意を付与するか、ユーザーの同意を許可してください |
| AADSTS53003 | 条件付きアクセスによってブロックされました | サインインログを確認してください(上記のセクションを参照) |
| AADSTS28000 | 1つのリクエストに複数リソースのスコープが含まれています | Graph と API のスコープは、別々のログインでリクエストしてください |
| リフレッシュトークンなし | offline_access 不足 |
スコープと権限に追加してください |
Entra 管理センター サインインログ 各試行の正確な失敗理由を表示し、 logsEnabled: true プラグイン設定で、デバイス上に authorize URL とトークンレスポンスを出力します。
アップデートの配信
Policy changes on the Entra side are instant, but client-side fixes still need to reach devices. TypeScript changes such as scopes or error handling can ship through Capgo Live Updates。ネイティブの変更(インテントフィルター、新しいスキームなど)にはストア向けの新しいビルドが必要ですが、 Capgo のビルド はローカルの Mac なしで生成できます。