我已经知道如何使用 Apps Script UrlFetchApp.fetch 和 HTML 服务让用户通过 Facebook 登录到我的 Apps Script 应用程序。我还可以使用 Apps 脚本在 Facebook 上发帖。
- 您不需要 Facebook Javascript SDK
一般概述
有 8 个不同的 Facebook 平台:
我与 Apps 脚本一起使用的 Facebook 平台是网站平台。该网站不嵌入在 Facebook 中运行。
你不能做什么:(据我所知)
- 与 Facebook 交互的网站与 Facebook 页面选项卡之间存在差异。 Facebook
Page Tab 嵌入在 Facebook 中运行。我的经验是,Apps 脚本 URL 会导致 Facebook Page Tab 出现问题。 Apps Script 的 URL 在末尾附加了很多 Facebook 似乎删除的内容。 (或类似的东西)我在 Facebook 上尝试了许多不同的 Google 产品变体。您可以将 Google 站点用作 Facebook 页面选项卡,但如果您将 Google Apps 脚本嵌入到站点中,即在 Facebook 页面选项卡中,则会导致跨域错误。因此,我可以开始工作的唯一选择是网站选项。
我用于解决页面选项卡问题的一种解决方法是使用 Thunderpenny 静态 HTML 作为 Facebook 页面选项卡,然后链接到我的 Apps 脚本应用程序或 GAE 应用程序。 (取决于我是否需要 HTTP S)
您可以将 Google 站点用作页面选项卡,但 Thunderpenny 可以像往常一样使用 HTML、Javascript 和 CSS 设计应用程序。哦!我已经尝试在 Thunderpenny 中使用 Facebook Javascript SDK,但没有成功。此外,Thunderpenny 应用程序没有后端,Apps Script 有一个后端(.gs 代码),您可以在其中隐藏您的 Facebook 应用程序令牌。
基本步骤是:
- 从您的应用中触发指向 Facebook oauth 的链接。
- href="https://www.facebook.com/dialog/oauth?client_id=yourIDhereNoQuotes&redirect_uri=https://script.google.com/macros/s
- 使用 OnLoad 函数从 URL 捕获返回的 Facebook 令牌
- 您无法使用
doGet(e) 将 Facebook 重定向 URL 处理回您的应用程序。这就是为什么。 Apps 脚本需要在 URL 中看到问号才能解析 URL。 Facebook 返回具有不同配置的 URL。
- 在您的应用加载时运行脚本以处理 Facebook 令牌
- window.onload=function(){};
-
onload 函数运行 .gs 函数来验证令牌
- 使用
UrlFetchApp.fetch 到debug 令牌
- 在私有缓存中缓存登录状态
- var cache = CacheService.getPrivateCache();
- 在需要时检查登录状态。
请注意,Facebook 会独立跟踪您的应用登录状态。但是,当 Facebook 令牌过期时,您的应用程序的登录状态可能仍然处于活动状态。所以你需要一种方法来检查。
使用官方 Facebook 图形
Brand Resources - Facebook
下载 Facebook 图形。阅读注意事项
创建 Facebook 登录按钮
<div id="FbLog">
<a href="https://www.facebook.com/dialog/oauth?client_id=YourClientID&redirect_uri=https://script.google.com/macros/s/YourAppsScript/exec?&response_type=token&scope=publish_stream"><img src="https://FacebookGraphic.png" height="95%" width="95%"></a>
</div>
为 Facebook 登录按钮设置样式
#FbLog {
padding: 10px;
background-color: white;
margin-left:auto;
margin-right:auto;
cursor: pointer;
}
窗口加载代码
这是我用来捕获 Facebook 令牌的 window.onload 客户端代码。这仅用于捕获 Facebook 令牌,而不用于验证令牌。我的应用程序允许 Facebook 登录和常规登录。 Facebook 令牌的验证在另一个代码中完成。
window.onload=function(){
//console.log("This onload did run");
//get the URL out of the browsers address bar
//the URL can have multiple different strings attached depending upon the situation.
//1)Sign in with Facebook. 2) Someone wanting to buy an item 3) Someone wanting to input an item for sale.
//Any situation other than the FB log in is initiated in the back end.
window.daURL = window.location;
window.urlStrng = daURL.toString();
//console.log("url: " + urlStrng);
//If there was a FaceBook login, there will be a hashtag in the url string
window.hashPos = urlStrng.indexOf("#");
if (window.hashPos > 0) {
mainStart('InputBlock', 'InputForm');
window.urlEnd = urlStrng.split("#", 2);
window.urlTkn = urlEnd[1].split("&", 2);
window.fbAcsTkn = urlTkn[0].split("=", 2);
window.finalTkn = fbAcsTkn[1];
window.scndExpire = urlStrng.substring(urlStrng.indexOf("_in=")+4, urlStrng.length);
console.log("scndExpire: " + scndExpire);
google.script.run.withFailureHandler(onFailure)
.withSuccessHandler(showFBsuccess)
.processFB_LogIn(window.finalTkn, scndExpire)
}
else {
//If it's not a Facebook log in, go to next two choices
//If the URL string has &L in it, then item listing info is being passed because someone clicked 'Buy'
window.whatToLoad = urlStrng.indexOf("&L");
console.log("Second option of onload ran");
if (window.whatToLoad > 0) {
google.script.run.withFailureHandler(onFailure)
.withSuccessHandler(injectBuyForm)
.include('MutualCmit');
} else {
google.script.run.withFailureHandler(onFailure)
.withSuccessHandler(injectSignInForm)
.include('SignIn');
};
};
};
请注意,即使 Facebook 登录是在前端触发的,验证也会在 .gs 代码中进行。有人可以注入虚假的 Facebook 令牌,但它不会通过服务器端代码的检查。
这是处理 Facebook 登录的.gs 代码:
验证 Facebook Token GS 代码
//I put this cache line at the very top of the `.gs` file. The other code
// can be put somewhere lower.
var cache = CacheService.getPrivateCache();
function processFB_LogIn(argFB_Tkn, expTime) {
cache.put('fbTkn', argFB_Tkn, 4000);
cache.put('fbExpr', expTime, 4000);
var meFBtkn = cache.get('fbTkn');
Logger.log("FaceBook Token: " + meFBtkn);
//This section is for verifying (debug) the user actually signed in through Facebook
//The first FB token is passed in from the URL right after the user signs in, and when this apps Script loads.
//IMPORTANT!!! IMPORTANT!!! You MUST escape the | character with code %7C
var AppAccssTkn = 'YourAppID%7YourAppToken'; //This never changes unless app secret changes
var optnGetTkn = {"method" : "get", "muteHttpExceptions" : true};
//This 'Debugs' the token returned in the URL after the user signed in with Facebook. You "should" verify that the token is real.
var rsltDebug = UrlFetchApp.fetch("https://graph.facebook.com/debug_token?input_token=" + meFBtkn + "&access_token=" + AppAccssTkn, optnGetTkn);
var debugTxt = rsltDebug.getContentText();
Logger.log("debugTxt: " + debugTxt);
var jsonObj = JSON.parse(debugTxt);
Logger.log("jsonObj: " + jsonObj);
//This is the FB user ID
var useIdTxt = jsonObj.data.user_id;
cache.put('pubIDcache', useIdTxt, 4000);
var tknValid = jsonObj.data.is_valid;
Logger.log("reslt of the debug: " + useIdTxt);
Logger.log("tknValid: " + tknValid);
var getFbUseName = UrlFetchApp.fetch("https://graph.facebook.com/" + useIdTxt + "/?fields=first_name&access_token=" + AppAccssTkn, optnGetTkn);
var objUseName = JSON.parse(getFbUseName);
var arryFirstName = objUseName.first_name;
Logger.log("user name: " + arryFirstName);
cache.put('fbFrstName', arryFirstName, 4000);
if (tknValid === false) {
return 'notValid';
}
else if (arryFirstName != null) {
//This is how it's determined if someone is logged in or not.
cache.put('imin', '9847594ujglfugfjogj', 4000);
return arryFirstName;
};
};
您需要一个不会更改的 one time 应用令牌,除非您更改应用机密。您必须使用一次性运行的代码生成它。
使用此代码获取您的应用访问令牌:
//A Facebook App Token never changes unless you go to the Facebook Developers Console, and you
//change the App Secret. So, do NOT keep requesting a new App Token. Just get it once, then
//hard code it into a backend secret function.
// The App Token can be used to modify your App, but you can just do that 'Manually'
function getOneTimeFB_AppToken() {
Logger.log("getOneTimeFB_AppToken ran");
//keep this info secret
//Generate an App Access Token
var ysshAppID = 'Your App ID';
var ysshAppSecret = 'Your App Secret';
var optnAppTkn = {"method" : "get"};
var getAppTknURL = "https://graph.facebook.com/oauth/access_token?client_id=" + ysshAppID + "&client_secret=" + ysshAppSecret + "&grant_type=client_credentials"
var getAppTkn = UrlFetchApp.fetch(getAppTknURL, optnAppTkn);
Logger.log("Object returned from GET: " + getAppTkn)
var myAppTkn = getAppTkn.getContentText();
Logger.log("myAppTkn: " + myAppTkn);
};
发布到 Facebook GS 代码
function fncPostItemFB(arg1ToPost, arg2ToPost, arg3ToPost, argEtcToPost) {
var fbCacheTkn = cache.get('fbTkn');
Logger.log("fbCacheTkn: " + fbCacheTkn);
if (fbCacheTkn === null) {
return false;
};
Logger.log("fncPostItemFB ran: " + fbCacheTkn);
return fncPostSecurly_(arg1ToPost, arg2ToPost, arg3ToPost, argEtcToPost);
};
function fncPostSecurly_(arg1ToPost, arg2ToPost, arg3ToPost, argEtcToPost) {
Logger.log("fncPostSecurly ran");
var sttsUpdate = argToPost + "your text to post here" + argToPost;
var fromLogInTkn = cache.get('fbTkn');
Logger.log("cache FB token: " + fromLogInTkn);
//This is added security https://developers.facebook.com/docs/graph-api/securing-requests/
var appsecret_sig = Utilities.computeHmacSha256Signature(fromLogInTkn, "YourAppSecret");
var optnPostFB = {"method" : "post"}; //
var PostToFB_URL = "https://graph.facebook.com/FacebookPageOrGroupID/feed?message=" + sttsUpdate + "&access_token="
+ fromLogInTkn; // + "&appsecret_proof=" + appsecret_sig;
//Make a post to the Page
var whatHappened = UrlFetchApp.fetch(PostToFB_URL, optnPostFB );
//The return from facebook is an object. Has to be converted to a string.
var strFrmFbObj = whatHappened.getContentText();
Logger.log("Return value of Post: " + strFrmFbObj);
//When a post is successfully made to Facebook, a return object is passed back with an id value.
var rtrnVerify = strFrmFbObj.indexOf('{\"id\":\"');
Logger.log("rtrnVerify: " + rtrnVerify);
if (rtrnVerify != -1) {
return true;
} else {
return false;
};
};
发布到 Facebook 前端 Javascript 代码
<script>
window.WriteInput = function(whereToPost) {
window.strngCtgry = document.getElementById('id_Category').value;
window.strngMaker = document.getElementById('id_Maker').value;
window.strngAskingPrice = document.getElementById('id_AskingPrice').value;
window.strngType = document.getElementById('id_ShrtDesc').value;
window.strngFunction = document.getElementById('id_Function').value;
window.strngCosmetic = document.getElementById('id_Cosmetic').value;
window.strngDescription = document.getElementById('id_Description').value;
window.strngUserID = document.getElementById('pubID_Holder').textContent;
window.addrIP = document.getElementById('IP_Holder').textContent;
if (whereToPost === 'fb') {
console.log("fncPostToFB ran" + strngDescription);
if (strngDescription === "" || strngAskingPrice === "") {alert("Missing Input"); return false;};
google.script.run.withFailureHandler(postFbFail)
.withSuccessHandler(savedToFB)
.fncPostItemFB(strngCtgry, strngMaker, strngAskingPrice, strngType, strngDescription, strngFunction, strngCosmetic, addrIP);
} else {
google.script.run.withFailureHandler(onFailure)
.withSuccessHandler(savedLst)
.fncSaveItem(strngCtgry, strngMaker, strngAskingPrice, strngType, strngDescription, strngFunction, strngCosmetic, addrIP);
};
};
window.savedLst = function(rtrnInput) {
if (rtrnInput === false) {
alert("Failed to Save Data");
}
else if (rtrnInput === "NotLogged") {
alert("You are not logged in!");
mainStart('SignInBody', 'SignIn');
}
else if (rtrnInput === "noItemForPic") {
alert("You Need to Save an Item to attach the Picture to");
}
else {
alert("Your Data Was Saved!");
//Show the listing that was just saved next to the upload Pics button
document.getElementById('listToPic').innerHTML = document.getElementById('id_ShrtDesc').value +
", " + document.getElementById('id_Description').value +
", - Made By: " + document.getElementById('id_Maker').value +
", Price: $" + document.getElementById('id_AskingPrice').value;
};
};
window.postFbFail = function() {
alert("Failed to Post to Facebook! Try Signing In Again.");
unsignFB();
};
window.savedToFB = function(pstFbStat) {
if (pstFbStat === false) {
alert("You are Not Signed in to Facebook!");
unsignFB();
google.script.run.withFailureHandler(onFailure)
.signOutFB();
} else {
alert("Your Item was Posted to Facebook!");
};
};
</script>