Auth Adapters refactoring (#3177)

* Moves all authentication providers to Adapter/Auth

* refactors specs

* Deprecates oauth option in favor of auth option

- Deprecates facebookAppIds option (in favor of auth.facebook.appIds)
- Adds warnings about the deprecated options

* nits
This commit is contained in:
Florent Vilmart
2016-12-06 17:09:43 -05:00
committed by Arthur Cinader
parent a9067260fc
commit c1dcaf1271
28 changed files with 407 additions and 267 deletions

View File

@@ -1,145 +1,13 @@
var OAuth = require("../src/authDataManager/OAuth1Client");
var request = require('request');
var Config = require("../src/Config");
var defaultColumns = require('../src/Controllers/SchemaController').defaultColumns;
var authenticationLoader = require('../src/Adapters/Auth');
var path = require('path');
describe('OAuth', function() {
it("Nonce should have right length", (done) => {
jequal(OAuth.nonce().length, 30);
done();
});
it("Should properly build parameter string", (done) => {
var string = OAuth.buildParameterString({c:1, a:2, b:3})
jequal(string, "a=2&b=3&c=1");
done();
});
it("Should properly build empty parameter string", (done) => {
var string = OAuth.buildParameterString()
jequal(string, "");
done();
});
it("Should properly build signature string", (done) => {
var string = OAuth.buildSignatureString("get", "http://dummy.com", "");
jequal(string, "GET&http%3A%2F%2Fdummy.com&");
done();
});
it("Should properly generate request signature", (done) => {
var request = {
host: "dummy.com",
path: "path"
};
var oauth_params = {
oauth_timestamp: 123450000,
oauth_nonce: "AAAAAAAAAAAAAAAAA",
oauth_consumer_key: "hello",
oauth_token: "token"
};
var consumer_secret = "world";
var auth_token_secret = "secret";
request = OAuth.signRequest(request, oauth_params, consumer_secret, auth_token_secret);
jequal(request.headers['Authorization'], 'OAuth oauth_consumer_key="hello", oauth_nonce="AAAAAAAAAAAAAAAAA", oauth_signature="8K95bpQcDi9Nd2GkhumTVcw4%2BXw%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="123450000", oauth_token="token", oauth_version="1.0"');
done();
});
it("Should properly build request", (done) => {
var options = {
host: "dummy.com",
consumer_key: "hello",
consumer_secret: "world",
auth_token: "token",
auth_token_secret: "secret",
// Custom oauth params for tests
oauth_params: {
oauth_timestamp: 123450000,
oauth_nonce: "AAAAAAAAAAAAAAAAA"
}
};
var path = "path";
var method = "get";
var oauthClient = new OAuth(options);
var req = oauthClient.buildRequest(method, path, {"query": "param"});
jequal(req.host, options.host);
jequal(req.path, "/"+path+"?query=param");
jequal(req.method, "GET");
jequal(req.headers['Content-Type'], 'application/x-www-form-urlencoded');
jequal(req.headers['Authorization'], 'OAuth oauth_consumer_key="hello", oauth_nonce="AAAAAAAAAAAAAAAAA", oauth_signature="wNkyEkDE%2F0JZ2idmqyrgHdvC0rs%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="123450000", oauth_token="token", oauth_version="1.0"')
done();
});
function validateCannotAuthenticateError(data, done) {
jequal(typeof data, "object");
jequal(typeof data.errors, "object");
var errors = data.errors;
jequal(typeof errors[0], "object");
// Cannot authenticate error
jequal(errors[0].code, 32);
done();
}
it("Should fail a GET request", (done) => {
var options = {
host: "api.twitter.com",
consumer_key: "XXXXXXXXXXXXXXXXXXXXXXXXX",
consumer_secret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
};
var path = "/1.1/help/configuration.json";
var params = {"lang": "en"};
var oauthClient = new OAuth(options);
oauthClient.get(path, params).then(function(data){
validateCannotAuthenticateError(data, done);
})
});
it("Should fail a POST request", (done) => {
var options = {
host: "api.twitter.com",
consumer_key: "XXXXXXXXXXXXXXXXXXXXXXXXX",
consumer_secret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
};
var body = {
lang: "en"
};
var path = "/1.1/account/settings.json";
var oauthClient = new OAuth(options);
oauthClient.post(path, null, body).then(function(data){
validateCannotAuthenticateError(data, done);
})
});
it("Should fail a request", (done) => {
var options = {
host: "localhost",
consumer_key: "XXXXXXXXXXXXXXXXXXXXXXXXX",
consumer_secret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
};
var body = {
lang: "en"
};
var path = "/";
var oauthClient = new OAuth(options);
oauthClient.post(path, null, body).then(function(){
jequal(false, true);
done();
}).catch(function(){
jequal(true, true);
done();
})
});
describe('AuthenticationProviers', function() {
["facebook", "github", "instagram", "google", "linkedin", "meetup", "twitter", "janrainengage", "janraincapture", "vkontakte"].map(function(providerName){
it("Should validate structure of "+providerName, (done) => {
var provider = require("../src/authDataManager/"+providerName);
var provider = require("../src/Adapters/Auth/"+providerName);
jequal(typeof provider.validateAuthData, "function");
jequal(typeof provider.validateAppId, "function");
jequal(provider.validateAuthData({}, {}).constructor, Promise.prototype.constructor);
@@ -325,5 +193,90 @@ describe('OAuth', function() {
});
});
function validateValidator(validator) {
expect(typeof validator).toBe('function');
}
})
function validateAuthenticationHandler(authenticatonHandler) {
expect(authenticatonHandler).not.toBeUndefined();
expect(typeof authenticatonHandler.getValidatorForProvider).toBe('function');
expect(typeof authenticatonHandler.getValidatorForProvider).toBe('function');
}
it('properly loads custom adapter', (done) => {
var validAuthData = {
id: 'hello',
token: 'world'
}
let adapter = {
validateAppId: function() {
return Promise.resolve();
},
validateAuthData: function(authData) {
if (authData.id == validAuthData.id && authData.token == validAuthData.token) {
return Promise.resolve();
}
return Promise.reject();
}
};
let authDataSpy = spyOn(adapter, 'validateAuthData').and.callThrough();
let appIdSpy = spyOn(adapter, 'validateAppId').and.callThrough();
let authenticationHandler = authenticationLoader({
customAuthentication: adapter
});
validateAuthenticationHandler(authenticationHandler);
let validator = authenticationHandler.getValidatorForProvider('customAuthentication');
validateValidator(validator);
validator(validAuthData).then(() => {
expect(authDataSpy).toHaveBeenCalled();
// AppIds are not provided in the adapter, should not be called
expect(appIdSpy).not.toHaveBeenCalled();
done();
}, (err) => {
jfail(err);
done();
})
});
it('properly loads custom adapter module object', (done) => {
let authenticationHandler = authenticationLoader({
customAuthentication: path.resolve('./spec/support/CustomAuth.js')
});
validateAuthenticationHandler(authenticationHandler);
let validator = authenticationHandler.getValidatorForProvider('customAuthentication');
validateValidator(validator);
validator({
token: 'my-token'
}).then(() => {
done();
}, (err) => {
jfail(err);
done();
})
});
it('properly loads custom adapter module object', (done) => {
let authenticationHandler = authenticationLoader({
customAuthentication: { module: path.resolve('./spec/support/CustomAuthFunction.js'), options: { token: 'valid-token' }}
});
validateAuthenticationHandler(authenticationHandler);
let validator = authenticationHandler.getValidatorForProvider('customAuthentication');
validateValidator(validator);
validator({
token: 'valid-token'
}).then(() => {
done();
}, (err) => {
jfail(err);
done();
})
});
});

136
spec/OAuth1.spec.js Normal file
View File

@@ -0,0 +1,136 @@
var OAuth = require("../src/Adapters/Auth/OAuth1Client");
describe('OAuth', function() {
it("Nonce should have right length", (done) => {
jequal(OAuth.nonce().length, 30);
done();
});
it("Should properly build parameter string", (done) => {
var string = OAuth.buildParameterString({c:1, a:2, b:3})
jequal(string, "a=2&b=3&c=1");
done();
});
it("Should properly build empty parameter string", (done) => {
var string = OAuth.buildParameterString()
jequal(string, "");
done();
});
it("Should properly build signature string", (done) => {
var string = OAuth.buildSignatureString("get", "http://dummy.com", "");
jequal(string, "GET&http%3A%2F%2Fdummy.com&");
done();
});
it("Should properly generate request signature", (done) => {
var request = {
host: "dummy.com",
path: "path"
};
var oauth_params = {
oauth_timestamp: 123450000,
oauth_nonce: "AAAAAAAAAAAAAAAAA",
oauth_consumer_key: "hello",
oauth_token: "token"
};
var consumer_secret = "world";
var auth_token_secret = "secret";
request = OAuth.signRequest(request, oauth_params, consumer_secret, auth_token_secret);
jequal(request.headers['Authorization'], 'OAuth oauth_consumer_key="hello", oauth_nonce="AAAAAAAAAAAAAAAAA", oauth_signature="8K95bpQcDi9Nd2GkhumTVcw4%2BXw%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="123450000", oauth_token="token", oauth_version="1.0"');
done();
});
it("Should properly build request", (done) => {
var options = {
host: "dummy.com",
consumer_key: "hello",
consumer_secret: "world",
auth_token: "token",
auth_token_secret: "secret",
// Custom oauth params for tests
oauth_params: {
oauth_timestamp: 123450000,
oauth_nonce: "AAAAAAAAAAAAAAAAA"
}
};
var path = "path";
var method = "get";
var oauthClient = new OAuth(options);
var req = oauthClient.buildRequest(method, path, {"query": "param"});
jequal(req.host, options.host);
jequal(req.path, "/"+path+"?query=param");
jequal(req.method, "GET");
jequal(req.headers['Content-Type'], 'application/x-www-form-urlencoded');
jequal(req.headers['Authorization'], 'OAuth oauth_consumer_key="hello", oauth_nonce="AAAAAAAAAAAAAAAAA", oauth_signature="wNkyEkDE%2F0JZ2idmqyrgHdvC0rs%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="123450000", oauth_token="token", oauth_version="1.0"')
done();
});
function validateCannotAuthenticateError(data, done) {
jequal(typeof data, "object");
jequal(typeof data.errors, "object");
var errors = data.errors;
jequal(typeof errors[0], "object");
// Cannot authenticate error
jequal(errors[0].code, 32);
done();
}
it("Should fail a GET request", (done) => {
var options = {
host: "api.twitter.com",
consumer_key: "XXXXXXXXXXXXXXXXXXXXXXXXX",
consumer_secret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
};
var path = "/1.1/help/configuration.json";
var params = {"lang": "en"};
var oauthClient = new OAuth(options);
oauthClient.get(path, params).then(function(data){
validateCannotAuthenticateError(data, done);
})
});
it("Should fail a POST request", (done) => {
var options = {
host: "api.twitter.com",
consumer_key: "XXXXXXXXXXXXXXXXXXXXXXXXX",
consumer_secret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
};
var body = {
lang: "en"
};
var path = "/1.1/account/settings.json";
var oauthClient = new OAuth(options);
oauthClient.post(path, null, body).then(function(data){
validateCannotAuthenticateError(data, done);
})
});
it("Should fail a request", (done) => {
var options = {
host: "localhost",
consumer_key: "XXXXXXXXXXXXXXXXXXXXXXXXX",
consumer_secret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
};
var body = {
lang: "en"
};
var path = "/";
var oauthClient = new OAuth(options);
oauthClient.post(path, null, body).then(function(){
jequal(false, true);
done();
}).catch(function(){
jequal(true, true);
done();
})
});
});

View File

@@ -1,4 +1,4 @@
let twitter = require('../src/authDataManager/twitter');
let twitter = require('../src/Adapters/Auth/twitter');
describe('Twitter Auth', () => {
it('should use the proper configuration', () => {

View File

@@ -102,7 +102,7 @@ var defaultConfiguration = {
bundleId: 'bundleId',
}
},
oauth: { // Override the facebook provider
auth: { // Override the facebook provider
facebook: mockFacebook(),
myoauth: {
module: path.resolve(__dirname, "myoauth") // relative path as it's run from src

View File

@@ -0,0 +1,12 @@
module.exports = {
validateAppId: function() {
return Promise.resolve();
},
validateAuthData: function(authData) {
if (authData.token == 'my-token') {
return Promise.resolve();
}
return Promise.reject();
}
}

View File

@@ -0,0 +1,14 @@
module.exports = function(validAuthData) {
return {
validateAppId: function() {
return Promise.resolve();
},
validateAuthData: function(authData) {
if (authData.token == validAuthData.token) {
return Promise.resolve();
}
return Promise.reject();
}
}
}