Guidance on Enabling oAuth 2.0 in Generated Swagger Docs

Sorry but I don’t know what you’re referring to, who’s “their login” I thought this was about using Swagger UI on your own Services? and to repeat, only Basic Auth is supported within Swagger UI atm.

I also don’t know what you mean by “authenticating with OAuth2 using a BearerToken”, ServiceStack’s OAuth provider supports authentication via OAuth redirection flow, not a bearer token, support for authenticating with an AccessToken was only added to selected OAuth providers of which only Google OAuth2 is the only OAuth2 provider that supports it, but for this clients would send the AccessToken via an Authenticate Request DTO, not a HTTP Bearer Token.

by their login I was referring to the login page on Auth server.

We (as has anyone else using Identity Server with a SS API - see e.g. here: http://estynedwards.com/blog/2016/01/30/ServiceStack-IdentityServer-Angular/ ) wrote a custom provider that implements AuthProvider. This requires the bearer token be populated. The question is how to do it.

Because we use Identity Server, our APIs work this way. Our whole API suite is WEB API right now because of its superior integration, but I am doing a POC on Service Stack to see if we can move over. I wrote the custom auth provider above which solves the issue of authentication, but the Swagger integration (which works perfectly fine with Swashbuckle and Web API 2) is currently blocking.

If you have some other suggested approach for us to secure our APIs with the bearer tokens from our Identity Server that will allow Swagger integration, please let me know.

@xplicit Can you look at adding Security Definitions object to the OpenApiOperation Open API DTO (currently missing) so a custom OperationFilter can be used to populate it which is needed to be able to define an API Key via HTTP Authorization header. I’m assuming that would enable the API Key support in Swagger UI which would be sent transparently for each request.

This thread suggests Swagger UI doesn’t support Bearer Tokens explicitly and so would require the format:

BearerToken {JWT or BearerToken}

In the API Key UI dialog, which looks like the only way to get Swagger UI to send bearer tokens unless explicit support has been added in the meantime.

Custom SecurityDefinitions are supported by ServiceStack.Api.OpenApi plugin and can be added using ApiDeclarationFilter. There is a property OpenApiDeclaration.SecurityDefinitions which allow to add/remove any custom Open API security definitions and reference them in OpenApiOperation.Security.

For example for basic auth security definition is referenced like this:

While it’s defined here

Here is the sample how to add custom Open API security:

new OpenApiFeature 
{
    ApiDeclarationFilter = api =>  api.SecurityDefinitions.Add("customauth", new OpenApiSecuritySchema {/*Security definition here */}),
    ApiOperationFilter = (name, op) => {
        if (op.Security?.Count > 0)   //basic auth security is already here when service or method is annotated with [Authenticate] attribute
       {
           op.Security.Add(new Dictionary(){{"customauth", new List<string>()}});
       }
    }
}

Can one of you connect the dots here for me - I am assuming this approach means doing something different on the API then implementing a custom AuthProvider. Also, what would I need to do on the Swagger side?
thanks!

@MSWCRB To add your own oauth authentication to Swagger UI you need to modify openapi.json which is generated by ServiceStack.Api.OpenApi plugin and available if you point to http://yoursite.com/openapi URL. You need to add securityDefinitions object to this json and describe here how your authentication provider works. Here is the blog post which explains how security definitions can be configured for json file.

When you’ll get what need to be added to json file for describing your authentication schema, you can move these changes to auto-generated json which is available at /openapi path. To do this you need to add ApiDeclarationFilter which I mentioned in previous post and add OpenApiSecuritySchema in it which is mapped 1 to 1 from json definition you’ve got on previous step.

@MSWCRB For generating OpenApi specification which supports bearer token you can use following code

Plugins.Add(new OpenApiFeature{
    ApiDeclarationFilter = api => api.SecurityDefinitions.Add("bearer", new OpenApiSecuritySchema
        {
            Type =  "apiKey",
            Name = "Authorization",
            In = "header"
        }),
    OperationFilter = (name, op) =>
    {
        if (op.Security?.Count > 0) op.Security.Add(
            new Dictionary<string, List<string>>() {{"bearer", new List<string>() }}
        );
    }
});

As @mythz mentioned above you should write BearerToken {JWT or BearerToken} in Swagger UI api_key authorization, because Swagger UI does not support bearer tokens out of the box yet.

Thanks for the responses, but I’m not sure I follow. Let me ask some questions:

  1. In the approach you describe above, before we even get to Swagger, do I need to replace the custom AuthProvider implementation with something else? if so, what?
  2. If not and the API continues to function as it does today, and all we are changing is related to Swagger, I don’t understand how to wire up to the Identity Server login page in a way that will cause the token it returns to be passed in to the API call when someone clicks “try it out.”

I think we are getting there - just need some additional guidance.

thanks!

You do not need to change your custom AuthProvider, all changes are related only to Swagger UI/OpenAPI.

For the solution I’ve described it works with token you’ve already got before you clicked “authorize” button in Swagger UI. I think you have to customize Swagger UI authorization page and include your own methods to get this token from Identity Server and insert it into api_key field when authorizing.

You can override built-in Swagger UI sources as described here and put your customized page instead of default one.

Thanks again - getting to clarity. I added the code you detailed to add the OpenApiFeature for bearer token, and switched my Plugin registration to OpenApiFeature (from SwaggerFeature) but that broke the following code:

   ApiDeclarationFilter = api =>
                {
                    api.Apis.RemoveAll(x => x.Path.Contains("/auth"));
                }

whats the OpenApi equivalent configuration?

Also, I can’t figure out where the file is that produces the auth popup page. I dont see anything resembling the /swagger-ui
/css
/images
/lib
index.html
mentioned in the docs…

In ServiceStack.Api.OpenApi you’d deal with api.Paths dictionary. Its keys contains the route path.

ApiDeclarationFilter = api =>
{
   foreach(var key in api.Paths.Key.ToArray())
   {
      if (key.Contains("/auth")
         api.Paths.Remove(key);
   }
}

I can’t tell what exactly need to change in Swagger UI to modify auth popup, because it’s a separate project which does not belong to ServiceStack, but you can get Swagger UI 2.2.10 sources here and ask Swagger UI devs where to inject the code for customizing this popup. I recommend to use v 2.2.10, because v 3+ had issues with header params.

What you need to do just copy swagger files to the directory, all of these folders (swagger-ui/css, swagger-ui/fonts etc) came from distribution files of Swagger UI 2.2.10

First off - thanks again for the response. In digging around the interwebs (partially because it altogether wasn’t clear to me how to proceed with overwriting the auth popup page in compiled Swagger code) I discovered indications that oauth2 IS supported natively in Swagger, and proceeded to add the following configuration:

  Plugins.Add(new OpenApiFeature
  {
      ApiDeclarationFilter = api => api.SecurityDefinitions.Add(
          "bearer", new OpenApiSecuritySchema { 
                Type = "oauth2", 
                AuthorizationUrl= "https://OURAUTHSERVER/connect/authorize", 
                Flow="implicit", 
                Scopes = new Dictionary<string, string>() { { "myapi", "My API" } }, 
                Description="oAuth2 Implicit Grant", 
                Name = "Authorization", 
                In = "header" }),
       
      OperationFilter = (name, op) => { 
           if (op.Security?.Count > 0) 
              op.Security.Add(new Dictionary<string, List<string>>() { { "bearer", new List<string>() } }); }
 });                 

Now, when the Authorization is clicked (the blue exclamation point button with each API) there is a popup that appears with an authorize button. When clicked, it automatically redirects to the oAuth server specified and with some fiddling (see issues below) it allows a login attempt and return back to the Swagger doc with a token that is used in the header when it attempts to call the API. I am hoping this approach can work as it is much preferred vs overwriting custom auth popup. Here are the issues that exist, I am hoping you/someone can help them to resolution such that we will have a complete guide on how to integrate ServiceStack/Swagger with oAuth2 for us and any other users.

Open Issues:

  1. The auth popup displays both oAuth2 as an option along with Basic Authentication. Basic should not be there - how do we make it go away? I assume this has to do with the code you suggested Adding to the Security collection.
  2. The scope specified in my code (myapi), is not displayed as a selectable option in the oauth2 section in the popup. Based on the Swagger documentation I have seen, it should be shown there. Because it is not shown, it is not selected and therefore the URL that gets generated and passed to the Auth server when the authenticate button is clicked is invalid as it shows scope= without any value.
  3. The URL generated also doesn’t insert the clientid field (instead it had clientid=your_client_id. This isn’t surprising because the configuration gave no place to specify the clientid. The fact that the URL is generated with the clientid field would imply there is some way to specify it…
  4. The Auth server responds with both a JWT and a reference token and the Swagger doc decided to use the reference token instead of the JWT and pass that in as the bearer token in the header. The API fails as it expects the JWT.

I believe these issues are solvable as we have Swashbuckle running on top of WebAPI in our current version. I am including below the configuration sections from that implementation in case that helps at all:

GlobalConfiguration.Configuration 
            .EnableSwagger(c =>
               c.OAuth2("oauth2")
                        .Description("OAuth2 Implicit Grant")
                        .Flow("implicit")
                        .AuthorizationUrl("https://AUTHURL/connect/authorize")
                        .Scopes(scopes =>
                        {
                            scopes.Add("myapi", "My API");
                        });
                c.OperationFilter<AssignOAuth2SecurityRequirements>();
              })
            .EnableSwaggerUi(c =>
                   c.EnableOAuth2Support(
                        clientId: "myui",
                        realm: "realm",
                        appName: "Swagger UI"
                    );
                });
                  
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

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

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

        operation.security.Add(oAuthRequirements);
}}

