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

How do I set up Swashbuckle and Swagger UI to Authorize using Open ID Connect Discovery so it can supply the right Bearer token?

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

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

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

    // Defines the Open ID Connect discovery scheme - see also 
    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);
}

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

app.UseSwaggerUI(c =>
{
    c.SwaggerEndpoint("/swagger/v1/swagger.json", "MyService v1");
    c.OAuthClientId(myClientId);
    c.OAuthUsePkce();
});

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

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

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

由于 Swagger UI 使用网络浏览器上下文来发出请求,我发现在顶部提供一个 link 将它们带到任何 API需要授权的调用,然后在所有需要授权的功能上也添加安全要求。

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

首先,Startup.cs 中的 Swagger 配置,包括用于登录的 link user-friendly:

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                                                             
});

在上面的示例中,我的端点 /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>() } }
            };
        }
    }
}

这有效地规避了 Swagger UI 的 built-in 授权支持,我无法使用它。