S3+AWS SDKで動的Webサイトホスティング

 API AWS-SDK JavaScript Facebook OAuth2.0
2014.06.13

S3+Jekyllで静的Webサイトの記事を書いていたら、Mediumにおもしろい記事が載りました。
 
Dynamic Websites Using the AWS SDK for JavaScript in the Browser
内容に従い、実際に動かした過程をログに残します。

 

この方法を使えば

だけでなく、静的サイトでは難しい

といったこともやれそうです。
使ってみましょう。
 

Part 1: Facebookアカウントでユーザ認証

AWS SDK for JavaScript in the Browser(以下、SDK)は認証方法が独特です。
 
このSDKで作るアプリはサイト訪問者のブラウザ上で動くので、AWSのAPIを使おうにも
AWSの秘密鍵をそのままJavaScriptに書くわけにはいかない事情があります。
そこで
【AWS発表】 AWS IAM が Amazon、Facebook、GoogleのID連携をサポート
でユーザを認証し、AWSへのアクセスを許可することになります。
 
 
Part1では “サイト訪問者をFacebookで認証する” Webサイトを作ります。
作業は5ステップです。
 
 

1.1. Facebookにアプリケーションを登録

 

元記事は米Amazonアカウントでの認証でしたが・・
ここは記事に従わず、日本人により身近なFacebookを使います。
 

まず 開発者ページ: https://developers.facebook.com/apps をひらきます。
 
初めてFacebookにアプリ登録する場合はまず、開発者登録します。
“Register Now” をクリックすると開発者になる?と聞かれるので、なります!と登録。

 
改めてhttps://developers.facebook.com/appsにもどり、”Create a New App” をクリック。

 
DisplayNameと Namespaceを入力して “アプリケーションを作成” をクリックします。

 
アプリケーションが登録されたら、アプリケーションID(App ID) をメモします。
さらに、”Add Platform” を押して、アプリの種類を登録します。

 
今回のアプリは、Websiteです。

 
公開するドメイン名を “サイトURL” と “App Domains” に指定します。
(ローカルで開発するなら、localhostも指定できます)
メールアドレスも記入し保存します。ここ重要です。
これを省くと、アプリを一般公開できません。

 
Status & Review画面をひらき、公開しますか?をYESに変更しましょう。

 
最後に、こういう話 もあるので、できれば OAuthのredirect URIは制限しておきましょう。
SettingsのAdvancedタブ “Valid OAuth redirect URIs” に公開予定のURLを記入し保存。
Facebookの設定はこれで終わりです。けっこうめんどう!

 
 
 

1.2. DynamoDBにテーブルを作る

 

次に1.2では DynamoDBに “サイト閲覧者のブラウザが何か”
というデータを保存するための領域(テーブル)を作成します。
 

https://console.aws.amazon.com/dynamodb/home をひらき、
“Create Table” をクリック。

 
テーブル名を “browsers” としましょう。
プライマリキー種別(Primary Key Type)は “Hash and Range” を選び
ハッシュキー(Hash Attribute Name)はStringで “browser“、
レンジキー(Range Attribute Name)もStringで “version” を指定します。

 
Add Indexesページはそのまま次へ。

 
Provisioned Throughput Capacityページでは秒間あたりの読み書き回数、
つまりこのDBの性能を指定しますが、今回はサンプルなのでそのまま次へ。

 
Throughput Alarmsでは、一定の負荷を超えたらメール送信といった設定ができますが
今回は使わないので “Use Basic Alarms” のチェックを外して次へ。

 
値段を確認します。
問題なければ、テーブルを “Create” しましょう。

 
テーブル一覧画面にもどったら、いま作った browsersテーブルの詳細を開き
Amazon Resource Nameの値(arnから始まる文字) をメモします。

 
 
 

1.3. AWS Role for Web Identity Federationを作る

 

