OpenIddict

認証ライブラリ.NETOAuth2OpenID ConnectASP.NET CoreJWT認証認可

認証ライブラリ

OpenIddict

概要

OpenIddictは、.NETアプリケーション向けの柔軟で汎用性の高いOAuth 2.0/OpenID Connectスタックです。

詳細

OpenIddictは、.NETアプリケーションでOAuth 2.0およびOpenID Connectプロトコルの実装を提供するモダンな認証フレームワークです。Microsoftが開発し、オープンソースとして提供されています。OpenIddictは3つの独立したスタック(サーバー、クライアント、バリデーション)を提供し、開発者のニーズに応じて個別に設定できます。プロトコルレベルでの完全なコントロールを提供し、カスタム認証ロジックの実装を可能にします。Entity Framework Core、MongoDB、カスタムプロバイダーなど複数のデータストレージオプションをサポートし、ASP.NET CoreやOWIN/Katanaなど様々なホスティングプラットフォームと統合できます。OpenIddictは認証プロセスのOAuth 2.0/OpenID Connect側面にのみ焦点を当て、ユーザー認証は実装者に委ねることで高い柔軟性を実現しています。

メリット・デメリット

メリット

  • プロトコル準拠: OAuth 2.0とOpenID Connectの完全実装
  • 高い柔軟性: 3つの独立したスタック(サーバー、クライアント、バリデーション)
  • 複数フロー対応: 認可コード、クライアントクレデンシャル、ハイブリッドフローなど全対応
  • ストレージ抽象化: Entity Framework、MongoDB、カスタムプロバイダー対応
  • プラットフォーム独立: ASP.NET Core、OWIN対応
  • エンタープライズ対応: 大規模アプリケーションでの実用性
  • OpenID Connect認証: 認証プロバイダー認証ツールでの認証実装対応

デメリット

  • 学習コスト: OAuth 2.0/OpenID Connect知識が必要
  • 設定複雑さ: 高度な機能には詳細な設定が必要
  • ドキュメント: 高度な用途では詳細なドキュメント参照が必要
  • デバッグ難易度: プロトコルレベルの問題の診断が困難
  • .NET依存: .NET エコシステム外では使用不可

主要リンク

書き方の例

基本的なサーバー設定

// Program.cs または Startup.cs
services.AddOpenIddict()
    .AddServer(options =>
    {
        // エンドポイントの設定
        options.SetTokenEndpointUris("connect/token")
               .SetAuthorizationEndpointUris("connect/authorize");

        // グラントタイプの有効化
        options.AllowAuthorizationCodeFlow()
               .AllowClientCredentialsFlow()
               .AllowRefreshTokenFlow();

        // 開発用証明書の追加
        options.AddDevelopmentEncryptionCertificate()
               .AddDevelopmentSigningCertificate();

        // ASP.NET Core統合
        options.UseAspNetCore()
               .EnableTokenEndpointPassthrough()
               .EnableAuthorizationEndpointPassthrough();
    });

クライアント設定(外部プロバイダー)

services.AddOpenIddict()
    .AddClient(options =>
    {
        // 認可コードフローの有効化
        options.AllowAuthorizationCodeFlow();

        // 暗号化証明書の設定
        options.AddDevelopmentEncryptionCertificate()
               .AddDevelopmentSigningCertificate();

        // ASP.NET Core統合
        options.UseAspNetCore()
               .EnableRedirectionEndpointPassthrough();

        // System.Net.Http統合
        options.UseSystemNetHttp();

        // GitHub統合
        options.UseWebProviders()
               .AddGitHub(options =>
               {
                   options.SetClientId("your-client-id")
                          .SetClientSecret("your-client-secret")
                          .SetRedirectUri("callback/login/github");
               });
    });

トークン検証設定

services.AddOpenIddict()
    .AddValidation(options =>
    {
        // OpenID Connect Discovery使用
        options.SetIssuer("https://localhost:44319/");

        // 暗号化キーの設定
        options.AddEncryptionKey(new SymmetricSecurityKey(
            Convert.FromBase64String("your-encryption-key")));

        // System.Net.Http統合
        options.UseSystemNetHttp();

        // ASP.NET Core統合
        options.UseAspNetCore();
    });

認証コントローラー

[ApiController]
public class AuthorizationController : ControllerBase
{
    [HttpPost("~/connect/token")]
    public async Task<IActionResult> Exchange()
    {
        var request = HttpContext.GetOpenIddictServerRequest();
        
        if (request.IsClientCredentialsGrantType())
        {
            var identity = new ClaimsIdentity(
                TokenValidationParameters.DefaultAuthenticationType, 
                Claims.Name, 
                Claims.Role);

            identity.SetClaim(Claims.Subject, request.ClientId);
            identity.SetClaim(Claims.Name, "API Client");

            identity.SetDestinations(static claim => claim.Type switch
            {
                Claims.Name when claim.Subject.HasScope(Scopes.Profile)
                    => new[] { Destinations.AccessToken, Destinations.IdentityToken },
                _ => new[] { Destinations.AccessToken }
            });

            return SignIn(new ClaimsPrincipal(identity), 
                         OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
        }

        throw new NotImplementedException("The specified grant is not implemented.");
    }
}

クライアントクレデンシャルフロー使用

// DIコンテナからサービス取得
var service = serviceProvider.GetRequiredService<OpenIddictClientService>();

// アクセストークン取得
var result = await service.AuthenticateWithClientCredentialsAsync(new());
var accessToken = result.AccessToken;

// APIリクエストで使用
using var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = 
    new AuthenticationHeaderValue("Bearer", accessToken);

var response = await httpClient.GetAsync("https://api.example.com/data");

アプリケーション登録(プログラム)

public class Worker : IHostedService
{
    private readonly IServiceProvider _serviceProvider;

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        using var scope = _serviceProvider.CreateScope();
        var manager = scope.ServiceProvider
            .GetRequiredService<IOpenIddictApplicationManager>();

        if (await manager.FindByClientIdAsync("api-client") is null)
        {
            await manager.CreateAsync(new OpenIddictApplicationDescriptor
            {
                ClientId = "api-client",
                ClientSecret = "your-client-secret",
                Permissions =
                {
                    Permissions.Endpoints.Token,
                    Permissions.GrantTypes.ClientCredentials,
                    Permissions.Scopes.Profile
                }
            });
        }
    }
}

高度な設定(カスタムイベントハンドラー)

services.AddOpenIddict()
    .AddServer(options =>
    {
        options.AddEventHandler<HandleConfigurationRequestContext>(builder =>
            builder.UseInlineHandler(context =>
            {
                // カスタムメタデータの追加
                context.Metadata["custom_metadata"] = "custom_value";
                return default;
            }));
    });