Skip to content

Commit

Permalink
getSSOData should call /ssodata from the ULP (#733)
Browse files Browse the repository at this point in the history
* getSSOData should call /ssodata from the ULP

* pr comments

* rename tests
  • Loading branch information
luisrudge authored Apr 6, 2018
1 parent 5293b7d commit 3d7ab72
Show file tree
Hide file tree
Showing 4 changed files with 255 additions and 107 deletions.
7 changes: 6 additions & 1 deletion src/authentication/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
var urljoin = require('url-join');
var qs = require('qs');

var RequestBuilder = require('../helper/request-builder');
var qs = require('qs');
var objectHelper = require('../helper/object');
var assert = require('../helper/assert');
var ssodata = require('../helper/ssodata');
var windowHelper = require('../helper/window');
var responseHandler = require('../helper/response-handler');
var parametersWhitelist = require('../helper/parameters-whitelist');
var Warn = require('../helper/warn');
Expand Down Expand Up @@ -381,6 +382,10 @@ Authentication.prototype.getSSOData = function(withActiveDirectories, cb) {
var WebAuth = require('../web-auth/index'); // eslint-disable-line
this.auth0 = new WebAuth(this.baseOptions);
}
var isHostedLoginPage = windowHelper.getWindow().location.host === this.baseOptions.domain;
if (isHostedLoginPage) {
return this.auth0._universalLogin.getSSOData(withActiveDirectories, cb);
}
if (typeof withActiveDirectories === 'function') {
cb = withActiveDirectories;
}
Expand Down
35 changes: 35 additions & 0 deletions src/web-auth/hosted-pages.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
var urljoin = require('url-join');
var qs = require('qs');

var UsernamePassword = require('./username-password');
var RequestBuilder = require('../helper/request-builder');
var responseHandler = require('../helper/response-handler');
var objectHelper = require('../helper/object');
var windowHelper = require('../helper/window');
var Warn = require('../helper/warn');
Expand All @@ -7,6 +12,7 @@ var assert = require('../helper/assert');
function HostedPages(client, options) {
this.baseOptions = options;
this.client = client;
this.request = new RequestBuilder(this.baseOptions);

this.warn = new Warn({
disableWarnings: !!options._disableDeprecationWarnings
Expand Down Expand Up @@ -95,4 +101,33 @@ HostedPages.prototype.signupAndLogin = function(options, cb) {
});
};

HostedPages.prototype.getSSOData = function(withActiveDirectories, cb) {
var url;
var params = '';

if (typeof withActiveDirectories === 'function') {
cb = withActiveDirectories;
withActiveDirectories = false;
}

assert.check(withActiveDirectories, {
type: 'boolean',
message: 'withActiveDirectories parameter is not valid'
});
assert.check(cb, { type: 'function', message: 'cb parameter is not valid' });

if (withActiveDirectories) {
params =
'?' +
qs.stringify({
ldaps: 1,
client_id: this.baseOptions.clientID
});
}

url = urljoin(this.baseOptions.rootUrl, 'user', 'ssodata', params);

return this.request.get(url, { noHeaders: true }).withCredentials().end(responseHandler(cb));
};

module.exports = HostedPages;
257 changes: 151 additions & 106 deletions test/authentication/authentication.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ var RequestMock = require('../mock/request-mock');
var request = require('superagent');

var RequestBuilder = require('../../src/helper/request-builder');
var windowHelper = require('../../src/helper/window');
var storage = require('../../src/helper/storage');
var Authentication = require('../../src/authentication');
var WebAuth = require('../../src/web-auth');
Expand All @@ -17,7 +18,10 @@ var telemetryInfo = new RequestBuilder({}).getTelemetryData();
describe('auth0.authentication', function() {
before(function() {
this.webAuthSpy = {
checkSession: spy()
checkSession: spy(),
_universalLogin: {
getSSOData: spy()
}
};
});
describe('initialization', function() {
Expand Down Expand Up @@ -267,135 +271,176 @@ describe('auth0.authentication', function() {
});

context('getSSOData', function() {
before(function() {
this.auth0 = new Authentication(this.webAuthSpy, {
domain: 'me.auth0.com',
clientID: '...',
redirectUri: 'http://page.com/callback',
responseType: 'code',
_sendTelemetry: false
});
stub(storage, 'getItem', function(key) {
expect(key).to.be('auth0.ssodata');
return JSON.stringify({
lastUsedConnection: 'lastUsedConnection',
lastUsedUsername: 'lastUsedUsername',
lastUsedSub: 'the-user-id'
context('when outside of the hosted login page', function() {
before(function() {
this.auth0 = new Authentication(this.webAuthSpy, {
domain: 'me.auth0.com',
clientID: '...',
redirectUri: 'http://page.com/callback',
responseType: 'code',
_sendTelemetry: false
});
stub(storage, 'getItem', function(key) {
expect(key).to.be('auth0.ssodata');
return JSON.stringify({
lastUsedConnection: 'lastUsedConnection',
lastUsedUsername: 'lastUsedUsername',
lastUsedSub: 'the-user-id'
});
});
});
});
after(function() {
storage.getItem.restore();
});
it('fails if callback is not a function', function() {
var _this = this;
expect(function() {
_this.auth0.getSSOData(null, null);
}).to.throwError();
});
it('works if callback is the second param', function(done) {
this.auth0.getSSOData(null, function(err, result) {
done();
after(function() {
storage.getItem.restore();
});

this.webAuthSpy.checkSession.lastCall.args[1](null, {
idTokenPayload: { sub: 'some-other-id' }
beforeEach(function() {
stub(windowHelper, 'getWindow', function() {
return { location: { host: 'other-domain.auth0.com' } };
});
});
});
it('uses correct scope and responseType', function() {
this.auth0.getSSOData(function() {});
expect(this.webAuthSpy.checkSession.lastCall.args[0]).to.be.eql({
responseType: 'token id_token',
scope: 'openid profile email',
connection: 'lastUsedConnection',
timeout: 5000
afterEach(function() {
windowHelper.getWindow.restore();
});
});
it('returns sso:false if checkSession fails', function(done) {
this.auth0.getSSOData(function(err, result) {
expect(err).to.be.eql({ some: 'error' });
expect(result).to.be.eql({ sso: false });
done();
it('fails if callback is not a function', function() {
var _this = this;
expect(function() {
_this.auth0.getSSOData(null, null);
}).to.throwError();
});
it('works if callback is the second param', function(done) {
this.auth0.getSSOData(null, function(err, result) {
done();
});

this.webAuthSpy.checkSession.lastCall.args[1]({ some: 'error' });
});
it("returns sso:false if lastUsedSub is different from checkSesion's sub", function(done) {
this.auth0.getSSOData(function(err, result) {
expect(err).to.be.eql(null);
expect(result).to.be.eql({ sso: false });
done();
this.webAuthSpy.checkSession.lastCall.args[1](null, {
idTokenPayload: { sub: 'some-other-id' }
});
});
it('uses correct scope and responseType', function() {
this.auth0.getSSOData(function() {});
expect(this.webAuthSpy.checkSession.lastCall.args[0]).to.be.eql({
responseType: 'token id_token',
scope: 'openid profile email',
connection: 'lastUsedConnection',
timeout: 5000
});
});
it('returns sso:false if checkSession fails', function(done) {
this.auth0.getSSOData(function(err, result) {
expect(err).to.be.eql({ some: 'error' });
expect(result).to.be.eql({ sso: false });
done();
});

this.webAuthSpy.checkSession.lastCall.args[1](null, {
idTokenPayload: { sub: 'some-other-id' }
this.webAuthSpy.checkSession.lastCall.args[1]({ some: 'error' });
});
});
it('do not return error if error === login_required', function(done) {
this.auth0.getSSOData(function(err, result) {
expect(err).to.be(null);
expect(result).to.be.eql({ sso: false });
done();
it("returns sso:false if lastUsedSub is different from checkSesion's sub", function(done) {
this.auth0.getSSOData(function(err, result) {
expect(err).to.be.eql(null);
expect(result).to.be.eql({ sso: false });
done();
});

this.webAuthSpy.checkSession.lastCall.args[1](null, {
idTokenPayload: { sub: 'some-other-id' }
});
});
it('do not return error if error === login_required', function(done) {
this.auth0.getSSOData(function(err, result) {
expect(err).to.be(null);
expect(result).to.be.eql({ sso: false });
done();
});

this.webAuthSpy.checkSession.lastCall.args[1]({
error: 'login_required',
error_description: 'foobar'
this.webAuthSpy.checkSession.lastCall.args[1]({
error: 'login_required',
error_description: 'foobar'
});
});
});
it('provides a better description for consent_required error', function(done) {
this.auth0.getSSOData(function(err, result) {
expect(err).to.be.eql({
it('provides a better description for consent_required error', function(done) {
this.auth0.getSSOData(function(err, result) {
expect(err).to.be.eql({
error: 'consent_required',
error_description: 'Consent required. When using `getSSOData`, the user has to be authenticated with the following scope: `openid profile email`.'
});
expect(result).to.be.eql({ sso: false });
done();
});

this.webAuthSpy.checkSession.lastCall.args[1]({
error: 'consent_required',
error_description: 'Consent required. When using `getSSOData`, the user has to be authenticated with the following scope: `openid profile email`.'
error_description: 'foobar'
});
expect(result).to.be.eql({ sso: false });
done();
});
it('returns ssoData object with lastUsedConnection and idTokenPayload.name when there is no idTokenPayload.email', function(
done
) {
this.auth0.getSSOData(function(err, result) {
expect(err).to.be(null);
expect(result).to.be.eql({
lastUsedConnection: { name: 'lastUsedConnection' },
lastUsedUserID: 'the-user-id',
lastUsedUsername: 'last-used-user-name',
lastUsedClientID: '...',
sessionClients: ['...'],
sso: true
});
done();
});

this.webAuthSpy.checkSession.lastCall.args[1]({
error: 'consent_required',
error_description: 'foobar'
});
});
it('returns ssoData object with lastUsedConnection and idtokenpayload.name when there is no idtokenpayload.email', function(
done
) {
this.auth0.getSSOData(function(err, result) {
expect(err).to.be(null);
expect(result).to.be.eql({
lastUsedConnection: { name: 'lastUsedConnection' },
lastUsedUserID: 'the-user-id',
lastUsedUsername: 'last-used-user-name',
lastUsedClientID: '...',
sessionClients: ['...'],
sso: true
this.webAuthSpy.checkSession.lastCall.args[1](null, {
idTokenPayload: { sub: 'the-user-id', name: 'last-used-user-name' }
});
done();
});
it('returns ssoData object with lastUsedConnection and idTokenPayload.email by default', function(
done
) {
this.auth0.getSSOData(function(err, result) {
expect(err).to.be(null);
expect(result).to.be.eql({
lastUsedConnection: { name: 'lastUsedConnection' },
lastUsedUserID: 'the-user-id',
lastUsedUsername: 'last-used-user-email',
lastUsedClientID: '...',
sessionClients: ['...'],
sso: true
});
done();
});

this.webAuthSpy.checkSession.lastCall.args[1](null, {
idTokenPayload: { sub: 'the-user-id', name: 'last-used-user-name' }
this.webAuthSpy.checkSession.lastCall.args[1](null, {
idTokenPayload: {
sub: 'the-user-id',
email: 'last-used-user-email',
name: 'do not use me'
}
});
});
});
it('returns ssoData object with lastUsedConnection and idtokenpayload.email by default', function(
done
) {
this.auth0.getSSOData(function(err, result) {
expect(err).to.be(null);
expect(result).to.be.eql({
lastUsedConnection: { name: 'lastUsedConnection' },
lastUsedUserID: 'the-user-id',
lastUsedUsername: 'last-used-user-email',
lastUsedClientID: '...',
sessionClients: ['...'],
sso: true

context('when inside of the hosted login page', function() {
before(function() {
this.auth0 = new Authentication(this.webAuthSpy, {
domain: 'me.auth0.com',
clientID: '...',
redirectUri: 'http://page.com/callback',
responseType: 'code',
_sendTelemetry: false
});
done();
});

this.webAuthSpy.checkSession.lastCall.args[1](null, {
idTokenPayload: { sub: 'the-user-id', email: 'last-used-user-email', name: 'do not use me' }
beforeEach(function() {
stub(windowHelper, 'getWindow', function() {
return { location: { host: 'me.auth0.com' } };
});
});
afterEach(function() {
windowHelper.getWindow.restore();
});
it('calls webauth._universalLogin.getSSOData with same params', function() {
this.auth0.getSSOData('withActiveDirectories', 'cb');
expect(this.webAuthSpy._universalLogin.getSSOData.lastCall.args).to.be.eql([
'withActiveDirectories',
'cb'
]);
});
});
});
Expand Down
Loading

0 comments on commit 3d7ab72

Please sign in to comment.