ぶろぐ

日記です

curlでcognito oauth2認証

久しぶり、かつpythonで実装して超めんどくさかったのでメモ。
はまらなければすぐできると思う。

Authorization Code Grantの流れ
  • cognito hosted uiでログイン
  • サーバーのアプリでredirectを受け付ける
  • 認可コードをtoken endpoint(xxxx.amazoncognito.com/oauth2/token)に送る
  • id_token, refresh_token, access_tokenを取得
  • 有効期限が切れたら refresh_token を使用して token をrefresh

docs.aws.amazon.com

この辺を見れば分かる

token取得
curl -X "POST" "https://{domain}.auth.us-east-1.amazoncognito.com/oauth2/token" \
     -H 'Content-Type: application/x-www-form-urlencoded' \
     --data-urlencode "grant_type=authorization_code" \
     --data-urlencode "client_id=xxxxx" \
     --data-urlencode "client_secret=xxx" \
     --data-urlencode "code=xxxx" \
     --data-urlencode "redirect_uri=https://{redirect_url}"
token refresh
curl -X "POST" "https://{domain}.auth.us-east-1.amazoncognito.com/oauth2/token" \
     -H 'Content-Type: application/x-www-form-urlencoded' \
     --data-urlencode "grant_type=refresh_token" \
     --data-urlencode "client_id=xxxx" \
     --data-urlencode "client_secret=xxxx" \
     --data-urlencode "refresh_token=xxxx"

optionalでnonceも送る。

Amazon SNS Mobile PushでPUSH通知

予めAWSでやっておくこと

  • アプリケーションプラットホームを作成しておく。APNsやFCMの情報を登録する。
    • ここに端末のdevice tokenを登録することでendpointを取得できる。
    • サーバーではこのendpoint arnをDBに保存しておく。
      • ユーザー(端末)を指定して個別にpush通知を送りたいときに使う
  • 全体PUSH通知用にTOPICを作成
    • endpoint arnを作成したらこのTOPICをsubscribeしておく
    • TOPICにmessageをpublishしたらsubscriberに伝播して全端末にpush通知が飛ぶ
    • 端末個別にpush通知が飛ばせればいいのであれば作らなくてもいい。

コードサンプル

コピペで動くはず

<?php
$client = new SnsClient([
    'profile' => 'dev_sns',
    'region'  => 'ap-northeast-1',
    'version' => 'latest',
]);

// アプリケーションプラットホームのエンドポイントを作成
// このendpointにpublishすることで端末指定でpush通知が可能
$result = $client->createPlatformEndpoint([
    'Token'                  => $token,
    'CustomUserData'         => $studentId,
    'PlatformApplicationArn' => 'arn:aws:sns:ap-northeast-1:xxxxxxxxxxxxxx:app-push-notification-dev'
]);
$endpoint = $result['EndpointArn'];

// 全体通知用のトピックをsubscribeする
$client->subscribe([
    'Protocol'   => 'application',
    'Endpoint'   => $endpoint,
    'TopicArn'   => 'arn:aws:sns:ap-northeast-1:xxxxxxxxxxxxxx:app/GCM/app-android-dev',
    'Attributes' => [], // android/ios指定でフィルタできるように設定してもいいかも
]);

// endpointを指定して個別にpush通知
$client->publish([
    'Subject'   => 'title',
    'Message'   => 'message',
    'TargetArn' => $endpoint,
]);

// topicを指定して全体にpush通知
$client->publish([
    'Subject'   => 'title',
    'Message'   => 'message',
    'TargetArn' => 'arn:aws:sns:ap-northeast-1:xxxxxxxxxxxxxx:app/GCM/app-android-dev',
]);

CannotStartContainerError: Error response from daemon: failed to initialize logging driver: failed to create Cloudwatch log stream: ResourceNotFoundException: The specified log group does not exist. status code: 400, request id: xxx

  • タスクロール ecsTaskExecutionRole はCloudwatchLogsのグループを作成する権限がないっぽい
    • こういうのが出る CannotStartContainerError: Error response from daemon: failed to initialize logging driver: failed to create Cloudwatch log stream: ResourceNotFoundException: The specified log group does not exist. status code: 400, request id:
    • 初回だけ手動で作ればOK
  • 起動タイプをEC2にした場合、自分でEC2を起動してクラスタにjoinさせる手順が必要

いろいろ知らなくてハマったのでメモ

sam local start-apiで Invalid API Gateway Response Keys: set([u'multiValueHeaders']) が出るとき

今の最新バージョンは、sam local start-apiコマンドに multiValueHeaders 対応が入っていないというバグがあるっぽい。

ローカル検証用に起動するコマンド
sam local start-api
samのバージョン
sam --version
SAM CLI, version 0.16.1
対処方法

未だリリースはされていないが、developブランチには修正pull-requestがマージされている。
brewを使って最新のソースコードを使用してビルドする。

brew uninstall aws-sam-cli
brew install --HEAD aws-sam-cli

ここで言及されている。次リリースされるらしい。

github.com

Goだと多分全部のリクエストでコケる


なるほど。他のruntimeはどうかわからないが、Goを選択した場合APIGatewayProxyResponseのMultiValueHeadersにomitemptyがついていなくて、いかなる場合でもresponse bodyにmultiValueHeadersが含まれて全部のリクエストでNGになってしまう感じか。これomitemptyつけてもいい気がするけどな。

github.com

おそらく他のランタイムだと multiValueHeaders キーを作らずにresponseを返すことができて、その場合はエラーにならないから気づかかなかった and 修正の優先順位が低かったとかなのかな。(違うかもしれないけど)

アーキテクチャ

フレームワークの上で開発しているといろいろ調べたりコード読んだりする必要が出てきて、なんかストレス溜まるし、一から書いたほうが早いのでは、みたいな気持ちになることがある。
実際はちゃんと逃げ道を用意してくれていたり、DIっぽくその処理は外部から注入できますよ( LaravelならXxxProviderとか )という風になっているので、ドキュメントをちゃんと読めば解決する。それでも試行錯誤してしまうけど。ある程度思想を理解しないといけない為だ。
この気持何かに似ているなと思ったら、人が作ったシステム・アーキテクチャの上で開発するときの気持ちに似ているかも。
join時の痛みで、有る種の成長痛である場合もある。
あまりにもやりたこととミスマッチしていたり、変更しづらすぎる、追加開発をしすぎて複雑になってしまっている、また開発者に業務知識が十分にある(どう作ればいいか知っている)場合だと一から書いたほうがいい場合もあると思う。

自分でアーキテクチャを設計し、継続的に開発して育てていく感じなら、こうはならずに使いやすいように随時少しずつリファクタしていけると考える。そういうのいいな。