简体   繁体   English

在Swashbuckle中启用Oauth2客户端凭据流

[英]Enable Oauth2 client credentials flow in Swashbuckle

Im using IdentityServer3 to secure a Web API with the client credentials grant. 我使用IdentityServer3来保护具有客户端凭据授权的Web API。 For documentation Im using Swashbuckle but can't figure out how to enable Oauth2 in the SwaggerConfig for the client credentials (application) flow. 对于文档我使用Swashbuckle但无法弄清楚如何在SwaggerConfig中为客户端凭据(应用程序)流启用Oauth2。 Any help would be appreciated! 任何帮助,将不胜感激!

I was able to get this working. 我能够让这个工作。 Most of the answer can be found here . 大部分答案都可以在这里找到。

There were a few parts I had to change to get the client_credential grant to work. 我必须更改一些部分才能使client_credential授权生效。 The first part is in the EnableSwagger and EnableSwaggerUi calls: 第一部分是在EnableSwagger和EnableSwaggerUi调用中:

config.EnableSwagger(c => 
  {
    c.SingleApiVersion("v1", "sample api");
    c.OAuth2("oauth2")
     .Description("client credentials grant flow")
     .Flow("application")
     .Scopes(scopes => scopes.Add("sampleapi", "try out the sample api"))
     .TokenUrl("http://authuri/token");
    c.OperationFilter<AssignOAuth2SecurityRequirements>();
  }).EnableSwaggerUi(c =>
  {
    c.EnableOAuth2Support("sampleapi", "samplerealm", "Swagger UI");
  });

The important change here is .Flow("application") I also used the .TokenUrl call instead of .AuthorizationUrl This is just dependent on your particular authorization scheme is set up. 这里的重要变化是.Flow("application")我也使用了.TokenUrl调用而不是.AuthorizationUrl这只是依赖于您设置的特定授权方案。

I also used a slightly different AssignOAuth2SecurityRequirements class 我还使用了稍微不同的AssignOAuth2SecurityRequirements

public class AssignOAuth2SecurityRequirements : IOperationFilter
{
    public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
    {
      var authorized = apiDescription.ActionDescriptor.GetCustomAttributes<AuthorizeAttribute>();
      if (!authorized.Any()) return;

      if (operation.security == null)
          operation.security = new List<IDictionary<string, IEnumerable<string>>>();

      var oAuthRequirements = new Dictionary<string, IEnumerable<string>>
      {
          {"oauth2", Enumerable.Empty<string>()}
      };

      operation.security.Add(oAuthRequirements);
    }
}

This should be sufficient to get the authentication switch to show. 这应足以让身份验证开关显示。 The other problem for me was that the default authentication dialog is set up so a user just has to select a scope and then click authorize. 另一个问题是我设置了默认的身份验证对话框,因此用户只需选择一个范围,然后单击“授权”。 In my case this didn't work due to the way I have authentication set up. 在我的情况下,由于我设置了身份验证的方式,这不起作用。 I had to re-write the dialog in the swagger-oauth.js script and inject it into the SwaggerUI. 我不得不在swagger-oauth.js脚本中重新编写对话框并将其注入SwaggerUI。

I had a bit more trouble getting this all working, but after a lot of perseverance I found a solution that works without having to inject any JavaScript into the SwaggerUI. 我在使这一切工作时遇到了一些麻烦,但经过大量的坚持后,我找到了一个无需将任何JavaScript注入SwaggerUI的解决方案。 NOTE: Part of my difficulties might have been due to using IdentityServer3, which is a great product, just didn't know about a configuration issue. 注意:我的部分困难可能是由于使用IdentityServer3,这是一个很棒的产品,只是不知道配置问题。

Most of my changes are similar to bills answer above, but my Operation Filter is different. 我的大多数更改都类似于上面的账单回答,但我的操作过滤器是不同的。 In my controller all the methods have an Authorize tag with no Roles like so: 在我的控制器中,所有方法都有一个没有角色的Authorize标签,如下所示:

[Authorize]
// Not this
[Authorize(Roles = "Read")] // This doesn't work for me.

