Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
653 views
in Technique[技术] by (71.8m points)

asp.net web api2 - SwashBuckle/Swagger - OAuth Resource Owner Password Flow

I'm trying to implement swagger into my Asp.Net Web API, and i'm running into a problem.

I'm using the password resource owner flow, and i'm having to add a work around in order to do this, which is covered in the following stack overflow question :-

Swagger/Swashbuckle: OAuth2 with Resource Owner Password Credentials Grant

I've got everything working, the Bearer token is added via javascript to the request header in the current browser window, but the api calls to the controller methods requiring authorization are still return "401 - Authorization Failed".

Here is the JavaScript that gets the bearer token and adds the header :-

 $('#input_apiKey').change(function () {
    var key = $('#input_apiKey')[0].value;
    var credentials = key.split(':'); //username:password expected
    $.ajax({
        url: "http://localhost:42291/token",
        type: "post",
        contenttype: 'x-www-form-urlencoded',
        data: "grant_type=password&username=" + credentials[0] + "&password=" + credentials[1],
        success: function (response) {
            var bearerToken = 'Bearer ' + response.access_token;

            window.swaggerUi.api.clientAuthorizations.add('Authorization', new window.SwaggerClient.ApiKeyAuthorization('Authorization', bearerToken, 'header'));
            window.swaggerUi.api.clientAuthorizations.remove('api_key');

            alert("Login Succesfull!");
        },
        error: function (xhr, ajaxoptions, thrownerror) {
            alert("Login failed!");
        }
    });
}); 

The Curl in the response in Swagger is :-

curl -X GET --header "Accept: application/json" --header "Authorization: Bearer NqlSG-WyTx2zkYE8xFklGyZWlQDZdsCKZBHruEXvX47N7PAzw4-jZ4eH5D0yFzQTXj13RwKFFt1rUZt2fzWj1vR5UR87wdlKC3YvsTojYV4-3DsWwY7qYRfiKPuM0j09c3X5lnrtlBVJ1rBRUH0TLjfw_yGxgoLBwOJl9xyC1YWNoPOe2nzL4lMOHodAnMem0IBMJmUo3Rt575tnWAbBsQXWhlImDIxCZXvkZdJtlXfIfBSUdY9gfRWL0ZjKbf7m2-yLzH0gpMAMuKaADmJlIudJc0d4SP1Nn2Kh2HuVH8CX4QgZuu4egl9N6rY2smorP2vBSC4_dC4CpmYYzOTu2wUnUhHDY2Q6NWl377ijDKwZLcW9jtD-2tBiEGmFuRV0mVGnh0zc4w9Ao9jPCdtrbSyGitgloBW-UG2bfyao3eE" "http://localhost:42291/api/v1/claims"

I cant see anything wrong with this at all.

I've then used Postman to call the exact same URL call, using the same access token that was generated in the javascript call...

Guess what... it works fine.

EDIT

I've tried removing the authorization attribute from the controller, so that i can check the request as it hits the controller method.

looking in the request headers, the Authorization property is null.

Not sure why this is. the CURL suggests its been placed into the request.

EDIT 2

Ive included my Security Definitions:-

"securityDefinitions": {
        "oauth2": {
            "type": "oauth2",
            "description": "OAuth2 Password Grant",
            "flow": "password",
            "tokenUrl": "http://localhost:42291/token",
            "scopes": {}
        }
    }

EDIT 3 The cURL displayed in the Swagger UI for this api call, when exposed through cURL directly at the command line works without issue.

Now I'm completely confused.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I've managed to correct the problem. It was a simple type mismatch that has caused me days of grief.

In the onComplete.JS, i needed to create a key that matches the key presented in the swagger specification.

If you examine my code snippets above you will see that i created a key and called it "Authorization". But that does not match the named security definition "oauth2".

The working code :-

$('#input_apiKey').change(function () {
    var key = $('#input_apiKey')[0].value;
    var credentials = key.split(':'); 
    $.ajax({
        url: "http://localhost:42291/token",
        type: "post",
        contenttype: 'x-www-form-urlencoded',
        data: "grant_type=password&username=" + credentials[0] + "&password=" + credentials[1],
        success: function (response) {

            var bearerToken = "Bearer " + response.access_token;

            window.swaggerUi.api.clientAuthorizations.remove('api_key');

            var apiKeyAuth = new SwaggerClient.ApiKeyAuthorization("Authorization", bearerToken, "header");

            window.swaggerUi.api.clientAuthorizations.add('oauth2', apiKeyAuth);

            alert("Login Succesfull!");

        },
        error: function (xhr, ajaxoptions, thrownerror) {
            alert("Login failed!");
        }
    });
});

Just to explain this a bit further, you need to create an implementation of IOperationFilter so that swagger can determine which methods of the api require Authorizaion. When you have configured this correctly, you should see a security definition against each api call in the swagger specification :-

enter image description here

My implementation of IOperationFilter :-

public class AssignOAuth2SecurityRequirements : IOperationFilter
    {
        /// <summary>
        /// Apply Security Measures.
        /// </summary>
        /// <param name="operation"></param>
        /// <param name="schemaRegistry"></param>
        /// <param name="apiDescription"></param>
        /// <exception cref="NotImplementedException"></exception>
        public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
        {
            // Determine if the operation has the Authorize attribute
            var authorizeAttributes = apiDescription.ActionDescriptor.GetCustomAttributes<AuthorizeAttribute>();

            if (!authorizeAttributes.Any())
                return;

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

            // Add the appropriate security definition to the operation
            var oAuthRequirements = new Dictionary<string, IEnumerable<string>>
            {
                { "oauth2", Enumerable.Empty<string>() }
            };

            operation.security.Add(oAuthRequirements);
        }
    }

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...