Thanks in advance for whoever picks up the mantle here :slight_smile:

The issues you’re having is trying to configure the 3rd Party Swagger UI to work with your Custom IdentityServer based Auth Provider using Swagger UI’s built-in OAuth2 support. This isn’t a supported configuration and one we don’t have access to.

We’ve only enabled support for Basic Auth in Swagger UI and as I mentioned in my initial answer if you’re using any of the other built-in ServiceStack Auth Providers you can authenticate outside Swagger UI then return to Swagger UI to make authenticated requests using Cookies.

There’s 2 parts to Swagger UI, the generated OpenAPI spec which ServiceStack Automatically generates based on your Services and the Filters available on OpenApiFeature which is where you can customize the OpenAPI spec response that’s sent to Swagger UI as @xplicit has provided an example of for enabling API Key and Bearer Token Support.

The end result is consumed by Swagger UI to generate its Dynamic UI, we use the vanilla Swagger UI source-code from the https://github.com/swagger-api/swagger-ui project embedded in the ServiceStack.Api.OpenApi.dll which as linked to earlier, you can override the Swagger UI static files used by providing local copies in your project.

So you need to first find what a valid openapi.json configuration which works with Swagger UI then use the Custom Filters to apply the same modifications in the OpenApiFeature which should be straight-forward as they follow a 1:1 mapping with the Open API 2.0 Specificiation, if there are any properties missing let us know and we’ll add it.

