Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Web API with OAUTH using Swagger / Swashbuckle

I am trying to get my Web API project to use Swagger 'pretty' documentation, etc. (http://swagger.io/)

I'm using Swashbuckle for .NET, installed from NuGet and the version I am using is 4.0.1

I've been able to install and use Swagger. Everything seems normal at this point. The only hurdle I have is to disable API key's and have the ability to use OAuth, like in the PetStore sample (http://petstore.swagger.wordnik.com/#!/pet/addPet)

I've tried everything I could find on the web. Let me list them below:

First, Here is my Startup.cs

public void Configuration(IAppBuilder app)
{
    var config = new HttpConfiguration();

    WebApiConfig.Register(config);

    Swashbuckle.Bootstrapper.Init(config);
}

Now, my SwaggerConfig.cs:

public static void Register()
{
    Swashbuckle.Bootstrapper.Init(GlobalConfiguration.Configuration);

    SwaggerSpecConfig.Customize(c =>
    {
        c.IgnoreObsoleteActions();

        c.IncludeXmlComments(GetXmlCommentsPath());

        c.ApiInfo(new Info
        {
            Title = "Work you",
            Description = "testing some stuffs",
            Contact = "[email protected]"
        });

        c.Authorization("oauth2", new Authorization
        {
            Type = "oauth2",
            Scopes = new List<Scope>
                {
                    new Scope { ScopeId = "products.read", Description = "View products" },
                    new Scope { ScopeId = "products.manage", Description = "Manage products" }
                },
            GrantTypes = new GrantTypes
            {
                ImplicitGrant = new ImplicitGrant
                {
                    LoginEndpoint = new LoginEndpoint
                    {
                        Url = "https://www.mysecure.website.com"
                    },
                    TokenName = "access_token"
                }
            }
        });
    });


    SwaggerUiConfig.Customize(c =>
    {
        c.EnableOAuth2Support("client_id", "test-realm", "app Name");

        var thisAssembly = typeof(SwaggerConfig).Assembly;

        c.SupportHeaderParams = true;
        c.DocExpansion = DocExpansion.List;
        c.SupportedSubmitMethods = new[] { HttpMethod.Get, HttpMethod.Post, HttpMethod.Put, HttpMethod.Head };
        c.EnableDiscoveryUrlSelector();

    });

}

I have a SwaggerExtensions folder, in there I have the files that should be required. For example:

Files for SwaggerExt

I have classes decorated with:

[ScopeAuthorize("this.scope")]

However, the OAuth option never displays for me on the swagger page. I can't see where I am supposed to be able to enter custom headers either.

I do see the title and documentation description, email address, etc is being read from the SwaggerConfig.cs so I know its at least being read.

I can't figure it out. :(

Any ideas?

like image 905
bugnuker Avatar asked Jan 19 '15 21:01

bugnuker


2 Answers

I got my solution. This this is powerful, just not 100% straight forward to configure.

Here are the steps I took:

Install the NuGet Package, I used PM> Install-Package Swashbuckle -Version 4.1.0 but the link is at https://www.nuget.org/packages/Swashbuckle/ and I would recommend getting the latest, but I know 4.1.0 worked. EDIT I just updated to 5.X and it broke it. 4.1.0 works but latest did not. I haven't researched more into why yet.

Once you install this, your are almost done.

The install will create a SwaggerConfig.cs file. This is the code I used (Copied from github master)

public class SwaggerConfig
    {
        public static void Register()
        {
            Swashbuckle.Bootstrapper.Init(GlobalConfiguration.Configuration);

            SwaggerSpecConfig.Customize(c =>
            {
                c.IgnoreObsoleteActions();

                //c.SupportMultipleApiVersions(
                //    new[] { "1.0", "2.0" },
                //    ResolveVersionSupportByRouteConstraint);

                //c.PolymorphicType<Animal>(ac => ac
                //    .DiscriminateBy(a => a.Type)
                //    .SubType<Kitten>());

                c.OperationFilter<AddStandardResponseCodes>();
                c.OperationFilter<AddAuthResponseCodes>();
                c.OperationFilter<AddOAuth2Scopes>();

                //c.IncludeXmlComments(GetXmlCommentsPath());

                c.ApiInfo(new Info
                {
                    Title = "Swashbuckle Dummy",
                    Description = "For testing and experimenting with Swashbuckle features",
                    Contact = "[email protected]"
                });

                c.Authorization("oauth2", new Authorization
                {
                    Type = "oauth2",
                    Scopes = new List<Scope>
                        {
                            new Scope { ScopeId = "test1", Description = "test1" },
                            new Scope { ScopeId = "test2", Description = "test2" }
                        },
                    GrantTypes = new GrantTypes
                    {
                        ImplicitGrant = new ImplicitGrant
                        {
                            LoginEndpoint = new LoginEndpoint
                            {
                                Url = "https://your.Oauth.server/Authorize"
                            },
                            TokenName = "access_token"
                        }
                    }
                });
            });

            SwaggerUiConfig.Customize(c =>
            {
                var thisAssembly = typeof(SwaggerConfig).Assembly;

                c.SupportHeaderParams = true;
                c.DocExpansion = DocExpansion.List;
                c.SupportedSubmitMethods = new[] { HttpMethod.Get, HttpMethod.Post, HttpMethod.Put, HttpMethod.Head };
                //c.InjectJavaScript(typeof(SwaggerConfig).Assembly, "WebApplication4.SwaggerExtensions.onComplete.js");
                //c.EnableDiscoveryUrlSelector();
                //c.InjectJavaScript(thisAssembly, "Swashbuckle.Dummy.SwaggerExtensions.testScript1.js");
                //c.InjectStylesheet(thisAssembly, "Swashbuckle.Dummy.SwaggerExtensions.testStyles1.css");

                c.EnableOAuth2Support("client_id", "realm", "Swagger UI");
            });
            // NOTE: If you want to customize the generated swagger or UI, use SwaggerSpecConfig and/or SwaggerUiConfig here ...
        }
        private static string GetXmlCommentsPath()
        {
            return String.Format(@"{0}\XmlComments.xml", AppDomain.CurrentDomain.BaseDirectory);
        }

Now we've told Swagger we want to use OAuth, and this is how we want to use it. Done, right? Nope.

You need to add this folder, and files to your solution: https://github.com/domaindrivendev/Swashbuckle/tree/master/Swashbuckle.Dummy.Core/SwaggerExtensions

(you only need the .cs files)

Make sure your namespaces are right...

Then, you need to decorate your class in WebAPI, like this:

[ScopeAuthorize("test1")]

Now when you run it and get to the swagger page, you will see each operation that has that declaration will have the OAuth switch in the top right corner. When you click it, you can use Implicit grant flow and obtain a token that will be added to your reqeust.

This will only work with implicit grant from what I've found. It does seem they've tried to get AuthorizationCode Grant going, but the js files they have built only support implicit from what I can see.

Hope this helps someone. This is a powerful tool and I hope we see more sites use something like this.

Thanks and good luck!

like image 199
3 revs Avatar answered Sep 28 '22 10:09

3 revs


I think most of what you had originally was ok. I'm using Swashbuckle 5.2.1 and got things working pretty well. I've just written a blog post (http://knowyourtoolset.com/2015/08/secure-web-apis-with-swagger-swashbuckle-and-oauth2-part-2/) that explains this in some detail, but the gist is adding the OperationFilter class(es) that define which of your API methods will get the OAuth21 toggle button on them. There are (sample) definitions of those in the SwaggerExtensions folder from GitHub as noted above, but really all you need as at least one class that implements IOperationFilter and its Apply method. I've got a sample class below. The class name really doesn't matter (nor where it is), you just need to include it (and any others if you have more) in the SwaggerConfig where the OperationFilter(s) are specified.

 public class AssignOAuth2SecurityRequirements : IOperationFilter
{
    public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
    {
        var actFilters = apiDescription.ActionDescriptor.GetFilterPipeline();
        var allowsAnonymous = actFilters.Select(f => f.Instance).OfType<OverrideAuthorizationAttribute>().Any();
        if (allowsAnonymous)
            return; // must be an anonymous method


        //var scopes = apiDescription.ActionDescriptor.GetFilterPipeline()
        //    .Select(filterInfo => filterInfo.Instance)
        //    .OfType<AllowAnonymousAttribute>()
        //    .SelectMany(attr => attr.Roles.Split(','))
        //    .Distinct();

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

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

        operation.security.Add(oAuthRequirements);
    }
}
like image 42
Erik Dahl Avatar answered Sep 28 '22 11:09

Erik Dahl