さわらブログ

さわら(@xhiroga)の技術ブログ

Cognito UserPool 実践入門 では書けなかった闇をブログに記す #技術書典

#技術書典 で "Cognito UserPool実践入門" という本を販売します。

BOOTHでも取り扱っています。 hiroga.booth.pm

これ、元々プロジェクトで経験したUserPool移行の苦労話を書こうと思って始めたのですが、いつの間にか入門書を書いていました。
なので苦労話の方はお蔵入りにしたのですが、もったいないのでここに記します。

Cognito UserPool 実践ユーザー移行 UserPool to UserPool with Federated Identity編

悲劇の始まり

Cognito初心者が絶対つまづく(と私が思っている)設定として、エイリアス属性(IDの代わりにログインに利用できる属性)があとから変更できないことがあります。

f:id:hiroga_cc:20190921161501p:plain
デフォルトで設定されるエイリアス属性

デフォルト設定だとメールアドレス・電話番号でのログインは不可能ですが、この設定は後から変更不可能です

私が関わっているプロジェクトもUserPoolを利用していたのですが、エイリアス属性に設定されていたのはメールアドレスだけでした。
そこにきて、「電話番号でログインできるようにしたい」という要件が登場したわけです。

初めはちゃちゃっと対応できると思ってましたが...Federated Identityを併用していたことで話が大変になります。

堅牢なFederated Identity

正直言ってUserPoolからUserPoolにデータを移行するだけなら話は簡単でした。
恐ろしいのは Federated Identityとの連携を維持しなければいけないことです。

f:id:hiroga_cc:20190921162412p:plain
⑤アプリケーションが利用するユーザーの一意識別子はFederated IdentityのIdentity ID

UserPoolとFederated Identityを連携している場合、バックエンドのアプリケーションで利用するユーザー識別子には Federated IdentityのIdentity IDを利用しなくてはいけません(じゃないと、OpenID Provider → Federaed Identityでも利用している場合に困りますよね?)

ところが!Federated IdentityのIdentity IDに対して、管理者権限で任意のログイン情報を紐付けることは不可能なのです
(それ自体はセキュリティの観点からあるべき姿だと思いますが...。)

f:id:hiroga_cc:20190921163333p:plain
古いUserPoolのユーザープロファイルとFederated IdentityのIdentity IDが紐付いている。管理者が勝手に新しいUserPoolのIdentity IDを連携することはできない。

これを解決するための、たった一つだけ許されたやり方...それは、管理者がユーザーの代わりにログインすることです。
それならユーザー移行Lambdaトリガーで余裕じゃん!と思われるかもしれませんが、余裕ではありませんでした

たったひとつの冴えないやり方

結論から言えば、ユーザー移行Lambdaトリガーを以下のように実装すれば対応可能です。

f:id:hiroga_cc:20190921170014p:plain
UserPool to UserPool with Federated Identity のユーザー移行

  1. クライアントは新UserPoolにログインを試行
  2. 新UserPoolはユーザーが見つからないのでユーザー移行Lambdaトリガーを起動
  3. Lambdaはユーザーの代わりに旧UserPoolにログイン。この際にIdToken(旧)を受け取る。
  4. Lambdaは新UserPoolに対して AdminCreateUser アクションを実行し、ユーザー移行Lambdaトリガー内でユーザーを作成 ※1
  5. Lambdaは新UserPoolに作成したユーザーとしてログイン。IdToken(新)を受け取る。
  6. 新旧のIdTokenを使って、 Federated IdentityにgetOpenIdToken アクションを実行。結果、旧のユーザーで取得したIdentity IDが新のユーザーに紐付く
  7. LambdaはエラーをThrow(自爆)(そうしないと、UserPoolが後続処理でユーザーを作成しようとし、そのユーザーはStep2で作成済みでエラーになるため)
  8. UserPoolはクライアントにエラーを返す ※2

※1 ユーザー移行Lambdaトリガーは、その返り値でUserPoolにユーザー作成のための情報を渡すLambda。これはバッドハックです。
※2 クライアント側もエラーを握りつぶすように修正しました。

なお、これに加えてパスワードリセット時は旧のUserPoolのパスワードをリセットするように設定する必要があります。