続いてSDKを通じてAWSにアクセスするための、”ロール”を作ります。
ロールとは、例えば “Facebookで認証されたユーザにこのAWSリソースを使わせる”
といった、特定リソースへの特定操作を許可、もしくは拒否するためのものです。

 
https://console.aws.amazon.com/iam/home をひらき、
“Roles”、そして “Create New Role” をクリック。

 
Roleの名前は、今回のサンプルのためとわかるものにしましょう。

 
Select Role Typeでは、”Role for Identity Provider Access” を選び、
かつ “Grant access to web identity provider” を選択します。

 
ユーザ認証をしてもらう先(Identity Provider)が “Facebook” なのを確認、
手順1.1で控えておいたアプリケーションIDを指定したら、次へ。

 
Verify Role Trustでは、ロールが「指定したFacebookアプリに承認された人」
に対して動作するよう、画像のように正しく定義されていることを確認。次へ。

 
続いて、その人に何を許可するか、を定義します。
Set Permissionsで “Policy Generator” を選び、Selectを押して次へ。

 
Edit Permissionsでは
「DynamoDB、browsersテーブルへのすべてのアクセスを許可する」ように
Effectは Allow、AWS Serviceには Amazon DynamoDB
Actionsに All Actions Selectedを指定し
手順1.2でメモしておいたAmazon Resource Nameの値をARN欄にコピペします。

 
内容に問題がなさそうなことを確認して、次へ。

 
最後に Role ARNの値(arnから始まる文字)をメモして、”Create Role” しましょう。

 
 
 

1.4. HTMLを書く

 

以下のソースコードをindex.htmlとしてローカルに保存します。
( awsRoleArnと facebookAppIdの値はそれぞれ
手順1.3と1.1でメモしておいたものに置き換えてください)

<!DOCTYPE html>
<html lang="ja">
<head>
  <title>AWS Javascript Browser SDK Example</title>
  <meta charset="UTF-8">
  <script src="https://sdk.amazonaws.com/js/aws-sdk-2.0.16.min.js"></script>
</head>
<body>

  <fb:login-button scope="public_profile" onlogin="check();">
  </fb:login-button>

  <script type="text/JavaScript">
  // IAM Role that you created for Login With Facebook
  var awsRoleArn = 'arn:aws:iam::123456789:role/example',
      awsRegion = 'ap-northeast-1',
      facebookAppId = '0123456789';

  window.fbAsyncInit = function () {
    FB.init({appId : facebookAppId, cookie : true,
        xfbml : true, version : 'v2.0'});
  };
  function check() {
    FB.getLoginStatus(function (response) {
      if (response.status !== 'connected') return;

      AWS.config.credentials = new AWS.WebIdentityCredentials({
        RoleArn: awsRoleArn,
        ProviderId: 'graph.facebook.com',
        WebIdentityToken: response.authResponse.accessToken
      });
      AWS.config.region = awsRegion;

      // Do something you want
      doSomethingWithAwsSdk();
    });
  }
  function doSomethingWithAwsSdk() {
    // TODO
  }
 // Load the SDK asynchronously
  (function(d, s, id) {
    var js, fjs = d.getElementsByTagName(s)[0];
    if (d.getElementById(id)) return;
    js = d.createElement(s); js.id = id; js.async = true;
    js.src = "//connect.facebook.net/ja_JP/sdk.js";
    fjs.parentNode.insertBefore(js, fjs);
  }(document, 'script', 'facebook-jssdk'));
  </script>
</body>
</html>

 
 
 

1.5. S3へHTMLアップロード & 静的Webサイトホスティングの設定

 

いま作った index.htmlを AWS S3にアップロードし、Webサイトとして公開します。

 
https://console.aws.amazon.com/s3/home をひらき、
“Create Bucket” をクリック。

 
バケットに名前をつけます。
この名前は世界で唯一にしなければいけません。

 
いま作ったバケットの名前以外の部分をクリックして(図を参照してください!)
Propertiesの Static Website Hostingを選び、
Enable website hosting“を選択します。
Index Documentに index.htmlを指定して、Saveします。
 
続いて Permissionsをクリックして、”Add bucket policy” をクリックします。

 

以下の “your.backet.name” 部分を書き換えて、保存します。

