cordova-plugin-auth-dialog
Version:
Adds support for authentication dialogs into Apache Cordova.
225 lines (208 loc) • 9.67 kB
JavaScript
/*jshint -W030 */
/*global WinJS, cordova*/
/**
* Shows UI for entering credentials and calls callback with credentials supplied.
* @param uri Uri that will be shown in credentials dialog
* @param successCB Success callback
* @param errorCB Error callback
*/
function requestCredentials(uri, successCB, errorCB) {
// In case of Windows phone 8 reuse native authentication dialog
if (cordova.platformId === 'windowsphone') {
var exec = cordova.require('cordova/exec');
exec(successCB, errorCB, 'AuthDialog', 'requestCredentials', [uri]);
return;
}
var authDialog = document.createElement('div');
authDialog.style.cssText = "position: absolute; left: 0; top: 0; width: 100%; height: 100%; background: black; color: white; text-transform: none; font-family: Segoe; overflow: hidden;";
authDialog.innerHTML = "" +
'<div style="padding: 0px 12px;">' +
'<h1 style="font-size: 28pt; font-weight: 300; letter-spacing: 0; line-height: 1.1429; text-align: left;">Sign in</h1>' +
'</div>';
authDialog.innerHTML +=
'<div style="font-size: 15pt; font-weight: 500; line-height: 1.3636; padding: 0px 12px;">' +
'<p>Website</br>' + uri + '</p>' +
'<span style="font-size: 10pt; font-weight: 300; line-height: 1.3636;">Username:</span></br>' +
'<input type="text" id="username" style="-ms-user-select: element; min-height: 38px; border-width: 3px; border-style: solid; width: 100%;"></br>' +
'<span style="font-size: 10pt; font-weight: 300; line-height: 1.3636;">Password:</span></br>' +
'<input type="password" id="password" style="-ms-user-select: element; min-height: 38px; border-width: 3px; border-style: solid; width: 100%;"></br>' +
'<div style="text-align: right; margin: auto;">' +
'<button id="login" style="color: white; min-height: 39px; min-width: 108px; padding: 0px 6px; border: white solid 2.25px; background-clip: padding-box; max-width: 100%; margin: 12px; font-size: 14pt; font-weight: 600; background-color: black;">Login</button>' +
'<button id="cancel" style="color: white; min-height: 39px; min-width: 108px; padding: 0px 6px; border: white solid 2.25px; background-clip: padding-box; max-width: 100%; margin: 12px; font-size: 14pt; font-weight: 600; background-color: black;">Cancel</button>' +
'</div>' +
'</div>';
document.body.appendChild(authDialog);
var loginButton = document.getElementById('login');
var cancelButton = document.getElementById('cancel');
var usernameField = document.getElementById('username');
var passwordField = document.getElementById('password');
cancelButton.addEventListener('click', function () {
document.body.removeChild(authDialog);
errorCB && errorCB("Login cancelled");
});
loginButton.addEventListener('click', function () {
document.body.removeChild(authDialog);
successCB && successCB({ username: usernameField.value, password: passwordField.value });
});
}
/**
* Bootstarapper for wrapping XHR object
* Adds transparent https authentication support for all XHRs
* @param win Object to bootstrap
*/
function bootstrapXHR(win) {
var aliasXHR = win.XMLHttpRequest;
var XHRShim = function () { };
win.XMLHttpRequest = XHRShim;
XHRShim.noConflict = aliasXHR;
XHRShim.UNSENT = 0;
XHRShim.OPENED = 1;
XHRShim.HEADERS_RECEIVED = 2;
XHRShim.LOADING = 3;
XHRShim.DONE = 4;
XHRShim.prototype = {
isAsync: true,
onreadystatechange: null,
readyState: 0,
_url: '',
timeout: 0,
withCredentials: false,
_requestHeaders: null,
statusText: '',
responseText: '',
responseXML: '',
status: 404,
open: function (reqType, uri, isAsync, user, password) {
this._reqType = reqType;
this._url = uri;
this.isAsync = isAsync === false ? false : true;
this.wrappedXHR = new aliasXHR();
var self = this;
if (this.timeout > 0) {
this.wrappedXHR.timeout = this.timeout;
}
Object.defineProperty(this, 'timeout', {
set: function (val) {
this.wrappedXHR.timeout = val;
},
get: function () {
return this.wrappedXHR.timeout;
}
});
if (this.withCredentials) {
this.wrappedXHR.withCredentials = this.withCredentials;
}
Object.defineProperty(this, 'withCredentials', {
set: function (val) {
this.wrappedXHR.withCredentials = val;
},
get: function () {
return this.wrappedXHR.withCredentials;
}
});
Object.defineProperty(this, 'status', {
get: function () {
return this.wrappedXHR.status;
}
});
Object.defineProperty(this, 'responseText', {
get: function () {
return this.wrappedXHR.responseText;
}
});
Object.defineProperty(this, 'statusText', {
get: function () {
return this.wrappedXHR.statusText;
}
});
Object.defineProperty(this, 'responseXML', {
get: function () {
return this.wrappedXHR.responseXML;
}
});
Object.defineProperty(this, 'response', {
get: function () {
return this.wrappedXHR.response;
}
});
Object.defineProperty(this, 'responseType', {
set: function (val) {
return (this.wrappedXHR.responseType = val);
}
});
this.getResponseHeader = function (header) {
return this.wrappedXHR.getResponseHeader(header);
};
this.getAllResponseHeaders = function () {
return this.wrappedXHR.getAllResponseHeaders();
};
this.wrappedXHR.onreadystatechange = function onreadystatechangeListener(e) {
// Magic is here :)
// Try to catch HTTP 401 code and resend an authorized request then
if (e.target.status && e.target.status === 401) {
// Got 401? First remove an existing onreadystatechange event handler from stale XHR
self.wrappedXHR.onreadystatechange = null;
// Then ask for credentials and do magic
requestCredentials(self._url, function (creds) {
// Create an authorization request and wrap new XHR with credentials supplied
self.wrappedXHR = new aliasXHR();
self.wrappedXHR.open(self._reqType, self._url, self.isAsync, creds.username, creds.password);
// and bind onreadystatechange event handler to new XHR
self.wrappedXHR.onreadystatechange = onreadystatechangeListener;
self.wrappedXHR.send(self._data);
});
} else {
self.changeReadyState(e.target.readyState);
}
};
return this.wrappedXHR.open(reqType, uri, isAsync, user, password);
},
changeReadyState: function (newState) {
var evt;
this.readyState = newState;
if (this.onreadystatechange) {
// mimic simple 'readystatechange' event which should be passed as per spec
evt = { type: 'readystatechange', target: this, timeStamp: new Date().getTime() };
this.onreadystatechange(evt);
}
if (this.readyState === XHRShim.DONE) {
if (this.status !== 0) {
evt = { type: 'load', target: this, timeStamp: new Date().getTime() };
this.onload && this.onload(evt);
} else {
evt = { type: 'error', target: this, timeStamp: new Date().getTime() };
this.onerror && this.onerror(evt);
}
}
},
addEventListener: function (type, listener, useCapture) {
this.wrappedXHR.addEventListener(type, listener, useCapture);
},
removeEventListener: function (type, listener, useCapture) {
this.wrappedXHR.removeEventListener(type, listener, useCapture);
},
setRequestHeader: function (header, value) {
this.wrappedXHR.setRequestHeader(header, value);
},
getResponseHeader: function (header) {
return this.wrappedXHR.getResponseHeader(header);
},
getAllResponseHeaders: function () {
this.wrappedXHR.getAllResponseHeaders();
},
overrideMimeType: function (mimetype) {
return this.wrappedXHR.overrideMimeType(mimetype);
},
abort: function () {
return this.wrappedXHR.abort();
},
send: function (data) {
this._data = data;
return this.wrappedXHR.send(data);
}
};
}
var isWindowsPhone = ((cordova.platformId === 'windows') && WinJS.Utilities.isPhone) || cordova.platformId === 'windowsphone';
if (isWindowsPhone) {
bootstrapXHR(window);
}