Facebookへのログイン
Facebook を ID プロバイダーにしたユーザー認証については、AWS の以下にドキュメントがあります。
このドキュメントを参考に、以下の手順で、Facebook のログインを実装します。
- Facebook にアプリケーションを登録
- AWS Cognito の Identity pool に、外部プロバイダーとしてFacebook を登録
- Facebook SDK を使用してログイン処理を実装
Facebookにアプリケーションを登録
- Facebook のアカウントを持っていなければ、Facebook にアカウントを作成します。
- facebook for developersの、「新しいアプリを追加」からアプリケーションを登録します。
- 前回の、Node.js のサーバーの開発環境をそのまま使用してでアプリケーションを作成するため、Facebook の「設定」画面の「Add Platform」で、Website を追加します。
- サイト URL には、ローカルPC上のNode.js のサーバーのURL の、 http://localhost:3000 を指定します。
- Facebook では、Websiteを一つしか登録できないため、サーバー上にアッブロードして動作させる時は、サイト URL をサーバーのURLに変更してください。
AWS Cognito の Identity pool に、外部プロバイダーとしてFacebook を登録
- AWS Cognito コンソールから、「Create new Identity pool」を選択し、Cognito の identity pool を作成します。
- Unauthenticated role, Authenticated role は、今回は使用しないのでデフォルトのままでかまいません。
- すでに作成済みの場合は、「Edit Identity pool」でIdentity poolの設定を変更します。
- Authentication providers で Facebookタブを選択して、Facebook の AppIDを入力します。
Facebook の AppID は、facebook for developersの設定ページで「アプリID」で確認できます。
Facebook SDK を使用してログインを実装
- Facebook 関連は、facebook.js にまとめました。
checkLoginStatus 関数で、FB.getLoginStatus を呼び出しています。
この関数のコールバックで、ログインしているかどうかを調べています。
(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/ja_JP/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
window.fbAsyncInit = function() {
console.log('fbAsyncInit');
FB.init({
appId : '<facebook AppID>',
cookie : true, // enable cookies to allow the server to access
// the session
xfbml : true, // parse social plugins on this page
version : 'v2.8' // use version
});
if (g_fb) {
g_fb.checkLoginStatus();
} else {
console.log("g_fb is null");
}
};
function Facebook() {
var m_logInController = null;
this.init = function (logInController) {
console.log('facebook.init');
m_logInController = logInController;
};
this.checkLoginStatus = function() {
FB.getLoginStatus(function(response) {
statusChangeCallback(response);
});
}
function statusChangeCallback(response) {
console.log('statusChangeCallback');
console.log(response);
// The response object is returned with a status field that lets the
// app know the current login status of the person.
// Full docs on the response object can be found in the documentation
// for FB.getLoginStatus().
if (response.status === 'connected') {
// Logged into your app and Facebook.
console.log('Welcome! Fetching your information.... ');
FB.api('/me', function(response) {
console.log('Successful login for: ' + response.name);
document.getElementById('fb_status').innerHTML =
'Thanks for logging in, ' + response.name + '!';
});
// Check if the user logged in successfully.
if (m_logInController) {
m_logInController.logIn(response.authResponse);
}
} else if (response.status === 'not_authorized') {
// The person is logged into Facebook, but not your app.
document.getElementById('fb_status').innerHTML = 'Please log ' +
'into this app.';
if (m_logInController) {
m_logInController.logOut();
}
} else {
// The person is not logged into Facebook, so we're not sure if
// they are logged into this app or not.
document.getElementById('fb_status').innerHTML = 'Please log ' +
'into Facebook.';
if (m_logInController) {
m_logInController.logOut();
}
}
}
}
- ログインボタンの配置
- index.html に、Facebook のログインボタンを配置します。
<div class="fb-login-button" data-max-rows="1" data-size="medium" data-show-faces="false" data-auto-logout-link="true" onlogin="g_fb.checkLoginStatus();"></div>
<div id="fb_status"></div>
- ログインボタンを押すと、ログイン画面が表示されます。
- ユーザー名、パスワードを入力すると、 onlogin="g_fb.checkLoginStatus(); で指定した、facebook.js のcheckLoginStatus 関数が呼び出されます。
- ログイン、ログアウトの状態が変わったときに、facebookクラスのinit関数で指定したコールバックオブジェクトで通知を受けます。
この例では、ログインすると、g_cognito.logIn、ログアウトすると、g_cognito.logOutが呼び出されます。
- index.html
g_fb = new Facebook();
g_fb.init(g_cognito);
- facebook.js
function statusChangeCallback(response) {
...
if (response.status === 'connected') {
...
if (m_logInController) {
m_logInController.logIn(response.authResponse);
}
} else if (response.status === 'not_authorized') {
...
if (m_logInController) {
m_logInController.logOut();
}
} else {
...
if (m_logInController) {
m_logInController.logOut();
}
}
Facebookログインからcognitoの認証
- FB.getLoginStatus のコールバック関数の引数のresponse.authResponse.accessToken を使用して、cognitoの認証を行います。
- AWS.CognitoIdentityCredentials(params) のパラメータに、Cognitoのpool Id, roleのarn, facebook のaccessTokenを指定した後、AWS.config.credentials.get を呼び出すと、Confignito Identity Id を取得できます。
- AWS.config に Cognito の credential を格納します。
- この credential の情報をこの後、DYnamoDB のアクセスで使用します。
- cognito.js
this.logIn = function(credential) {
var params = {
IdentityPoolId: m_identityPoolId,
RoleArn: m_roleArn,
Logins: {
'graph.facebook.com' : credential.accessToken
}
};
AWS.config.credentials = new AWS.CognitoIdentityCredentials(params);
AWS.config.credentials.clearCachedId(); // これがないと、ユーザー切り替え時に。get が失敗する。
// Obtain AWS credentials
AWS.config.credentials.get(function(err){
if (err) {
console.log("credentials.get error:" + err);
if (m_logInCallback) {
m_logInCallback(false, "credentials.get error code:\n" + JSON.stringify(err, null, 2));
}
} else {
console.log("Cognito Identity Id: " + AWS.config.credentials.identityId);
if (m_logInCallback) {
m_logInCallback(true, AWS.config.credentials.identityId);
}
}
});
};
Cognitoを使用した、DynamoDB のアクセス制限
Cognito認証したユーザーのみが、DynamoDBの自分のnoteデータにアクセスできるようにアクセス制限を行います。
アクセス制限は、AWS IAM コンソールで、ポリシーを作成し、ロールに割り当てることで実現します。
IAM ロールのポリシーで以下を行います。
- DynamoDB のテーブルに対して使用できるAPIの制限
- CognitoのIdentity Id を使用した、DynamoDBのレコードのアクセス制限
関連する AWS のドキュメントは、以下にあります。
- ポリシーの作成
- AWS IAM コンソールのポリシー画面で、「ポリシーの作成」をクリック
- 独自のポリシーを作成の「選択」をクリックして、以下をポリシードキュメントに貼りつけ、ポリシー名を入力して、「ポリシーの作成」をクリック
- ポリシー名を、「mynote-dynamo-cognito」とします。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:BatchGetItem",
"dynamodb:BatchWriteItem",
"dynamodb:DeleteItem",
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:Query",
"dynamodb:UpdateItem"
],
"Condition": {
"ForAllValues:StringEquals": {
"dynamodb:LeadingKeys": [
"${cognito-identity.amazonaws.com:sub}"
]
}
},
"Resource": [
"<DynamoDBのテーブルのARN>"
]
}
]
}
- 次に、AWS IAM コンソールのロール画面で、「新しいロールの作成」をクリックします。
- ロール名入力でロール名を、mynote-cognito-role とします。、
- 「ロールタイプの選択」で、「IDプロバイダ用のアクセスロール」をチェックして、「ウェブ ID プロバイダにアクセスを付与」を選択します。
- ID プロバイダに「Amazon Cognito」を選択、IDプールのID に、Cognito の Identity pool のID を入力して、「次のステップ」をクリックします。
- 「ロールの信頼の確認」では、「次のステップ」クリックします。
- ポリシーのアタッチ
作成したポリシー(mynote-dynamo-cognito)を選択して、「次のステップ」をクリックします。
- 確認画面で、「ロールの作成」をクリックします。このとき、作成したロールのロールARNを控えておきます。
DynamoDB アクセス
ここまでで、Cognitoに認証での、DynamoDBの制限指定ができたので、DynamoDBをアクセスするコードを作成します。
Node.js を使用したサーバーでは、DynamoDB のアクセスをDbControllerServer.js で Node.js のサーバー経由で行っていましたが、今回は、dbControllerClientCognito.js というクラスから、 DynamoDB に直接アクセスします。
dbControllerClientCognito.js は、DbCOntrollerServer.js と同じインターフェイスにしたので、Node.js 版の index.html の DbControllerServer をDbControllerClientCognito に変えるだけで、DynamoDBのアクセス方法を変更できます。
DbControllerClientCognito.js で、DynamoDB にアクセスするコードは、Node.js で実装したコードと基本的に同じです。
AWS.DynamoDB をnew で生成する時に、AWS.config.credentials が゛、m_dynamoDB.config にコピーされます。
この config の情報が、mynote-dynamo-cognito のポリシーを指定したIAM role で参照され、DynamoDBのアクセスが、Cognitoで認証した IdentityId のデータしかアクセスできないように制限されます。
- DynamoDB の初期化
dbControllerClientCognito.js
var m_dynamoDB = null;
var m_dynamodbDoc = null;
var region = "<リージョン名>";
var endpoint = "https://dynamodb." + region + ".amazonaws.com";
var table = "Notes";
this.init = function() {
};
this.open = function() {
if (m_dynamoDB === null) {
console.log('open');
m_dynamoDB = new AWS.DynamoDB();
m_dynamoDB.config.update({
region: region,
endpoint: endpoint
});
m_dynamodbDoc = new AWS.DynamoDB.DocumentClient({service:m_dynamoDB});
}
};
- Noteリストの取得の例
dbControllerClientCognito.js
this.getNoteList = function(userId, callback) {
console.log("userId", userId);
var params = {
TableName : table,
KeyConditionExpression: "user_id = :userId",
ExpressionAttributeValues: {
":userId": userId
}
};
console.log("query param ", JSON.stringify(params));
m_dynamodbDoc.query(params, function(err, data) {
if (err) {
var msg = JSON.stringify(err, null, 2);
console.log("Unable to query. Error:", msg);
callback(false, null, "faild to qurey:\n" + msg);
} else {
var notes = new Array();
console.log("Query succeeded.");
var itemSize = data.Items.length;
if (itemSize == 0) {
callback(true, "new", notes);
} else {
var replyCount = 0;
data.Items.forEach(function(item) {
replyCount++;
console.log(" -", JSON.stringify(item));
var note = {
user_id : item.user_id,
date_time : item.date_time,
text : item.text
};
notes.push(note);
if (replyCount >= itemSize) {
callback(true, "new", notes);
}
});
}
}
});
};
Cognito の未認証ユーザーでアクセス制限
AWS Cognito には、未認証ユーザーも使用できるようになっています。
Facebook 認証で、ログオフしたときに、この、Cognitoの未認証ユーザーでDynamoDBをアクセスをするようにします。
Identity Id を指定しないで、AWS.CognitoIdentityCredentials(params); で、credential を生成すると、毎回違ったIdentity Id で未認証ユーザーが作成されるので、一度作成した IdentityId をLocal Storage に保存しておいて2回目以降は、保存したIdentity Idでユーザーを作成するようにします。
未認証ユーザーでの認証
- cognito.js
this.logOut = function() {
if (m_unauthUserId !== null) {
var params = {
IdentityPoolId: m_identityPoolId,
RoleArn: m_roleArn,
IdentityId: m_unauthUserId
};
AWS.config.credentials = new AWS.CognitoIdentityCredentials(params);
// Obtain AWS credentials
AWS.config.credentials.get(function(err){
if (err) {
console.log("credentials.get error:" + err);
if (m_logOutCallback) {
m_logOutCallback(false, "credentials.get error code:\n" + JSON.stringify(err, null, 2));
}
} else {
console.log("Unauthenticated Cognito Identity Id: " + AWS.config.credentials.identityId);
if (m_logOutCallback) {
m_logOutCallback(true, AWS.config.credentials.identityId);
}
}
});
} else {
var params = {
IdentityPoolId: m_identityPoolId,
RoleArn: m_roleArn
};
AWS.config.credentials = new AWS.CognitoIdentityCredentials(params);
AWS.config.credentials.clearCachedId(); // これがないと、ユーザー切り替え時に。get が失敗する。
// Obtain AWS credentials
AWS.config.credentials.get(function(err){
if (err) {
console.log("credentials.get error:" + err);
if (m_logOutCallback) {
m_logOutCallback(false, "credentials.get error code:\n" + JSON.stringify(err, null, 2));
}
} else {
console.log("Unauthenticated Cognito Identity Id: " + AWS.config.credentials.identityId);
if (m_logOutCallback) {
m_logOutCallback(true, AWS.config.credentials.identityId);
}
}
});
}
};
Local storage でのid の保存
未認証ユーザーのIdentity Id を保持するために、Local Storage を使用します。
storage.js がLocal Storage のためのクラスです。
- myNoteDoc.js
var StorageName = 'test.mynote-01';
var StorageVersion = '1.0';
var StorageKey_UNAUTH_USER_ID = 'unauth_user_id';
- index.html
function initApp() {
console.log('initApp');
AWS.config = new AWS.Config({
region : g_region
});
g_cognito = new Cognito();
g_cognito.init(logInCallback, logOutCallback);
g_fb = new Facebook();
g_fb.init(g_cognito);
g_storage = new Storage(StorageName, StorageVersion);
g_storage.load();
g_doc = new MyNoteDoc();
g_doc.init();
g_doc.setStorage(g_storage);
g_view = new MyNoteView();
g_view.init(g_doc);
g_dbControllerClientCognito = new DbControllerClientCognito();
g_dbControllerClientCognito.init();
g_doc.setDbController(g_dbControllerClientCognito);
setStatusFromStorage();
}
function setStatusFromStorage() {
var unauthUserId = g_storage.getItem(StorageKey_UNAUTH_USER_ID);
if (unauthUserId !== null) {
g_cognito.setUnauthUserId(unauthUserId);
}
}
ローカルでの動作確認
Node.js を使用したサーバーの時と同様に、ローカルPCでの動作確認は、Node.jsでローカルサーバーを起動して行います。
c:\projects\mynote>set DEBUG=mynote:* & npm start
> mynote@0.0.0 start c:\projects\mynote
> node ./bin/www
mynote:server Listening on port 3000 +0ms
http://localhost:3000 でローカルPCのサーバーにアクセスできます。
サーバー環境の構築
Node.js を使用したサーバーは、AWS EC2 のインスタンスで、Node.js のサーバーからDynamoDBのアクセスを行いましたが、今回は、クライアント側でDynamoDB のアクセスを行うため、AWS EC2 のインスタンスを起動する必要はありません。
EC2 のインスタンスも停止してかまいません。
サーバー環境は、AWS S3 に WebSite用のバケットを生成し、そこに、index.html等のファイルを配置します。
WebSite の作成方法は、AWS のチュートリアルが参考になります。
以下、S3 での配置手順です。
S3 にバケットを作成
- AWS S3 コンソールで、「バケットを作成」をクリックして、バケットの作成を開始します。
- 「バケットの作成 - パケット名とリージョンの選択」画面で、バケット名とリージョンを指定します。
- 作成したバケットを選択し、「プロパティ」をクリックし、プロパティの表示から、「アクセス許可」を選択し、「バケットポリシーの追加」をクリックします。
- バケットポリシーエディターで以下のポリシーを入力し、「保存」をクリックします。
{
"Version":"2012-10-17",
"Statement": [{
"Sid": "Allow Public Access to All Objects",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::作成したバケット名/*"
}
]
}
- アクセス許可の「保存」をクリックして、設定内容を確定します。
WebSiteのアップロード
- AWS S3 コンソールで、作成したバケットを選択します。
作成したばかりなので中身は空のはずです。
- 「アクション」→「アップロード」を選択します。
- エクスプローラ上で、mynote/public 内のファイル、ディレクトリをすべて選択して、「アップロード - ファイルとフォルダの選択」画面にドラッグアンドドロップし、「アップロードの開始」をクリックします。
- アップロードが完了したら、パケットの「プロパティ」をクリックし、「静的ウェブサイトホスティング」を開きます。
- 「ウェブサイトのホスティングを有効にする」を選択し、「インデックスドキュメント」に index.html を指定の後、「保存」をクリックします。
- バケットのindex.html をクリックすると、プロパティのリンクにURLが表示されます。
このURLをクリックすると、WebSiteとして、アプリケーションを起動できます。
また、AWS のチュートリアルにあるように、ドメイン名を登録すると便利だと思います。
AWS S3のURLは、https: なので、index,html 内のリンクに、http: があるとロード時に "Mixed Content:" エラーとなります。
"http://xxx" の記述は、"//xxx"とすることでエラーはなくなります。
Facebook のアプリケーションで登録しているWebSite の URL も AWS S3 のURLに変更する必要があります。
ただし、Facebook では、WebSite の URL をドメインで判定しているようなので、AWS S3 の同じドメインのURLからでも、アクセス可能となるようです。
最終的に、S3 でWebSite を公開する場合は、別途ドメインを取得するのがよいと思います。
サンプルコードの実行方法
- mynote-sample-cognito.zip をダウンロードしてください。
- ダウンロード後、zip ファイルを展開してください。
- zipファイルの中には、Node.js のmodule は含まれていないので、npm install で、各 module のインストールが必要です。
c:\projects>cd mynote
c:\projects\mynote>npm install
- mynote\public\index.html のリージョン名を指定してください。
var g_region = '<リージョン名>';
- mynote\public\js\cognito.js のIAM Role の ARN, Cognito Identity pool Id を指定してください。
var m_roleArn = '<Role ARN>';
var m_identityPoolId = '<cognito identity pool id>';
- mynote\NoteCreateTable.js のリージョン名を指定してください。
var region = "<リージョン名>";
次回
次は、
Lambdaを使用したアプリケーション作成でAWS Lambda でDynamoDBののアクセスを行います。