目次


Bluemix と MEAN スタックを使用して自動投稿 Facebook アプリケーションを作成する: パート 1 Facebook をログイン・ソースとして使用する

Comments

Facebook ユーザーは、自分がログインしていないときでも、自分の代わりにサーバーから Facebook に投稿させたいと思うことがあるものです。例えば、ビジネス・ページのオーナーが、顧客の購買心をあおるために、特定の製品が限られた数量になった時点で、その旨を通知する投稿を送信するという場合です。あるいは単純に、ユーザーが自分のタイムラインにランダムな間隔でメッセージを投稿したいという場合もあります。

こうしたことを実現するためのサーバーを作成するのは可能ですが、簡単ではありません。3 つのチュートリアルからなるこのシリーズでは、IBM Bluemix をクラウド・プロバイダーとして使用して、サーバーから Facebook に投稿させる方法を紹介します。また、シリーズを通して、MEAN スタックを構成する 4 つすべてのコンポーネントの基本も抑えます。具体的な機能をデモンストレーションするために、ランダムなタイミングでユーザーに代わってジョークを投稿するアプリケーションを作成する方法を紹介します。

  • パート 1 (このチュートリアル) では、Facebook をログイン・ソース兼認証メカニズムとして使用する方法を説明します。
  • パート 2 では、Facebook から取得したユーザー情報を保管するように MongoDB を構成する方法を説明します。
  • パート 3 では、Facebook REST API を使用して、サーバーにユーザーの代理を務めさせる方法を説明します。

注: Bluemix に統合されている Single Sign On サービスを利用すれば、Facebook やその他のソースからのログインを受け付けることができますが、このシリーズでは敢えてこのサービスを利用しません。ここで説明するようなアプリケーションは、Facebook と緊密に連携することから、他のアカウントでユーザーがログインできるようにしたとしても意味がないからです。

アプリケーションを作成するために必要となるもの

Facebook API

Facebook アプリケーションの開発者は、次の 2 つの API を使用することができます。

  • ブラウザー API
  • Web サーバー API

ブラウザー API

Facebook の標準 API を使用する場合には、ユーザーがブラウザーからアプリケーションにログインし、ブラウザーで実行される JavaScript コードからほとんどのアクションを実行することが想定されています。

セキュリティーの観点では、これは優れたメカニズムと言えます。なぜなら、Facebook でホストされているライブラリーがユーザーに直接、許可を求めることができるためです。しかし、このチュートリアルのアプリケーションには有効なメカニズムではありません。このサンプル・アプリケーションは、ログインしていないユーザーの代理を務める必要があるためです。

Web サーバー API

サーバーから直接使用できる API もあります。Web サーバー API は HTTP リクエスト・ベースの REST を使用することから、やや使いにくいとは言え、最近のサーバー・プログラミング環境からは確実に使用することができます。このシリーズでは、メイン・インターフェースとして Web サーバー API を使用します。

ステップ 1. Node.js Bluemix アプリケーションを作成する

まず始めに、Bluemix にログインして、Bluemix で Node.js アプリケーションを作成します。ヘルプが必要な場合は、developerWorks の IBM Bluemix ページを参照してください。

このチュートリアルでは、デフォルトの Node.js スターター・アプリケーションに変更を加えていくことを前提とします。

ステップ 2. Facebook アプリケーションを作成する

