Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Added Basic HTTP Authorization support #32

Closed
wants to merge 2 commits into from

3 participants

gkostov Neil Mansilla Alex
gkostov

Basic HTTP Auth can be specified to have the server send the "Authorization" HTTP header with given username and password.

Neil Mansilla

Thanks for the contrib. Basic auth is definitely something I/O Docs needs.

  • We should consider making "auth" an object that can contain multiple authorization schemes, instead of there only being one possible authorization config. It's not uncommon that we have clients that require both API key and basic auth.

  • For Basic auth, do you have any thoughts as to whether the configuration should address where the username and password values are obtained -- e.g. very top, where the API key/secret inputs are located, to be applied to every single resource/method, OR by parameter name, so that methods that have parameters names that match, the user/pass values will come from the form values on each individual method.

Example 1 - "auth" block is an object containing authorization scheme objects. In the "basicAuth" object below, there are no values, thus, the default behavior would be to render a view that has username/password input fields at the top (adjacent to the API key/secret fields) -- and these values would apply to every resource/method. Also in this example, note the "key" authorization object is moved under the "auth" object, and how "keyParam" is now "param".

      "phgsandbox": {
        "name": "Performance Horizon API (Sandbox)",
        "protocol": "http",
        "baseURL": "stagingapi.performancehorizon.com",
        "publicPath": "",
        "privatePath": "",
        "auth": {
             "basicAuth": {

             },
             "key": {
                   "param": "api_key"
             }
         ],
    }

Example 2 - Just like the example above, but the "username" and "password" keys are in the "basicAuth" block with parameter names defined -- meaning that the basic auth will pull the user/pass values from the method inputs for parameter names that match "username->param" and "password->param".

      "phgsandbox": {
        "name": "Performance Horizon API (Sandbox)",
        "protocol": "http",
        "baseURL": "stagingapi.performancehorizon.com",
        "publicPath": "",
        "privatePath": "",
        "auth": {
             "basicAuth": {
                  "username": {
                      "param": "accountName"
                  }
                  "password": {
                      "param": "accountPass"
                  }
             },
             "key": {
                   "param": "api_key"
             }
         ],
    }

Example 2.1 - The method configuration would look like this (to complement Example 2 basic auth schema convention above):

