Guidance on Enabling oAuth 2.0 in Generated Swagger Docs

Can anyone provide guidance on how to allow a ServiceStack API secured with oAuth 2.0 to be tested via the Swagger documentation generated by ServiceStack? There is the Try Now button but of course it returns unauthorized. Swashbuckle has a straightforward way to allow this, but SS doesn’t integrate with it.

thanks!

Only Basic Auth in Swagger UI is supported, to authenticate with other OAuth methods you’d need to do that outside Swagger UI then you can revisit Swagger UI as an authenticated user.

How would it work outside of Swagger? oAuth 2 works on redirecting to their login page, returning a bearer token and then that bearer token must be massed with each API call. If we have a landing page that redirects to their login page, and then redirects back to the Swagger page after success, how does the bearer token get passed in with the API calls when we click the Try Now button? This is why Swashbuckle handles that redirect from within the swagger dock and then stores and passes the token back to the API call…

All of ServiceStack’s OAuth/OAuth2 AuthProviders sets up a Users Session and populates its Session Cookies on the client so subsequent requests made from the client will include the session cookies to make its authenticated requests.

Then maybe I don’t understand what you mean by “outside of swagger.” How do I get that Service Stack User Session populated when I’m logging in via the Auth server page?

As in go to the AuthProvider url you want to Authenticate with directly, e.g. /auth/facebook, /auth/GoogleOAuth which will initiate the OAuth flow, when successful your browser will be populated with the Session Cookies so you can return to /swagger-ui/ to make Authenticated Requests.

Something is missing here. When I login via the Auth flow on their login, that doesn’t automatically cause the API call initiated by “Try Now” to pass in the Bearer token in the Authorization header. On my SS API side, the custom auth provider we have requires the Bearer token be populated.

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.