With no Roles defined on the Authorize tag the OperationFilter looks like this: 如果Authorize标记上没有定义Roles,则OperationFilter如下所示:

    public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
    {
        // Correspond each "Authorize" role to an oauth2 scope, since I don't have any "Roles" defined, this didn't work
        // and is in most of the Apply methods I found online.  If you are like me and your [Authorize] tag doesn't contain
        // any roles this will not work.
        //var scopes = apiDescription.ActionDescriptor.GetFilterPipeline()
        //    .Select(filterInfo => filterInfo.Instance)
        //    .OfType<AuthorizeAttribute>()
        //    .SelectMany(attr => attr.Roles.Split(','))
        //    .Distinct();

        var scopes = new List<string>() { "Read" }; // For me I just had one scope that is added to all all my methods, you might have to be more selective on how scopes are added.

        if (scopes.Any())
        {
            if (operation.security == null)
                operation.security = new List<IDictionary<string, IEnumerable<string>>>();

            var oAuthRequirements = new Dictionary<string, IEnumerable<string>>
            {
                { "oauth2", scopes }
            };

            operation.security.Add(oAuthRequirements);
        }
    }

The SwaggerConfig looks like this: SwaggerConfig看起来像这样:

public static void Register()
{
    var thisAssembly = typeof(SwaggerConfig).Assembly;
    GlobalConfiguration.Configuration
        .EnableSwagger(c =>
        {
           c.SingleApiVersion("v1", "waPortal");
           c.OAuth2("oauth2")
                .Description("OAuth2 Client Credentials Grant Flow")
                .Flow("application")
                .TokenUrl("http://security.RogueOne.com/core/connect/token")
                .Scopes(scopes =>
                {
                    scopes.Add("Read", "Read access to protected resources");
                });
            c.IncludeXmlComments(GetXmlCommentsPath());
            c.UseFullTypeNameInSchemaIds();
            c.DescribeAllEnumsAsStrings();
            c.OperationFilter<AssignOAuth2SecurityRequirements>();
        })
        .EnableSwaggerUi(c =>
        {
            c.EnableOAuth2Support(
                clientId: "swaggerUI",
                clientSecret: "BigSecretWooH00",
                realm: "swagger-realm",
                appName: "Swagger UI"
            );
        });
}

The last part was the hardest to figure out, which I finally did with the help of the Chrome Developer tools that showed a little red X on the network tag showing the following error message: 最后一部分是最难以弄清楚的,我最终在Chrome开发者工具的帮助下做了这一点,在网络标记上显示了一点红色X,显示以下错误消息:

XMLHttpRequest cannot load http://security.RogueOne.com/core/connect/token. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:62561' is therefore not allowed access.

I described this error here Swagger UI not parsing reponse which was due to IdentityServer3 correctly not adding a response header of "Access-Control-Allow-Origin: http://localhost:62561 " You can force IdentityServer3 to send that header by updating you client creation to be the following: 我在这里描述了这个错误Swagger UI没有解析响应 ,这是由于IdentityServer3正确没有添加响应头“Access-Control-Allow-Origin: http:// localhost:62561 ”你可以通过更新你强制IdentityServer3发送该头客户端创建如下:

new Client
{
    ClientName = "SwaggerUI",
    Enabled = true,
    ClientId = "swaggerUI",
    ClientSecrets = new List<Secret>
    {
        new Secret("PasswordGoesHere".Sha256())
    },
    Flow = Flows.ClientCredentials,
    AllowClientCredentialsOnly = true,
    AllowedScopes = new List<string>
    {
        "Read"
    },

    Claims = new List<Claim>
    {
        new Claim("client_type", "headless"),
        new Claim("client_owner", "Portal"),
        new Claim("app_detail", "allow")
    },
    PrefixClientClaims = false
    // Add the AllowedCorOrigins to get the Access-Control-Allow-Origin header to be inserted for the following domains
    ,AllowedCorsOrigins = new List<string>
    {
        "http://localhost:62561/"
        ,"http://portaldev.RogueOne.com"
        ,"https://portaldev.RogueOne.com"
    }
}    

The AllowedCorsOrigins was the last piece of my puzzle. AllowedCorsOrigins是我拼图的最后一块。 Hopefully this helps someone else who is facing the same issue 希望这可以帮助面临同样问题的其他人

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM