閉域からのCognito認証とSESによるMFAの勘所

224 Views

September 29, 25

スライド概要

2025年9月29日に開催した社内イベント「AWSランチセッション第10回」の発表資料です。

閉域からのCognito認証と、SESを使用したEメールによるMFAについて、要点を分かりやすく解説しています。
インフラと密接に関係するクライアントアプリについても、2通りの実装方法をコードを交えて解説しています。
また、SESの本稼働に必要となる考慮点やアーキテクチャもあわせて解説しています。

これを機に、意外と需要が高い閉域でのユーザー認証を覗いてみませんか?

profile-image

SFとコンピュータが好き

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

ダウンロード

関連スライド

各ページのテキスト
1.

2025-09-29 AWSランチセッション 第10回 AWS 閉域からのCognito認証とSESによるMFAの勘所 山崎 拓也

2.

山崎 拓也 所属: SIer 仕事: • AWS案件のアプリやインフラのリード • 社内AWSサポート 好き: 低レイヤ、SF、AWS AWSアワード: • 2024~2025 Japan AWS Top Engineer • 2022~2025 Japan All AWS Certifications Engineer

3.

アジェンダ • はじめに • 閉域からのCognito認証 • SESを使ったEメールによるMFA • まとめ • SES本稼働にむけて

4.

はじめに

5.

AWS Cloudからインターネットに出られない状況を「閉域」とする • Direct ConnectやSite-to-Site VPNなどからアクセスする場合、 クライアントはインターネットにアクセスできないものとする

6.

今回は便宜上、閉域のEC2にてWebサーバーやWebアプリを扱う • 必要なパッケージを持ったAMIを作成し、閉域環境にてインスタンス作成 • ブラウザが必要なためWindows Serverを用いる • フレームワークはNext.jsを用いる • アプリの操作はSSMから行う

7.

閉域からのCognito認証

8.

閉域からAWSサービスへのアクセスにはVPCエンドポイントを使う

9.

閉域からCognitoでユーザー認証したいけど・・・

10.

CognitoはVPCエンドポイントをサポートしていない • ユーザー認証にはCognitoのAPIが呼び出せればいい・・・

11.

API GatewayでCognitoエンドポイントをプロキシできる • 通信がAWSのネットワークから出ない

12.

API Gatewayの設定 • HTTP統合でCognitoユーザープールのエンドポイントへ転送する Amazon Cognito エンドポイントとクォータ: https://docs.aws.amazon.com/ja_jp/general/latest/gr/cognito.html

13.

閉域からはCognitoマネージドログインページに到達できない • アプリ側でログイン周りの実装が必要となる

14.

実装方法は2通り 1. AWS SDKを使用する • Cognito API単位での操作を自由に実装できる 2. AWS Amplifyライブラリを使用する • API呼び出しがラップされるため実装が簡単 • 認証情報の取り回しも楽 • おすすめ

15.

ユーザー認証に関わるCognito API No. 操作 呼び出すAPI 1 サインアップ SignUp 2 サインアップ時のメールアドレス検証 ConfirmSignUp 3 サインイン InitiateAuth 4 認証情報のリフレッシュ InitiateAuth (リフレッシュトークンを送る) 5 サインアウト GlobalSignOut 各APIをAPI Gatewayに向けてリクエストする 同じAPI

16.
[beta]
AWS SDKを使用したサインインの実装例
❶

// Cognitoクライアント作成
const client = new CognitoIdentityProviderClient({
region: 'ap-northeast-1',
endpoint: '<API GatewayのURL>'
});

❷

// Cognito APIのコマンド作成
const command = new InitiateAuthCommand({
AuthFlow: 'USER_PASSWORD_AUTH',
ClientId: '<CognitoのクライアントID>',
AuthParameters: {
USERNAME: email,
PASSWORD: password
}
});

❸

// API呼び出し
const result = await client.send(command);

InitiateAuth APIはサインイン用のAPI

MFA無効時はそのままトークンが返る
• AccessToken
• IdToken
• RefreshToken

17.
[beta]
AWS Amplifyライブラリを使用したサインインの実装例
❶

import { Amplify } from 'aws-amplify';
import { signIn, fetchAuthSession } from 'aws-amplify/auth';
Amplify.configure({
Auth: {
Cognito: {
userPoolId: '<CognitoのユーザープールID>',
userPoolClientId: '<CognitoのクライアントID>',
userPoolEndpoint: '<API GatewayのURL>'
}
}
});

❷

// サインイン
await signIn({ username: email, password });

❸

// トークン取得
const session = await fetchAuthSession();

セッションから以下トークンが取得できる
• AccessToken
• IdToken

• リフレッシュトークンに触ることはできない
•

fetchAuthSession()

•

fetchAuthSession({ forceRefresh: true })

で必要に応じて自動的にリフレッシュされる
で強制的にリフレッシュも可能

18.

SPAで直接Cognito APIを呼び出す場合はCORSの設定が必要 • Amplifyライブラリを使用する場合はこちらになる • 必要となるAccess-Control-Allow-Headersは以下(トライ&エラーで絞り込みました) • 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token, X-Amz-User-Agent,User-Agent,cache-control,amz-sdk-request, amz-sdk-invocation-id,X-Amz-Target' • '*'でも大丈夫

19.

API GatewayのOPTIONSメソッドの設定 Amplifyライブラリが リクエストに使用する ヘッダーを許可する

20.

APIサーバーを置ける場合はCORSの設定が不要 • APIサーバーからAPI Gatewayへリバースプロキシする • Next.jsの場合、Cognito SDKを使ってAPI RoutesでAPIを実装する

21.

SESを使ったEメールによるMFA (SES: Amazon Simple Email Service)

22.

CognitoのEメールによるMFAには、SESの利用が必須 • CognitoのデフォルトのEメール送信は利用できない

23.

SESはドメインをIDとして検証することでメール送信可能になる • Route53管理下のドメインなら、自動でレコード追加されるため設定が簡単 • Route53以外の場合、ドメイン検証に「_amazonses.<domain>」の TXT レ コードが必要 • DKIMやSPF、DMARCなどの設定は「Eメールの認証方法」*を参照 ID: example.com * Eメールの認証方法: https://docs.aws.amazon.com/ja_jp/ses/latest/dg/send-email-authentication-spf.html

24.

ではSESでドメインを検証すればCognitoのEメールMFAが可能? • それだけではできない ID: example.com

25.

Cognitoの送信元メールアドレスは、SESで検証済みである必要がある • ドメインではなく、送信に使用するメールアドレス自体がSESでIDとして 検証済みである必要がある Cognitoの設定画面 検証済みメールアドレスを選択する SESの設定画面 メールアドレスをIDとして登録 するため、検証が必要

26.

SESでのメールアドレス検証には、メールを受信できる必要がある • メールアドレス検証時に検証用メールが送信される • メール内のリンクにアクセスする必要がある * Eメールの認証方法: https://docs.aws.amazon.com/ja_jp/ses/latest/dg/send-email-authentication-spf.html

27.

SESでもメールを受信できる • 検証済みドメインにMXレコードを追加する • MXレコードの値は「10 inbound-smtp.region.amazonaws.com」 • 受信したメールはS3などに配信できる 検証済みドメイン宛て [email protected] Amazon SES E メール受信の設定: https://docs.aws.amazon.com/ja_jp/ses/latest/dg/receiving-email-setting-up.html

28.

ようやくCognitoでのEメールMFAが設定できる

29.

Cognito認証のEメールによるMFA設定手順の整理 1. MFAコード送信元メールアドレスのドメインをSESにIDとして登録し、検 証する 2. SESでMFAコード送信元メールアドレスへのメールを受信可能にする (MXレコードの設定も) 3. MFAコード送信元メールアドレスをSESにIDとして登録し、検証する 4. Cognitoの送信元Eメールアドレスに、 MFAコード送信元メールアドレス を設定 5. CognitoのMFA設定からEメールを選択

30.

