如何设置 Swashbuckle 和 Swagger UI 以使用 Open ID Connect Discovery 进行授权,以便它可以提供正确的承载令牌?

Pat*_*ski 7 c# swagger swagger-ui openid-connect swashbuckle

如何设置 Swashbuckle 和 Swagger UI 以使用 Open ID Connect Discovery 进行授权(在我的例子中是 Azure AD)?

到目前为止,这是我的(未完全工作的)SwaggerGen 设置,基于/sf/answers/4630359751/

SwaggerGenOptions c => {
    OpenApiInfo apiInfo = new()  { /* ...snip... */ };
    c.SwaggerDoc("v1", apiInfo);
    IncludeXmlFileForSwagger(c);

    // Defines the Open ID Connect discovery scheme - see also /sf/answers/4630359751/
    OpenApiSecurityScheme mainScheme = new()
    {
        Type = SecuritySchemeType.OpenIdConnect,
        OpenIdConnectUrl = new Uri($"https://login.microsoftonline.com/{myTenantId}/.well-known/openid-configuration"),
    };
    c.AddSecurityDefinition("OpenIdConnect", mainScheme);

    // Adds a reference to the above scheme as required for every API action (we can get more nuanced later)
    //   Note: if just try to use mainScheme instead, it doesn't apply a Bearer token)
    OpenApiSecurityScheme securityScheme = new() {
        Reference = new OpenApiReference {
            Type = ReferenceType.SecurityScheme, Id = "OpenIdConnect"
        }
    };
    OpenApiSecurityRequirement securityRequirements = new() { {securityScheme, Array.Empty<string>()} };
    c.AddSecurityRequirement(securityRequirements);
}
Run Code Online (Sandbox Code Playgroud)

...在静态配置方法中,我...

SwaggerGenOptions c => {
    OpenApiInfo apiInfo = new()  { /* ...snip... */ };
    c.SwaggerDoc("v1", apiInfo);
    IncludeXmlFileForSwagger(c);

    // Defines the Open ID Connect discovery scheme - see also https://stackoverflow.com/a/66147996/7453
    OpenApiSecurityScheme mainScheme = new()
    {
        Type = SecuritySchemeType.OpenIdConnect,
        OpenIdConnectUrl = new Uri($"https://login.microsoftonline.com/{myTenantId}/.well-known/openid-configuration"),
    };
    c.AddSecurityDefinition("OpenIdConnect", mainScheme);

    // Adds a reference to the above scheme as required for every API action (we can get more nuanced later)
    //   Note: if just try to use mainScheme instead, it doesn't apply a Bearer token)
    OpenApiSecurityScheme securityScheme = new() {
        Reference = new OpenApiReference {
            Type = ReferenceType.SecurityScheme, Id = "OpenIdConnect"
        }
    };
    OpenApiSecurityRequirement securityRequirements = new() { {securityScheme, Array.Empty<string>()} };
    c.AddSecurityRequirement(securityRequirements);
}
Run Code Online (Sandbox Code Playgroud)

因此,上面的代码让我可以验证并检查名为“openid”的范围,并显示“锁定的”挂锁。当我尝试通过 Swagger UI 运行操作时,它提供了一个不记名令牌,但它返回无效。

我是否需要以某种方式指定一个显式命名范围,而不是“openid”?如何?

我的设置正确吗?如果是这样,为什么令牌无效?有什么解决问题的想法吗?

Pat*_*ski 3

由于 Swagger UI 使用 Web 浏览器上下文来发出请求,因此我发现只需在顶部提供一个链接,将它们带到任何需要身份验证的 API 调用,然后还对所有内容添加安全要求,就可以更轻松、更简单地进行请求。需要授权的功能。

如果您的 API 自动重定向并使用浏览器功能进行登录(我认为大多数人都会这样做),那么这将起作用。用户登录后,来自 Swagger UI 的所有未来 HTTP 请求都将发送 auth cookie,就像直接访问浏览器中的端点一样。

首先,Startup.cs 中的 Swagger 配置,包括一个方便用户登录的链接:

services.AddSwaggerGen(c => {
    OpenApiInfo apiInfo = new()
    {
        Title = "MyService",
        Version = "v1",
        Description = "<p>An API for working with ... "
            + "<p>If you get '<b>Failed to fetch</b>' below on an action that shows a padlock icon, this likely "
            + "means you are not <b>signed in</b>, so "
            + "<a target=\"_blank\" href=\"/api/v1/security/signIn\">sign in here</a>, then "
            + "your sign-in will take effect for any action below.",
    };
    c.SwaggerDoc("v1", apiInfo);

    /* put other configuration here, such as c.IncludeXmlComments */

    c.OperationFilter<MethodNeedsAuthorizationFilter>(); // puts auth UI on the right actions                                                             
});
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,我的端点/api/v1/security/signIn需要授权;您可以使用需要授权的任何端点。

然后,这是您需要启用适当显示打开的挂锁图标的 MethodNeedsAuthorizationFilter:

using Microsoft.AspNetCore.Authorization;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System;
using System.Collections.Generic;
using System.Linq;
namespace GeneralMills.TradePlannerService;
/// <summary>
/// Provides a method that applies Swagger UI security requirements to all controller actions that need authorization.
/// </summary>
public class MethodNeedsAuthorizationFilter : IOperationFilter
{
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        if (operation is null) throw new ArgumentNullException(nameof(operation));
        if (context is null) throw new ArgumentNullException(nameof(context));

        object[] methodAttributes = context.MethodInfo.GetCustomAttributes(true);

        bool needsAuth =
            methodAttributes.OfType<AuthorizeAttribute>().Any()
            || (context.MethodInfo.DeclaringType != null
                && context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any()
                && !methodAttributes.OfType<AllowAnonymousAttribute>().Any());

        if (needsAuth)
        {
            OpenApiSecurityScheme scheme = new()
            {
                Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "ExampleName" },
                Scheme = "ExampleName",
                Name = "ExampleName",
                In = ParameterLocation.Header,
            };

            operation.Security = new List<OpenApiSecurityRequirement>()
            {
                new () { {scheme, new List<string>() } }
            };
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这有效地规避了 Swagger UI 的内置授权支持,而我无法使用它。