目標
管理者(私)とそのメンバーだけが閲覧できる会員専用ページ的なものを作る。
参考ページ
めちゃくちゃお世話になったサイト
・http://penguinitis.g1.xrea.com/computer/programming/AWS/S3_web/S3_web.html
基本的にはここを参照すればいい。足りなかった部分は補足する。
目次
- webページを公開する
- Cognitoを用いて認証機能をつける
- 認証されないユーザーからのアクセスをはじく
- 認証時に管理者に通知を送る
- 後片付け
1. webページを公開する
ここは公式のtutorialを追うだけなので割愛。
バケットを作成する。バケット名はのちにドメインのプレフィックスに使う部分と一致させる必要がある。
いずれのオブジェクトもパブリック読み取りアクセスを付与しておく。
また、「パブリックアクセスをすべてブロック」のチェックは外しておく。
S3 > Static website hosting > エンドポイントのURLにアクセスしてページが表示されればOK。
2. Cognitoを用いて認証機能をつける
ユーザープールの作成
Cognitoを開く。
- プール名を決めてステップに従って設定。
- 「どの標準属性が必要ですか?」項目でe-mailのチェックを外す。[次のステップ]
- 「ユーザーに自己サインアップを許可しますか?」項目の「管理者のみにユーザーの作成を許可する」にチェックする。[次のステップ]
- 全て[次のステップ]で[プールの作成]
- 右のサイドバーから[アプリクライアント]を選択。[アプリクライアントを追加]→「クライアントシークレットを生成」のチェックを外して作成。
- アプリクライアントIDを控えておく。
- 右のサイドバーから[アプリクライアントの設定]を選択。有効なIDプロバイダの下の「Cognito User Pool」にチェック。
- コールバックURLにindex.htmlのオブジェクトURLを指定する。サインアウトURLはサインアウト後に遷移するページなので適当にgoogleあたりのURLを指定しておく。
- 「OAuth 2.0」は[OAuthフロー]にImplicit grantを、[OAuth スコープ]にopenidを指定する。
- 右のサイドバーから[ドメイン名]を選択。「プレフィックス」にはs3のバケット名を埋め、[使用可能かチェック]→[変更の保存]
以下からログインのテスト。
とりあえず、以下の画面まで行ければOK
ユーザーの追加
[ユーザーとグループ]からユーザーを追加する。このWebページは管理者が作成するユーザしかアクセスできないので、作成する。パスワードは一定以上の強度がないと登録不可らしい。
以下のチェックは外す。
- この新規ユーザーに招待を送信しますか?
- 電話番号を検証済みにしますか?
- E メールを検証済みにしますか?
作成したユーザー名とパスワードで先ほどのログイン画面からログイン。パスワード変更を求められる。同じものでもいい。
3. 認証されないユーザーからのアクセスをはじく
今の状態では以下のいずれからも画像にアクセスできてしまう。
- 認証されないユーザーのindex.htmlへのアクセス
- オブジェクトURLから画像に直接アクセス
認証されないユーザーのアクセスをはじく
PhotoViewer.jsにlogonの処理をいれるために冒頭を書き換え。
var albumBucketName = '<バケット名>';
const params = new URLSearchParams(location.hash.slice(1));
AWS.config.region = '<リージョン>';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: '<IDプールのID>',
Logins : {
'cognito-idp.ap-northeast-1.amazonaws.com/<ユーザープールID>' : params.get("id_token")
},
});
アクセスポリシーから
認証されないユーザーのポリシー「Cognito_testPoolUnauth_Role」のJSONのEffectをDenyに書き換える。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": [
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::<バケット名>"
]
}
]
}
認証したユーザーのポリシーに下記を適応。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::<バケット名>"
},
{
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<バケット名>/*"
}
]
}
ユーザープールをIDプールと統合する
参考サイトにはここが抜けていて少し戸惑ったところ。
この設定をしないとMissing credentials in config
エラーとなる。
テスト
- 上記の設定を一通り終えてもう一度ログインへ→OK。
- コールバックURL
https://<バケット名>.s3-ap-northeast-1.amazonaws.com/index.html
から直接アクセスすると今度はAccess Denied
ではじかれる。 - Static website hostingのエンドポイントURLからアクセス→
Access Denied
これで認証がないとアクセスできない仕組みを構築できた。次はオブジェクトへの直接のアクセス。
オブジェクトへの直接アクセスをはじく
うまくいったと思ったがブラウザのキャッシュが残っているために表示できているような気がする。試しにブラウザを変えてみると画像にアクセスできない。この辺りはCloudFrontなど別サービスも視野に入れてみる。
参考ページのコードを丸パクリします!
バケットのjsファイルを更新する。
オブジェクトへのURLを用いずに直接アクセスするため、設定からパブリックアクセスを閉じる。対象のオブジェクトのパブリックアクセスの[Everyone]を選択し、「オブジェクトの読み取り」のチェックを外す。
テスト
上記の設定を一通り終えてもう一度ログインへ→OK(なぜかアルバムを開いた1回目は画像が表示されない、原因探査中)。コールバックURLhttps://<バケット名>.s3-ap-northeast-1.amazonaws.com/index.html
から直接アクセスすると今度はAccess Denied
ではじかれる。Static website hostingのエンドポイントURLからアクセス→Access Denied
画像のオブジェクトURLから直接アクセス→以下のページが出て読み取れない。
This XML file does not appear to have any style information associated with it. The document tree is shown below.
#4. 認証時に管理者に通知を送る
Cognitoのユーザープールのサイドバーにある[トリガー]では認証時(前後など細かく分かれてる)に指定するlambdaをコールすることができる。
今回は誰かがアクセスしようとする度に管理者(私)のslackに通知が来るようにしてみた。
ググればでるので通知部分の処理は省略。
重要なのはコールバック関数でeventをCognitoに渡してあげるようにしないと認証時にエラーとなる。
exports.handler = (event, context, callback) => {
// 処理
// Return to Cognito
callback(null, event);
};
#5. 後片付け
ユーザープールだけは削除手順があり、まず[アプリの統合]→[ドメイン名]からドメインを削除してからプールの削除をしないとエラーで怒られる。
User pool cannot be deleted. It has a domain configured that should be deleted first.