クライアントアプリもMFA認証に対応させる No. 操作 呼び出すAPI 1 サインアップ SignUp 2 サインアップ時のメールアドレス検証 ConfirmSignUp 3 サインイン InitiateAuth (MFA有効化後は、チャレンジ名とセッションが返る) 4 認証情報のリフレッシュ InitiateAuth (リフレッシュトークンを送る) 5 サインアウト GlobalSignOut 6 認証チャレンジへの応答 RespondToAuthChallenge (MFAコードを送る) (認証トークンが返る) • サインイン時、InitiateAuth API からチャレンジ名とセッションが返る • チャレンジ応答として、RespondToAuthChallenge APIでセッションとメー ルで受信したコードを送る

31.

サインイン時のAPIとアプリ画面の遷移 InitiateAuth API RespondToAuthChallenge API

32.
[beta]
AWS SDKを使用したサインインの実装例(MFA版)
❶

// InitiateAuth API(サインイン)
const init_auth_cmd = new InitiateAuthCommand({
AuthFlow: 'USER_PASSWORD_AUTH',
ClientId: '<CognitoのクライアントID>',
AuthParameters: {
USERNAME: email,
PASSWORD: password
}
});
const init_auth_res = await client.send(init_auth_cmd);

❷

// RespondToAuthChallenge API(チャレンジ応答)
const challenge_cmd = new RespondToAuthChallengeCommand({
ClientId: '<CognitoのクライアントID>',
ChallengeName: init_auth_res.ChallengeName,
Session: init_auth_res.Session,
ChallengeResponses: {
EMAIL_OTP_CODE: '<メールで受信したコード>',
USERNAME: email
}
});
const challenge_res = await client.send(challenge_cmd);

EメールMFAのチャレンジ名は
• ChallengeName: EMAIL_OTP
トークンが返る
• AccessToken
• IdToken
• RefreshToken

33.
[beta]
AWS Amplifyライブラリを使用したサインインの実装例(MFA版)
import { Amplify } from 'aws-amplify';
import { signIn, fetchAuthSession, confirmSignIn } from 'aws-amplify/auth';
Amplify.configure({
Auth: {
Cognito: {
userPoolId: '<CognitoのユーザープールID>',
userPoolClientId: '<CognitoのクライアントID>',
userPoolEndpoint: '<API GatewayのURL>'
}
}
});

❶

// サインイン
const res = await signIn({ username: email, password });

❷

if (res.nextStep.signInStep === 'CONFIRM_SIGN_IN_WITH_EMAIL_CODE') {
// チャレンジ応答
await confirmSignIn({ challengeResponse: '<メールで受信したコード>' });

❸

// トークン取得
const session = await fetchAuthSession();

}

セッションからトークン取得
• AccessToken
• IdToken

34.

