forked from coolaj86/walnut.js
		
	
		
			
	
	
		
			327 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			327 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
								 | 
							
								(function () {
							 | 
						||
| 
								 | 
							
								  'use strict';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  console.log('[DAPLIE oauth3.js]');
							 | 
						||
| 
								 | 
							
								  console.log(window.location);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var iter = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function main() {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var rpc = {};
							 | 
						||
| 
								 | 
							
								    //var myself = location.protocol + '//' + location.host + location.pathname;
							 | 
						||
| 
								 | 
							
								    var incoming;
							 | 
						||
| 
								 | 
							
								    var forwarding = {};
							 | 
						||
| 
								 | 
							
								    var anchor;
							 | 
						||
| 
								 | 
							
								    var err;
							 | 
						||
| 
								 | 
							
								    var browserState;
							 | 
						||
| 
								 | 
							
								    var browserCallback;
							 | 
						||
| 
								 | 
							
								    var action;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function parseParams() {
							 | 
						||
| 
								 | 
							
								      var params = {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      function parseParamsString(str) {
							 | 
						||
| 
								 | 
							
								        str.substr(1).split('&').filter(function (el) { return el; }).forEach(function (pair) {
							 | 
						||
| 
								 | 
							
								          pair = pair.split('=');
							 | 
						||
| 
								 | 
							
								          var key = decodeURIComponent(pair[0]);
							 | 
						||
| 
								 | 
							
								          var val = decodeURIComponent(pair[1]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          if (params[key]) {
							 | 
						||
| 
								 | 
							
								            console.warn("overwriting key '" + key + "' '" + params[key] + "'");
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								          params[key] = val;
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      anchor = document.createElement('a');
							 | 
						||
| 
								 | 
							
								      anchor.href = window.location.href;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      parseParamsString(anchor.search);
							 | 
						||
| 
								 | 
							
								      parseParamsString(anchor.hash);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      return params;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function querystringify(params) {
							 | 
						||
| 
								 | 
							
								      var arr = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      Object.keys(params).forEach(function (k) {
							 | 
						||
| 
								 | 
							
								        arr.push(encodeURIComponent(k) + '=' + encodeURIComponent(params[k]));
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      return arr.join('&');
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function phoneAway(/*redirectURi, params*/) {
							 | 
						||
| 
								 | 
							
								      // TODO test for ? / #
							 | 
						||
| 
								 | 
							
								      window.location.href = incoming.redirect_uri + '#' + querystringify(forwarding);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function lintAndSetRedirectable(browserState, params) {
							 | 
						||
| 
								 | 
							
								      if (!params.redirect_uri) {
							 | 
						||
| 
								 | 
							
								        window.alert('redirect_uri not defined');
							 | 
						||
| 
								 | 
							
								        err = new Error('redirect_uri not defined');
							 | 
						||
| 
								 | 
							
								        console.error(err.message);
							 | 
						||
| 
								 | 
							
								        console.warn(err.stack);
							 | 
						||
| 
								 | 
							
								        params.redirect_uri = document.referer;
							 | 
						||
| 
								 | 
							
								        return false;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (!browserState) {
							 | 
						||
| 
								 | 
							
								        forwarding.error = "E_NO_BROWSER_STATE";
							 | 
						||
| 
								 | 
							
								        forwarding.error_description = "you must specify a state parameter";
							 | 
						||
| 
								 | 
							
								        return false;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      localStorage.setItem('oauth3.states.' + browserState, JSON.stringify(params));
							 | 
						||
| 
								 | 
							
								      return true;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function redirectCallback() {
							 | 
						||
| 
								 | 
							
								      var redirect_uri = incoming.redirect_uri;
							 | 
						||
| 
								 | 
							
								      forwarding.callback = browserState;
							 | 
						||
| 
								 | 
							
								      forwarding.action = 'close';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var url = redirect_uri + '#' + querystringify(forwarding);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      console.log('[debug] redirect_uri + params:', url);
							 | 
						||
| 
								 | 
							
								      window.location.href = url;
							 | 
						||
| 
								 | 
							
								      setTimeout(function () {
							 | 
						||
| 
								 | 
							
								        if (iter >= 3) {
							 | 
						||
| 
								 | 
							
								          console.log("dancing way too much... stopping now");
							 | 
						||
| 
								 | 
							
								          return;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        iter += 1;
							 | 
						||
| 
								 | 
							
								        console.log("I'm dancing by myse-e-elf");
							 | 
						||
| 
								 | 
							
								        // in case I'm redirecting to myself
							 | 
						||
| 
								 | 
							
								        main();
							 | 
						||
| 
								 | 
							
								      }, 0);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    rpc = {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Act as a provider and log the user out
							 | 
						||
| 
								 | 
							
								    rpc.logout = function (browserState, incoming) {
							 | 
						||
| 
								 | 
							
								      var url;
							 | 
						||
| 
								 | 
							
								      if (!lintAndSetRedirectable(browserState, incoming)) {
							 | 
						||
| 
								 | 
							
								        // TODO fail
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      localStorage.setItem('oauth3.states.' + browserState, JSON.stringify(incoming));
							 | 
						||
| 
								 | 
							
								      url = '/#/logout/' + browserState;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // TODO specify specific account or all?
							 | 
						||
| 
								 | 
							
								      window.location.href = url;
							 | 
						||
| 
								 | 
							
								      setTimeout(function () {
							 | 
						||
| 
								 | 
							
								        // in case I'm redirecting to myself
							 | 
						||
| 
								 | 
							
								        main();
							 | 
						||
| 
								 | 
							
								      }, 0);
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Act as a provider and inform the consumer the logout is complete
							 | 
						||
| 
								 | 
							
								    rpc.logout_callback = function (browserState/*, incoming*/) {
							 | 
						||
| 
								 | 
							
								      // TODO pass redirect_uri and state through here so we can avoid localStorage
							 | 
						||
| 
								 | 
							
								      var forwarding = {};
							 | 
						||
| 
								 | 
							
								      var originalRequest;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (!browserState) {
							 | 
						||
| 
								 | 
							
								        forwarding.error = "E_NO_BROWSER_STATE";
							 | 
						||
| 
								 | 
							
								        forwarding.error_description = "you must specify a state parameter";
							 | 
						||
| 
								 | 
							
								        if (incoming.redirect_uri) {
							 | 
						||
| 
								 | 
							
								          phoneAway(incoming.redirect_uri, forwarding);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      originalRequest = JSON.parse(localStorage.getItem('oauth3.states.' + browserState));
							 | 
						||
| 
								 | 
							
								      forwarding.action = 'close';
							 | 
						||
| 
								 | 
							
								      forwarding.state = browserState;
							 | 
						||
| 
								 | 
							
								      //phoneAway(originalRequest.redirect_uri, forwarding);
							 | 
						||
| 
								 | 
							
								      window.location.href = originalRequest.redirect_uri + '#' + querystringify(forwarding);
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    rpc.directives = function (browserState, incoming) {
							 | 
						||
| 
								 | 
							
								      if (!lintAndSetRedirectable(browserState, incoming)) {
							 | 
						||
| 
								 | 
							
								        phoneAway();
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var updatedAt = new Date(localStorage.getItem('oauth3.directives.updated_at')).valueOf();
							 | 
						||
| 
								 | 
							
								      var fresh = (Date.now() - updatedAt) < (24 * 60 * 60 * 1000);
							 | 
						||
| 
								 | 
							
								      var directives = localStorage.getItem('oauth3.directives');
							 | 
						||
| 
								 | 
							
								      var redirected = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      function redirectIf() {
							 | 
						||
| 
								 | 
							
								        if (redirected) {
							 | 
						||
| 
								 | 
							
								          return;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        redirected = true;
							 | 
						||
| 
								 | 
							
								        redirectCallback();
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (directives) {
							 | 
						||
| 
								 | 
							
								        forwarding.directives = directives;
							 | 
						||
| 
								 | 
							
								        redirectIf();
							 | 
						||
| 
								 | 
							
								        if (fresh) {
							 | 
						||
| 
								 | 
							
								          return;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var req = new XMLHttpRequest();
							 | 
						||
| 
								 | 
							
								      req.open('GET', 'oauth3.json', true);
							 | 
						||
| 
								 | 
							
								      req.addEventListener('readystatechange', function () {
							 | 
						||
| 
								 | 
							
								        if (4 !== req.readyState) {
							 | 
						||
| 
								 | 
							
								          return;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (200 !== req.status) {
							 | 
						||
| 
								 | 
							
								          forwarding.error = "E_STATUS_" + req.status;
							 | 
						||
| 
								 | 
							
								          forwarding.error_description = "expected 200 OK json or text response for oauth3.json but got '" + req.status + "'";
							 | 
						||
| 
								 | 
							
								          redirectIf();
							 | 
						||
| 
								 | 
							
								          return;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        try {
							 | 
						||
| 
								 | 
							
								          directives = btoa(JSON.stringify(JSON.parse(req.responseText)));
							 | 
						||
| 
								 | 
							
								          forwarding.directives = directives;
							 | 
						||
| 
								 | 
							
								          forwarding.callback = browserState;
							 | 
						||
| 
								 | 
							
								          localStorage.setItem('oauth3.directives', directives);
							 | 
						||
| 
								 | 
							
								          localStorage.setItem('oauth3.directives.updated_at', new Date().toISOString());
							 | 
						||
| 
								 | 
							
								        } catch(e) {
							 | 
						||
| 
								 | 
							
								          forwarding.error = "E_PARSE_JSON";
							 | 
						||
| 
								 | 
							
								          forwarding.error_description = e.message;
							 | 
						||
| 
								 | 
							
								          console.error(forwarding.error);
							 | 
						||
| 
								 | 
							
								          console.error(forwarding.error_description);
							 | 
						||
| 
								 | 
							
								          console.error(req.responseText);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        redirectIf();
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								      req.send();
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // the provider is contacting me
							 | 
						||
| 
								 | 
							
								    rpc.close = function (browserState, incoming) {
							 | 
						||
| 
								 | 
							
								      incoming.callback = browserState;
							 | 
						||
| 
								 | 
							
								      catchAll();
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    // the provider is contacting me
							 | 
						||
| 
								 | 
							
								    rpc.redirect = function (/*browserState, incoming*/) {
							 | 
						||
| 
								 | 
							
								      catchAll();
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function catchAll() {
							 | 
						||
| 
								 | 
							
								      function phoneHome() {
							 | 
						||
| 
								 | 
							
								        if (browserCallback === 'completeLogin') {
							 | 
						||
| 
								 | 
							
								          // Deprecated
							 | 
						||
| 
								 | 
							
								          (window.opener||window.parent).completeLogin(null, null, incoming);
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								          console.log('I would be closed by my parent now');
							 | 
						||
| 
								 | 
							
								          (window.opener||window.parent)['__oauth3_' + browserCallback](incoming);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (!(incoming.browser_state || incoming.state)) {
							 | 
						||
| 
								 | 
							
								        window.alert("callback URLs should include 'browser_state' (authorization code)"
							 | 
						||
| 
								 | 
							
								          + " or 'state' (implicit grant))");
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      setTimeout(function () {
							 | 
						||
| 
								 | 
							
								        // opener is for popup window, new tab
							 | 
						||
| 
								 | 
							
								        // parent is for iframe
							 | 
						||
| 
								 | 
							
								        phoneHome();
							 | 
						||
| 
								 | 
							
								      }, 10);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // iOS Webview (namely Chrome) workaround
							 | 
						||
| 
								 | 
							
								      setTimeout(function () {
							 | 
						||
| 
								 | 
							
								        console.log('I would close now');
							 | 
						||
| 
								 | 
							
								        window.open('', '_self', '');
							 | 
						||
| 
								 | 
							
								        window.close();
							 | 
						||
| 
								 | 
							
								      }, 50);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      setTimeout(function () {
							 | 
						||
| 
								 | 
							
								        var i;
							 | 
						||
| 
								 | 
							
								        var len = localStorage.length;
							 | 
						||
| 
								 | 
							
								        var key;
							 | 
						||
| 
								 | 
							
								        var json;
							 | 
						||
| 
								 | 
							
								        var fresh;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for (i = 0; i < len; i += 1) {
							 | 
						||
| 
								 | 
							
								          key = localStorage.key(i);
							 | 
						||
| 
								 | 
							
								          // TODO check updatedAt
							 | 
						||
| 
								 | 
							
								          if (/^oauth3\./.test(key)) {
							 | 
						||
| 
								 | 
							
								            try {
							 | 
						||
| 
								 | 
							
								              json = localStorage.getItem(key);
							 | 
						||
| 
								 | 
							
								              if (json) {
							 | 
						||
| 
								 | 
							
								                json = JSON.parse(json);
							 | 
						||
| 
								 | 
							
								              }
							 | 
						||
| 
								 | 
							
								            } catch (e) {
							 | 
						||
| 
								 | 
							
								              // ignore
							 | 
						||
| 
								 | 
							
								              json = null;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            fresh = json && (Date.now() - json.updatedAt < (5 * 60 * 1000));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (!fresh) {
							 | 
						||
| 
								 | 
							
								              localStorage.removeItem(key);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        forwarding.updatedAt = Date.now();
							 | 
						||
| 
								 | 
							
								        localStorage.setItem('oauth3.' + (forwarding.browser_state || forwarding.state), JSON.stringify(forwarding));
							 | 
						||
| 
								 | 
							
								      }, 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function parseAction(params) {
							 | 
						||
| 
								 | 
							
								      if (params.action) {
							 | 
						||
| 
								 | 
							
								        return params.action;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (params.close) {
							 | 
						||
| 
								 | 
							
								        return 'close';
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      if (params.logout_callback) {
							 | 
						||
| 
								 | 
							
								        return 'logout_callback';
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      if (params.logout) {
							 | 
						||
| 
								 | 
							
								        return 'logout';
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      if (params.callback) {
							 | 
						||
| 
								 | 
							
								        return 'close';
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      if (params.directives) {
							 | 
						||
| 
								 | 
							
								        return 'directives';
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      return 'redirect';
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    incoming = parseParams();
							 | 
						||
| 
								 | 
							
								    browserState = incoming.browser_state || incoming.state;
							 | 
						||
| 
								 | 
							
								    action = parseAction(incoming);
							 | 
						||
| 
								 | 
							
								    forwarding.url = window.location.href;
							 | 
						||
| 
								 | 
							
								    forwarding.browser_state = browserState;
							 | 
						||
| 
								 | 
							
								    forwarding.state = browserState;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!incoming.provider_uri) {
							 | 
						||
| 
								 | 
							
								      browserCallback = incoming.callback || browserState;
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      // deprecated
							 | 
						||
| 
								 | 
							
								      browserCallback = 'completeLogin';
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    console.log('[debug]', action, incoming);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (rpc[action]) {
							 | 
						||
| 
								 | 
							
								      rpc[action](browserState, incoming);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      window.alert('unsupported action');
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  main();
							 | 
						||
| 
								 | 
							
								}());
							 |