{
    "endpoints": [
        {
            "name": "Rockin Resources",
            "methods": [
                {
                    "MethodName": "The Foo Method",
                    "Synopsis": "A method that requires basic auth, as well as API key authentication",
                    "HTTPMethod": "GET",
                    "URI": "/foo",
                    "RequiresOAuth": "N",
                    "parameters": [
                        {
                            "Name": "accountName",
                            "Required": "Y",
                            "Default": "",
                            "Type": "string",
                            "Description": "Username, used in basic authorization"
                        },
                        {
                            "Name": "accountPass",
                            "Required": "Y",
                            "Default": "",
                            "Type": "string",
                            "Description": "Password, used in basic authorization"
                        },
                        {
                            "Name": "bar",
                            "Required": "N",
                            "Default": "12345",
                            "Type": "int",
                            "Description": "Just another arbitrary parameter for this method"
                        }
                }
               .......

Please share your thoughts.

p.s. the object/key naming in the examples are not ideas in stone -- clearly, there needs to be some cleanup around these conventions.. e.g. "sigParam", "keyParam", etc.

p.p.s. and yes, even the use of arrays vs. nested objects is worth a discussion. :)

gkostov

Hey,

I like these suggestions and I checked with our QA (who's actually driving our run with iodocs) and he's all up for that too.

Regarding the format of the "basicAuth" object I think the two examples present two different use cases so the user will need to be able to provide whichever configuration they need and iodocs shall behave accordingly, so maybe:

"basicAuth":{

}

would render username/password fields at the top,

"basicAuth":{
    "username":"testUser",
    "password":"testing"
}

would not render anything but use the provided name/pass for authentication (I know hard-coded stuff is not very nice but I'm 100% sure this is a very real use case), and

"basicAuth":{
    "username":{
        "param": "accountName"
    },
    "password":{
        "param": "accountPass"
    }
}

shall tell iodocs to use those parameters from the method inputs.

I've also had another thought about a more rare case when the API would require a different authentication mechanism or simply a little enhanced security for some of the methods - that is, the API may be freely accessible through most methods but require authentication for a limited set. To cater for that case the "auth" object could be "cascaded" or overridden in method definitions (at the method level) or at the endpoint level thus affecting all methods of the endpoint. I'm sure this feature can be given lowest priority but still worth considering.

As for arrays vs. objects I always decide on a very simple basis - I prefer objects when possible as I tend to read them easier and I think they give better sense of hierarchy, but when the order of properties matters then arrays should be used.

Cheers

Stephen Houston egeek referenced this pull request
Closed

Updated BASIC AUTH support #172

Alex alexadkins added the auth label
Neil Mansilla
Owner

Basic auth feature added for both Node server level and API request level.

Neil Mansilla mansilladev closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
Showing with 39 additions and 23 deletions.
  1. +4 −0 app.js
  2. +4 −2 public/javascripts/docs.js
  3. +31 −21 views/api.jade
4 app.js
View
@@ -478,6 +478,10 @@ function processRequest(req, res, next) {
options.path += apiConfig.keyParam + '=' + apiKey;
}
+ if (apiConfig.auth=='basicAuth') {
+ options.headers['Authorization']='Basic '+new Buffer(reqQuery.apiUsername+':'+reqQuery.apiPassword).toString('base64');
+ }
+
// Perform signature routine, if any.
if (apiConfig.signature) {
if (apiConfig.signature.type == 'signed_md5') {
6 public/javascripts/docs.js
View
@@ -173,9 +173,11 @@
var params = $(this).serializeArray(),
apiKey = { name: 'apiKey', value: $('input[name=key]').val() },
apiSecret = { name: 'apiSecret', value: $('input[name=secret]').val() },
- apiName = { name: 'apiName', value: $('input[name=apiName]').val() };
+ apiName = { name: 'apiName', value: $('input[name=apiName]').val() },
+ apiUsername = { name: 'apiUsername', value: $('input[name=username]').val() },
+ apiPassword = { name: 'apiPassword', value: $('input[name=password]').val() };
- params.push(apiKey, apiSecret, apiName);
+ params.push(apiKey, apiSecret, apiName, apiUsername, apiPassword);
// Setup results container
var resultContainer = $('.result', self);
52 views/api.jade
View
@@ -14,30 +14,40 @@ form#credentials
img(src='/images/accept.png')
- else
h2 API Credentials
- img(src='/images/key.png')
+ img(src='/images/key.png')
- - if (apiInfo.oauth)
- input(type='hidden', name='oauth', value='authrequired')
-
- - if (apiInfo.auth.defaultKey)
- - var defaultKey =apiInfo.auth.defaultKey
- - else
- - var defaultKey =''
-
- - if (apiInfo.auth.defaultSecret)
- - var defaultSecret =apiInfo.auth.defaultSecret
+ - if(apiInfo.auth == 'basicAuth')
+ div (basic HTTP authorization)
+ br
+ div
+ label(for='key') User name
+ input(id='username', name='username', style='color=#EEEEEE')
+ div
+ label(for='key') Password
+ input(id='password', name='password', value=defaultKey, style='color=#EEEEEE')
- else
- - var defaultSecret =''
- div
- label(for='key') API Key
- input(id='key', name='key', value=defaultKey, style='color=#EEEEEE')
- div
- - if (apiInfo.oauth || apiInfo.signature)
- label(for='secret') Shared Secret
- input(id='secret', name='secret', value=defaultSecret, style='color=#EEEEEE')
- - if (apiInfo.oauth && apiInfo.oauth.type !='two-legged')
+ - if (apiInfo.oauth)
+ input(type='hidden', name='oauth', value='authrequired')
+
+ - if (apiInfo.auth.defaultKey)
+ - var defaultKey =apiInfo.auth.defaultKey
+ - else
+ - var defaultKey =''
+
+ - if (apiInfo.auth.defaultSecret)
+ - var defaultSecret =apiInfo.auth.defaultSecret
+ - else
+ - var defaultSecret =''
+ div
+ label(for='key') API Key
+ input(id='key', name='key', value=defaultKey, style='color=#EEEEEE')
div
- input(name='oauth', value='Authenticate with OAuth', type='submit', id='oauth-auth')
+ - if (apiInfo.oauth || apiInfo.signature)
+ label(for='secret') Shared Secret
+ input(id='secret', name='secret', value=defaultSecret, style='color=#EEEEEE')
+ - if (apiInfo.oauth && apiInfo.oauth.type !='two-legged')
+ div
+ input(name='oauth', value='Authenticate with OAuth', type='submit', id='oauth-auth')
div(id='controls')
ul
Something went wrong with that request. Please try again.