参考までに、通常のユーザー移行Lambdaトリガーのフローも載せておきます。

f:id:hiroga_cc:20190921171501p:plain
通常のユーザー移行Lambdaトリガーのフロー

シンプル!!!!!

しんどさ

検証のためにUserPoolになんどもユーザーを作成するのが一番大変でした。
その結果生まれたのが cognito-cli です。

github.com 注意: ちょっとバグってます。後で直す。

CLIでUserPoolの非管理者用APIを立たけるようになったことで生産性が爆上がりました。

それからデプロイもしんどかったです。Cognito UserPool 実践入門 でも書きましたが、ユーザー移行Lambdaトリガーは 2019-09-21時点でCloudFormation非対応なので...。
たぶん世の中的にCognitoをCloudFormationで管理している人が稀なのですが(だってそんなに更新しないでしょ?)

でもやってよかった

同じ問題にぶち当たる人は二度と現れてほしくないですが(だから執筆したんです!)、私個人はとても勉強になりました。

  • OpenID ConnectのIdTokenの中身を見たり...
  • Cognitoの仕様にちょっとだけ詳しくなったり...
  • 初の技術書典サークル参加につながったり...

人間必要になったときが一番勉強するんで、認証認可について学びを深められたのは良かったです。

最後に...

色々書きましたが、認証・認可をマネージドに済ませてアプリに集中するには、Cognitoはとてもいいサービスだと思っています。
もしCognito検討する人がいたら、公式ドキュメントに加えて私の本もチラッと見ていただければ幸いです!!!!!
hiroga.booth.pm

「あとで読む」を読む - DNSの勉強法、Istio、jlink、Bluetooth

f:id:hiroga_cc:20190529163402p:plain

夏休みで時間ができたので、日々のPocketの積ん読を消化していきました。

Auth0 Ebooks

Identity Resources - Webinars, eBooks, Case Studies - Auth0

Auth0の資料が認証認可の基本の理解のためにかなり良さそう、という話。
Etheriumについての解説まであるんだね。Auth0とEtherium...?

情報セキュリティ白書 2019

情報セキュリティ白書2019:IPA 独立行政法人 情報処理推進機構

無料なのでどんなものかと思って読んでみた。実装レベルの話ではなく、国内外のセキュリティ動向を概観したいときに読む本だ。

DNS Study

DNS Study

DNSの仕組み、どこかでちゃんと抑えなきゃ...と思ってた。
ふわっと本を読もうと思っていたら「本に書いてあることはだいたい間違っています」と来た...そうなのかもしれないけどさ、その言い方って怪しい健康食品と変わらないよね...

マイクロサービスアーキテクチャ向けにサービスメッシュを提供する「Istio」の概要と環境構築、トラフィックルーティング設定

マイクロサービスアーキテクチャ向けにサービスメッシュを提供する「Istio」の概要と環境構築、トラフィックルーティング設定 | さくらのナレッジ

サービスメッシュについて頭の中が整理されてきた。
マイクロサービスの場合には通信先のサービスのIPアドレスを事前に知ることができない、という制約でどうするか。
AWSのCloudMapやKubernetesのServiceのようにサービスディスカバリを用いることもできるし、各コンテナにサイドカーとしてのプロキシサーバー(データプレーン)を置く、という選択もできる。後者のほうが多機能なものが多い、ということのようだ。

アプリケーション配布用に小さなJREを作る

アプリケーション配布用に小さなJREを作る

JRE入のコンテナイメージが重い!って問題に対処できるらしい。
全然分かってなかったんだけど、つまり、
JREJVMJavaの標準ライブラリから成る
Javaの標準ライブラリにはたくさん機能があるが、そこまで使わないことのほうが多い
・だからカスタムJRE(=自分が使うライブラリとJVMだけのJRE)を作ってしまおう!
ということらしい。

Bluetoothで山手線の乗降パターンを追跡してみた

高木浩光@自宅の日記 - Bluetoothで山手線の乗降パターンを追跡してみた , ユビキタス社会の歩き方(6) Bluetoothの「デバイスの公開」「検出可能にする」..

