Cognito + Google OAuthで詰まる3つの罠|マンチーがハマった解決策

AWS・クラウド構築

はじめに

Cognito(AWS)にGoogleログインを連携する案件で、マンチーが3つの罠を全部踏み抜いた話です。

ECサイトに会員機能(SSO)を実装する時に発生した詰まりポイントなんですが、公式ドキュメント通りやってるのに動かないっていう、よくあるパターン。

ハマってる人の検索でこの記事に辿り着いてくれたら嬉しいので、解決策を共有します。


構成の前提

実装した構成はざっくり以下:

[ユーザー]
   ↓
[CloudFront(AWS) (+ CloudFront Functions)]
   ↓
[S3(AWS) (静的ホスティング)]
   ↓ ログインボタン押下
[Cognito Hosted UI]
   ↓ Continue with Google
[Google OAuth 2.0]
   ↓ 認可コード返却
[サイト:PKCE でトークン交換]
   ↓
[ログイン完了]

ECサイトを CloudFront + S3 で配信して、会員機能は Cognito で Google フェデレーション。 よくある構成です。


💢 罠その1:Cognitoの “name” 属性が空になる

何が起きた?

ユーザーが Google ログインを完了した瞬間に、Cognito 側でエラー:

attributes required: [name]

name属性が必須なのに空です」と弾かれる。

設定は問題ないはず

Google IdP の属性マッピングを見ると:

Cognito属性Google属性
emailemail
namename

公式ドキュメント通りに name ← name で設定済み。何も間違ってない

原因

Google が返す Open ID Connect(以降:OIDC)レスポンスに、name(フルネーム)が含まれないケースがあるんです。

理由は色々:

  • ユーザーが Google アカウントで本名を非公開設定
  • Workspace アカウントで管理者がフルネーム表示を制限してる
  • ニックネームのみ登録の人

→ つまり、Googleの仕様上、name は必ず返る保証がない

それなのに Cognito User Pool 側は name必須属性にしてた。作成後は変更不可なので、ここが詰まる。

🔑 解決策

属性マッピングを変更:

Cognito属性Google属性
namegiven_name

given_name(名のみ)は、Google 側で必ず返るフィールド。

姓・名のフルネームじゃなくなるけど、確実に動く

学び

OIDC の「標準仕様」と「実装の挙動」は乖離する。

特にフェデレーション(Google・Facebook・Apple等)を使う場合は:

  • name(フルネーム)に依存しない
  • given_name / family_name(個別フィールド)へフォールバック設計
  • 必須属性は最小限にする(後で追加できないため)

💢 罠その2:CloudFront Functions JS-2.0 で set-cookie 仕様が変わってた

何が起きた?

CloudFront Functions で認証Cookieを発行する処理を実装。 レスポンスヘッダーに set-cookie を入れて返したら、エラー:

The CloudFront function returned an invalid value:
response.headers cannot contain 'set-cookie' header.
Use response.cookies instead.
(MisplacedCookies)

set-cookie ヘッダーは使うな、response.cookies を使え」と弾かれる。

でも昔のサンプルでは動いてた

CloudFront Functions の古い情報をネットで調べると、set-cookie ヘッダーで書いてるコードがいっぱい出てくる。

// 昔のやり方(JS-1.0)
return {
  statusCode: 302,
  headers: {
    'location': { value: '/' },
    'set-cookie': { value: 'munchie_unlock=valid; Path=/; ...' }
  }
};

→ これだと MisplacedCookies エラー。

原因

CloudFront Functions のランタイムが JS-1.0 → JS-2.0 で仕様変更されてた。

バージョンCookie の渡し方
JS-1.0headers['set-cookie'] でOK
JS-2.0cookies フィールドに分離。set-cookie ヘッダーは禁止

🔑 解決策

response.cookies の独立フィールドで書く:

// 新しいやり方(JS-2.0)
return {
  statusCode: 302,
  headers: { 
    'location': { value: '/' } 
  },
  cookies: {
    'munchie_unlock': {
      value: 'valid',
      attributes: 'Path=/; Max-Age=2592000; Secure; HttpOnly; SameSite=Lax'
    }
  }
};

→ これでエラー解消。

学び

新ランタイムを使う時はリリースノート確認が必須

特に AWS のサーバーレス系は、バージョン違いで仕様が破壊的に変わることがある。

MisplacedCookies というエラーメッセージで検索したら一発で原因特定できたので、エラーメッセージはそのまま Google に投げるのが最速ルート。


💢 罠その3:OAuthコールバック URL のスラッシュ不一致

何が起きた?

Cognito Hosted UI で Google ログインを完了 → リダイレクト処理が動かない。 URL に ?code=xxx の認可コードが付かない。

Cognito 側の登録URL

https://xxx.cloudfront.net

末尾スラッシュなしで登録。

JS 側の redirect_uri

const redirectUri = window.location.origin + window.location.pathname;
// → "https://xxx.cloudfront.net/"

末尾スラッシュありになる(CloudFront のデフォルトドメインがそうなってるから)。

原因

OAuth 2.0 の仕様上、redirect_uri は完全一致を要求する。

場所URL
Cognito登録https://xxx.cloudfront.net
JS生成https://xxx.cloudfront.net/ ← 末尾スラッシュ

1文字違いで認証失敗

これ、エラーメッセージが出ないケースもあるので、原因特定に時間かかった。

🔑 解決策

Cognito の許可コールバック URLに、両方の表記を登録:

https://xxx.cloudfront.net

https://xxx.cloudfront.net/

→ どっちで来ても通る。

学び

OAuth 2.0 のURIマッチングは厳密

特に CloudFront のデフォルトドメイン(*.cloudfront.net)は末尾スラッシュ付きでアクセスされる挙動なので、スラッシュあり/なし両方を許可URLに登録しておくのが安全です。カスタムドメインを使う場合や、パス付きURLでも、同じ罠が発生します。


まとめ

Cognito と Google OAuth 連携で踏んだ罠と対策:

原因対策
1. name属性が空でエラーGoogleが name を返さないケースありgiven_name にマッピング変更
2. set-cookie ヘッダー禁止CloudFront Functions JS-2.0 仕様response.cookies に分離
3. redirect_uri 不一致スラッシュ1文字の差両方の表記を登録

教訓

公式ドキュメント通り設定しても、仕様外の挙動で詰まることはある

特にフェデレーション認証は:

  • IdP(Google等)の挙動が標準仕様と乖離
  • AWSサービスのランタイムバージョンで仕様変更
  • URI完全一致の罠

エラーメッセージをそのまま検索するのが、解決の最短ルートでした。

コメント

タイトルとURLをコピーしました