RicoSuter / NSwag

The Swagger/OpenAPI toolchain for .NET, ASP.NET Core and TypeScript.
http://NSwag.org
MIT License
6.79k stars 1.29k forks source link

Disable security for certain operations #2357

Open pfeigl opened 5 years ago

pfeigl commented 5 years ago

Before going into Details: I'm using WebApi2 with OWIN and scheme set to OpenApi3

With this setup, I'm unable to create a swagger.json which disables the security for given operations (actions).

For this I tried to implement a custom IOperationProcessor, which honors the AllowAnonymousAttribute.

On how to disable security for operations, the details can be found here: https://swagger.io/docs/specification/authentication/

Global security can be overridden in individual operations to use a different authentication type, different OAuth/OpenID scopes, or no authentication at all:

There is also a sample about this, technically it just means returning an empty security for this operation: security: [] # No security.

The actual problem is probably somewhere in the Json serialization. What I would expect: context.OperationDescription.Operation.Security = null => No security node at all context.OperationDescription.Operation.Security = new List<OpenApiSecurityRequirement>(); => security shows up but does not have entries (what I want)

What is actually happening: Both situations do not render the security node at all

I also tried various other combinations, which did not work:

pfeigl commented 5 years ago

This can be partially solved by #2304, because than I could disable the global security and add security on a "per operation" basis for all operations and skip those which do not require security.

However this is not ideal and I'd prefer to have security on a global level and disable for single operations.

On another note: The actuall AllowAnonymous Support would have been created in #1933, but I think even with the PR it would not work right now because of the resulting JSON

RicoSuter commented 5 years ago

Is this even possible with openapi (disable global security)?

RicoSuter commented 5 years ago

Try to write a custom document/operation processor to get to the desired document. Then we can discuss implementing it out-of-the-box.

cjlotz commented 4 years ago

Hi. I would also like the ability to define security at the global level to apply to all operations and then only override the one or two operations that allows anonymous access. As @pfeigl mentioned, this is supported in the OpenApi specification and I've confirmed that tooling like SwaggerUI works correctly if I manually create a specification where I override specific operations to allow anonymous access using security: []. I created an IOperationProcessor in an attempt to override the security based on the presence of the AllowAnonumousAttribute but as @pfeigl mentions the JSON serialization seems to omit the empty array.

    public class ResourceActionSecurityProcessor : IOperationProcessor
    {
        public ResourceDocsGeneratorSettings Settings { get; }

        public ResourceActionSecurityProcessor(ResourceDocsGeneratorSettings settings)
        {
            Settings = settings;
        }

        #region IOperationProcessor Members

        public bool Process(OperationProcessorContext operationProcessorContext)
        {
            if (operationProcessorContext is ResourceActionProcessorContext context)
            {
                if (context.ControllerType.GetCustomAttribute<AllowAnonymousAttribute>() != null || context.MethodInfo.GetCustomAttribute<AllowAnonymousAttribute>() != null)
                {
                    context.ResourceAction.Operation.Operation.Security = new List<OpenApiSecurityRequirement>();
                }
            }

            return true;
        }

        #endregion
    }

The reason why it is important to me to define it on the global level is that API tools like Postman will then add the Authorization on the Collection level instead of the individual operation levels when importing my specification.

image

The individual operation levels will be set to Inherit auth from parent as shown below. This implies I can configure the global collection using a single collection variable for the access token and all operations will automatically use the token instead of me having to now manually set the access token on all the operations when importing the collection into Postman.

image

jtrounce commented 2 years ago

I've just encountered this issue too and was able to work-around it.

The problem is the OpenApiDocument serialization is using JsonSchema.CreateJsonSerializerContractResolver() which creates a IgnoreEmptyCollectionsContractResolver contract resolver. Instead of trying to resolve the issue with the contract resolver I worked around it by defining the "security" property in the operation extension data.

                if (AllowAnonymous(context))
                {
                    if (operation.ExtensionData == null)
                    {
                        operation.ExtensionData = new Dictionary<string, object>();
                    }

                    operation.ExtensionData["security"] = new Collection<OpenApiSecurityRequirement>();
                }