面白い!これを追いかけるだけで、例えば店頭で「あなたは何回目の来訪です」というのが言えちゃうわけか。
(あなた、が誰かはわからないとしても)

NASA Books

NASA e-Books | NASA

NASAの無料公開している電子書籍システム開発に関する本があったのでダウンロードしました。

Amazon EventBridge

Amazon EventBridge入門 PagerDuty連携を試してみた | DevelopersIO

AWS版のZapierってことかな?

20日間fish体験記

macOSのデフォルトのログインシェルがbashからzshになるってことで、せっかくなら予めzsh慣れておこう → いや、zshでなくてもいいよね?
ということでfishにしてみましたが...。

ちなみにこんな設定でやってます。
github.com

感想

メリット

・fishにしたことで、生産性意識の高い人の情報が自然と流れてくるようになった。
(例えばghq, peco, z, etc...) ・シンタックスハイライトが賢い!
・promptが関数でできてる思い切りが分かりやすくて好き。

デメリット

bash文法非互換なので、仕事用のシェルを検証するときにいちいちbashに切り替えが必要
・たまにVSCodeのコンソールの表示がバグる(超ダサフォントになる)
・nvmの設定がbashで書かれているので使えない...こういうの他にもありそう。 ・環境変数の設定をbashと共有するのがしんどい

使いこなせていないと感じるところ

・コマンド履歴が賢い、というかおせっかいに感じる。もっと慣れれば違いそう... ・POSIX互換の文法よりはスッキリしている...らしいけど、シェルってそんな毎日書くものでもないし多少文法おかしくてもいいじゃん...?

今後について

bash非互換はつらいので、互換性のある拡張をいれつつ、もしかしたらzshに移るかもしれません。
うーん。

自分的Dockerのユースケースをまとめてみた

ふだん雰囲気でDocker使っているのですが、もっと自由にDockerを使えるようになりたい!
ということで、自分的なDockerのユースケースを振り返ってまとめてみました。

1. リポジトリごとの開発環境の構築

リポジトリごとに開発で利用するPythonの環境を、Dockerを使って構築しています。

コマンド

