読者です 読者をやめる 読者になる 読者になる

まめ畑

ゆるゆると書いていきます

S3とCORS

javascript S3 AWS cognito

最近、AWSのサービスも少し様変わりしてきて、インフラレイヤーから、アプリレイヤーまで幅広くサービスが出てきています。SDKも充実してきていて、デバイスやブラウザから直接AWSサービスにアクセスして、S3にデータをUploadしたり、DynamoDBにデータを入れたり、Kinesisにログを流し込んだりなどなど今までサーバをかえして行っていた処理が突き詰めればサーバレスで行えるようになってきました。
もちろん、データを受け取って整形など複雑な処理を行いたいという場合には今まで通りAPIサーバなりを用意する方法になるかと思います。


ここで一つ問題になるのが、認証周りかと思います。例えば、ブラウザやデバイスから直接S3にデータをUploadしたり読み出したりするアプリがあった場合、S3にアクセスするための認証情報をセットする必要があります。このキーの管理はなかなか難しいところがありますが、今までだとTVMと呼ばれるIdentity Brokerを用意して一時的なキーを払い出すという方法がありました。今だと、先日独自認証プロバイダにも対応したAmazon Cognitoを使うことで簡単に実装出来ます。Congnitoは認証だけでなく、デバイス間でのデータ同期なども備えているため是非試してみてはいかがでしょうか。
実はCognitoはデバイスアプリだけで無く、Javascript SDKでも動作するようになりました
Use Amazon Cognito in your website for simple AWS authentication - AWS Developer Blog - Mobile

 window.fbAsyncInit = function () {
     FB.init({
         appId: 'xxx'
     });

     FB.login(function (response) {
         AWS.config.region = 'us-east-1';
         AWS.config.credentials = new AWS.CognitoIdentityCredentials({
             AccountId: 'xxxxxx',
             RoleArn: 'arn:aws:iam::xxx0:role/Cognito_FBTestAuthRole',
             IdentityPoolId: 'us-east-1:xxxxxxxxx',
             Logins: {
                 'graph.facebook.com': response.authResponse.accessToken
             }
         });

         fbUserId = response.authResponse.userID;
         console.log("FB ID: " + fbUserId);

         AWS.config.credentials.get(function(err) {
             if(!err) console.log("Congnito Identity Id: " + AWS.config.credentials.identityId);
             else console.log(err, err.stack);
         });

         s3 = new AWS.S3();
         // Uploadとか
     });
   };
  };

こんなかんじで、FBのアカウントと連携して作れます。このへんは今回の本題では無いので、詳細は省略します。

本題

画像なり動画なり大きなデータをブラウザから直接S3にUploadしたいという場合、S3に対してCORSの設定をすることになりますが、一点だけ注意点があります。

Bucketを作成してすぐにCORSを設定してJavascript SDK経由でアクセスすると、CORSが効いていないよなエラーが表示されて、リクエストが失敗します。

なぜかというと、ChromeだとDeveloper Consoleに以下のような出力が出ています

XMLHttpRequest cannot load https://BucketName.s3.amazonaws.com/?prefix=facebook-xxxx. The request was redirected to 'https://BucketName.s3-ap-northeast-1.amazonaws.com/?prefix=facebook-xxxx', which is disallowed for cross-origin requests that require preflight.

見てわかると通り、S3のエンドポイントのリダイレクト(s3共有ぽいURLからリージョンスペシフィックなURL)が起こっています。このリダイレクト先がCORSを引き継いでいないため失敗しています。
これはしばらくまつとリダイレクトされなくなり、CORSも効きアクセスが成功します。

もし、新規Bucketでうまくリクエストがいかないなぁという時に確認してみてはいかがでしょうか。

おまけ

CORSは以下のような感じでBucket Properties->Permissions->Edit CORS Configurationで設定します。
AllowedMethodは許可するものを選んでください。

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <AllowedMethod>PUT</AllowedMethod>
        <AllowedMethod>POST</AllowedMethod>
        <AllowedMethod>DELETE</AllowedMethod>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

また、CORSとは違いますが、Bucket PolicyでこのFacebookのこのユーザIDの人は自分のバケットだけ操作出来るというIAMで使える変数もあるのでご活用ください。
IAM Policy Variables Overview - AWS Identity and Access Management