diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5509140 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.DS_Store diff --git a/.htaccess b/.htaccess index 63fe657..b0cdf22 100644 --- a/.htaccess +++ b/.htaccess @@ -1,3 +1,18 @@ +Order Deny,Allow +Deny from 204.12.217.130 95.216.5.232 + RewriteEngine On RewriteCond %{SERVER_PORT} 80 -RewriteRule ^(.*)$ https://twitchtokengenerator.com/$1 [R,L] \ No newline at end of file +RewriteRule ^(.*)$ https://twitchtokengenerator.com/$1 [R,L] + +order allow,deny +allow from all + + +deny from 81.92.203.76 +deny from 81.92.203.0/24 +deny from 84.66.181.27 +deny from 94.109.185.22 +deny from 90.108.238.38 +deny from 73.245.107.0 +deny from 180.150.32.18 diff --git a/README.md b/README.md index 2a17426..38dbb86 100644 --- a/README.md +++ b/README.md @@ -31,5 +31,9 @@ An API exists on TwitchTokenGenerator allowing the creation of tokens and implem 4. Occasionally you will find that your Twitch access token has expired. This is new as of Twitch's oAuth2 implementation. To refresh, use the "refresh" token that you received in step 3 and hit the /api/refresh/ endpoint to get a new token. - Example refresh: `https://twitchtokengenerator.com/api/refresh/{refresh_token}` +### Logging and Website Attacks +Over the last year or two, the website has seen significant increase in traffic, and with that has come additional bot attacks and malicious users. I've added significantly more logging, as well as attempts at detecting such behavior. I've also added mitigations including scrambling the website code, recaptcha, and a few other methods to try to prevent malicious users from generating access and refresh tokens to perform large scale chat spam attacks on Twitch, and mass followings of streamers. +- That is to say, if you use this website to mass generate tokens for hundreds and thousands of accounts, and these accounts are used to attack Twitch streamers, I will (and have) shared in bulk, accounts that I feel are suspicious with Twitch's safety team. + ### License -MIT License. © 2018 Cole \ No newline at end of file +MIT License. © 2021 Cole \ No newline at end of file diff --git a/api/create.php b/api/create.php index 847945a..f3c0b18 100644 --- a/api/create.php +++ b/api/create.php @@ -8,12 +8,19 @@ exit(json_encode(array("success" => false, "error" => 14, "message" => "invalid title"))); $unique = randStrGen(20); -$dao->insertAPI($unique, $title, $scopes, $_SERVER['REMOTE_ADDR']); +$recaptchaNow = rand(0,1); +if(count($redirectUrl) > 0) { + $dao->insertAPI($unique, $title, $scopes, $_SERVER['REMOTE_ADDR'], $redirectUrl, $recaptchaNow); +} else { + $dao->insertAPI($unique, $title, $scopes, $_SERVER['REMOTE_ADDR'], "", $recaptchaNow); +} exit(json_encode(array("success" => true, 'id' => $unique, "message" => "https://twitchtokengenerator.com/api/".$unique))); function validateScopes($scopes, $validScopes) { - + $scopes = explode(' ', $scopes); + $validScopeNames = getValidScopes($validScopes); + $scopes = getScopesFromIds($scopes, $validScopes); $checkScopes = array(); if (strpos($scopes, ' ') !== false) $checkScopes = explode(" ", $scopes); @@ -21,7 +28,7 @@ function validateScopes($scopes, $validScopes) { array_push($checkScopes, $scopes); foreach($checkScopes as $scope) { - if(!in_array($scope, $validScopes)) + if(!in_array($scope, $validScopeNames)) return false; } @@ -38,4 +45,22 @@ function randStrGen($len){ } return $result; } + +function getScopesFromIds($scopes, $rawScopes) { + $scopeNames = array(); + foreach($rawScopes as $rawScope) { + if(in_array($rawScope['id'], $scopes)) { + array_push($scopeNames, $rawScope['scope']); + } + } + return $scopeNames; +} + +function getValidScopes($rawScopes) { + $validScopes = array(); + foreach($rawScopes as $rawScope) { + array_push($validScopes, $rawScope['scope']); + } + return $validScopes; +} ?> \ No newline at end of file diff --git a/api/found.php b/api/found.php new file mode 100644 index 0000000..decd3b3 --- /dev/null +++ b/api/found.php @@ -0,0 +1,35 @@ + + +Twitch Token Generator by swiftyspiffy - Redirecting... + + + + + + + +
+
+
+
+

Twitch Token Generator - Redirecting...

+
+
+
+ Redirecting you to authorize on Twitch for .
If you are not redirected in 5 seconds, click here. +

