当授权标记不包含角色时,我不会得到范围复选框,Ajax授权请求也不会发送范围

时间:2022-03-08 09:39:58

UPDATE 2: If I change my controller Authorize tag from this

更新2:如果我改变我的控制器授权标签从这个

[Authorize]

to this

这个

[Authorize(Roles = "Read")]

then I get the checkbox for scope selection and the ajax token request contains the correct scope and completes successfully. I still end up with a red exclamation mark however. It looks like Swagger or Swashbuckle is requiring that the roles match the scope definitions? Is it possible to use the application flow with no Roles defined when using Swashbuckle? And if so how do you get that to work? Do I have to manually set the scope in the operation filter class? If it's not possible to use Swashbuckle without listing Roles in the Authorize tag, then I need to know how to assign clients roles in IdentityServer3.

然后我得到范围选择的复选框,ajax令牌请求包含正确的范围并成功完成。然而,我还是以一个红色的惊叹号结束。看起来像大摇大摆或虚张声势,要求角色匹配范围定义?是否可以在使用Swashbuckle时使用没有定义角色的应用程序流?如果是的话,你怎么让它工作呢?是否需要手动设置操作过滤器类中的范围?如果不列出Authorize标记中的角色就不能使用Swashbuckle,那么我需要知道如何在IdentityServer3中分配客户机角色。

UPDATE 3 If I change the Operation Filter to something like this the scope appears, but after selecting the scope and clicking on Authorize, the page just reloads. The ajax authorization was sent successfully first. This is closer, but the authorization still doesn't stick (not sure what term to use here, but stick seems to sum it up.) How do I get the authorization to stick?

更新3如果我将操作过滤器更改为类似这样的东西,范围就会出现,但是在选择范围并单击Authorize后,页面就会重新加载。首先成功地发送了ajax授权。这更接近了,但是授权仍然没有执行(不确定在这里使用什么术语,但是stick似乎总结了它)。我怎样才能得到授权?