`docker-compose run --rm python /bin/bash

version: '3'
services:
  python:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - ./src:/root/work

2. 開発用のデータベースの構築

command

(開発用のサーバーの起動前などに) docker-compose up -d dynamo

docker-compose.yml

version: '3'

services:
  dynamo:
    image: amazon/dynamodb-local:1.11.477
    ports:
      - "8001:8000"

3. ミドルウェアの実行

技術書典で出品するPDFファイルのビルドのために利用しています。 (Thanks for カウプラン機関極東支部 さん!)

コマンド

docker-compose run --rm review

docker-compose.yml

version: "3"
services:
  review:
    image:
      kauplan/review2.5
    volumes:
      - $PWD:/work
    entrypoint:
      /bin/bash -c "cd /work; rake pdf"

4. cliツールのバイナリを直接インストールする代わりにする

例えば jq のバイナリを直接ローカルに置かない。
alias jq="docker run -i --rm mbentley/jq"

jq以外にも、例えばβ版のcliツールを利用する場合なんかは安心して使えますね。

参考
hub.docker.com

反省

ブログを書くに当たり、改めてDockerについて学びがありました。

  • 基本的に docker-composeを利用すること。

  • docker-compose up には --build オプション、 docker-compose run には --rm オプションをつけること。
    ...しかし、どうして docker-compose run には --build オプションがないんだろう。一貫性が足りないよね。

github.com

  • Dockerの仕様として、ADD, COPYで取得したファイルのハッシュが代わっていたら、それ以降のRUNのキャッシュは利用されないこと(つまり、 package.json が変更されていれば npm install は再実行される)

個人のメールアドレスに届くGitHub・CircleCIの通知を会社のアドレスに転送する

justInCaseでは、リポジトリをBitBucketからGitHubに移行する計画があります。

そこで個人のGitHubアカウントを使って開発をしていますが、一つ問題が。
仕事のPRやビルドの失敗のメールが、個人のアドレスに流れてしまうのです。

GMailのメールの転送設定で解決できそうなので、やり方をメモしておきます。

1. 転送先のメールアドレスを設定

公式のガイドに従って特に躓かずに設定できました。

support.google.com

2. フィルタの作成

こちらも公式のガイドどおりに。
support.google.com

こんな感じのフィルタになりました。 f:id:hiroga_cc:20190727110931p:plain

以上、特別難しい手順はありませんが、誰かのお役に立てば幸いです。

ググって出てきたCognito UserPoolのベストプラクティスを雑にまとめる

(このブログは、 #技術書典7 で出展する「Cognito屋さん/ Cognito ユーザー移行入門(仮)」の下調べのために書いてます.)

タイトル通り、ググって出てきたCognito UserPoolのベストプラクティスを雑にまとめていきます。

Security / Best Practice for poolData (UserPoolId, ClientId) in a browser JS app

github.com

UserPoolIDとClientIDは、JSのコード内で公開していいの?という質問。
スレッドにある通り、これらの値は unauthenticated APIs のリクエストにしか使われないので大丈夫。

Cognito user pools, in my experience, are terrible to work with

www.reddit.com

ベストプラクティスじゃないけど...Cognito使うのしんどいよね、って話。
いっそ新しいサービス出したらいいんじゃない?って指摘には私も同意。
Cognito is a flawed product that should become redundant. AWS should either build a new service with a new name or build a backwards incompatible Cognito 2.0.

[レポート] サーバーレスアーキテクチャデザインパターンとベストプラクティス! #reinvent #ARC305

dev.classmethod.jp

たしかに、Cognito使いたい人ってAPI Gatewayを認証付きで提供したい人だよね。
1.一時的なAWS認証 or 2.UserPoolのトークン or 3.Lambdaオーソライザー。特に 1.と2. は混同しやすいのでは?

HOW TO USE NEW ADVANCED SECURITY FEATURES FOR AMAZON COGNITO USER POOLS

noise.getoto.net

せっかくだしMFAもちょっと触ってから本を書いたほうがいい気がしてきた

SaaS Identity and Isolation with Amazon Cognito on the AWS Cloud

https://aws-quickstart.s3.amazonaws.com/saas-identity-cognito/doc/saas-identity-and-isolation-with-cognito-on-the-aws-cloud.pdf

弊社(justInCase)のアーキテクチャじゃんこれ!!!知らずしてここにたどり着いたんだなあ...
CloudMapを使ってるとこだけが違う。

Are email addresses supposed to be case-sensitive?

https://forums.aws.amazon.com/forum.jspa?forumID=173&start=0

そうそう、メアドが大文字小文字違いで登録できちゃうんだよね。

「あとで読む」を読む - 2019-06-23

f:id:hiroga_cc:20190529163402p:plain

難民をテック人材へ、3団体協働のもと日本初「難民プログラミング道場」が今秋スタート | LoveTechMedia - ラブテックメディア - Part 2

ドイツに「ReDI School」という同じスタイルのプログラミングスクールがあるらしい。
素敵な取り組みだな、と思ってたらWELgeeってASACで名前見たことあるな。
プログラミング教室もパソコンの貸与も、他の団体と協力してやってる、ってのが素敵だなって思った。

speakerdeck.com WebAuthnについて概略をつかめた。
OAuthがクライアントから認可を分離したように、WebAuthnは認可サーバーから認証を分離した、って表現は正しいかな...? ちょっと思ったのは、もし人間の脳みそが超ハイスペックに拡張されていたら、サービスごとの秘密鍵を暗記&署名を暗算できるようになり、結果この仕組もいらないのかな?

aws.amazon.com

自分で考えて、責任を持って、実施までできる、という立場がスタートアップ。 良い表現...。

blog.cybozu.io カスタムリソースを考慮したことがなかったけどかなり便利かも。なんならCloudWatch LogsのSubscriptionをLambdaに後から追加する、とかもCDKじゃなくてこっちでOKでは?

その他

  • Googleのコード検索ツールの記事良かった。なるほど、自分の参考リポジトリにindex貼っておくのは賢いかも。あれ、ということは、自分のお気に入りの技術ドキュメントだけにindexを貼る、とかもやれば余裕なのか。
  • dockle試した。よかった。