AWS Amplify Gen2でパスキー認証を実装してみる
はじめに
AWS公式サンプルのAmazon Cognito Passwordless Authを使ってApmlify Gen2上にパスキー認証を実装してみました。
なお、フロントエンドはRemixのSPAモードにしてみました。(深い意味はなく使ったことないのでお試ししてみた感じです。)
Amplify Gen2で作ってみた経緯
パスキーは以前から試してみたいなと思っていて調べていたら上記のサンプルを見つけたのですが、載っている構成図がこんな感じなわけですね。
SPAのフロントエンドにCognitoで認証の時点でAmplifyと相性がよさそうですが、バックエンドはCDKのコンストラクタが提供されているとなればAmplify Gen2がハマりそうだなと思ってやってみた感じです。
作ったもの
レポジトリ
作ったコードは公開しておきます。
事前準備(SESのIDを検証済みにしておく)
今回はパスキーによる認証とメールでマジックリンクを送信する認証の2種類が使えます。しかし、パスキーは1度登録しないと使えません。つまり少なくとも初回ログインではメールを受け取る必要があります。
逆に言うとアプリからメールを送る必要がありますので、Amazon SESにそのための設定が必要です。
- SES経由でユーザーにメールを送る必要があるため、送信元メールアドレスのSESのIDを
検証済み
にしておきます。- アカウントがサンドボックス状態の場合は、送信先メールアドレスも
検証済み
にする必要があります。AWSアカウントのデフォルトはサンドボックスですのでご注意ください。
- アカウントがサンドボックス状態の場合は、送信先メールアドレスも
使い方(sandbox環境+ローカルで動かす場合)
バックエンドのサンドボックスを作る
- レポジトリをクローンしたら、必要なパッケージをインストールします。
npm i
- 必要な環境変数を用意します。
.env
でも良いです。- フロントエンドのURLとホスト名、認証用のメールを送るメールアドレスが必要になります
AUTH_FRONTEND_URL=http://localhost:5173
AUTH_FRONTEND_HOST=localhost
AUTH_EMAIL_FROM_ADDRESS=(送信元メールアドレス)
- Amplify Gen2のサンドボックスに、バックエンドをデプロイします。
npx ampx sandbox --profile sandbox
しばらく待てば、AWS環境に必要なリソースが用意されます。
Cognitoユーザープールにユーザーを追加する
今回はユーザー作成機能を作り込んでいないので、AWSのコンソールからユーザーを作成してください。
Cognitoのユーザープールはできているはずです。
ユーザーのステータスがパスワードを強制的に変更
になってしまいますが、今回はパスワードを使わないので気にしないことにします。
フロントエンドを起動する
以下のコマンドでフロントエンドを起動します。
npm run dev
パスキーを登録する
初回はパスキーが登録されていないのでメールでログインしてください。メールアドレスを入力するとマジックリンクが送られます。
(メールが届かないときは、SESの設定をミスっているか、バックエンド構築時の環境変数をミスっている可能性が高いです。)
ログインすると右上からボタンが出てくるので、そこからパスキーを登録できます。
登録したら、ログアウトしたり他のブラウザから接続すれば、パスキーでログインできます。
使い方(Amplifyにホスティングする場合)
Githubのレポジトリを適当のforkして、Amplify Gen2に繋げばOKです。
注意点としてデプロイ時の環境変数にフロントエンドのホスト名とURLが必要ですが、1度デプロイしないと分からないので、環境変数設定後に再デプロイしてください。
作り方
以下は構築方法を簡単に説明します。
と言っても、前述のAWS公式のサンプルをAmplify Gen2上のアプリに適用しただけではあります。
フロントエンドのベースを作る
まずは、RemixのSPAモードをテンプレで構築しました。
npx create-remix@latest --template remix-run/remix/templates/spa
とりあえず、起動してみました。
npm run dev
Amplify Gen2で認証機能を作る
Amplify Gen2を初期化します。
npm create amplify@latest
AmplifyフォルダとAuthとdataが作られました。
今回はdataは扱わないので消してしまいました。(Authは残す。)
import { defineBackend } from '@aws-amplify/backend';
import { auth } from './auth/resource';
- import { data } from './data/resource';
defineBackend({
auth,
- data,
});
ここまでで一旦Amplifyにdeployしておきました。
- GitHubにpush
- ブラウザでAWS Amplifyのコンソールから画面に従って設定
- 基本デフォルトで行けたが出力ディレクトリだけ
build/client
に変更
- 基本デフォルトで行けたが出力ディレクトリだけ
デプロイできました。
一応amplify.yml
を作って、レポジトリ直下においておきます。
ベースが作れたので、次はパスワードレス認証をを実装していきます。
amazon-cognito-passwordless-authを導入
cognitoでパスワードレス認証(パスキーを含む)を実現できるamazon-cognito-passwordless-auth
を導入します。
単純にインストールしようとしたら依存関係でエラーになってしまったので、こちらのissueを参考にreactのバージョンを指定した上でインストールしました。
npm i react@18.2.0 react-dom@18.2.0
npm i amazon-cognito-passwordless-auth
バックエンド実装
ライブラリのドキュメントやAmplify Gen2のドキュメントを見ながらamplify/backend.ts
にcdkのコードを書きました。
なお、書き方に自信がないのが、以下の部分です。
// ユーザープールとクライアントを取得
const userPool = backend.auth.resources.userPool as cdk.aws_cognito.UserPool;
const userPoolClient = backend.auth.resources.userPoolClient as cdk.aws_cognito.UserPoolClient;
// AuthのCDK Stackを取得
const authStack = Stack.of(userPool);
UserPool
とUserPoolClient
はそのままだと型が合わなかったので、型アサーションで書換えたのですがスマートではない感じがします。また、StackをStack.of()
で取ってくるのは無理矢理な感じがします。きれいな書き方があれば教えて欲しいです。
ともあれ、この状態でデプロイすれば、パスワードレス認証に必要なCognito,Lambda,DynamoDB,Api Gateway等を一通り作ってくれます。
フロントエンド実装
続いて、ライブラリのReactのドキュメントを見ながら、フロントエンドの実装をします。
app/entry.client.tsx
まずは、entry.client.tsx
に初期設定を追加します。
Amplifyの初期設定もしていますが後で見返したら今回は特に使ってませんでした。Passwordless.configure()
が重要です。
app/root.tsx
続いてroot.tsx
で認証機能を実装します。
ここで使っているPasswordless
は、さきほどentry.client.tsx
内で使ったPasswordless
と別物なので注意が必要です。(同一ライブラリ内で別な物に同じ名前をつけるってどうなの?)
<Passwordless>
で囲むことで認証が通ったユーザーのみがコンテンツにアクセスできるようになります。また<Fido2Toast />
を配置することで、ログイン後にパスキー登録するためのToastが表示されます。
app/routes/_index.tsx
前述の部分で、最低限のパスキー認証ができる実装にはなっていますが、ついでなのでusePasswordless()
で使える機能を一部実装してみました。
- ログアウトする
- 認証情報を消す
- アクセストークンやIDトークンを取得する
usePasswordlessの詳細はドキュメントをご確認ください。
まぁログアウトや認証情報削除がないと動作確認が面倒くさすぎたというだけではあります。
最後に
パスキーやFido2の認証は普通に作るとかなり大変なので、比較的簡単に作れるライブラリをAWSが出してくれているのはありがたいです。
対象としている構成がReact+Cognito+CDKだったのでAmplify Gen2に良さそうと思って試してみたのですが、かなりよいと思いました。
参考文献
AWS公式ドキュメント類の他にこちらの記事を参考にさせていただきました。
NCDC株式会社( ncdc.co.jp/ )のエンジニアチームです。 募集中のエンジニアのポジションや、採用している技術スタックの紹介などはこちら( github.com/ncdcdev/recruitment )をご覧ください! ※エンジニア以外も記事を投稿することがあります
Discussion