public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{

    var scopes = new List<string>() { "Read" };

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

Original Post I'm trying to configure Swashbuckle to allow clients to test a REST service that is protected by an OAuth2 client credentials flow. The toggle never appears on the page, should it?, but I do get a red circle with an exclamation mark telling me the resource is not protected. I'm using the nuget package Swashbuckle.Core Version 5.4.0. The answer here Enable Oauth2 client credentials flow in Swashbuckle seems to follow what I've done, and have used the AssignOAuth2SecurityRequirements class verbatim. I haven't injected any javascript and don't believe I have to since my authorization scheme is fairly standard. When I remove the Authorize key word on the Controller that method no longer has the red exclamation in the Swagger UI which I'm hoping means I'm close, but I'm not finding the missing link. Since this Flow is "application" and I only have one scope I wanted to make sure that it looks configured correctly and the clientSecret is loaded in the correct spot.

我正在尝试配置Swashbuckle,以允许客户端测试受OAuth2客户端凭据流保护的REST服务。这个切换不会出现在页面上,是吗?但是,我确实得到了一个红色的圆圈,上面有一个感叹号,告诉我资源没有得到保护。我用的是nuget包装的Swashbuckle。5.4.0核心版本。这里的答案是,在Swashbuckle中启用Oauth2客户端凭据流,这似乎符合我的做法,并完全使用了AssignOAuth2SecurityRequirements类。我还没有注入任何javascript,也不相信我必须这样做,因为我的授权方案相当标准。当我删除控制器上的授权关键字时,该方法在Swagger UI中不再有红色惊叹号,我希望它意味着我已经接近了,但是我没有找到丢失的链接。由于这个流是“应用程序”,我只有一个范围,我希望确保它看起来配置正确,并将clientSecret加载到正确的位置。

UPDATE 1 I've been able to debug the AJAX call and can see that the scope is not set and therefor not sent in the request. Why is the scope not being set? Why don't I have a checkbox to select the scope?

我已经调试了AJAX调用,可以看到范围没有设置,因此没有发送请求。为什么没有设置范围?为什么我没有一个复选框来选择范围呢?

Here is my SwaggerConfig.cs

这是我SwaggerConfig.cs

public class SwaggerConfig
{
    public static void Register()
    {
        var thisAssembly = typeof(SwaggerConfig).Assembly;

        GlobalConfiguration.Configuration
            .EnableSwagger(c =>
            {
                c.SingleApiVersion("v1", "waRougeOneApp");
                c.OAuth2("oauth2")
                    .Description("OAuth2 Client Credentials Grant Flow")
                    .Flow("application")
                    .TokenUrl("https://securitydev.rougeone.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: "client_id",
                    clientSecret: "client_secret",
                    realm: "swagger-realm",
                    appName: "Swagger UI"
                );
            });
    }

    protected static string GetXmlCommentsPath()
    {
        return System.String.Format(@"{0}bin\\waRougeOne.xml", System.AppDomain.CurrentDomain.BaseDirectory);
    }
}

And the AssignOAuth2SecurityRequirements class is

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

I've been trying to find a working example with a client credentials flow without success, so I'm not 100% sure I'll see a toggle button when everything is working correctly. In the examples for the implicit flow if you hover over the red exclamation circle you see the grant types listed, clicking on the red exclamation circle shows the options for scopes listed out in which you select one and then click authorize and it comes back with a blue exclamation.

我一直在尝试寻找一个使用客户端凭证流的工作示例,但没有成功,所以我不能100%确定在一切正常工作时是否会看到一个切换按钮。隐流的例子如果你悬停在红色感叹圆你看到列出的格兰特类型,点击红色感叹圆显示范围的选项列出了你选择一个,然后点击授权,与蓝色的感叹它回来。

For me I never get a checkbox to select a scope, but I've only defined one scope. What am I doing wrong? I found this while debugging the swagger ui JavaScript which seems to point to having all the data it needs?

对于我来说,我从来没有选择范围的复选框,但是我只定义了一个范围。我做错了什么?我在调试华丽的ui JavaScript时发现了这一点,它似乎指出了它需要的所有数据?

authorizations
:
null
auths
:
Array[1]
0
:
Object
name
:
"oauth2"
type
:
"oauth2"
value
:
Object
description
:
"OAuth2 Client Credentials Grant Flow"
flow
:
"application"
scopes
:
Object
Read
:
"Read access to protected resources"
__proto__
:
Object
tokenUrl
:
"https://security.starrwarrs.com/core/connect/token"
type
:
"oauth2"
__proto__
:
Object
__proto__
:
Object
length
:
1
__proto__
:
Array[0]

2 个解决方案

#1


1  

These are the steps which we have done and worked:

这些是我们所做和努力的步骤:

  1. In the SwaggerConfig file, add the below settings:
  2. 在SwaggerConfig文件中,添加以下设置:
c.OAuth2("oauth2")
 .Description("OAuth2 Implicit Grant") 
 .Flow("implicit")
 .AuthorizationUrl(swaggerConfigurations["IssuerUri"].ToString())
 .Scopes(scopes =>
  {
    scopes.Add("user_impersonation", "Access REST API");
  });

The attributes are:

的属性有:

  • Name of the authorization scheme (oauth2 in the above sample)
  • 授权方案名称(以上示例中的oauth2)
  • Description of the authorization scheme
  • 授权方案的说明
  • Flow – Type of grant to be used
  • 流程-将使用的授权类型
  • Authorization Url – Should be the STS url (eg: https://auth2.test.com/oauth2/authorize)
  • 授权Url——应该是STS Url(例如:https://auth2.test.com/oauth2/authorize)
  • Scopes – The scope name
  • 作用域——作用域名称

II. In the SwaggerConfig file, add the below settings also under the swagger ui configuration section:

二世。在SwaggerConfig文件中,在SwaggerConfig部分还添加了以下设置:

c.EnableOAuth2Support(swaggerConfigurations["ClientId"].ToString(), string.Empty, swaggerConfigurations["RedirectUri"].ToString(), "Swagger", " ", new Dictionary<string, string> { { "resource", WebApi.Utility.GetAudiences().First() } });

The method accepts the below parameters:

该方法接受以下参数:

  • clientId – This should be client ID for swagger configured in Security Token Service
  • clientId——这应该是在安全令牌服务中配置的swagger的客户端ID
  • clientSecret – This should be client secret key. This is required only in case of Code grant type
  • clientSecret——这应该是客户端密钥。这只在代码授予类型的情况下才需要
  • realm – This should be the redirect url (this should be [base address] + swagger/ui/o2c-html)
  • 领域-这应该是重定向url(这应该是[基本地址]+ swagger/ui/o2c-html)
  • appName – This should be swagger
  • appName——这应该是虚张声势
  • scopeSeperator – This is not required to be passed if there is only scope
  • 作用域分析器——如果只有作用域,则不需要传递它
  • additionalQueryStringParams – This should have the list of valid audiences and this corresponds to the resource for which the token is issued.
  • additionalQueryStringParams——应该有有效的受众列表,它对应于发出令牌的资源。

III. Create a new Operation Filter in the web api project as shown below:

三世。在web api项目中创建一个新的操作过滤器,如下所示:

public class AssignOperationFilters : IOperationFilter
    {              
        public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
        {     
            string clientId = "clientID";
            if (apiDescription != null)
            {
                var actFilters = apiDescription.ActionDescriptor.GetFilterPipeline();

                var allowsAnonymous = actFilters.Select(f => f.Instance).OfType<OverrideAuthorizationAttribute>().Any();
                if (allowsAnonymous)
                {
                    return; // must be an anonymous method
                }
            }

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

                var authRequirements = new Dictionary<string, IEnumerable<string>>
                {
                    { "oauth2", new List<string> { clientId } }
                };

                operation.security.Add(authRequirements);
            }
        }
    }

This class will be used to bind the OAuth scopes to the individual operations

这个类将用于将OAuth范围绑定到各个操作

IV. Add the above filter in the swagger config file (see code below)

IV.在swagger配置文件中添加上述过滤器(参见下面的代码)

c.OperationFilter<AssignOperationFilters>();

V. Configure the Client ID, Secret, Redirect Url and Resource in Security Token Service

在安全令牌服务中配置客户端ID、Secret、重定向Url和资源

VI. In the Web API project, if there is an index.html being used to inject API specific UI fields/styles, then make sure that all the javascript code is kept intact with the Swashbuckle version of the index.html file (as provided in the location - https://github.com/domaindrivendev/Swashbuckle/blob/master/Swashbuckle.Core/SwaggerUi/CustomAssets/index.html)

VI.在Web API项目中,如果有索引。html用于注入API特定的UI字段/样式,然后确保所有的javascript代码与索引的Swashbuckle版本保持一致。html文件(如位置所示——https://github.com/domaindrivendev/Swashbuckle/blob/master/Swashbuckle.Core/SwaggerUi/CustomAssets/index.html)

#2


1  

Solution!! 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.

The error message finally connected the dots below, until then the on OAuthComplete complete JavaScript function would be called, but with no token. The network tab show "This request has no response data available", but I'd see a Content-Length in the Response headers with a content-type of Json. Fiddler also showed the response which looked like (and was) well formed JSON.

错误消息最终连接了下面的点,直到调用on OAuthComplete complete JavaScript函数,但是没有任何标记。network选项卡显示“这个请求没有可用的响应数据”,但我将在响应头中看到Json内容类型的内容长度。Fiddler还展示了看起来(和是)格式良好的JSON响应。

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:

我在这里描述了这个错误,没有解析reponse,这是由于IdentityServer3没有正确地添加“访问控制允许-起源: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. Hopefully this helps someone else who is facing the same issue

我的拼图中最后一块是“许用科索里金”(AllowedCorsOrigins)。希望这能帮助其他面临同样问题的人

#1


1  

These are the steps which we have done and worked:

这些是我们所做和努力的步骤:

  1. In the SwaggerConfig file, add the below settings:
  2. 在SwaggerConfig文件中,添加以下设置:
c.OAuth2("oauth2")
 .Description("OAuth2 Implicit Grant") 
 .Flow("implicit")
 .AuthorizationUrl(swaggerConfigurations["IssuerUri"].ToString())
 .Scopes(scopes =>
  {
    scopes.Add("user_impersonation", "Access REST API");
  });

The attributes are:

的属性有:

  • Name of the authorization scheme (oauth2 in the above sample)
  • 授权方案名称(以上示例中的oauth2)
  • Description of the authorization scheme
  • 授权方案的说明
  • Flow – Type of grant to be used
  • 流程-将使用的授权类型
  • Authorization Url – Should be the STS url (eg: https://auth2.test.com/oauth2/authorize)
  • 授权Url——应该是STS Url(例如:https://auth2.test.com/oauth2/authorize)
  • Scopes – The scope name
  • 作用域——作用域名称

II. In the SwaggerConfig file, add the below settings also under the swagger ui configuration section:

二世。在SwaggerConfig文件中,在SwaggerConfig部分还添加了以下设置:

c.EnableOAuth2Support(swaggerConfigurations["ClientId"].ToString(), string.Empty, swaggerConfigurations["RedirectUri"].ToString(), "Swagger", " ", new Dictionary<string, string> { { "resource", WebApi.Utility.GetAudiences().First() } });

The method accepts the below parameters:

该方法接受以下参数:

  • clientId – This should be client ID for swagger configured in Security Token Service
  • clientId——这应该是在安全令牌服务中配置的swagger的客户端ID
  • clientSecret – This should be client secret key. This is required only in case of Code grant type
  • clientSecret——这应该是客户端密钥。这只在代码授予类型的情况下才需要
  • realm – This should be the redirect url (this should be [base address] + swagger/ui/o2c-html)
  • 领域-这应该是重定向url(这应该是[基本地址]+ swagger/ui/o2c-html)
  • appName – This should be swagger
  • appName——这应该是虚张声势
  • scopeSeperator – This is not required to be passed if there is only scope
  • 作用域分析器——如果只有作用域,则不需要传递它
  • additionalQueryStringParams – This should have the list of valid audiences and this corresponds to the resource for which the token is issued.
  • additionalQueryStringParams——应该有有效的受众列表,它对应于发出令牌的资源。

III. Create a new Operation Filter in the web api project as shown below:

三世。在web api项目中创建一个新的操作过滤器,如下所示:

public class AssignOperationFilters : IOperationFilter
    {              
        public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
        {     
            string clientId = "clientID";
            if (apiDescription != null)
            {
                var actFilters = apiDescription.ActionDescriptor.GetFilterPipeline();

                var allowsAnonymous = actFilters.Select(f => f.Instance).OfType<OverrideAuthorizationAttribute>().Any();
                if (allowsAnonymous)
                {
                    return; // must be an anonymous method
                }
            }

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

                var authRequirements = new Dictionary<string, IEnumerable<string>>
                {
                    { "oauth2", new List<string> { clientId } }
                };

                operation.security.Add(authRequirements);
            }
        }
    }

This class will be used to bind the OAuth scopes to the individual operations

这个类将用于将OAuth范围绑定到各个操作

IV. Add the above filter in the swagger config file (see code below)

IV.在swagger配置文件中添加上述过滤器(参见下面的代码)

c.OperationFilter<AssignOperationFilters>();

V. Configure the Client ID, Secret, Redirect Url and Resource in Security Token Service

在安全令牌服务中配置客户端ID、Secret、重定向Url和资源

VI. In the Web API project, if there is an index.html being used to inject API specific UI fields/styles, then make sure that all the javascript code is kept intact with the Swashbuckle version of the index.html file (as provided in the location - https://github.com/domaindrivendev/Swashbuckle/blob/master/Swashbuckle.Core/SwaggerUi/CustomAssets/index.html)

VI.在Web API项目中,如果有索引。html用于注入API特定的UI字段/样式,然后确保所有的javascript代码与索引的Swashbuckle版本保持一致。html文件(如位置所示——https://github.com/domaindrivendev/Swashbuckle/blob/master/Swashbuckle.Core/SwaggerUi/CustomAssets/index.html)

#2


1  

Solution!! 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.

The error message finally connected the dots below, until then the on OAuthComplete complete JavaScript function would be called, but with no token. The network tab show "This request has no response data available", but I'd see a Content-Length in the Response headers with a content-type of Json. Fiddler also showed the response which looked like (and was) well formed JSON.

错误消息最终连接了下面的点,直到调用on OAuthComplete complete JavaScript函数,但是没有任何标记。network选项卡显示“这个请求没有可用的响应数据”,但我将在响应头中看到Json内容类型的内容长度。Fiddler还展示了看起来(和是)格式良好的JSON响应。

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:

我在这里描述了这个错误,没有解析reponse,这是由于IdentityServer3没有正确地添加“访问控制允许-起源: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. Hopefully this helps someone else who is facing the same issue

我的拼图中最后一块是“许用科索里金”(AllowedCorsOrigins)。希望这能帮助其他面临同样问题的人