import urljoin from 'url-join';
import windowHelper from '../helper/window';
import objectHelper from '../helper/object';
import RequestBuilder from '../helper/request-builder';
import WebMessageHandler from './web-message-handler';
import responseHandler from '../helper/response-handler';
import Storage from '../helper/storage';
import * as times from '../helper/times';
function CrossOriginAuthentication(webAuth, options) {
this.webAuth = webAuth;
this.baseOptions = options;
this.request = new RequestBuilder(options);
this.webMessageHandler = new WebMessageHandler(webAuth);
this.storage = new Storage(options);
}
function getFragment(name) {
var theWindow = windowHelper.getWindow();
var value = '&' + theWindow.location.hash.substring(1);
var parts = value.split('&' + name + '=');
if (parts.length === 2) {
return parts
.pop()
.split('&')
.shift();
}
}
function createKey(origin, coId) {
return [
'co/verifier',
encodeURIComponent(origin),
encodeURIComponent(coId)
].join('/');
}
/**
* Logs in the user with username and password using the cross origin authentication (/co/authenticate) flow. You can use either `username` or `email` to identify the user, but `username` will take precedence over `email`.
* Some browsers might not be able to successfully authenticate if 3rd party cookies are disabled in your browser. [See here for more information.]{@link https://auth0.com/docs/cross-origin-authentication}.
* After the /co/authenticate call, you'll have to use the {@link parseHash} function at the `redirectUri` specified in the constructor.
*
* @method login
* @param {Object} options options used in the {@link authorize} call after the login_ticket is acquired
* @param {String} [options.username] Username (mutually exclusive with email)
* @param {String} [options.email] Email (mutually exclusive with username)
* @param {String} options.password Password
* @param {String} [options.realm] Realm used to authenticate the user, it can be a realm name or a database connection name
* @param {crossOriginLoginCallback} cb Callback function called only when an authentication error, like invalid username or password, occurs. For other types of errors, there will be a redirect to the `redirectUri`.
*/
CrossOriginAuthentication.prototype.login = function(options, cb) {
var _this = this;
var url = urljoin(this.baseOptions.rootUrl, '/co/authenticate');
options.username = options.username || options.email;
delete options.email;
var authenticateBody = {
client_id: options.clientID || this.baseOptions.clientID,
username: options.username
};
if (options.password) {
authenticateBody.password = options.password;
}
if (options.otp) {
authenticateBody.otp = options.otp;
}
var realm = options.realm || this.baseOptions.realm;
if (realm) {
var credentialType =
options.credentialType ||
this.baseOptions.credentialType ||
'http://auth0.com/oauth/grant-type/password-realm';
authenticateBody.realm = realm;
authenticateBody.credential_type = credentialType;
} else {
authenticateBody.credential_type = 'password';
}
this.request
.post(url)
.withCredentials()
.send(authenticateBody)
.end(function(err, data) {
if (err) {
var errorObject = (err.response && err.response.body) || {
error: 'request_error',
error_description: JSON.stringify(err)
};
return responseHandler(cb, { forceLegacyError: true })(errorObject);
}
var popupMode = options.popup === true;
options = objectHelper.blacklist(options, [
'password',
'credentialType',
'otp',
'popup'
]);
var authorizeOptions = objectHelper
.merge(options)
.with({ loginTicket: data.body.login_ticket });
var key = createKey(_this.baseOptions.rootUrl, data.body.co_id);
_this.storage.setItem(key, data.body.co_verifier, {
expires: times.MINUTES_15
});
if (popupMode) {
_this.webMessageHandler.run(
authorizeOptions,
responseHandler(cb, { forceLegacyError: true })
);
} else {
_this.webAuth.authorize(authorizeOptions);
}
});
};
function tryGetVerifier(storage, key) {
try {
var verifier = storage.getItem(key);
storage.removeItem(key);
return verifier || '';
} catch (e) {
return '';
}
}
/**
* Runs the callback code for the cross origin authentication call. This method is meant to be called by the cross origin authentication callback url.
*
* @method callback
*/
CrossOriginAuthentication.prototype.callback = function() {
var targetOrigin = decodeURIComponent(getFragment('origin'));
var theWindow = windowHelper.getWindow();
var _this = this;
theWindow.addEventListener('message', function(evt) {
if (evt.data.type !== 'co_verifier_request') {
return;
}
var key = createKey(evt.origin, evt.data.request.id);
var verifier = tryGetVerifier(_this.storage, key);
evt.source.postMessage(
{
type: 'co_verifier_response',
response: {
verifier: verifier
}
},
evt.origin
);
});
theWindow.parent.postMessage({ type: 'ready' }, targetOrigin);
};
export default CrossOriginAuthentication;