/* Concerto Authentication Javascript
* Handles authenticating the player browser to Concerto and sending it * to the frontend. * * Basic workflow: * - If the screen does not yet have a valid token: * + displays the temporary token * + polls the local server until the local server has a permanent token * + continues to the next section: * - If the screen has a valid token: * + requests the relevant data from the local server * + performs a one-time authenticated request to concerto to get * an authentication cookie for the browser * + redirects to the frontend */
var checkIntHandle; // Handle for the polling event var verbose = false;
window.arrify = function(args) {
return (args.length == 1) ? args[0] : Array.prototype.slice.call(args);
}
// usage: log(‘inside coolFunc’,this,arguments); // paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/ window.log = function(){
if(this.console) console.log( arrify(arguments) );
}; window.error = function(){
// we'll use warn for "errors" so we can clarify between // logistical problems and actual JS errors. if(this.console) console.warn( arrify(arguments) );
}; window.debug = function(){
if(this.console && verbose) console.debug( arrify(arguments));
};
function update_status(statstring) {
var stat=document.getElementById("status"); stat.innerHTML=statstring;
}
function check_token() {
// make a request to localhost to see if we are ready to redirect var req = new XMLHttpRequest(); req.addEventListener("load", function() { var error_shown = false var token_shown = false if (req.status != 200) { update_status("Error! Something went wrong with Bandshell."); error("Recieved an unexpected response from Bandshell, "+ "status = "+req.status+" ("+req.statusText+")"); } else { update_status("..."); try { data = JSON.parse(this.responseText); } catch (e) { update_status("Error: Bad data from Bandshell."); error("There was a problem with the JSON response from Bandshell:\n"+ " "+e.toString()+"\n"+ "The text in question was:\n"+ " "+this.responseText); return; } if (data.error){ error("received error: "+data.error); error_shown = true; show_error(data.error, data.error_res); } else if (data.accepted==1) { update_status("Success!"); log("Temporary token accepted by the server."); redirect_with_auth(data.url, data.user, data.pass); } else if (data.temp_token) { update_status("..."); token_shown = true; show_token(data.temp_token); debug("Have temp token, but it has not been accepted yet."); } }
// if (!error_shown) {hide_error();}
if (!token_shown) {hide_token();} }); req.addEventListener("error", function() { update_status("Error! Bandshell is not responding."); error("Request to Bandshell failed. "+ "status = "+req.status+"."); }); req.open("GET", "authenticate.json", true); req.send();
}
function show_token(temp_token) {
document.getElementById("instructions").style.display = 'block'; document.getElementById("screen_temp_token").innerHTML = temp_token;
}
function hide_token() {
document.getElementById("instructions").style.display = 'none';
}
function show_error(error, error_res) {
document.getElementById("error").style.display = 'block'; document.getElementById("error_text").innerHTML = error; if (error_res) { document.getElementById("error_res_text").innerHTML = error_res; } else { document.getElementById("error_res_text").innerHTML = ""; }
} function hide_error(error, error_res) {
document.getElementById("error").style.display = 'none';
}
/* Basic theory of operation:
* We use a complicated CORs setup to login to the frontend with an XHR * request. Normally frontend API requests should be stateless, but if * we pass ?request_cookie the frontend will assign a cookie with the * screen token, under Concerto's domain, to the client browser (important * because redirecting with basic auth is flaky). Now when we redirect to * the frontend, the cookie is used and the screen is authenticated. */
function redirect_with_auth(url,user,pass) {
log("Attempting pre-authorization for redirect to "+url+"."); var req = new XMLHttpRequest(); auth_url = url + "?request_cookie"; // Note: Firefox will prevent creating the request object for // cross-site requests if user and pass are given to open req.open("GET", auth_url, true); req.setRequestHeader("Authorization", "Basic " + btoa(user+":"+pass)) req.withCredentials = true; req.addEventListener("load", function() { if (req.status == 200) { log("Pre-authorization appears successful."); log("Redirecting to "+url+"."); document.location=url; } else { update_status("Error authenticating. Please ensuring Concerto is "+ "running properly."); error("Pre-authorization request resulted in status "+req.status+ " from Concerto."); } }); // TODO: review this error handling. req.addEventListener("error", function() { update_status("Concerto Server Inaccessible. Make sure it is up."); error("Pre-authentication request to Concerto errored out."); }); req.send();
}
window.onload = function () {
log("Authentication Script Initializing"); var nojs=document.getElementById("no-js"); nojs.parentNode.removeChild(nojs); // perform the first request ASAP update_status("..."); check_token(); // schedule polling in such a way that the user can see what's going on checkIntHandle = window.setInterval(function(){ update_status("Updating..."); setTimeout(function() { check_token() },500); }, 5000);
}