Ideally if you supply the correct configuration it will just work as-is, otherwise you’ll likely need to make changes to Swagger UI as well. If there’s something you need us to enable to assist with generating the required OpenAPI configuration we can add it, but we can’t provide any further insight than this.

Response appreciated but not sure where to go from here. Are you saying the approach I showed above of using the built in oauth2 type wont work (in which case I need to scap it and use a different approach altogether - e.g. overwriting the popup page which still isn’t clear how to do - that link you provide doesnt explain how)? Or are you saying you don’t know how to get it to work because its beyond ServiceStack’s code base (in which case maybe someone else can help me work through the 4 issues I mentioned above)?

thanks.

For removing BasicAuth you can use ApiDeclarationFilter and SecurityDefinitions property. Just remove basic auth from SecurityDefinitions and you won’t see it in authorization page.Also you need to use OperationFilter and remove basic from Security property.

Do I correctly understand that you have swashbuckle configuration which works with this oauth server and Swagger UI? If it works can you share swashbuckle-generated swagger json? In this case we can say which filters you need to apply to get the same output for Swagger UI.

I have no idea if Swagger UI’s OAuth2 Type will work, we’ve never used it and wouldn’t work with any of our built-in OAuth/OAuth2 providers which Authenticate using OAuth Web flow and explicit AccessToken, not a bearer token that it seems to require.

