認証・シングルサインオン(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 として以下を登録してください。

https://your-app.example.com/signin-oidc

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 が続く → アクセストークンが失効している可能性。リフレッシュしてから再送

次: REST API ドキュメントへ →