Origin for api-tokens

This commit is contained in:
Kasper Rynning-Tønnesen
2018-03-16 10:28:10 +01:00
parent 6ea96c769a
commit dbba54c7ad
7 changed files with 68 additions and 37 deletions

View File

@@ -38,7 +38,9 @@ var bodyParser = require('body-parser');
var cookieParser = require("cookie-parser"); var cookieParser = require("cookie-parser");
var cookies = require("cookie"); var cookies = require("cookie");
var helmet = require('helmet') var helmet = require('helmet')
app.use(helmet()) app.use(helmet({
frameguard: false
}));
app.use( bodyParser.json() ); // to support JSON-encoded bodies app.use( bodyParser.json() ); // to support JSON-encoded bodies
app.use(bodyParser.urlencoded({ // to support URL-encoded bodies app.use(bodyParser.urlencoded({ // to support URL-encoded bodies
extended: true extended: true

View File

@@ -168,6 +168,7 @@ function get_list_ajax() {
type: "POST", type: "POST",
data: { data: {
userpass: "", userpass: "",
token: zoff_api_token,
}, },
url: "/api/list/" + chan.toLowerCase(), url: "/api/list/" + chan.toLowerCase(),
success: function(response) { success: function(response) {
@@ -242,7 +243,8 @@ function get_np_ajax() {
type: "POST", type: "POST",
data: { data: {
userpass: "", userpass: "",
fetch_song: true fetch_song: true,
token: zoff_api_token
}, },
url: "/api/list/" + chan.toLowerCase() + "/__np__", url: "/api/list/" + chan.toLowerCase() + "/__np__",
success: function(response) { success: function(response) {
@@ -269,7 +271,8 @@ function del_ajax(id) {
type: "DELETE", type: "DELETE",
data: { data: {
adminpass: "", adminpass: "",
userpass: "" userpass: "",
token: zoff_api_token
}, },
url: "/api/list/" + chan.toLowerCase() + "/" + id, url: "/api/list/" + chan.toLowerCase() + "/" + id,
success: function(response) { success: function(response) {
@@ -301,6 +304,7 @@ function add_ajax(id, title, duration, playlist, num, full_num, start, end) {
duration: duration, duration: duration,
end_time: end, end_time: end,
start_time: start, start_time: start,
token: zoff_api_token
}, },
url: "/api/list/" + chan.toLowerCase() + "/" + id, url: "/api/list/" + chan.toLowerCase() + "/" + id,
success: function(response) { success: function(response) {
@@ -327,7 +331,8 @@ function vote_ajax(id) {
type: "PUT", type: "PUT",
data: { data: {
adminpass: "", adminpass: "",
userpass: "" userpass: "",
token: zoff_api_token
}, },
url: "/api/list/" + chan.toLowerCase() + "/" + id, url: "/api/list/" + chan.toLowerCase() + "/" + id,
success: function(response) { success: function(response) {

View File

@@ -29,6 +29,7 @@ var showDiscovery = false;
var player_ready = false; var player_ready = false;
var viewers = 1; var viewers = 1;
var temp_user_pass = ""; var temp_user_pass = "";
var zoff_api_token = "AhmC4Yg2BhaWPZBXeoWK96DAiAVfbou8TUG2IXtD3ZQ=";
var retry_frontpage; var retry_frontpage;
var chromecast_specs_sent = false; var chromecast_specs_sent = false;
var dragging = false; var dragging = false;

View File

@@ -16,6 +16,8 @@ $(document).ready(function() {
$(".token-form").on("submit", function(e) { $(".token-form").on("submit", function(e) {
e.preventDefault(); e.preventDefault();
var email = $("#email_address").val(); var email = $("#email_address").val();
var origin = $("#origin").val();
$("#origin").attr("readonly", true);
$("#email_address").attr("readonly", true); $("#email_address").attr("readonly", true);
$(".submit").toggleClass("disabled"); $(".submit").toggleClass("disabled");
$(".full-form-token").removeClass("hide"); $(".full-form-token").removeClass("hide");
@@ -24,6 +26,7 @@ $(document).ready(function() {
type: "POST", type: "POST",
url: "/api/apply", url: "/api/apply",
data: { data: {
origin: origin,
email: email, email: email,
"g-recaptcha-response": captcha_response, "g-recaptcha-response": captcha_response,
}, },
@@ -34,6 +37,7 @@ $(document).ready(function() {
} else { } else {
$("#email_address").attr("readonly", false); $("#email_address").attr("readonly", false);
$(".submit").toggleClass("disabled"); $(".submit").toggleClass("disabled");
$("#origin").attr("readonly", false);
grecaptcha.reset(); grecaptcha.reset();
Materialize.toast("Something went wrong. Sure that email hasn't been used for another token?", 3000, "red lighten"); Materialize.toast("Something went wrong. Sure that email hasn't been used for another token?", 3000, "red lighten");
} }

View File

@@ -11,8 +11,6 @@
{{/if}} {{/if}}
{{> channel/tabs}} {{> channel/tabs}}
</div> </div>
<!--<div id="playbar">
</div>-->
</main> </main>
</div> </div>
<ul class="context-menu-list context-menu-root hide"> <ul class="context-menu-list context-menu-root hide">

View File

@@ -12,20 +12,6 @@
</div> </div>
</nav> </nav>
{{> modal/about}} {{> modal/about}}
<div id="donation" class="modal">
<div class="modal-content">
<h4>Thanks!</h4>
<p>Thanks for your donation, we love you &lt;3
<br><br>
We will use the money for something awesome, just you wait and see!
<br><br>
We might also add your name somewhere in the code as a sign of gratitude, see if you can find it! (Might take a day or two for us to see the donation and implement it..)
</p>
</div>
<div class="modal-footer">
<a href="#" class="modal-action modal-close waves-effect waves-green btn-flat">I'm awesome! (Close)</a>
</div>
</div>
</header> </header>
<div id="main-container" class="token-container"> <div id="main-container" class="token-container">
<main class="center-align container"> <main class="center-align container">
@@ -41,6 +27,7 @@
<p>Apply for a API-token with your email here! You'll get an email on the specified address, with a link. Follow that link, and the token will be shown to you! Take good care of it, and don't lose it. It won't be shown to you again.</p> <p>Apply for a API-token with your email here! You'll get an email on the specified address, with a link. Follow that link, and the token will be shown to you! Take good care of it, and don't lose it. It won't be shown to you again.</p>
<p>If you're wondering anything about how the api works, there is a guide on our GitHub. You can also click <a href="https://zoff.me/api/help">HERE</a> to be taken to the detailed README.</p> <p>If you're wondering anything about how the api works, there is a guide on our GitHub. You can also click <a href="https://zoff.me/api/help">HERE</a> to be taken to the detailed README.</p>
<p>As of now, the tokens have a limit of 100 requests a second. If you need a higher limit, just contact the team and we'll set you up for as much as you need.</p> <p>As of now, the tokens have a limit of 100 requests a second. If you need a higher limit, just contact the team and we'll set you up for as much as you need.</p>
<p>If you want to restrict the token for use on one domain only, you can change the Origin from * to the website of your choice (if you want for https://zoff.me, then you'd input <b>zoff.me</b>).</p>
{{/if}} {{/if}}
</div> </div>
{{#if activated}} {{#if activated}}
@@ -50,7 +37,11 @@
<div class="row center"> <div class="row center">
<div class="input-field col s6 offset-s3"> <div class="input-field col s6 offset-s3">
<label for="email_address" class="noselect">Email</label> <label for="email_address" class="noselect">Email</label>
<input type="email" autocomplete="off" class="validate" id="email_address" /> <input type="email" autocomplete="off" class="validate" id="email_address" />
</div>
<div class="input-field col s6 offset-s3">
<label for="origin" class="noselect">Origin</label>
<input type="text" autocomplete="off" id="origin" value="*" />
</div> </div>
</div> </div>
<div class="col offset-m3"> <div class="col offset-m3">

View File

@@ -75,7 +75,7 @@ var error = {
results: [], results: [],
}, },
wrong_token: { wrong_token: {
status: 403, status: 400,
error: "You're using a faulty token. Try getting a new token, or send the request without the token.", error: "You're using a faulty token. Try getting a new token, or send the request without the token.",
success: false, success: false,
results: [], results: [],
@@ -185,14 +185,18 @@ router.route('/api/list/:channel_name/:video_id').delete(function(req, res) {
} }
token_db.collection("api_token").find({token: token}, function(err, token_docs) { token_db.collection("api_token").find({token: token}, function(err, token_docs) {
var authorized = false; var authorized = false;
if(token_docs.length == 1 && token_docs[0].token == token) { var origin;
try {
origin = req.headers.referer.split("/")[2];
} catch(e) { origin = ""; }
if(token_docs.length == 1 && token_docs[0].token == token && (token_docs[0].origin == "*" || token_docs[0].origin == origin)) {
authorized = true; authorized = true;
} }
checkOveruseApiToken(authorized, token_docs, res, function() { checkOveruseApiToken(authorized, token_docs, res, function() {
checkTimeout(guid, res, authorized, "DELETE", function() { checkTimeout(guid, res, authorized, "DELETE", function() {
if(token != "" && !authorized) { if(token != "" && !authorized) {
updateTimeout(guid, res, authorized, "DELETE", function(err, docs) { updateTimeout(guid, res, authorized, "DELETE", function(err, docs) {
res.status(403).send(JSON.stringify(error.wrong_token)); res.status(400).send(JSON.stringify(error.wrong_token));
return; return;
}); });
} else { } else {
@@ -336,14 +340,18 @@ router.route('/api/conf/:channel_name').put(function(req, res) {
} }
token_db.collection("api_token").find({token: token}, function(err, token_docs) { token_db.collection("api_token").find({token: token}, function(err, token_docs) {
var authorized = false; var authorized = false;
if(token_docs.length == 1 && token_docs[0].token == token) { var origin;
try {
origin = req.headers.referer.split("/")[2];
} catch(e) { origin = ""; }
if(token_docs.length == 1 && token_docs[0].token == token && (token_docs[0].origin == "*" || token_docs[0].origin == origin)) {
authorized = true; authorized = true;
} }
checkOveruseApiToken(authorized, token_docs, res, function() { checkOveruseApiToken(authorized, token_docs, res, function() {
checkTimeout(guid, res, authorized, "CONFIG", function() { checkTimeout(guid, res, authorized, "CONFIG", function() {
if(token != "" && !authorized) { if(token != "" && !authorized) {
updateTimeout(guid, res, authorized, "CONFIG", function(err, docs) { updateTimeout(guid, res, authorized, "CONFIG", function(err, docs) {
res.status(403).send(JSON.stringify(error.wrong_token)); res.status(400).send(JSON.stringify(error.wrong_token));
return; return;
}); });
} else { } else {
@@ -459,14 +467,18 @@ router.route('/api/list/:channel_name/:video_id').put(function(req,res) {
} }
token_db.collection("api_token").find({token: token}, function(err, token_docs) { token_db.collection("api_token").find({token: token}, function(err, token_docs) {
var authorized = false; var authorized = false;
if(token_docs.length == 1 && token_docs[0].token == token) { var origin;
try {
origin = req.headers.referer.split("/")[2];
} catch(e) { origin = ""; }
if(token_docs.length == 1 && token_docs[0].token == token && (token_docs[0].origin == "*" || token_docs[0].origin == origin)) {
authorized = true; authorized = true;
} }
checkOveruseApiToken(authorized, token_docs, res, function() { checkOveruseApiToken(authorized, token_docs, res, function() {
checkTimeout(guid, res, authorized, "PUT", function() { checkTimeout(guid, res, authorized, "PUT", function() {
if(token != "" && !authorized) { if(token != "" && !authorized) {
updateTimeout(guid, res, authorized, "PUT", function(err, docs) { updateTimeout(guid, res, authorized, "PUT", function(err, docs) {
res.status(403).send(JSON.stringify(error.wrong_token)); res.status(400).send(JSON.stringify(error.wrong_token));
return; return;
}); });
} else { } else {
@@ -549,14 +561,18 @@ router.route('/api/list/:channel_name/__np__').post(function(req, res) {
} }
token_db.collection("api_token").find({token: token}, function(err, token_docs) { token_db.collection("api_token").find({token: token}, function(err, token_docs) {
var authorized = false; var authorized = false;
if(token_docs.length == 1 && token_docs[0].token == token) { var origin;
try {
origin = req.headers.referer.split("/")[2];
} catch(e) { origin = ""; }
if(token_docs.length == 1 && token_docs[0].token == token && (token_docs[0].origin == "*" || token_docs[0].origin == origin)) {
authorized = true; authorized = true;
} }
checkOveruseApiToken(authorized, token_docs, res, function() { checkOveruseApiToken(authorized, token_docs, res, function() {
checkTimeout(guid, res, authorized, "POST", function() { checkTimeout(guid, res, authorized, "POST", function() {
if(token != "" && !authorized) { if(token != "" && !authorized) {
updateTimeout(guid, res, authorized, "POST", function(err, docs) { updateTimeout(guid, res, authorized, "POST", function(err, docs) {
res.status(403).send(JSON.stringify(error.wrong_token)); res.status(400).send(JSON.stringify(error.wrong_token));
return; return;
}); });
} else { } else {
@@ -672,14 +688,18 @@ router.route('/api/list/:channel_name/:video_id').post(function(req,res) {
} }
token_db.collection("api_token").find({token: token}, function(err, token_docs) { token_db.collection("api_token").find({token: token}, function(err, token_docs) {
var authorized = false; var authorized = false;
if(token_docs.length == 1 && token_docs[0].token == token) { var origin;
try {
origin = req.headers.referer.split("/")[2];
} catch(e) { origin = ""; }
if(token_docs.length == 1 && token_docs[0].token == token && (token_docs[0].origin == "*" || token_docs[0].origin == origin)) {
authorized = true; authorized = true;
} }
checkOveruseApiToken(authorized, token_docs, res, function() { checkOveruseApiToken(authorized, token_docs, res, function() {
checkTimeout(guid, res, authorized, "POST", function() { checkTimeout(guid, res, authorized, "POST", function() {
if(token != "" && !authorized) { if(token != "" && !authorized) {
updateTimeout(guid, res, authorized, "POST", function(err, docs) { updateTimeout(guid, res, authorized, "POST", function(err, docs) {
res.status(403).send(JSON.stringify(error.wrong_token)); res.status(400).send(JSON.stringify(error.wrong_token));
return; return;
}); });
} else { } else {
@@ -897,14 +917,18 @@ router.route('/api/conf/:channel_name').post(function(req, res) {
token_db.collection("api_token").find({token: token}, function(err, token_docs) { token_db.collection("api_token").find({token: token}, function(err, token_docs) {
var authorized = false; var authorized = false;
if(token_docs.length == 1 && token_docs[0].token == token) { var origin;
try {
origin = req.headers.referer.split("/")[2];
} catch(e) { origin = ""; }
if(token_docs.length == 1 && token_docs[0].token == token && (token_docs[0].origin == "*" || token_docs[0].origin == origin)) {
authorized = true; authorized = true;
} }
checkOveruseApiToken(authorized, token_docs, res, function() { checkOveruseApiToken(authorized, token_docs, res, function() {
checkTimeout(guid, res, authorized, "POST", function() { checkTimeout(guid, res, authorized, "POST", function() {
if(token != "" && !authorized) { if(token != "" && !authorized) {
updateTimeout(guid, res, authorized, "DELETE", function(err, docs) { updateTimeout(guid, res, authorized, "DELETE", function(err, docs) {
res.status(403).send(JSON.stringify(error.wrong_token)); res.status(400).send(JSON.stringify(error.wrong_token));
return; return;
}); });
} else { } else {
@@ -1016,14 +1040,18 @@ router.route('/api/list/:channel_name').post(function(req, res) {
token_db.collection("api_token").find({token: token}, function(err, token_docs) { token_db.collection("api_token").find({token: token}, function(err, token_docs) {
var authorized = false; var authorized = false;
if(token_docs.length == 1 && token_docs[0].token == token) { var origin;
try {
origin = req.headers.referer.split("/")[2];
} catch(e) { origin = ""; }
if(token_docs.length == 1 && token_docs[0].token == token && (token_docs[0].origin == "*" || token_docs[0].origin == origin)) {
authorized = true; authorized = true;
} }
checkOveruseApiToken(authorized, token_docs, res, function() { checkOveruseApiToken(authorized, token_docs, res, function() {
checkTimeout(guid, res, authorized, "POST", function() { checkTimeout(guid, res, authorized, "POST", function() {
if(token != "" && !authorized) { if(token != "" && !authorized) {
updateTimeout(guid, res, authorized, "POST", function(err, docs) { updateTimeout(guid, res, authorized, "POST", function(err, docs) {
res.status(403).send(JSON.stringify(error.wrong_token)); res.status(400).send(JSON.stringify(error.wrong_token));
return; return;
}); });
} else { } else {
@@ -1092,6 +1120,8 @@ try {
return; return;
} }
if(req.recaptcha.error == null) { if(req.recaptcha.error == null) {
var origin = "*";
if(req.body.origin != undefined && req.body.origin != "") origin = req.body.origin;
var name = req.body.email; var name = req.body.email;
var id = crypto.createHash('sha256').update(uniqid()).digest('base64'); var id = crypto.createHash('sha256').update(uniqid()).digest('base64');
var uniqid_link = crypto.createHash('sha256').update(uniqid()).digest('hex'); var uniqid_link = crypto.createHash('sha256').update(uniqid()).digest('hex');
@@ -1102,7 +1132,7 @@ try {
} }
token_db.collection("api_links").find({token: token}, function(e, d) { token_db.collection("api_links").find({token: token}, function(e, d) {
if(results_find.length == 0 || (d.length == 0 && results_find.length > 0 && !results_find[0].active)) { if(results_find.length == 0 || (d.length == 0 && results_find.length > 0 && !results_find[0].active)) {
token_db.collection("api_token").insert({name: name, token: id, usage: 0, active: false, limit: 100}, function(err, docs){ token_db.collection("api_token").insert({name: name, origin: origin, token: id, usage: 0, active: false, limit: 100}, function(err, docs){
token_db.collection("api_links").insert({id: uniqid_link, token: id, createdAt: new Date()}, function(err, docs) { token_db.collection("api_links").insert({id: uniqid_link, token: id, createdAt: new Date()}, function(err, docs) {
let transporter = nodemailer.createTransport(mailconfig); let transporter = nodemailer.createTransport(mailconfig);