antek-drzewiecki / wine_bouncer

A Ruby gem that allows Oauth2 protection with Doorkeeper for Grape Api's
MIT License
112 stars 58 forks source link

Swagger Strategy #51

Open sunnyrjuneja opened 9 years ago

sunnyrjuneja commented 9 years ago

Hi,

When I use the the swagger strategy, my endpoint spits this out:

{
  "apiVersion": "0.1",
    "swaggerVersion": "1.2",
    "resourcePath": "/users",
    "produces": [
      "application/json"
      ],
    "apis": [
    {
      "path": "/users/me",
      "operations": [
      {
        "notes": "",
        "summary": "Return the the authenticated User",
        "nickname": "GET-users-me",
        "method": "GET",
        "parameters": [],
        "type": "void",
        "authorizations": {
          "oauth2": []
        }
      }
      ]
    }
  ],
    "basePath": "http://api.rails-app.dev:3000"
}

However, "authorizations": { "oauth2": [] } isn't correct.

It should look something like this (more info on auth spec here).

  "oauth2": {
    "type": "oauth2",
    "scopes": [
      {
        "scope": "email",
        "description": "Access to your email address"
      },
      {
        "scope": "pets",
        "description": "Access to your pets"
      }
    ],
    "grantTypes": {
      "implicit": {
        "loginEndpoint": {
          "url": "http://petstore.swagger.wordnik.com/oauth/dialog"
        },
        "tokenName": "access_token"
      },
      "authorization_code": {
        "tokenRequestEndpoint": {
          "url": "http://petstore.swagger.wordnik.com/oauth/requestToken",
          "clientIdName": "client_id",
          "clientSecretName": "client_secret"
        },
        "tokenEndpoint": {
          "url": "http://petstore.swagger.wordnik.com/oauth/token",
          "tokenName": "access_code"
        }
      }
    }
  }

This started when I posted this issue https://github.com/ruby-grape/grape-swagger-rails/issues/13 on grape-swagger-rails saying that entering an api key would not add it the requests. We believed it was a problem upstream with swagger ui but a maintainer mentioned that the syntax was not swagger compliant (see: https://github.com/swagger-api/swagger-js/issues/555).

antek-drzewiecki commented 9 years ago

Thank you. I will look into that.

sunnyrjuneja commented 9 years ago

no problem @antek-drzewiecki . if you can help me triage this (where the bugs are), i'm happy to put some time in time to get it fixed.

antek-drzewiecki commented 9 years ago

You got a good point there. There is currently no way to define the base swagger documentation with the correct authorization methods except for manually. Currently what I do is define it manually. I am thinking of a more lasting solution then my 'workaround'

This is what i've added to my API description.

class API < Grape::API
  format :json
  use ::WineBouncer::OAuth2

  SITE_URL = 'https://www.my-cool-website.com' # Put this in an initializer or somewhere else then here

  rescue_from WineBouncer::Errors::OAuthUnauthorizedError do
    Rack::Response.new(
        {
            id: 'unauthenticated',
            message: 'Request failed because user is not authenticated.'
        }.to_json, 401, 'Content-Type' => 'text/error').finish
  end

  oauth2
  get :hello do
    { hello: "world" }
  end

  add_swagger_documentation base_path: '/api',
                            format: :json,
                            authorizations: {
                                oauth2: {
                                    type: :oauth2,
                                    scopes: [
                                        {
                                            scope: 'EDIT ME: the scope name',
                                            description: 'EDIT ME: a scope description'
                                        }
                                    ],
                                    grantTypes: {
                                        implicit: {
                                            loginEndpoint: {
                                                url: "#{SITE_URL}/oauth/authorize"
                                            },
                                            tokenName: 'access_token'
                                        },
                                        authorization_code: {
                                            tokenRequestEndpoint: {
                                                url: "#{SITE_URL}/oauth/token",
                                                clientIdName: 'client_id',
                                                clientSecretName: 'client_secret'
                                            },
                                            tokenEndpoint: {
                                                url: "#{SITE_URL}//oauth/token/info",
                                                tokenName: 'auth_code'
                                            }
                                        }
                                    }
                                }
                            },
                            models: [] # optional models

end                      

Given your git repository at : https://github.com/whatasunnyday/gsr-api-key/blob/master/app/api/api.rb

Will this give the correct swagger docs?

sunnyrjuneja commented 9 years ago

That looks really good. As a temporary workaround, I've done something a bit simpler.

        desc 'Return the the authenticated User', {
          detail: 'Useful test to see if the client is '\
          'correctly authenticated.',
          entity: User::Entity,
          http_codes: [
            Errors::Unauthenticated.http_code
          ],
          headers: {
            'Authorization' => {
              description: 'OAuth Bearer token used for authentication.',
              required: true
            }
          }
        }
     get '/' do
        ...
     end

This gives me a field to add my access token to send in the header in the swagger docs.

antek-drzewiecki commented 9 years ago

Nice one, this also makes sense. The downside it that you need to paste the access token across all endpoints you are using. This can get nasty when you have 20 endpoints to consume.

I am trying to reproduce your description with, authorizations: { oauth: [] } but no success. maybe im running different gemsets.

sunnyrjuneja commented 9 years ago

Do you want me to update anything on gsr-api-key? I was hoping that would make it easy to reproduce. Or the test case here: https://github.com/ruby-grape/grape-swagger-rails/pull/25

Let me know what you're trying to reproduce. I will happily provide a minimal test case.

antek-drzewiecki commented 9 years ago

@whatasunnyday , i think there has been some confusion before. Swagger spec describes resource listing and API Declaration witch both have authorisations objects. The link and code snippets you described were for the resource listing authorisations spec. In case of OAuth2, this object describes the global OAuth2 configuration such as your authorization server endpoints.

I guess the actual problem is the (API Declaration authorisations spec)[https://github.com/swagger-api/swagger-spec/blob/master/versions/1.2.md#52-api-declaration]. In that case authorizations: { oauth2: [] } is a perfectly valid spec. Since it expects an array of tokens.

sunnyrjuneja commented 9 years ago

Yes, that was definitely the case. Thanks. As for the global authorization object, I'll think through a strategy for integrating it into wine_bouncer.

abarre commented 9 years ago

I got the same problem as @whatasunnyday. In the thread, it's not clear if you find a solution or not.

sunnyrjuneja commented 9 years ago

hey @abarre, there is currently no solution other than work around described above (passing headers key in second desc arg) or passing authorization override to add_swagger_doc.

antek-drzewiecki commented 9 years ago

Ill try to create an helper method to add the missing information to the swagger_docs. Thats the best i could do on short terms