OpenIDDictを使用した認証サーバーの設定


This article is part of a series called Setting up an Authorization Server with OpenIddict. The articles in this series will guide you through the process of setting up an OAuth2 + OpenID Connect authorization server on the the ASPNET Core platform using OpenIddict.




  • パートIV :認可コードフロー


  • ロビンソン / 認証サーバ


    OpenIDDictで実装された認証サーバ。


    この部分ではAuthorization Code Flow with PKCE extension . このフローは、単一ページアプリケーション(SPAの)とネイティブ/モバイルアプリケーションのための推奨される流れです.

    OpenIDDictの設定


    まず、認証コードフローを有効にする必要がありますStartup.cs :
    public void ConfigureServices(IServiceCollection services)
    {
        ...
    
        services.AddOpenIddict()
    
            ...
    
            .AddServer(options =>
            {
                options.AllowAuthorizationCodeFlow().RequireProofKeyForCodeExchange();
    
                ...
    
                options
                    .SetAuthorizationEndpointUris("/connect/authorize")
                    .SetTokenEndpointUris("/connect/token");
    
                ...
    
                options
                    .UseAspNetCore()
                    .EnableTokenEndpointPassthrough()
                    .EnableAuthorizationEndpointPassthrough(); 
            });
    
            ...
    }
    
    呼び出しAllowAuthorizationCodeFlow フローを有効にします.RequireProofKeyForCodeExchange の直後に呼び出されます.これは、すべてのクライアントがPKCE(コード交換用の証明キー)を使用する必要があることを確認します.
    許可コードフローは、ユーザーが最初に顧客に要求をするために顧客に認可することを決定する.したがって、ユーザーがそれを許すとき、認可コードをクライアントに返す認可エンドポイントを実装する必要があります.
    クライアントは、クライアント資格情報の流れのために既に作成したトークンエンドポイントを呼び出すことで、アクセストークンの認証コードを交換できます.
    まず、承認終了点を作成しますEnableAuthorizationEndpointPassthrough インStartup.cs コントローラ内でエンドポイントを実装することができます.
    その後、クライアントがアクセストークンの認証コードを交換できるように、トークンエンドポイントにいくつかのマイナーな調整を行います.

    承認終了点


    認証エンドポイントのように、認証コントローラの認証終了点を実装します.
    [HttpGet("~/connect/authorize")]
    [HttpPost("~/connect/authorize")]
    [IgnoreAntiforgeryToken]
    public async Task<IActionResult> Authorize()
    {
        var request = HttpContext.GetOpenIddictServerRequest() ??
            throw new InvalidOperationException("The OpenID Connect request cannot be retrieved.");
    
        // Retrieve the user principal stored in the authentication cookie.
        var result = await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);
    
        // If the user principal can't be extracted, redirect the user to the login page.
        if (!result.Succeeded)
        {
            return Challenge(
                authenticationSchemes: CookieAuthenticationDefaults.AuthenticationScheme,
                properties: new AuthenticationProperties
                {
                    RedirectUri = Request.PathBase + Request.Path + QueryString.Create(
                        Request.HasFormContentType ? Request.Form.ToList() : Request.Query.ToList())
                });
        }
    
        // Create a new claims principal
        var claims = new List<Claim>
        {
            // 'subject' claim which is required
            new Claim(OpenIddictConstants.Claims.Subject, result.Principal.Identity.Name),
            new Claim("some claim", "some value").SetDestinations(OpenIddictConstants.Destinations.AccessToken)
        };
    
        var claimsIdentity = new ClaimsIdentity(claims, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
    
        var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
    
        // Set requested scopes (this is not done automatically)
        claimsPrincipal.SetScopes(request.GetScopes());
    
        // Signing in with the OpenIddict authentiction scheme trigger OpenIddict to issue a code (which can be exchanged for an access token)
        return SignIn(claimsPrincipal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
    }
    
    クライアント資格情報の流れとは異なり、承認コードフローにはエンドユーザーが承認されます.我々はすでにプロジェクトで認証を実装していることを覚えておいてください.したがって、Authorizeメソッドでは、ユーザーが既にログインしているかどうかを判断します.
    ユーザーが認証されると、OpenDict認証方式で署名するために使用される新しいクレームプリンシパルが作成されます.
    宛先が設定されている場合、アクセストークンに追加されるプリンシパルにクレームを追加できますAccessToken . The subject クレームが要求され、常にアクセストークンに追加されます.このクレームの目的地を指定する必要はありません.
    クライアントによって要求されるスコープはすべて、呼び出しで与えられますclaimsPrincipal.SetScopes(request.GetScopes()) , 我々は、この例では簡単に物事を維持する同意画面を実装していないため.同意を実装するとき、これは要求されたスコープをフィルターする場所です.
    The SignIn コールはOpenIDDictミドルウェアに対し、トークンエンドポイントを呼び出すことによってクライアントがアクセストークンと交換できる認証コードを送信するようにします.
    認証コードの流れをサポートしているので、トークンエンドポイントを変更する必要があります.
    [HttpPost("~/connect/token"), Produces("application/json")]
    public async Task<IActionResult> Exchange()
    {
        ...
    
        if (request.IsClientCredentialsGrantType())
        {
            ...
        }
    
        else if (request.IsAuthorizationCodeGrantType())
        {
            // Retrieve the claims principal stored in the authorization code
            claimsPrincipal = (await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)).Principal;
        }
    
        else
        {
            throw new InvalidOperationException("The specified grant type is not supported.");
        }
    
        ...
    }
    
    承認メソッドで作成されたクレームプリンシパルは、承認コードに格納されているので、要求からクレームプリンシパルを取得し、SignIn メソッド.OpenIDDictは、アクセストークンで応答します.
    さて、postmanを使用して認証コードフローを使用してアクセストークンを要求できるかどうか確認しましょう.

    これは、クライアントが認可コードフローを使用することを許されず、また、RedirectUris クライアントの.
    Postmanの場合のリダイレクトURIhttps://oauth.pstmn.io/v1/callback . 認証コードは、認証後にここで送信されます.
    更新後、PostmanクライアントTestData.cs 次のようになります.
    ClientId = "postman",
    ClientSecret = "postman-secret",
    DisplayName = "Postman",
    RedirectUris = { new Uri("https://oauth.pstmn.io/v1/callback") },
    Permissions =
    {
        OpenIddictConstants.Permissions.Endpoints.Authorization,
        OpenIddictConstants.Permissions.Endpoints.Token,
    
        OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode,
        OpenIddictConstants.Permissions.GrantTypes.ClientCredentials,
    
        OpenIddictConstants.Permissions.Prefixes.Scope + "api",
    
        OpenIddictConstants.Permissions.ResponseTypes.Code
    }
    
    すべてが正しく動作している場合、自分自身を認証した後、PostManとのアクセストークンを得ることができるはずです.

    Note. A client secret is optional when configuring a client with OpenIddict, this is useful for public clients which aren't able to securely store a client secret. When the client secret is omitted from the configuration, you can also omit it from the request.
    The PKCE-enhanced Authorization Code Flow introduces a secret created by the calling application that can be verified by the authorization server (source). However, PKCE doesn't replace client secrets. PKCE and client secrets are complementary and you should use them both when possible (typically, for server-side apps). ( )



    おめでとう、OpenDictとPKCEと認証コードの流れを実装している!OpenID Connectプロトコルを活用する方法を示します.