1

I'm attempting to set up Swagger (via Swashbuckle) onto my webApi. I've got it to the point that its showing my methods successfully, and the open methods work fine.

Most of the methods on my Api use oAuth2 to authenticate, using the client_credentials grant type. I'm attempting to set up the Swagger UI so that the user can enter their credentials into textboxes, and have it use that.

This is what I have so far:

Swashbuckle Config

public static class SwashbuckleConfig
{
    public static void Configure(HttpConfiguration config)
    {
        config.EnableSwagger(c =>
            {
                c.SingleApiVersion("v1", "Configuration Api Config");
                c.OAuth2("oauth2")
                    .Description("OAuth2")
                    .Flow("application")
                    .TokenUrl("http://localhost:55236/oauth/token")
                    .Scopes(scopes =>
                    {
                        scopes.Add("write", "Write Access to protected resources");
                    });

                c.OperationFilter<AssignOAuth2SecurityRequirements>();
            })
            .EnableSwaggerUi(c =>
            {
                c.EnableOAuth2Support("Test", "21", "Test.Documentation");
                c.InjectJavaScript(Assembly.GetAssembly(typeof(SwashbuckleConfig)), 
                          "InternalAPI.Swagger.client-credentials.js");

            });
    }

    public class AssignOAuth2SecurityRequirements : IOperationFilter
    {
        public void Apply(Operation operation, SchemaRegistry schemaRegistry, 
                      ApiDescription apiDescription)
        {
            //All methods are secured by default, 
            //unless explicitly specifying an AllowAnonymous attribute.
            var anonymous = apiDescription.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>();
            if (anonymous.Any()) return;

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

            var requirements = new Dictionary<string, IEnumerable<string>>
            {
                { "oauth2", Enumerable.Empty<string>() }
            };

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

client-credentials.js

(function () {
    $(function () {
        var basicAuthUi =
            '<div class="input">' +
                '<label text="Client Id" /><input placeholder="clientId" id="input_clientId" name="Client Id" type="text" size="25">' +
                '<label text="Client Secret" /><input placeholder="secret" id="input_secret" name="Client Secret" type="password" size="25">' +
                '</div>';

        $(basicAuthUi).insertBefore('div.info_title');
        $("#input_apiKey").hide();

        $('#input_clientId').change(addAuthorization);
        $('#input_secret').change(addAuthorization);
    });

    function addAuthorization() {
        var username = $('#input_clientId').val();
        var password = $('#input_secret').val();

        if (username && username.trim() !== "" && password && password.trim() !== "") {

            //What do I need to do here??
            //var basicAuth = new SwaggerClient.oauth2AUthorisation(username, password);
            //window.swaggerUi.api.clientAuthorizations.add("oauth2", basicAuth);

            console.log("Authorization added: ClientId = " 
               + username + ", Secret = " + password);
        }
    }
})();

The client-side example I was attempting to modify was from here. Obviously this is for Basic authentication, but I need to modify it to suit oAuth.

What do I need to do to make it generate a token prior to calling the Api method?

Obsidian Phoenix
  • 4,083
  • 1
  • 22
  • 60
  • I think this is what you are looking at: http://stackoverflow.com/questions/39729188/im-not-getting-a-scope-checkbox-when-the-authorize-tag-doesnt-contain-roles-a/39750143#39750143 – Silly John Oct 04 '16 at 14:25

1 Answers1

0

First i want to say you MUST not use client credentials flow from client side; But still i also have broke that rule today : ). Well until it is entered by user it should be fine(i hope you're using TLS).

Normally you would implement implicit type here you can check here on how to(personally i have not tried it): https://danielwertheim.se/use-identityserver-in-swaggerui-to-consume-a-secured-asp-net-webapi/

So todo that you will need to remove this security definition : c.OperationFilter(); you can leave the rest.

and just add this code:

  function addApiKeyAuthorization() {

            var clientId = $('#input_clientid')[0].value;
            var clientSecret = $('#input_clientsecret')[0].value;

            if (clientId == '' || clientSecret == "")
                return;

            var token = getToken(clientId, clientSecret, 'a defined scope');

            var authKeyHeader = new SwaggerClient.ApiKeyAuthorization("Authorization", "Bearer " + token.access_token, "header");
            console.log("authKeyHeader", authKeyHeader);
            window.swaggerUi.api.clientAuthorizations.add("Authorization", authKeyHeader);

            //handle token expiration here
        }

        function getToken(clientId, clientSecret, scope) {

            var authorizationUrl = '<url to token service>/connect/token';
            var authorizationParams = "grant_type=client_credentials&scope=" + scope;
            var basicAuth = window.btoa(clientId + ":" + clientSecret)
            var token = "";

            $.ajax({
                type: 'POST',
                url: authorizationUrl,
                contenttype: 'x-www-form-urlencoded',
                headers: {
                    'Authorization': 'basic ' + basicAuth
                },
                data: authorizationParams,
                success: function (data) {
                    token = data;
                },
                error: function (data) {
                    // don't worry about it, swagger will respond that something went wrong : )
                },
                async: false
            });

            return token;
            //example response format: {access_token: "86f2bc88dcd1e6919ef0dadbde726c52", expires_in: 3600, token_type: "Bearer"}
        }

        $('#explore').on('click', addApiKeyAuthorization);

I hope this helps. Have fun : )