Refactored into it's own library.
This commit is contained in:
parent
1a7c79fb18
commit
8a8e87c0d4
3 changed files with 262 additions and 76 deletions
209
static/authenticator.js
Normal file
209
static/authenticator.js
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
(function(){
|
||||||
|
'use strict';
|
||||||
|
//This is how the world accesses it!
|
||||||
|
var Auth = window.$Auth = {};
|
||||||
|
var error;
|
||||||
|
var expireListeners = [];
|
||||||
|
|
||||||
|
//Init should be called before any other functions are used
|
||||||
|
//settings: {
|
||||||
|
// clientID: client_id for oauth request
|
||||||
|
// redirectURI: where redirect to with the token after authorizing
|
||||||
|
// authEndpoint: OAuth endpoint to retrieve token
|
||||||
|
// requiredPerms: what permissions to request from the user
|
||||||
|
//}
|
||||||
|
Auth.init = function(settings) {
|
||||||
|
var isValid = Auth.verifySettings(settings);
|
||||||
|
if(!isValid) {
|
||||||
|
console.error("authenticator($Auth) initialized with invalid settings! \
|
||||||
|
There may be problems...");
|
||||||
|
}
|
||||||
|
Auth.settings = {};
|
||||||
|
Auth.settings.clientID = settings.clientID;
|
||||||
|
Auth.settings.redirectURI = settings.redirectURI;
|
||||||
|
Auth.settings.authEndpoint = settings.authEndpoint;
|
||||||
|
Auth.settings.requiredPerms = settings.requiredPerms;
|
||||||
|
|
||||||
|
if(Auth.getToken()) {
|
||||||
|
if(Date.now() >= Auth.getToken().expiresTime) {
|
||||||
|
Auth.removeToken();
|
||||||
|
} else {
|
||||||
|
window.setTimeout(Auth.expire, Auth.getToken().expiresTime - Date.now());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(window.location.hash !== "") {
|
||||||
|
var hashParams = Auth.parseParams(window.location.hash);
|
||||||
|
|
||||||
|
if(hashParams.error_reason) {
|
||||||
|
error = error_reason;
|
||||||
|
window.location.hash = "";
|
||||||
|
}
|
||||||
|
if(hashParams.access_token) {
|
||||||
|
if(hashParams.state === Auth.getState()) {
|
||||||
|
Auth.setToken(hashParams.access_token, hashParams.expires_in);
|
||||||
|
} else {
|
||||||
|
console.error("Invalid state! Something fishy here. Ignoring token...");
|
||||||
|
console.error("Our state: ", hashParams.state, " Received state: ", hashParams.state);
|
||||||
|
}
|
||||||
|
clearHash();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValid;
|
||||||
|
};
|
||||||
|
|
||||||
|
//Used to verify a settings object for init. Also checked before each call
|
||||||
|
//that uses the settings.
|
||||||
|
Auth.verifySettings = function(settings) {
|
||||||
|
return (
|
||||||
|
settings &&
|
||||||
|
settings.clientID &&
|
||||||
|
settings.redirectURI &&
|
||||||
|
settings.authEndpoint &&
|
||||||
|
Object.prototype.toString.call(settings.requiredPerms) === '[object Array]'
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Test if the current $Auth.settings are valid.
|
||||||
|
Auth.isInit = function(){
|
||||||
|
return Auth.verifySettings(Auth.settings);
|
||||||
|
};
|
||||||
|
|
||||||
|
Auth.isAuthed = function() {
|
||||||
|
return Auth.getToken();
|
||||||
|
};
|
||||||
|
|
||||||
|
Auth.wasError = function() {
|
||||||
|
return error;
|
||||||
|
};
|
||||||
|
|
||||||
|
//Redirect to the auth endpoint and request an implicit authorization
|
||||||
|
// according to the current $Auth.settings.
|
||||||
|
Auth.gotoAuth = function() {
|
||||||
|
if(!Auth.isInit()) {
|
||||||
|
console.error(
|
||||||
|
"Error! Please configure with $Auth.init() before using $Auth"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
redirectToAuthEndpoint();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
//User needs to know when things have expires so they can react to it.
|
||||||
|
|
||||||
|
|
||||||
|
Auth.addExpireListener = function(listener) {
|
||||||
|
expireListeners.push (listener);
|
||||||
|
};
|
||||||
|
|
||||||
|
Auth.removeExpireListener = function(listener) {
|
||||||
|
expireListeners.forEach(function(element, i) {
|
||||||
|
if(element === listener) {
|
||||||
|
expireListeners[i] = undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Auth.addExpireListener = function(listener) {
|
||||||
|
expireListeners.push (listener);
|
||||||
|
};
|
||||||
|
|
||||||
|
Auth.removeExpireListener = function(listener) {
|
||||||
|
expireListeners.forEach(function(element, i) {
|
||||||
|
if(element === listener) {
|
||||||
|
expireListeners[i] = undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
Auth.expire = function() {
|
||||||
|
Auth.removeToken();
|
||||||
|
expireListeners.forEach(function(listener) {
|
||||||
|
if(typeof listener === "function") {
|
||||||
|
listener();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Auth.parseParams = function(params) {
|
||||||
|
var result = params.slice(1).split("&").reduce(function(obj, param) {
|
||||||
|
var parsed = param.split("=");
|
||||||
|
obj[parsed[0]] = decodeURIComponent(parsed[1]);
|
||||||
|
return obj;
|
||||||
|
}, {});
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
Auth.encodeParams = function(paramObj) {
|
||||||
|
return Object.keys(paramObj).map(function(key) {
|
||||||
|
return key + "=" + encodeURIComponent(paramObj[key]);
|
||||||
|
}).join("&");
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
Auth.getToken = function() {
|
||||||
|
try {
|
||||||
|
return JSON.parse(window.localStorage.getItem("$Auth_token"));
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error parsing token string from storage.")
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Auth.setToken = function(value, expires) {
|
||||||
|
var token = {
|
||||||
|
value: value,
|
||||||
|
expiresTime: Date.now() + expires * 1000
|
||||||
|
};
|
||||||
|
window.setTimeout(Auth.expire, expires * 1000 - 60000);
|
||||||
|
|
||||||
|
window.localStorage.setItem("$Auth_token", JSON.stringify(token));
|
||||||
|
};
|
||||||
|
|
||||||
|
Auth.removeToken = function() {
|
||||||
|
window.localStorage.removeItem("$Auth_token");
|
||||||
|
};
|
||||||
|
|
||||||
|
Auth.getState = function() {
|
||||||
|
if(!window.localStorage.getItem("$Auth_state")) {
|
||||||
|
window.localStorage.setItem("$Auth_state", get15RandomSafeChars());
|
||||||
|
}
|
||||||
|
return window.localStorage.getItem("$Auth_state");
|
||||||
|
};
|
||||||
|
|
||||||
|
function redirectToAuthEndpoint(perms) {
|
||||||
|
var payload = {
|
||||||
|
client_id: Auth.settings.clientID,
|
||||||
|
redirect_uri: Auth.settings.redirectURI,
|
||||||
|
state: Auth.getState(),
|
||||||
|
response_type: "token",
|
||||||
|
scope: Auth.settings.requiredPerms.join(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
window.location = Auth.settings.authEndpoint + "?" + Auth.encodeParams(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
function get15RandomSafeChars() {
|
||||||
|
var characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
|
||||||
|
var string = "";
|
||||||
|
var numbers = [0,1,2].map(function() {
|
||||||
|
return Math.floor(Math.random() * Math.pow(2,32));
|
||||||
|
});
|
||||||
|
numbers.forEach(function(num) {
|
||||||
|
var bits = num;
|
||||||
|
for(var i = 0; i < 5; ++i) {
|
||||||
|
string += characters[bits & 0x3f];
|
||||||
|
bits = bits >> 6;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
function clearHash() {
|
||||||
|
if(window.history && typeof window.history.replaceState !== "undefined") {
|
||||||
|
history.replaceState({}, "", window.location.pathname + window.location.search);
|
||||||
|
} else {
|
||||||
|
window.location.hash = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
|
@ -3,11 +3,17 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>Redundant Feed!</title>
|
<title>Redundant Feed!</title>
|
||||||
|
<script type="text/javascript" src="authenticator.js"></script>
|
||||||
<script type="text/javascript" src="loadfeed.js"></script>
|
<script type="text/javascript" src="loadfeed.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="feed">
|
<div id="app">
|
||||||
|
<div id="header">
|
||||||
...loading...
|
...loading...
|
||||||
</div>
|
</div>
|
||||||
|
<div id="feed">
|
||||||
|
Your feed is empty. You must be really boring...
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,87 +1,58 @@
|
||||||
(function(){
|
(function(){
|
||||||
'use strict';
|
'use strict';
|
||||||
var CLIENT_ID = "1944365805820399";
|
var AUTH_SETTINGS = {
|
||||||
var REDIRECT_URI = "http://localhost:3000/";
|
clientID: "1944365805820399",
|
||||||
var AUTH_ENDPOINT = "https://www.facebook.com/v2.10/dialog/oauth";
|
redirectURI: "http://localhost:3000/",
|
||||||
var PERMS = "user_posts";
|
authEndpoint: "https://www.facebook.com/v2.10/dialog/oauth",
|
||||||
var token = window.localStorage.getItem("token");
|
requiredPerms: ["user_posts"]
|
||||||
var state = window.localStorage.getItem("state");
|
|
||||||
if (!state) {
|
|
||||||
state = get15RandomSafeChars();
|
|
||||||
window.localStorage.setItem("state", state);
|
|
||||||
}
|
}
|
||||||
console.log("state: ", state);
|
|
||||||
|
|
||||||
if(window.location.hash !== "") {
|
var store = {
|
||||||
var hashParams = window.location.hash.slice(1).split("&");
|
status: "unauthenticated",
|
||||||
hashParams = hashParams.reduce(function(obj, param) {
|
userName: "",
|
||||||
var parsed = param.split("=");
|
feed: []
|
||||||
obj[parsed[0]] = decodeURIComponent(parsed[1]);
|
}
|
||||||
return obj;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
if(hashParams.access_token) {
|
$Auth.init(AUTH_SETTINGS);
|
||||||
if(hashParams.state === state) {
|
|
||||||
token = hashParams.access_token;
|
if(document.readyState === "loading") {
|
||||||
window.localStorage.setItem("token", token);
|
document.addEventListener("DOMContentLoaded", onload);
|
||||||
} else {
|
} else {
|
||||||
console.log("Invalid state! Something fishy here. Ignoring token...");
|
onload();
|
||||||
console.log("Our state: ", state, " Received state: ", hashParams.state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(!token) {
|
|
||||||
console.log("NOT AUTHED!");
|
|
||||||
} else {
|
|
||||||
console.log("Probably authed!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
function onload(){
|
||||||
var feed = document.getElementById("feed");
|
var feed = document.getElementById("feed");
|
||||||
if(!token) {
|
var header = document.getElementById("header");
|
||||||
feed.textContent = "First you need to authorize Facebook to allow me to spy you.";
|
if(!$Auth.isAuthed()) {
|
||||||
|
if($Auth.wasError()){
|
||||||
|
header.textContent = "It won't work if I can't spy on you. Are you \
|
||||||
|
sure you don't want to authorize it?";
|
||||||
|
header.appendChild(createAuthButton("TRY AGAIN"));
|
||||||
|
} else {
|
||||||
|
header.textContent = "First you need to authorize Facebook to allow me \
|
||||||
|
to spy on you.";
|
||||||
|
header.appendChild(createAuthButton("AUTHORIZE"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$Auth.addExpireListener(onload);
|
||||||
|
header.textContent = "Hello person.";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderFeed() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function createAuthButton(buttonText) {
|
||||||
var button = document.createElement("Button");
|
var button = document.createElement("Button");
|
||||||
button.onclick = function(){redirectToAuthEndpoint(PERMS);};
|
button.onclick = function(){$Auth.gotoAuth();};
|
||||||
button.value = "authorize";
|
button.value = "authorize";
|
||||||
button.type = "button";
|
button.type = "button";
|
||||||
button.textContent = "AUTHORIZE";
|
button.textContent = buttonText;
|
||||||
feed.appendChild(button);
|
return button;
|
||||||
} else {
|
|
||||||
feed.textContent = "Hello person.";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
function redirectToAuthEndpoint(perms) {
|
|
||||||
var payload = {
|
|
||||||
client_id: CLIENT_ID,
|
|
||||||
redirect_uri: REDIRECT_URI,
|
|
||||||
state: state,
|
|
||||||
response_type: "token",
|
|
||||||
scope: perms
|
|
||||||
};
|
|
||||||
|
|
||||||
var params = Object.keys(payload).map(function(key) {
|
|
||||||
return key + "=" + encodeURIComponent(payload[key]);
|
|
||||||
}).join("&");
|
|
||||||
|
|
||||||
window.location = AUTH_ENDPOINT + "?" + params;
|
|
||||||
}
|
|
||||||
|
|
||||||
function get15RandomSafeChars() {
|
|
||||||
var characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
|
|
||||||
var string = "";
|
|
||||||
var numbers = [0,1,2].map(function() {
|
|
||||||
return Math.floor(Math.random() * Math.pow(2,32));
|
|
||||||
});
|
|
||||||
numbers.forEach(function(num) {
|
|
||||||
var bits = num;
|
|
||||||
for(var i = 0; i < 5; ++i) {
|
|
||||||
string += characters[bits & 0x3f];
|
|
||||||
bits = bits >> 6;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
Loading…
Reference in a new issue