以下の手順に従って、Facebook でアプリケーションを作成してください。

  1. Facebook にログインします。
  2. ブラウザーで Facebook アプリケーション・ダッシュボードを開いて、「Add a New App (新規アプリの追加)」をクリックします。
  3. 「Website (ウェブサイト)」を選択します。
  4. アプリケーションに名前を付けます (私のアプリケーションには「JokeSender」という名前が付いています)。名前を入力したら、「Create New Facebook App ID (新規 Facebook アプリ ID の作成)」をクリックします。
  5. カテゴリーを選択して、「Create App ID (アプリ ID の作成)」をクリックします。
  6. サイトの URL とモバイル・サイトの URL として、ステップ 1 で作成したアプリケーションのメイン URL を入力します。私の URL は、http://using-facebook.mybluemix.net/ です。
  7. 以下のような JavaScript コードが表示されます。このコードをコピーして、どこかに貼り付けます。このコードは、サブステップ 9 で必要になります。
    		<script>
    			window.fbAsyncInit = function() {
    				FB.init({
    					appId      : '833955603319525',
    					xfbml      : true,
    					version    : 'v2.3'
    				});
    			};
    
    			(function(d, s, id){
    				var js, fjs = d.getElementsByTagName(s)[0];
    				if (d.getElementById(id)) {return;}
    				js = d.createElement(s); js.id = id;
    				js.src = "//connect.facebook.net/en_US/sdk.js";
    				fjs.parentNode.insertBefore(js, fjs);
    			}(document, 'script', 'facebook-jssdk'));
    		</script>
  8. 「Next (次へ)」をクリックします。
  9. 開発環境で、先ほどコピーしたコードに「Like (いいね!)」ボタンの HTML を追加します。
    <div class="fb-like", data-share="true" data-width="450", data-show-faces="true">
    </div>
  10. サブステップ 9 の作業が終わった後のコードは、以下のようになります。public/index.html ファイルの本体をこのコードで置き換えます。appId には、ここに示されている値ではなく、皆さんのアプリケーションに固有の値を使用してください。
    <script>
    	window.fbAsyncInit = function() {
    		FB.init({
    			appId      : '833955603319525',
    			xfbml      : true,
    			version    : 'v2.3'
    		});
    	};
    
    	(function(d, s, id){
    		var js, fjs = d.getElementsByTagName(s)[0];
    		if (d.getElementById(id)) {return;}
    		js = d.createElement(s); js.id = id;
    		js.src = "//connect.facebook.net/en_US/sdk.js";
    		fjs.parentNode.insertBefore(js, fjs);
    	}(document, 'script', 'facebook-jssdk'));
    </script>	
    <div class="fb-like", data-share="true" data-width="450", data-show-faces="true">
    </div>
  11. アプリケーションを実行します。「Like (いいね!)」ボタンが表示されていることを確認してください。

Bluemix サーバー・エラーが発生した場合の対処方法 (Eclipse を使用している場合)

たまに、Bluemix と Ecipse が同期しなくなることがあります。その場合、アプリケーションをアップロードして実行しようとすると、エラーになります。

  1. クイック・アクセス領域に、「server」と入力します。
  2. 「Views (ビュー)」 > 「Servers (サーバー)」の順に選択します。
  3. 「IBM Bluemix」を右クリックして、「Update Password (パスワードの更新)」を選択します。
  4. 両方のフィールドに自分の IBM パスワードを入力し、「OK」をクリックします。
  5. パスワードを復元するかどうかを尋ねるプロンプトに対しては、「No (いいえ)」をクリックします。
  6. アプリケーションを再び実行してみます。
  7. 実行できない場合は、「Servers (サーバー)」ビューで Bluemix を展開してアプリケーションを削除して、最初からやり直してください。

ステップ 3. Facebook でアプリケーション・ログインを有効にする

次のステップは、ユーザーが Facebook を介してアプリケーションにログインできるようにすることです。

  1. Facebook アプリケーション・ダッシュボードに戻ります。
  2. 作成したアプリケーションを選択します。
  3. 「Show (表示)」をクリックし、App ID (アプリ ID) と App Secret (アプリ・シークレット) の両方をメモします。
  4. 「Advanced (詳細)」タブをクリックします。
  5. 「OAuth Settings (OAuth 設定)」という見出しの下で、「Client OAuth Login (クライアント OAuth ログイン)」と「Embedded browser OAuth Login (組み込みブラウザー OAuth ログイン)」の両方を「Yes (はい)」に設定します。
  6. 「Valid OAuth redirect URIs (有効な OAuth リダイレクト URI)」フィールドに、以下の内容を入力します。
    https://<application name>.mybluemix.net/index.html

    通常は、アプリケーションへのアクセスを暗号化するのが賢明です。https だけを指定することで、アプリケーションは、暗号化されていないログインをサポートしなくなります。
  7. 「Save Changes (変更内容を保存)」をクリックします。

ステップ 4. 初期段階のアプリケーションを実行する