+ Thanks for using TwitchTokenGenerator.com! +
+
+
+
+
+ + \ No newline at end of file diff --git a/api/index.php b/api/index.php index bdfd203..b511f26 100644 --- a/api/index.php +++ b/api/index.php @@ -20,6 +20,14 @@ } switch($action) { + case "verify": + if(count($parts) != 2) { + header('Content-Type: application/json'); + exit(json_encode(array('success' => false, 'error' => 64, 'message' => 'Unexpected argument count'))); + } + $identifier = $parts[1]; + include("recaptcha.php"); + break; case "waiting_texts": $results = $dao->getWaitingTexts(); header('Content-Type: application/json'); @@ -54,19 +62,34 @@ } $result = $dao->revokeToken($args[0]); if($result) { + $dao->insertRevokeRequest(1, $_SERVER['REMOTE_ADDR']); header('Content-Type: application/json'); exit(json_encode(array('success' => true))); } else { + $dao->insertRevokeRequest(0, $_SERVER['REMOTE_ADDR']); header('Content-Type: application/json'); exit(json_encode(array('success' => false, 'error' => 45, 'message' => 'Invalid oauth access token provided.'))); } case "create": - if(count($args) != 2) { - header('Content-Type: application/json'); - exit(json_encode(array('success' => false, 'error' => 14, 'message' => 'Only two arguments are to be provided: title (base64encoded), scopes (with + as delimeter)'))); - } + $redirectUrl = ""; + switch(count($args)) { + case 2: + break; + case 3: + $redirectUrl = base64_decode($args[2]); + if(!filter_var($redirectUrl, FILTER_VALIDATE_URL)) { + header('Content-Type: application/json'); + exit(json_encode(array('success' => false, 'error' => 114, 'message' => 'URL does not appear to be valid. It needs to be base64 encoded, and it needs to start with https:// or http://'))); + } + break; + default: + header('Content-Type: application/json'); + exit(json_encode(array('success' => false, 'error' => 14, 'message' => 'Only two or three arguments are to be provided: title (base64encoded), scopes (with + as delimeter), redirectUrl (base64encoded, OPTIONAL)'))); + break; + } $title = base64_decode($args[0]); $scopes = $args[1]; + include("create.php"); break; case "status": @@ -86,8 +109,10 @@ $scopes = str_replace(" ", "+", $resp['scopes']); $unique = $action; $state = base64_encode(json_encode(array('action' => 'api', 'id' => $unique))); - $url = "https://api.twitch.tv/kraken/oauth2/authorize?response_type=code&client_id=zkxgn9qm9y3kzrb1p0px68qa69t3ae&redirect_uri=https://twitchtokengenerator.com/api/success&scope=".$scopes."&state=".$state."&force_verify=true"; - exit(header("Location: ".$url)); + $url = "https://api.twitch.tv/kraken/oauth2/authorize?response_type=code&client_id=".API_CLIENT_ID."&redirect_uri=https://twitchtokengenerator.com/api/success&scope=".$scopes."&state=".$state."&force_verify=true"; + $ip = $_SERVER['REMOTE_ADDR']; + $title = $resp['title']; + include("found.php"); break; case 1: include("used.php"); diff --git a/api/recaptcha.php b/api/recaptcha.php new file mode 100644 index 0000000..158b508 --- /dev/null +++ b/api/recaptcha.php @@ -0,0 +1,144 @@ +getApiRecaptchaStatus($id); + if($data['error'] != "0") { + header('Content-Type: application/json'); + exit(json_encode(array('success' => false, 'message' => 'Unknown API workflow identifier'))); + } + + $captcha = $_POST['g-recaptcha-response']; + $ip = $_SERVER['REMOTE_ADDR']; + + if(!isValid($captcha, $ip)) + exit(json_encode(array('success' => false, 'message' => "reCaptcha was not valid!"))); + + switch($data['recaptcha_status']) { + case "0": + if(strlen($data['token']) == 0) { + // send to twitch to auth + $url = getTwitchAuthUrl($data['scopes'], $id); + exit(header("Location: ".$url)); + } else { + // send to success + $dao->updateApiRecaptchaStatus($id, "2"); + $title = $data['title']; + include("success_after_verify.php"); + exit(); + } + case "1": + // authing now + $dao->updateApiRecaptchaStatus($id, "2"); + $url = getTwitchAuthUrl($data['scopes'], $id); + exit(header("Location: ".$url)); + default: + header('Content-Type: application/json'); + exit(json_encode(array('success' => false, 'message' => 'Unknown API workflow identifier'))); + break; + } +} else { + $data = $dao->getApiRecaptchaStatus($identifier); + if(data['error'] != "0") { + header('Content-Type: application/json'); + exit(json_encode(array('success' => false, 'message' => 'Unknown API workflow identifier'))); + } + if($data['recaptcha_status'] == "0" && strlen($data['token']) == 0) { + // go to twitch + $url = getTwitchAuthUrl($data['scopes'], $identifier); + exit(header("Location: ".$url)); + } + exit("status: ".$data['recaptcha_status'].", token: '".$data['token']."'"); + if($data['recaptcha_status'] == "2") { + // go to success page + $title = $data['title']; + include("success_after_verify.php"); + exit(); + } + if($data['recaptcha_status'] == "1" && strlen($data['token']) > 0) { + header('Content-Type: application/json'); + exit(json_encode(array('success' => false, 'message' => 'Invalid API workflow state'))); + } +} + +?> + + + + + +Twitch Token Generator by swiftyspiffy - Recaptcha + + + + + + + +
+
+
+
+

Twitch Token Generator - Recaptcha

+
+
+
+ Thank you for using TwitchTokenGenerator! Please complete the reCaptcha below! +
+
+ +
+
+
+
+
+ + 'api', 'id' => $id)));; + + return $url."&state=".$state."&force_verify=true"; +} + +function isValid($captcha, $ip) { + try { + + $url = 'https://www.google.com/recaptcha/api/siteverify'; + $data = ['secret' => RECAPTCHA_SECRET, + 'response' => $captcha, + 'remoteip' => $ip]; + + $options = [ + 'http' => [ + 'header' => "Content-type: application/x-www-form-urlencoded\r\n", + 'method' => 'POST', + 'content' => http_build_query($data) + ] + ]; + + $context = stream_context_create($options); + $result = file_get_contents($url, false, $context); + return json_decode($result)->success; + } + catch (Exception $e) { + $dao->insertError("internal.php", "isValid", "failed to verify captcha with google"); + return null; + } +} + +?> \ No newline at end of file diff --git a/api/refresh.php b/api/refresh.php index af561d8..de2e19c 100644 --- a/api/refresh.php +++ b/api/refresh.php @@ -6,7 +6,7 @@ if($resp['success']) { $username = $dao->getUsername($resp['access']); $dao->insertRefreshRequest(1, $username, $_SERVER['REMOTE_ADDR']); - exit(json_encode(array('success' => true, 'token' => $resp['access'], 'refresh' => $resp['refresh']))); + exit(json_encode(array('success' => true, 'token' => $resp['access'], 'refresh' => $resp['refresh'], 'client_id' => API_CLIENT_ID))); } else { $dao->insertRefreshRequest(0, "", $_SERVER['REMOTE_ADDR']); exit(json_encode(array('success' => false, 'error' => 22, 'message' => 'Invalid refresh token received.'))); diff --git a/api/status.php b/api/status.php index c909a0c..94711af 100644 --- a/api/status.php +++ b/api/status.php @@ -1,6 +1,8 @@ false, 'error' => 1, 'message' => 'No id provided.'))); @@ -12,13 +14,17 @@ } else { switch($resp['status']) { case "0": + // user has not authorized using API workflow link exit(json_encode(array('success' => false, 'id' => $id, 'error' => 3, 'message' => 'Not authorized yet'))); case "1": + // expire API after first access to prevent accidental token leaks, then return authorization data $dao->expireAPI($id); - exit(json_encode(array('success' => true, 'id' => $id, 'scopes' => $resp['scopes'], 'token' => $resp['token'], 'refresh' => $resp['refresh'], 'username' => $resp['username'], 'user_id' => $resp['user_id']))); + exit(json_encode(array('success' => true, 'id' => $id, 'scopes' => $resp['scopes'], 'token' => $resp['token'], 'refresh' => $resp['refresh'], 'username' => $resp['username'], 'user_id' => $resp['user_id'], 'client_id' => API_CLIENT_ID))); case "2": + // workflow has already been accessed, reject request exit(json_encode(array('success' => false, 'id' => $id, 'error' => 4, 'message' => 'API instance has already expired (already activated). Create a new one.'))); - case "3": + case "3": + // expire token after period of time to prevent accidental token leaks exit(json_encode(array('success' => false, 'id' => $id, 'error' => 6, 'message' => 'API instance has already expired (hit time limit: \''.$expireAfterHours.'\' hour(s)). Create a new one.'))); default: exit(json_encode(array('success' => false, 'id' => $id, 'error' => 5, 'message' => 'Unknown status: '.$resp['status']))); diff --git a/api/success.php b/api/success.php index b76bea1..abf57d2 100644 --- a/api/success.php +++ b/api/success.php @@ -24,13 +24,18 @@ $refresh_token = $data['refresh']; $username = $dao->getUsername($access_token); $userid = $dao->getUserId($username, $access_token); - - $dao->updateAPIListing($unique, $access_token, $refresh_token, $username, $userid); - - $dao->logUsage($_SERVER['REMOTE_ADDR'], $status['scopes'], $dao->getCountry($_SERVER['REMOTE_ADDR']), $dao->getUsername($access_token), $_SERVER['HTTP_USER_AGENT'], ""); + $country = $dao->getCountry($_SERVER['REMOTE_ADDR']); $udata = $dao->getUserdata($username, $access_token); + + $dao->updateAPIListing($unique, $access_token, $refresh_token, $username, $udata['userid']); + $partner = $udata['partner'] ? "1" : "0"; $dao->logMetadata($username, $udata['userid'], $udata['followers'], $udata['views'], $partner); + logPush($status['title'], $username, $country, $status['scopes'], $partner, $udata['followers']); + + if(strlen($status['redirect_url']) > 1) { + exit(header("Location: ".$status['redirect_url'])); + } }catch(Exception $ex) { exit($ex); } @@ -58,4 +63,4 @@ Thank you for using TwitchTokenGenerator! Your username and auth token has been made available to ! You may now close this window/tab. - \ No newline at end of file + diff --git a/api/success_after_verify.php b/api/success_after_verify.php new file mode 100644 index 0000000..abbac3f --- /dev/null +++ b/api/success_after_verify.php @@ -0,0 +1,19 @@ +Twitch Token Generator by swiftyspiffy - API Success + + + + + + + +
+
+
+
+

Twitch Token Generator - API Success

+
+
+ Thank you for using TwitchTokenGenerator! Your username and auth token has been made available to ! You may now close this window/tab. +
+
+
\ No newline at end of file diff --git a/assets/css/styles.css b/assets/css/styles.css new file mode 100644 index 0000000..c5e90c5 --- /dev/null +++ b/assets/css/styles.css @@ -0,0 +1,93 @@ +html, body { + height:100%; + margin:0; + padding:0; + width:100%; + background-color:#f0f0f0; + color:#383D43; + font-family:Tahoma,Verdana,Arial,Helvetica,sans-serif; + font-size:12px; +} +table { + margin:0; + padding:0; + height:100%; +} +.mainContainer { + width:780px; + margin:0 auto; +} +.mainContainer td { + vertical-align:middle; + width:780px; + text-align:left +} +#wrapper { + border:0px solid #f6f6f6; +} +#wrapper .curveT { + background:transparent url(../images/curve-top.gif) no-repeat left top; + height:7px; +} +#wrapper .curveB { + background:transparent url(../images/curve-bot.gif) no-repeat left top; + height:7px; +} + +#wrapper .header { + clear:both; + width:100%; + height:51px; + vertical-align:middle; + padding-top:12px; + background:#ffffff; +} +#wrapper .description { + clear:both; + margin:0px; + padding: 0px 10px 4px; + background:#ffffff; +} +#wrapper .welcome { + padding:10px; + background:#EEEEFF; + font-size:15px; +} +#wrapper .description .links { + margin-top:10px; + padding:10px; + background:#EEEEFF; +} +#wrapper .links ul { + margin:0; + padding:0; + list-style-type:none; + float:left; + width:48%; +} +#wrapper .links ul li { + margin:0; + padding: 0px 0px 0px 12px; + line-height:25px; +} +#wrapper .links ul li a { + font-weight:bold; + color:#003399; + text-decoration:none; + vertical-align:middle; +} +#wrapper .links ul li a img { + padding-right:7px; + margin-top:-7px; +} +div.clear { + clear:both; + height:1px; +} +.leftDiv { + float:left; +} +.rightDiv { + float:right; + padding:8px 25px 0px 0px; +} diff --git a/assets/images/bubble.gif b/assets/images/bubble.gif new file mode 100644 index 0000000..2428e54 Binary files /dev/null and b/assets/images/bubble.gif differ diff --git a/assets/images/cpanel.gif b/assets/images/cpanel.gif new file mode 100644 index 0000000..d7bc524 Binary files /dev/null and b/assets/images/cpanel.gif differ diff --git a/assets/images/curve-bot.gif b/assets/images/curve-bot.gif new file mode 100644 index 0000000..647678e Binary files /dev/null and b/assets/images/curve-bot.gif differ diff --git a/assets/images/curve-top.gif b/assets/images/curve-top.gif new file mode 100644 index 0000000..10c1512 Binary files /dev/null and b/assets/images/curve-top.gif differ diff --git a/assets/images/email.gif b/assets/images/email.gif new file mode 100644 index 0000000..6ea42d4 Binary files /dev/null and b/assets/images/email.gif differ diff --git a/assets/images/globe.gif b/assets/images/globe.gif new file mode 100644 index 0000000..0e5193a Binary files /dev/null and b/assets/images/globe.gif differ diff --git a/assets/images/messenger.gif b/assets/images/messenger.gif new file mode 100644 index 0000000..7c7bc86 Binary files /dev/null and b/assets/images/messenger.gif differ diff --git a/assets/images/news.gif b/assets/images/news.gif new file mode 100644 index 0000000..4f13d8b Binary files /dev/null and b/assets/images/news.gif differ diff --git a/assets/images/support.gif b/assets/images/support.gif new file mode 100644 index 0000000..4f6aba3 Binary files /dev/null and b/assets/images/support.gif differ diff --git a/assets/images/video.gif b/assets/images/video.gif new file mode 100644 index 0000000..d489ee6 Binary files /dev/null and b/assets/images/video.gif differ diff --git a/assets/index.php b/assets/index.php deleted file mode 100644 index e69de29..0000000 diff --git a/assets/script.js b/assets/script.js index 566e674..bb8f6d2 100644 --- a/assets/script.js +++ b/assets/script.js @@ -22,7 +22,7 @@ $( document ).ready(function() { } } } - + getWaitingTexts(); waitingRotator = setInterval(rotateWaitingText, 3000); @@ -61,6 +61,10 @@ $( document ).ready(function() { } }); }); + + $('#wrong-account').click(function() { + alert("Is this not your bot account? Don't worry! Generate tokens for a different account by:\n1. Go to twitch.tv\n2. Logout of the current account\n3. Login to the account you want to generate a token for\n4. Come back to twitchtokengenerator.com\n5. Generate token!\n\nProtip: Bot accounts are actually just Twitch accounts. Register a Twitch account for your bot, and login to it to generate a token for it."); + }); }); function setAccessText(val) { @@ -113,6 +117,14 @@ function getScopes() { }).responseText); } +function addButtonMetrics(buttonId) { + $.ajax({ + type: 'GET', + url: 'https://twitchtokengenerator.com/metrics.php?security_code=' + securityCode + '&action=button&id=' + buttonId, + async: true + }) +} + function launchRequestModal() { $('#requestModal').modal("show"); } @@ -125,9 +137,9 @@ var quick_link_scopes; function launchQuickLinkModal() { $('#quick_link_permissions').empty(); - quick_link_scopes = gatherScopeSelections(); + quick_link_scopes = gatherScopeSelectionsAlt(); quick_link_scopes.forEach(function(scope) { - $('#quick_link_permissions').append('
  • ' + scope + '
  • '); + $('#quick_link_permissions').append('
  • ' + scope[1] + '
  • '); }); $('#quickLinkModal').modal('show'); } @@ -136,9 +148,9 @@ function fetchQuickLinkUrl() { var scopeString = ""; quick_link_scopes.forEach(function(scope) { if(scopeString == "") - scopeString = scope; + scopeString = scope[0]; else - scopeString += "+" + scope; + scopeString += "+" + scope[0]; }); $.ajax({ @@ -148,7 +160,7 @@ function fetchQuickLinkUrl() { context: document.body }).done(function(data) { if(data.success) { - $('#quick_generate_link').replaceWith('') + $('#quick_generate_link').replaceWith('
    '); $("#quick_generate_link").val(data.message); } else { alert(data.message); @@ -217,6 +229,7 @@ function selectAllScopes() { $('#check_' + scope).prop('checked', true); } }); + addButtonMetrics("select-all"); } function clearScopeSelections() { @@ -227,16 +240,19 @@ function clearScopeSelections() { $('#check_' + scope).prop('checked', false); } }); + addButtonMetrics("clear-all"); } function authenticate(force_verify = false) { // get user selected scopes var selectedScopes = gatherScopeSelections(); // check at least one scope is set + /* if(selectedScopes.length == 0) { alert("You need to select at least one scope to generate a token."); return; } + */ // build string to auth with twitch var scopeString = ""; selectedScopes.forEach(function(scope) { @@ -246,7 +262,7 @@ function authenticate(force_verify = false) { scopeString += "+" + scope; }); // redirect to twitch auth - window.location = auth_base + "/authorize?response_type=code&client_id=" + client_id + "&redirect_uri=" + redirect_uri + "&scope=" + scopeString + "&force_verify=" + (force_verify ? "true" : "false"); + window.location = auth_base + "/authorize?response_type=code&client_id=" + client_id + "&redirect_uri=" + redirect_uri + "&scope=" + scopeString + "&state=frontend|" + securityCode + "&force_verify=" + (force_verify ? "true" : "false"); } function gatherScopeSelections() { @@ -255,13 +271,26 @@ function gatherScopeSelections() { if($('#check_' + scope).is(':checked')) { selectedScopes.push(scope); } - if(!scope.includes("_") && $('#check_helix_' + scope.replaceAll(":", "_")).is(':checked')) { + if($('#check_helix_' + scope.replaceAll(":", "_")).is(':checked')) { selectedScopes.push(scope); } }); return selectedScopes; } +function gatherScopeSelectionsAlt() { + var selectedScopes = []; + scopes.forEach(function(scope) { + if($('#check_' + scope).is(':checked')) { + selectedScopes.push([$('#check_' + scope).attr('alt'), scope]); + } + if($('#check_helix_' + scope.replaceAll(":", "_")).is(':checked')) { + selectedScopes.push([$('#check_helix_' + scope.replaceAll(":", "_")).attr('alt'), scope]); + } + }); + return selectedScopes; +} + // Source: http://stackoverflow.com/a/4656873 function getUrlVars() { @@ -342,6 +371,7 @@ function copyInput(btn, el) { delay(function() { $(btn).html("Copy"); }, 5000); + addButtonMetrics($(btn).attr('id')); } // Source: https://stackoverflow.com/a/28173606 diff --git a/assets/style.css b/assets/style.css index 2adffee..fef266a 100644 --- a/assets/style.css +++ b/assets/style.css @@ -22,4 +22,8 @@ .fade-scale.in { opacity: 1; transform: scale(1); +} + +.table > tbody > tr > td { + vertical-align: middle; } \ No newline at end of file diff --git a/css/ie_8.css b/css/ie_8.css new file mode 100644 index 0000000..046d306 --- /dev/null +++ b/css/ie_8.css @@ -0,0 +1,6 @@ +@charset "utf-8"; +/* CSS Document */ +#block_error{ + behavior: url("js/pie.htc"); +} + diff --git a/css/main.css b/css/main.css new file mode 100644 index 0000000..c2e37f9 --- /dev/null +++ b/css/main.css @@ -0,0 +1,60 @@ +@charset "utf-8"; +/* CSS Document */ +html{ +} +body{ + margin: 0; + padding: 0; + background: #e7ecf0; + font-family: Arial, Helvetica, sans-serif; +} +*{ + margin: 0; + padding: 0; +} +p{ + font-size: 12px; + color: #373737; + font-family: Arial, Helvetica, sans-serif; + line-height: 18px; +} +p a{ + color: #218bdc; + font-size: 12px; + text-decoration: none; +} +a{ + outline: none; +} +.f-left{ + float:left; +} +.f-right{ + float:right; +} +.clear{ + clear: both; + overflow: hidden; +} +#block_error{ + width: 845px; + height: 384px; + border: 1px solid #cccccc; + margin: 72px auto 0; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + background: #fff url(../images/block.gif) no-repeat 0 51px; +} +#block_error div{ + padding: 100px 40px 0 186px; +} +#block_error div h2{ + color: #218bdc; + font-size: 24px; + display: block; + padding: 0 0 14px 0; + border-bottom: 1px solid #cccccc; + margin-bottom: 12px; + font-weight: normal; +} diff --git a/dao.php b/dao.php index 40f0380..928a2b8 100644 --- a/dao.php +++ b/dao.php @@ -5,15 +5,30 @@ class dao { private $conn; function __construct() { - $this->conn = new PDO("mysql:host=".DB_HOST.";dbname=".DB_ANONDATA, DB_USER, DB_PASS); + $this->conn = new PDO("mysql:host=".DB_HOST.";dbname=".DB_NAME, DB_USER, DB_PASS); $this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } + + function insertButtonMetrics($btnId, $ip) { + $statement = $this->conn->prepare("INSERT INTO `".TABLE_BUTTON_METRICS."` (`btn_id`, `ip`, `timestamp`) VALUES (?, ?, ?);"); + $statement->execute(array($btnId, $ip, time())); + } + + function insertMissingSecurityCode($ip, $useragent) { + $statement = $this->conn->prepare("INSERT INTO `".TABLE_MISSING_SECURITY_CODE."` (`ip`, `user_agent`, `utc`) VALUES (?, ?, ?);"); + $statement->execute(array($ip, $useragent, time())); + } function insertRefreshRequest($success, $username, $ip) { $statement = $this->conn->prepare("INSERT INTO `".TABLE_REFRESH_REQUESTS."` (`success`, `username`, `ip`) VALUES (?, ?, ?);"); $statement->execute(array($success, $username, $ip)); } + function insertRevokeRequest($success, $ip) { + $statement = $this->conn->prepare("INSERT INTO `".TABLE_REVOKE_REQUESTS."` (`success`, `ip`, `utc`) VALUES (?, ?, ?);"); + $statement->execute(array($success, $ip, time())); + } + function getSpamRules() { $rules = array(); @@ -32,7 +47,7 @@ function insertForgotLog($username, $ip) { } function deleteRecaptchaListing($id) { - $statement = $this->conn->prepare("DELETE FROM `".TABLE_RECAPTCHA."` WHERE `".TABLE_RECAPTCHA."`.`identifier` = ?"); + $statement = $this->conn->prepare("DELETE FROM `".TABLE_RECAPTCHA."` WHERE `identifier` = ?"); $statement->execute(array($id)); } @@ -67,7 +82,7 @@ function getAPISuccessStatus($id) { if($row->status != "0") { return array('available' => false); } else { - return array('available' => true, 'title' => $row->title, 'scopes' => $row->scopes); + return array('available' => true, 'title' => $row->title, 'scopes' => $row->scopes, 'redirect_url' => $row->redirect_url); } } return array('available' => false); @@ -93,9 +108,9 @@ function getAPIStatus($id, $expireAfterHours = 24) { return array(); } - function insertAPI($unique, $title, $scopes, $ip = "not_set") { - $statement = $this->conn->prepare("INSERT INTO `".TABLE_API."` (`unique_string`, `title`, `scopes`, `status`, `token`, `username`, `created_at`, `updated_at`, `creation_ip`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);"); - $statement->execute(array($unique, $title, $scopes, "0", "", "", time(), "0", $ip)); + function insertAPI($unique, $title, $scopes, $ip = "not_set", $redirect = "", $recaptcha = "0") { + $statement = $this->conn->prepare("INSERT INTO `".TABLE_API."` (`unique_string`, `title`, `scopes`, `status`, `token`, `username`, `created_at`, `updated_at`, `creation_ip`, `redirect_url`, `recaptcha_status`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"); + $statement->execute(array($unique, $title, $scopes, "0", "", "", time(), "0", $ip, $redirect, $recaptcha)); } function insertQuickLink($key, $scopes, $auth) { @@ -103,14 +118,19 @@ function insertQuickLink($key, $scopes, $auth) { $statement->execute(array($key, $scopes, $auth, time(), "0")); } + function insertReferrer($referrer) { + $statement = $this->conn->prepare("INSERT INTO `".TABLE_REFERRER."` (`ip`, `referrer`, `utc`) VALUES (?, ?, ?);"); + $statement->execute(array($_SERVER['REMOTE_ADDR'], $referrer, time())); + } + function getAPIData($id) { - $statement = $this->conn->prepare("SELECT unique_string, scopes, status FROM `".TABLE_API."` WHERE `unique_string` = ? ;"); + $statement = $this->conn->prepare("SELECT unique_string, scopes, status, title, recaptcha_status FROM `".TABLE_API."` WHERE `unique_string` = ? ;"); $statement->execute(array($id)); while($row=$statement->fetch(PDO::FETCH_OBJ)) { if($row->status != "0") { return array('error' => 1); } else { - return array('error' => 0, 'unique' => $row->unique_string, 'scopes' => $row->scopes); + return array('error' => 0, 'unique' => $row->unique_string, 'scopes' => $row->scopes, 'title' => $row->title); } } return array('error' => 2); @@ -138,7 +158,7 @@ function insertRequest($unique, $scopes, $name, $email) { function getUsername($token) { if($token == null || strlen($token) < 3) return null; - $usernameResult = file_get_contents("https://api.twitch.tv/kraken?oauth_token=" . $token); + $usernameResult = file_get_contents("https://api.twitch.tv/kraken?oauth_token=" . $token."&api_version=5"); $json_decoded_usernameResult = json_decode($usernameResult, true); return $json_decoded_usernameResult['token']['user_name']; } @@ -152,9 +172,46 @@ function getUserId($name, $token) { function getUserData($name, $token) { if($name == "[Not set]") return array('userid' => "-1", 'followers' => "-1", 'views' => "-1", 'partner' => false); - $data = file_get_contents("https://api.twitch.tv/kraken/channels/".$name."?oauth_token=".$token); + + $opts = array( + 'http'=>array( + 'method'=>"GET", + 'header'=>"Client-ID: ".FRONTEND_CLIENT_ID."\r\n" . + "Authorization: OAuth ".$token."\r\n" . + "Accept: application/vnd.twitchtv.v5+json\r\n" + ) + ); + $context = stream_context_create($opts); + + $data = file_get_contents("https://api.twitch.tv/kraken", false, $context); $res = json_decode($data, true); - return array('userid' => $res['_id'], 'followers' => $res['followers'], 'views' => $res['views'], 'partner' => $res['partner']); + $userid = $res['token']['user_id']; + + $data = file_get_contents("https://api.twitch.tv/kraken/channels/".$userid, false, $context); + $this->dumpRequest($name."(".$userid.")", $data); + $res = json_decode($data, true); + return array('userid' => $userid, 'followers' => $res['followers'], 'views' => $res['views'], 'partner' => $res['partner'], 'logo' => $res['logo']); + } + + function getUserViewCount($name) { + if($name == "[Not set]") + return array('userid' => "-1", 'followers' => "-1", 'views' => "-1", 'partner' => false); + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, "https://api.twitch.tv/helix/users?login=".$name); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET"); + + curl_setopt($ch, CURLOPT_HTTPHEADER, array( + 'Accept: application/vnd.twitchtv.v5+json', + 'Client-ID: '.FRONTEND_CLIENT_ID + )); + + $result = curl_exec($ch); + curl_close(); + + $json = json_decode($result, true); + return $json['data'][0]['view_count']; } function disableRequest($id) { @@ -180,12 +237,12 @@ function getRequestDetails($id) { function getScopes() { $res = array(); - $statement = $this->conn->prepare("SELECT api_set, scope, description FROM `".TABLE_SCOPES."`;"); + $statement = $this->conn->prepare("SELECT id, api_set, scope, description FROM `".TABLE_SCOPES."`;"); $statement->execute(); while($row=$statement->fetch(PDO::FETCH_OBJ)) { if(!array_key_exists($row->api_set, $res)) $res[$row->api_set] = array(); - array_push($res[$row->api_set], array('scope' => $row->scope, 'desc' => $row->description)); + array_push($res[$row->api_set], array('id' => $row->id, 'scope' => $row->scope, 'desc' => $row->description)); } return $res; @@ -194,10 +251,10 @@ function getScopes() { function getAllScopes() { $res = array(); - $statement = $this->conn->prepare("SELECT scope, description FROM `".TABLE_SCOPES."`;"); + $statement = $this->conn->prepare("SELECT id, scope, description FROM `".TABLE_SCOPES."`;"); $statement->execute(); while($row=$statement->fetch(PDO::FETCH_OBJ)) { - $res[$row->scope] = array('scope' => $row->scope, 'desc' => $row->description); + $res[$row->scope] = array('id' => $row->id, 'scope' => $row->scope, 'desc' => $row->description); } return $res; @@ -206,23 +263,15 @@ function getAllScopes() { function getRawScopes() { $res = array(); - $statement = $this->conn->prepare("SELECT scope, description FROM `".TABLE_SCOPES."`;"); + $statement = $this->conn->prepare("SELECT id, scope, description FROM `".TABLE_SCOPES."`;"); $statement->execute(); while($row=$statement->fetch(PDO::FETCH_OBJ)) { - array_push($res, $row->scope); + array_push($res, array('id' => $row->id, 'scope' => $row->scope)); } return $res; } - function getRecentAuth() { - $statement = $this->conn->prepare("SELECT utc, scopes, country FROM `".TABLE_DATA."` ORDER BY `id` DESC LIMIT 1"); - $statement->execute(); - while($row=$statement->fetch(PDO::FETCH_OBJ)) { - return array('utc' => $row->utc, 'scopes' => $row->scopes, 'country' => $row->country); - } - } - function getStats() { $res = array(); @@ -234,11 +283,6 @@ function getStats() { return $res; } - function logUsage($ip = "", $scopes = "", $country = "", $username = "", $userAgent = "", $flag = "not_set") { - $statement = $this->conn->prepare("INSERT INTO `".TABLE_DATA."` (`ip`, `scopes`, `country`, `username`, `useragent`, `flag`) VALUES (?,?,?,?,?,?);"); - $statement->execute(array($ip, str_replace(" ", ",", $scopes), $country, $username, $userAgent, $flag)); - } - function logMetadata($username = "", $userid = -1, $followers = -1, $views = -1, $partner = -1) { if($userid == null) $userid = -1; @@ -325,6 +369,39 @@ function getWaitingTexts() { return $results; } + + function insertRecaptchaCompletionListing($unique_string = "", $userid = "", $username = "") { + $statement = $this->conn->prepare("INSERT INTO `".TABLE_RECAPTCHA_COMPLETION."` (`unique_string`, `userid`, `username`, `created_at`, `completed`) VALUES (?, ?, ?, ?, ?);"); + $statement->execute(array($unique_string, $userid, $username, date(DATE_ATOM), '0')); + } + + function finishRecaptchaCompletionListing($unique_string) { + $statement = $this->conn->prepare("UPDATE `".TABLE_RECAPTCHA_COMPLETION."` SET `completed` = ? WHERE `".TABLE_RECAPTCHA_COMPLETION."`.`unique_string` = ?"); + $statement->execute(array("1", $unique_string)); + } + + function getApiRecaptchaStatus($id) { + $statement = $this->conn->prepare("SELECT title, token, scopes, recaptcha_status FROM `".TABLE_API."` WHERE `unique_string` = ? ;"); + $statement->execute(array($id)); + while($row=$statement->fetch(PDO::FETCH_OBJ)) { + return array('error' => 0, 'title' => $row->title, 'token' => $row->token, 'scopes' => $row->scopes, 'recaptcha_status' => $row->recaptcha_status); + } + return array('error' => 1); + } + + function updateApiRecaptchaStatus($identifier, $status) { + $statement = $this->conn->prepare("UPDATE `".TABLE_API."` SET `recaptcha_status` = ? WHERE `".TABLE_API."`.`unique_string` = ?"); + $statement->execute(array($status, $identifier)); + } + + function dumpRequest($username = "", $data = "") { + if($username == null || $username == "") + $username = "not_set"; + if($data == null || $data == "") + $data = "not_set"; + $statement = $this->conn->prepare("INSERT INTO `".TABLE_REQUEST_DEBUG."` (`username`, `data`, `utc`) VALUES (?, ?, ?);"); + $statement->execute(array($username, $data, time())); + } } diff --git a/images/block.gif b/images/block.gif new file mode 100644 index 0000000..dd4806b Binary files /dev/null and b/images/block.gif differ diff --git a/img/kappa.gif b/img/kappa.gif index 505386c..22b91c9 100644 Binary files a/img/kappa.gif and b/img/kappa.gif differ diff --git a/index.php b/index.php index 0bf4681..6490132 100644 --- a/index.php +++ b/index.php @@ -1,13 +1,38 @@ insertReferrer($referrer); + +if(isset($_GET['state'])) { + // ************************************************** + // Portion of code removed to preserve site security + // ************************************************** +} + +// log when security code is missing, as likely malicious +if(!$securityCheckPass) { + $state = "not_set"; + if(isset($_GET['state'])) { + $state = $_GET['state']; + } + $dao->insertMissingSecurityCode($_SERVER['REMOTE_ADDR'], $_SERVER['HTTP_USER_AGENT']); +} + +if(isset($_GET['code']) && $securityCheckPass) { $data = getAccessToken($_GET['code'], "frontend"); $access_token = $data['access']; $refresh_token = $data['refresh']; @@ -15,39 +40,51 @@ $username = $dao->getUsername($access_token); if($username == null) $username = "[Not set]"; - $country = $dao->getCountry($_SERVER['REMOTE_ADDR']); - - $spamRules = $dao->getSpamRules(); - $spamResult = isSpam($spamRules, $_SERVER['REMOTE_ADDR'], $_GET['scope'], $country, $username, $_SERVER['HTTP_USER_AGENT']); + $country = $dao->getCountry($_SERVER['REMOTE_ADDR']); + $scope = ""; if(isset($_GET['scope'])) { - $dao->logUsage($_SERVER['REMOTE_ADDR'], $_GET['scope'], $country, $username, $_SERVER['HTTP_USER_AGENT'], $spamResult); - $udata = $dao->getUserdata($username, $access_token); - $partner = $udata['partner'] ? "1" : "0"; - $dao->logMetadata($username, $udata['userid'], $udata['followers'], $udata['views'], $partner); - } else { - $dao->logUsage($_SERVER['REMOTE_ADDR'], "", $country, $username, $_SERVER['HTTP_USER_AGENT'], $spamResult); + $scope = $_GET['scope']; } - - if(isset($_GET['state'])) { - exit(header("Location: https://twitchtokengenerator.com/request/".$_GET['state']."/".$access_token."/".$refresh_token)); + $spamRules = $dao->getSpamRules(); + $spamResult = isSpam($spamRules, $_SERVER['REMOTE_ADDR'], $scope, $country, $username, $_SERVER['HTTP_USER_AGENT']); + if(isBotter($dao, $username) == "1") { + // ************************************************** + // Portion of code removed to preserve site security + // ************************************************** } + $udata = $dao->getUserdata($username, $access_token); + $userid = $udata['userid']; + $partner = $udata['partner'] ? "1" : "0"; + $logo = $udata['logo']; + $scopes = getScopes($access_token); + $dao->logMetadata($username, $udata['userid'], $udata['followers'], $udata['views'], $partner); + if(strlen($spamResult) > 0) { - $access_token = genFakeToken(30); - $refresh_token = genFakeToken(50); + // ************************************************** + // Portion of code removed to preserve site security + // ************************************************** } if($username != "[Not set]") { $id = generateRandomString(); $dao->insertRecaptchaListing($id, $access_token, $refresh_token, $username); + $dao->insertRecaptchaCompletionListing($id, $userid, $username); $access_token = "Please complete the Captcha"; $refresh_token = "Please complete the Captcha"; } + + if(strlen($spamResult) > 0) { + $dao->insertMissingSecurityCode($_SERVER['REMOTE_ADDR'], $_SERVER['HTTP_USER_AGENT']); + } } $scopes = $dao->getScopes(); +// ************************************************** +// Portion of code removed to preserve site security +// ************************************************** ?> @@ -61,6 +98,9 @@ } else { echo 'var captchaId = "";'; } + if($securityLocation == "1") { + echo 'var securityCode = "'.$securityCode.'";'; + } ?> @@ -181,7 +221,7 @@

    Twitch Token Generator Information

    - This tool is used to generate tokens for use with the Twitch API and Twitch Chat! To use the tool, simply select the scopes you want and click 'Generate Token!'. You will be prompted by Twitch to authorize your account with the selected scopes. Upon authorization, your access token will be placed in the textbox that says "Token will appear here..." . + This tool is used to generate tokens for use with the Twitch API and Twitch Chat! To use the tool, simply select the scopes you want and click 'Generate Token!'. You will be prompted by Twitch to authorize your account with the selected scopes. Upon authorization, your access token will be placed in the textbox that says "Token will appear here..." .
    Starting May 1st, all Helix API calls must use the CLIENT ID associated with the generated OAuth access token. TwitchTokenGenerator.com has a next textbox that has the CLIENT ID now.
    @@ -193,13 +233,28 @@ 1): ?> + + + + + + + + @@ -224,10 +290,10 @@ @@ -235,10 +301,21 @@ + + + + @@ -256,6 +333,17 @@ + + + +
    TWITCH ACCOUNT +
    +
    +
    + ... +
    +
    + (wrong?) +
    +
    +
    +
    ACCESS TOKEN -
    +
    - +
    @@ -208,10 +263,21 @@
    REFRESH TOKEN -
    +
    - + + +
    +
    CLIENT ID +
    + + +
    ACCESS TOKEN -
    +
    - +
    REFRESH TOKEN -
    +
    - + + +
    +
    CLIENT ID +
    + + +
    REFRESH TOKEN
    CLIENT ID +
    + + + + +
    +
    @@ -280,7 +368,7 @@ '; - echo ''; + echo ''; echo ''.$scope['scope'].''; echo ''.$scope['desc'].''; echo ''; @@ -293,7 +381,7 @@ '; - echo ''; + echo ''; echo ''.$scope['scope'].''; echo ''.$scope['desc'].''; echo ''; @@ -316,6 +404,14 @@ +'; + echo 'var securityCode = "'.$securityCode.'";'; + echo ''; +} +?> +

    Refresh Access Token

    @@ -441,6 +537,12 @@ /* --- Runtime PHP Generated JS Vars START -- */ var authSuccessful = 1 ? "true" : "false"); ?>; /* --- Runtime PHP Generated JS Vars END -- */ + + @@ -457,25 +559,24 @@ function generateRandomString($length = 10) { } function isSpam($rules, $ip, $scope, $country, $username, $useragent) { - foreach($rules as $rule) { - if(strlen($rule['ip']) > 0 && $rule['ip'] != $ip) - return false; - if(strlen($rule['scopes']) > 0 && $rule['scopes'] != $scope) - return false; - if(strlen($rule['country']) > 0 && $rule['country'] != $country) - return false; - if(strlen($rule['username']) > 0 && $rule['username'] != $username) - return false; - if(strlen($rule['useragent']) > 0 && $rule['useragent'] != $useragent) - return false; - - return $rule['id']; - } - return ""; + // ************************************************** + // Portion of code removed to preserve site security + // ************************************************** } -function genFakeToken($length = 10) { - return substr(str_shuffle(str_repeat($x='0123456789abcdefghijklmnopqrstuvwxyz', ceil($length/strlen($x)) )),1,$length); +function isBotter($dao, $username) { + // ************************************************** + // Portion of code removed to preserve site security + // ************************************************** +} + +function getScopes($accessKey) { + $results = file_get_contents("https://twitchtokengenerator.com/api/forgot/".$accessKey); + + $json = json_decode($results, true); + $scopes = $json['data']['scopes']; + + return join(" ", $scopes); } ?> \ No newline at end of file diff --git a/internal.php b/internal.php index 95d5593..6d3559f 100644 --- a/internal.php +++ b/internal.php @@ -18,6 +18,7 @@ $dao = new dao(); $result = $dao->getRecaptchaListing($id); $dao->deleteRecaptchaListing($id); +$dao->finishRecaptchaCompletionListing($id); if(!$result['found']) exit(json_encode(array('success' => false, 'message' => "Generation data not found on server!"))); diff --git a/metrics.php b/metrics.php new file mode 100644 index 0000000..6948717 --- /dev/null +++ b/metrics.php @@ -0,0 +1,32 @@ + false, 'message' => "no security code"))); +} +if(!validSecurityCode($_GET['security_code'], $gracePeriodSeconds)) { + exit(json_encode(array('successful' => false, 'message' => "invalid security code"))); +} + +if(!isset($_GET['action'])) { + exit(json_encode(array('successful' => false, 'message' => "no action provided"))); +} + +$dao = new dao(); + +switch($_GET['action']) { + case "button": + if(!isset($_GET['id'])) { + exit(json_encode(array('successful' => false, 'message' => "no id provided"))); + } + $dao->insertButtonMetrics($_GET['id'], $_SERVER['REMOTE_ADDR']); + exit(json_encode(array('successful' => true, 'message' => ""))); + break; + default: + exit(json_encode(array('successful' => false, 'message' => "unknown action: ".$_GET['action']))); +} + +?> \ No newline at end of file diff --git a/quick/create.php b/quick/create.php index 92538b7..7b0c78a 100644 --- a/quick/create.php +++ b/quick/create.php @@ -6,16 +6,20 @@ exit(json_encode(array('success' => false, 'error' => 30, 'message' => 'No scopes provided.'))); $dao = new dao(); -if(!validateScopes($scopes, $dao->getRawScopes())) +$raw = $dao->getRawScopes(); +if(!validateScopes($scopes, $raw)) exit(json_encode(array('success' => false, 'error' => 31, 'message' => 'Provided scopes are invalid.'))); $ran = generateRandomString(); -$dao->insertQuickLink($ran, $scopes, $auth); +$dao->insertQuickLink($ran, implode(' ', getScopesFromIds(explode(' ', $scopes), $raw)), $auth); exit(json_encode(array('success' => true, 'message' => 'https://twitchtokengenerator.com/quick/'.$ran))); function validateScopes($scopes, $validScopes) { + $scopes = explode(' ', $scopes); + $validScopeNames = getValidScopes($validScopes); + $scopes = getScopesFromIds($scopes, $validScopes); $checkScopes = array(); if (strpos($scopes, ' ') !== false) $checkScopes = explode(" ", $scopes); @@ -23,13 +27,31 @@ function validateScopes($scopes, $validScopes) { array_push($checkScopes, $scopes); foreach($checkScopes as $scope) { - if(!in_array($scope, $validScopes)) + if(!in_array($scope, $validScopeNames)) return false; } return true; } +function getScopesFromIds($scopes, $rawScopes) { + $scopeNames = array(); + foreach($rawScopes as $rawScope) { + if(in_array($rawScope['id'], $scopes)) { + array_push($scopeNames, $rawScope['scope']); + } + } + return $scopeNames; +} + +function getValidScopes($rawScopes) { + $validScopes = array(); + foreach($rawScopes as $rawScope) { + array_push($validScopes, $rawScope['scope']); + } + return $validScopes; +} + function generateRandomString($length = 10) { $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; $charactersLength = strlen($characters); diff --git a/quick/index.php b/quick/index.php index bd687b2..f6888cb 100644 --- a/quick/index.php +++ b/quick/index.php @@ -13,7 +13,6 @@ } else { $action = $_GET['id']; } - switch($action) { case "create": $scopes = $args[0]; diff --git a/request/request.php b/request/request.php index a672bd1..94a9f29 100644 --- a/request/request.php +++ b/request/request.php @@ -92,7 +92,7 @@ function processRedirect() { var id = ""; var scopes = ""; // twitch variables - var client_id = "gp762nuuoqcoxypju8c569th9wz7q5"; + var client_id = ""; // populated via db var redirect_uri = "https://twitchtokengenerator.com#" + id; // redirect diff --git a/request/success.php b/request/success.php index 2484103..8e58766 100644 --- a/request/success.php +++ b/request/success.php @@ -79,7 +79,7 @@ function fireEmail($email, $name, $token, $refresh, $scopes, $username) { $to = $email; $subject = 'TwitchTokenGenerator.com - Request Successful'; - $message = 'Hello '.$name."!\n\nYour TwitchTokenGenerator request has been completed successfully!\n\nUsername: ".$username."\nScopes Requested: ".$scopes."\nAccess Token: ".$token."\nRefresh Token: ".$refresh."\n\nCheers,\nswiftyspiffy"; + $message = 'Hello '.$name."!\n\nYour TwitchTokenGenerator request has been completed successfully!\n\nUsername: ".$username."\n\nScopes Requested: ".$scopes."\n\nAccess Token: ".$token."\n\nRefresh Token: ".$refresh."\n\nClient Id: ".FRONTEND_CLIENT_ID."\n\nCheers,\nswiftyspiffy"; $headers = 'From: requests@twitchtokengenerator.com' . "\r\n" . 'Reply-To: noreply@twitchtokengenerator.com' . "\r\n" . 'X-Mailer: PHP/' . phpversion(); diff --git a/stats/api.php b/stats/api.php index edcb4d9..6f15e77 100644 --- a/stats/api.php +++ b/stats/api.php @@ -22,7 +22,7 @@ function buildStats($stats) { } } else { $country = $stat['country']; - if (!empty($country)) { + if (!empty($country) && $country != "not_set") { if(in_array($country, $countryNames)) { $countryResults[$country]++; } else { diff --git a/stats/index.php b/stats/index.php index 326b321..f08bf46 100644 --- a/stats/index.php +++ b/stats/index.php @@ -30,10 +30,10 @@
    - +
    - +
    diff --git a/twitchtv.php b/twitchtv.php index 6e9c6e3..1d03844 100644 --- a/twitchtv.php +++ b/twitchtv.php @@ -18,7 +18,7 @@ function getAccessToken($code, $type) { default: exit("unknown getAccessToken type"); } - $curl = curl_init("https://api.twitch.tv/kraken/oauth2/token"); + $curl = curl_init("https://id.twitch.tv/oauth2/token"); curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_POST, 1);