{
    "Version": "2008-10-17",
    "Id": "S3BucketPolicy",
    "Statement": [
        {
            "Sid": "AllowsEveryoneToGetS3Objects",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:GetObject*",
            "Resource": "arn:aws:s3:::your.backet.name/*",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": "0.0.0.0/0"
                }
            }
        }
    ]
}

 

最後に、実際にローカルのindex.htmlをS3にアップロードします。
バケット名をクリックして画面が切り替わった後、”Upload” を押します。

ローカルの index.htmlを “Drag and drop files and folders to upload here” に
ドラッグ&ドロップするか、”Add Files” ボタンからファイルを指定してアップロードします。

 
Part1、すべて設定完了です。お疲れさまでした。
では、確認してみましょう!
 
バケットの Properties > Static Website Hosting を開き、
Endpoint をクリックしてみてください。
 
 
こんなのでました?

ログインをクリックしたら、こんなポップアップでました?

 
おめでとうございます!うまくいきました!!
 
これで、サイト閲覧者さんが各自のFacebookアカウントで “OK” すれば
IAMで定義したAWSリソース にブラウザからアクセスしてもらえます。
 
 
もしうまくいかない場合、次の点を見直してみてください。

 
 

Part 2: DynamoDBのテーブルを読み書きしてみる

この先は index.htmlの修正だけで機能追加できます。
(Part1長過ぎでしたね・・)
 
Part2では、利用ブラウザごとに、ログインが押された回数を記録します。
まず結果を表示するためのDIV要素を追加して・・

<body>

+  <div id="browser-count"></div>
  <fb:login-button scope="public_profile" onlogin="check();">
  </fb:login-button>

 
doSomethingWithAwsSdkの中身を実装します。
ブラウザを判定する関数も追加しましょう。

  function doSomethingWithAwsSdk() {
    var detectedBrowser = identifyBrowser();
    if (detectedBrowser == null) return;

    var params = {
      TableName: 'browsers',
      Key: {'browser': {'S': detectedBrowser.n},
          'version': {'S': detectedBrowser.v}
      },
      AttributeUpdates: {
        'count': { Action: 'ADD', Value: { N: '1'} }
      },
      ReturnValues: 'UPDATED_NEW'
    }

    // use DynamoDB's "Atomic Counter" function to update the
    // count page views by the type of browser that we detected
    var db = new AWS.DynamoDB();
    db.updateItem(params, function (err, data) {
      if (err) {
        console.log(err, err.stack);
        return;
      }
      document.getElementById('browser-count').innerHTML =
          'Successful authentications by people with browsers like yours: ' +
          data.Attributes.count.N;
      $('.fb_iframe_widget').hide();
    });
  }
  // detect the web browser
  function identifyBrowser(userAgent, elements) {
    var regexps = {
      'Chrome' : [ /Chrome\/(\S+)/ ],
      'Firefox' : [ /Firefox\/(\S+)/ ],
      'MSIE' : [ /MSIE (\S+);/ ],
      'Opera' : [
          /Opera\/.*?Version\/(\S+)/, /* Opera 10 */
          /Opera\/(\S+)/ /* Opera 9 and older */
      ],
      'Safari' : [ /Version\/(\S+).*?Safari\// ],
      'Silk' : [ /Silk\/(\S+)/ ]
    }, re, m, browser, version;

    if (userAgent === undefined) {
      userAgent = navigator.userAgent;
    }
    if (elements === undefined) {
      elements = 2;
    } else if (elements === 0) {
      elements = 1337;
    }
    for (browser in regexps) {
      while (re = regexps[browser].shift()) {
        if (m = userAgent.match(re)) {
          version = (m[1].match(new RegExp('[^.]+(?:\.[^.]+){0,'
              + --elements + '}')))[0];
          return {n : browser, v : version};
        }
      }
    }
    return null;
  }

 
変更したファイルを、さきほどと同じ手順でS3にアップロードします。
ブラウザを更新して、改めてログインを押してみると・・
 

 
うまくいきましたか?
 
 
今日はここまで・・。
元記事では

と続きます。また今度〜