次のステップでは、アプリケーションの Web ページから、ユーザーがクライアント・サイドの JavaScript インターフェースを使用して Facebook にログインするようにします。ここからは、Angular や Bootstrap などの数々のライブラリーも使用することになります。デフォルトのアプリケーションに加える変更は、すべてを一覧にするには多すぎるので、変更後のコードを記載し、説明をコメントに含めるようにします。

  1. public/scripts という名前の新規ディレクトリーを作成します。
  2. public/images ディレクトリーと public/stylesheets ディレクトリーを削除します。
  3. public/scripts/datamodel.js という名前のファイルを新規に作成して、以下のコードを含めます。
    // Angular data model
    
    // Create a new Angular application
    var myApp = angular.module("myApp", []);
    
    
    // Define the controller for Facebook interaction. The
    // controller also contains scope, which includes the
    // data model.
    myApp.controller("facebookCtrl", function($scope) {
    	
    	// For now, have one string in the data model,
    	// fbStatus. It will contain the status of the
    	// Facebook communication
    	$scope.fbStatus = ";
    });
    
    
    // This function sets the fbStatus variable to the parameter.
    // It is useful to have this function so that the rest of
    // the JavaScript code would be able to set the value of
    // $scope.fbStatus without having to know anything about
    // Angular.
    var setFacebookStatus = function(status) {
    	var scope = angular.element($("#facebookCtrl")).scope();
    	
    	// scope.$apply takes a function because of re-entrancy.
    	// The browser may not be able to handle changes in the
    	// scope variable immediately, in which case the function
    	// will be executed later.
    	scope.$apply(function() {
    		scope.fbStatus = status;
    	});
    };
  4. public/scripts/facebook.js という名前のファイルを新規に作成して、以下のコードを含めます (FB.init 内の appID は、忘れずに皆さんのアプリケーションに固有の値に変更してください)。
    // This function is called during initialization and after
    // the user clicks the logon button.
    function checkLoginState() {	
    	// Ask Facebook about the currently logged in user, and
    	// call statusChangeCallback when you get the response.
    	FB.getLoginStatus(function(response) {
    		statusChangeCallback(response);
    	});
    }
    
    
    // This function is called by FB.getLoginStatus after it gets the
    // results.
    function statusChangeCallback(response) {
    	// The response object is returned with a status field that lets the
    	// app know the current login status of the person.
    	//
    	// The response object can be found in the documentation
    	// for FB.getLoginStatus().
    	
    	if (response.status === 'connected') {
    		// Logged into your app and Facebook.
    		loggedOn();
    	} else if (response.status === 'not_authorized') {
    		setFacebookStatus("Please authorize this application");
    	} else {
    		// Not logged into Facebook
    		setFacebookStatus("Please log into Facebook");
    	}
    }
    
    
    // Facebook API initialization function. This function is called
    // after the Facebook API code is downloaded from Facebook's site.
    window.fbAsyncInit = function() {
    	
    	// Initialize the Facebook SDK. Make sure to change appId to your
    	// value.
    	FB.init({
    		appId      : '833955603319525',
    		cookie     : true,  // enable cookies to allow the server to access
    							// the session
    		xfbml      : true,  // parse social plugins on this page
    		version    : 'v2.2' // use version 2.2
    	});
    
    	// Check logged in status
    	checkLoginState();
    };
    
    
    // This code loads the Facebook SDK asynchronously. This way, the page
    // is displayed as soon as possible, and the Facebook elements are added
    // later when they are available.
    (function(d, s, id) {
    	var js, fjs = d.getElementsByTagName(s)[0];
    
    	// If there is already a facebook-jssdk element, do nothing.
    	// It means the Facebook SDK is already available.
    	if (d.getElementById(id)) return;
    	
    	// Create an element for the Facebook SDK and call it
    	// facebook-jssdk. Put the element into the variable
    	// js
    	js = d.createElement(s); js.id = id;
    	
    	// This is the script to get the source for the SDK.
    	js.src = "//connect.facebook.net/en_US/sdk.js";
    	
    	// Insert the script before the first element in the
    	// document
    	fjs.parentNode.insertBefore(js, fjs);
    }(document, 'script', 'facebook-jssdk'));
    
    
    
    
    // This function is called when we KNOW the user is logged on.
    var loggedOn = function() {
    	setFacebookStatus("You're in");
    }
  5. public/index.html ファイル内の head タグの主な目的は、この Web ページで使用するすべてのライブラリーを参照することです。したがって、以下のコードで置き換えます。
    <title>Using Facebook Sample App</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    	
    <!--  Use jQuery  -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js">
    </script>
    	
    <!-- Use the Bootstrap theme -->
    <link rel="stylesheet"
    	href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" />
    <link rel="stylesheet"
    href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap-theme.min.css" />
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js">
    </script>
    
    <!--  Use the Angular library  -->
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js">
    </script>
    	
    <!-- Use the application's scripts -->
    <script src="scripts/facebook.js">
    </script>
    <script src="scripts/datamodel.js">
    </script>
  6. public/index.html ファイル内の body タグの内容を以下のコードで置き換えます。
    <!-- Everything in the body is part of the myApp
    			Angular application -->
    <body ng-app="myApp">
    <!--  Everything in this div has access to the scope variables
    	  in facebookCtrl (defined in datamodel.js)  -->
    	<div ng-controller="facebookCtrl" id="facebookCtrl">
    	
    		<!-- The Facebook login button. The scope attribute
    		identifies the permissions we want the user
    		to give us  -->
    		<fb:login-button scope="public_profile,email"
    			onlogin="checkLoginState();">
    		Login
    		</fb:login-button>
    
    		<h3>Status</h3>
    		
    		<!-- When inside an Angular scope, such as facebookCtrl,
    		code encased in {{ }} is evaluated as JavaScript with
    		access to the scope variable ($scope). In this case,
    		it returns the value of $scope.fbStatus, which is
    		determined by setFacebookStatus().  -->
    		{{fbStatus}}
    	</div>
    </body>
  7. アプリケーションを実行します。ユーザーが Facebook にログインしているかどうか、そしてユーザーがアプリケーションに対して許可を与えるかどうかによってステータスが変わることを確認します。アプリケーションに許可が与えられない場合にどうなるかを確認するには、Facebook でアプリケーションに対する許可を取り消してください。