Cognito MFAでカスタム可能な要素 • MFAコードのタイムアウト時間 • タイムアウトした場合 RespondToAuthChallenge API にて エラーとなる • 「Cognito アプリケーションクライアント > 認証フローセッションの持続期間」から設定可能 • MFAコードのメール内容 • 件名とメッセージをカスタム可能 • 「{####}」部分がMFAコードに置き換わる

35.

作成したクライアントアプリの実装はGitHubに公開しています • https://github.com/kkmtyyz/aws-cognito-client-sample • アプリの実装は2つ • Cognito SDKを使用した実装: /apps/using-cognito-sdk • Amplifyライブラリを使用した実装: /apps/using-amplify-lib

36.

まとめ • CognitoのエンドポイントはAPI Gatewayでプロキシできる • クライアントにAmplifyライブラリを使う場合、CORS設定が必要 • Cognito SDKを使ってAPIサーバーでプロキシする場合、CORS設定は不要 • CognitoのEメールMFAにはSESの利用が必須 • 送信元メールアドレスは、受信可能かつSESで検証済みである必要がある • 次ページから「SES本稼働にむけて」

37.

SES本稼働にむけて

38.

SESにはサンドボックス環境と本番環境がある • デフォルトではサンドボックス環境として機能が制限されている • 例えば、 送信先は検証済みメールアドレス or ドメインのみ 24時間あたり最大200メッセージのみ送信可能 • 本番環境への移行には本稼働リクエストを行う必要がある 本稼働アクセスのリクエスト (Amazon SES サンドボックスからの移行): https://docs.aws.amazon.com/ja_jp/ses/latest/dg/request-production-access.html

39.

SESコンソールから本稼働リクエストが行える • リクエストを送信するとAWSサポートにサポートケースが作成され、 審査に必要な情報を返信するようメッセージが来る

40.

SES本稼働リクエストの審査でサポートから求められる内容 • 「メール送信プロセスや手順について、可能な限り詳しい情報を記載してく ださい」 • メールを送信する頻度 • 受信者リストのメンテナンス方法 • バウンス、申し立て、解除申請の管理方法 • 送信する予定のメールのサンプル • 返信後、早ければ24時間以内に承認される • バウンス、申し立ては以下ドキュメントを一読しておくとよい Amazon SES における E メール配信可能性の概要: https://docs.aws.amazon.com/ja_jp/ses/latest/dg/send-email-concepts-deliverability.html

41.

バウンス、申し立て、解除申請の対策は自前でも行う • バウンス対策、申し立て対策、ユーザーからの配信解除申請の対応はSESの 機能で実現できる • アカウントレベルのサプレッションリスト • 配信解除リンクの自動埋め込み、それを受けた配信停止 • しかし、柔軟な運用や、送信先アドレス管理のためにも自前でも仕組みを作 るのがおすすめ

42.

アカウントレベルのサプレッションリスト • バウンスや申し立てとなったアドレスは自動的に追加される • アドレスを手動で追加・削除することが可能 • サプレッションリストのアドレスへ送信されるメールはSESによりブロック される [email protected]にメール送信 サプレッションリストにあるからダメ!

43.

配信解除リンクの自動埋め込み、それを受けた配信停止(準備) • 設定セットを作成するとバウンス・申し立て・配信解 除申請などのイベントをSNSへ配信できる • 配信解除したアドレスを保存するため、 空のContactListを作成する • ContactListはコンソールから触れない点に注意 $ aws sesv2 create-contact-list --contact-list-name "MyContactList"

44.
[beta]
配信解除リンクの自動埋め込み、それを受けた配信停止(送信)
• 本文の {{amazonSESUnsubscribeUrl}} が配信停止URLに置き換わる
• 設定セットとContactListを指定して送信する
sesv2 = boto3.client('sesv2', region_name='ap-northeast-1')
# メール送信
response = sesv2.send_email(
FromEmailAddress='[email protected]', # 送信元
Destination={ 'ToAddresses': ['[email protected]'] }, # 送信先
Content={
'Simple': {
'Subject': { 'Data': '配信停止リンク付きメール' }, # 件名
'Body': {
'Html': {
'Data': '<a href="{{amazonSESUnsubscribeUrl}}">配信停止はこちら</a>'
}
}
}
},
ConfigurationSetName='MyConfigurationSet', # 設定セット
ListManagementOptions={ 'ContactListName': 'MyContactList', } # ContentList
)

45.

受信した配信解除リンク付きメールと、配信解除ページ 配信解除ページ (AWSがホストしてるWebページ) 配信解除リンク

46.
[beta]
配信解除するとContactListに登録される
• 以降、このアドレスへはメール送信不可となる

$ aws sesv2 list-contacts --contact-list-name MyContactList
{
"Contacts": [
{
"EmailAddress": "[email protected]",
"UnsubscribeAll": true,
"LastUpdatedTimestamp": "2025-09-28T05:58:12.895000+09:00"
}
]
}
$

47.

送信先アドレス管理のアーキテクチャ • DynamoDBで送信先アドレスを管理する • バウンス、申し立て、解除申請などのイベント時にLambda関数で管理テー ブルを更新する • メール送信前に管理テーブルで送信先の状態を確認する

48.

SESコンソールからバウンスや申し立てのテストが可能

49.

ご清聴ありがとうございました。