認証・シングルサインオン(Microsoft Entra ID)
本プラットフォームは Microsoft Entra ID(旧 Azure AD)を唯一の ID プロバイダーとして採用しています。すべての API と Web UI は OAuth 2.0 / OpenID Connect によるトークンで認証され、Microsoft 365 / Teams とそのまま連携できます。このページではテナント設定、アプリ登録、トークン取得、On-Behalf-Of フロー、トークン更新、ID/アクセストークンのクレーム例を解説します。
1. Microsoft Entra ID のセットアップ
Entra ID の管理者は、以下の 3 つを用意します。
- テナントID(例:contoso.onmicrosoft.com)
- アプリ登録(Web アプリ用と、Teams 連携する場合はモバイル/デスクトップ用の 2 つ)
- クライアントID / クライアントシークレット(Web アプリ側のみ)
■ リダイレクト URI
Web アプリのリダイレクト URI として以下を登録してください。
2. 認可コードフロー(Web アプリ標準)
ブラウザからのサインインでは OAuth 2.0 Authorization Code Flow を使用します。通常は ASP.NET Core の標準ミドルウェア(Microsoft.Identity.Web)が処理するため、アプリケーション側で手動実装する必要はありません。
■ フロー概要
- 1. ユーザーが /signin エンドポイントにアクセス
- 2. Entra ID のサインイン画面にリダイレクト
- 3. 認可コードがリダイレクト URI に返る
- 4. サーバー側で認可コードをアクセストークン + ID トークン + リフレッシュトークンに交換
- 5. セッション Cookie にトークンを格納
// Program.cs / Startup.cs
builder.Services
.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
3. トークン取得と呼び出し
サーバー側で保持したアクセストークンを API 呼び出しに使います。有効期限切れの場合はリフレッシュトークンで自動更新されます。
■ C# (Microsoft.Identity.Web) — 下流 API を呼ぶ
public class ProjectsController : Controller
{
private readonly ITokenAcquisition _tokens;
public async Task<IActionResult> Index()
{
var token = await _tokens.GetAccessTokenForUserAsync(
new[] { "api://{ClientId}/access_as_user" });
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
var res = await client.GetAsync("https://api.yourdomain.com/api/projects");
return Ok(await res.Content.ReadAsStringAsync());
}
}
■ curl — 取得したトークンで API を叩く
curl https://api.yourdomain.com/api/projects \ -H "Authorization: Bearer eyJ0eXAiOiJKV1Qi..."
4. On-Behalf-Of フロー(Teams SSO → API)
Microsoft Teams のタブアプリからプラットフォーム API を呼ぶ場合、ユーザーのコンテキストを保ったまま下流 API を呼ぶために On-Behalf-Of(OBO)フローを使用します。
- 1. Teams クライアントが getAuthToken() で SSO トークンを取得
- 2. Teams 用アプリ登録のクライアント ID 向けトークン
- 3. サーバー側で受け取ったトークンを OBO で交換し、プラットフォーム API 向けアクセストークンを取得
- 4. そのアクセストークンで API を呼び出す
■ C# — OBO トークン交換
// Receive the Teams SSO token in Authorization header,
// exchange for a platform-API token via OBO.
var teamsToken = Request.Headers["Authorization"]
.ToString().Replace("Bearer ", "");
var platformToken = await _tokens.GetAccessTokenForUserAsync(
scopes: new[] { "api://{PlatformApiClientId}/access_as_user" },
userFlow: null,
authenticationScheme: OpenIdConnectDefaults.AuthenticationScheme,
user: null,
tokenAcquisitionOptions: new TokenAcquisitionOptions
{
LongRunningWebApiSessionKey = TokenAcquisitionOptions.LongRunningWebApiSessionKeyAuto
});
5. オーディエンス(aud)とスコープ
API を叩くアクセストークンは、対象リソースに正しい aud を持っていなければなりません。
本プラットフォームの API のオーディエンスは api://{ClientId} です(ClientId は Web API 側アプリ登録の値)。
■ 代表的なスコープ
access_as_user— access_as_user — API の全エンドポイントに対するユーザー委任アクセスUser.Read— User.Read — Microsoft Graph 連携時の最小スコープ(プロフィール/メール取得)
6. トークン更新
アクセストークンは標準 1 時間で失効します。Microsoft.Identity.Web を使っていれば、リフレッシュトークンを用いて自動で更新します。手動でリフレッシュトークンを扱う場合は以下の注意点を守ってください。
- リフレッシュトークンはサーバー側だけで保管し、ブラウザに返さない
- 同一 refresh_token を並行使用しない(ローテーション検知で失効する)
- エラー invalid_grant が返ったら必ずサインイン画面へリダイレクト
7. ID トークンとアクセストークンのクレーム例
アプリケーション側でデコードして使える代表的なクレームです(例は本プラットフォームで実際に取得したものを一部マスクしています)。
■ ID トークンのクレーム例
{
"aud": "{ClientId}",
"iss": "https://login.microsoftonline.com/{TenantId}/v2.0",
"iat": 1737123456,
"exp": 1737127056,
"name": "山田 太郎",
"preferred_username": "yamada@contoso.onmicrosoft.com",
"oid": "00000000-0000-0000-0000-000000000000",
"tid": "{TenantId}",
"ver": "2.0"
}
■ アクセストークンのクレーム例
{
"aud": "api://{ClientId}",
"iss": "https://sts.windows.net/{TenantId}/",
"iat": 1737123456,
"exp": 1737127056,
"scp": "access_as_user",
"oid": "00000000-0000-0000-0000-000000000000",
"tid": "{TenantId}",
"ver": "2.0"
}
8. よくあるエラーと対処
- aud が一致しない → API 側アプリ登録のスコープ(access_as_user など)を明示的にリクエストしているか確認
- AADSTS65001(consent_required)→ テナント管理者による管理者同意が未付与。管理者で一度同意を実行
- 署名検証エラー → 公開鍵のキャッシュを確認(Microsoft.Identity.Web が通常は自動更新)
- 401 Unauthorized が続く → アクセストークンが失効している可能性。リフレッシュしてから再送