ステップ 5. HTTPS にリダイレクトする

HTTP などの暗号化されないチャネルで認証を受け付けるのは、賢明なことではありません。今のところ、ユーザーが HTTP でログインしようとすると、そのリクエストは Facebook で拒否されます。

エラー・メッセージのスクリーンショット
エラー・メッセージのスクリーンショット

単にリクエストを拒否するよりも、アプリケーションが自動的にブラウザーを HTTPS にリダイレクトするようにしたほうが、はるかにユーザー・フレンドリーです。この機能を追加するには、app.js ファイルを開き、app.use 関数を呼び出している箇所を以下のコードで置き換えます。app.get 関数を呼び出している箇所が存在する場合は、その部分を削除してください。他の JavaScript ファイルとは対照的に、app.js ファイルはサーバーで実行されることに注意してください。以下のコードが HTTP リクエストに応答して、app.js ファイルをブラウザーに送信します。

app.get('/*', function(req, res) {	
	// If the forwarded protocol isn't HTTPS, send a redirection
	if (req.headers["x-forwarded-proto"] != "https")
		// Always redirect to /. This is a single-page application
		// meaning users have only one URL they need to access
		// directly. Any changes on that page to display information
		// will be handled by client-side JavaScript
		res.redirect("https://" + req.headers.host + "/");
	else {  // Actually serve the request file
		
		// Get the path. req.params[0] is the first wildcard in the
		// file path. In the case, the file path in the get command
		// is "/*", so it is everything after the initial slash.
		//
		// If there is nothing there, then the user just asked
		// for the website, and we need to insert the index.html
		// file.
		var path = req.params[0] ? req.params[0] : 'index.html';
		
		// Actually send the file from the public directory.
		res.sendFile(path, {root: './public'});
	}		
});

リクエスト・フィールドを調べる

私は最初、使用する値が req.headers["x-forwarded-proto"] であることを知りませんでした。これを調べるために、まず、以下の行を関数に含めました。

console.log(Object.keys(req));

その結果、(Eclipse で使用可能な) コンソールに、req オブジェクトに含まれるフィールドのリストが表示されました。そのリストでヘッダーを調べることで、オブジェクトに含まれるフィールドを確認しました。

console.log(Object.keys(req.headers));

x-forwarded-proto を調べなければならない理由は、Bluemix アプリケーションは常に HTTP でリクエストを受け取るからです。インターネットと、HTTPS トンネルの終端となるアプリケーションを結ぶパスには、DataPower アプライアンスがあります。したがって、プロトコルに頼って HTTP と HTTPS を区別することはできません。

まとめ

これで、Bluemix 上でアプリケーションを実行するブラウザーに Facebook からログインできるようになりました。まだエキサイティングなログイン・メカニズムとは言えませんが、このシリーズの次回のチュートリアル「ユーザー情報をサーバーに保管する」では、このログイン・メカニズムを使用して、Facebook から取得したユーザー情報をサーバーに保管する方法を説明します。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Cloud computing, Web development
ArticleID=1012673
ArticleTitle=Bluemix と MEAN スタックを使用して自動投稿 Facebook アプリケーションを作成する: パート 1 Facebook をログイン・ソースとして使用する
publish-date=08202015