I would consider taking the approach @xplicit mentions in this comment where you’d take the openapi.json generated by ServiceStack’s /openapi endpoint, customize it to a working configuration, then once you find a working configuration apply the changes using the OpenApiFeature filters, if you don’t know how to properly apply the changes send us a working openapi.json configuration and we’ll show you how to add it and add any missing properties if need be.

@mythz I wrote a custom provider (article shown above) that implements SS AuthProvider. Its working fine on the SS API when I call it from consuming applications, postman, etc. The issue here is just with Swagger. If we can get the built in oAuth2 type working that would be ideal (again, not just for me, but for many).

@xplicit here are the relevant sections from the json that swashbuckle generates:
{“swagger”:“2.0”,“info”:{“version”:“v1”,“title”:“My API Documentation”,“description”:""},“host”:“localhost:49717”,“schemes”:[“http”],“paths”:{"/mypath/{id}/attachments":{“post”:{“tags”:[“Attachment”],“operationId”:“Attachment_Post”,“consumes”:[“application/form-data”],“produces”:[“application/json”,“text/json”,“application/xml”,“text/xml”],“parameters”:[{“name”:“id”,“in”:“path”,“description”:“Guid”,“required”:true,“type”:“string”},{“name”:“file”,“in”:“formData”,“description”:“ZIP file. Files other that ‘.zip’ won’t be accepted. If archived file pathes are to long, zip file won’t be accepted.”,“required”:true,“type”:“file”}],“responses”:{“200”:{“description”:“OK”,“schema”:{“type”:“string”}}},“security”:[{“oauth2”:[“myapi”]}]}},"/loan/{id}":{“get”:{“tags”:[“Loan”],“operationId”:“Loan_Get”,“consumes”:[],“produces”:

and

},"securityDefinitions":{“oauth2”:{“type”:“oauth2”,“description”:“OAuth2 Implicit Grant”,“flow”:“implicit”,“authorizationUrl”:“https://AUTHURL/connect/authorize",“scopes”:{“myapi”:"My API”}}}}

unfortunately, neither of those show how to specify the client ID. :frowning:
I included the Swashbuckle config above that shows how it was specified in code.

all that said, with your tip @xplicit I was able to get rid of basic authentication on the popup which also somehow resolved the issue with scope not showing, as it shows now, so we killed 2 of the 4 issues! The major remaining issue is issue #3 - how to populate client_id=your-client-id which is what currently gets generated (with that dummy value) with our specific clientid…

That dummy value seems to be coming from index.html in swagger UI. In their readme, they seem to show how to configure it, but not sure how to tap into that with the OpenApi implementation. If I can’t , then I suppose we need to overwrite the index.html but I’m not sure how to do that since the Swagger implementation here is compiled code. If we can’t use the in-code configuration, how do I overwrite index.html with my own so it gets picked up by the Swagger compiler?

I suppose you already made changes for SecurityDefinitions according to swashbuckle json. If not, here is

new OpenApiFeature(ApiDeclarationFilter = api =>
{
    api.SecurityDefinitions.Add("oauth2", new OpenApiSecuritySchema
    {
        Type = "oauth2",
        Description = "OAuth2 Implicit Grant",
        Flow = "implicit",
        AuthorizationUrl = "https://AUTHURL/connect/authorize",
        Scopes = new Dictionary<string, string> { { "myapi", "My API" } }
    });
},
OperationFilter = (name, op) =>
{
    if (op.Security?.Count > 0)
    {
        op.Security.RemoveAll(d => d.ContainsKey("basic"));
        op.Security.Add(
            new Dictionary<string, List<string>>() { { "oauth2", new List<string> { "myapi" } } }
        );
    }
});

About index.html: you can create /swagger-ui folder in your web site root folder and place modified index.html there as described in ServiceStack.Api.OpenApi docs It will override default index.html which is bundled with OpenApiFeature

@xplicit thanks for the response. Are we saying there is no way to configure the clientID from code, like they show in the Readme?

specifically, they show:

ui.initOAuth({
clientId: “your-client-id”,
clientSecret: “your-client-secret-if-required”,
realm: “your-realms”,
appName: “your-app-name”,
scopeSeparator: " ",
additionalQueryStringParams: {test: “hello”}
})

You can do this, just follow instructions from previous comment:

index.html for modifcation can be downloaded from ServiceStack.Api.OpenApi sources or from Swagger UI sources