🚀
もう迷わないCognito×Google連携:TerraformとReact(Vite)で作る認証付きSPA
目的
- React(Vite)で「Hello」ページを表示
- 未ログイン時はCognito Hosted UI経由でGoogleログイン
- IaCでCognitoを構築し、シークレットはシェル環境変数で安全に管理
完成イメージ
- フロント: React + Vite +
@aws-amplify/ui-react
のAuthenticator
- 認証: Cognito(Hosted UI, Authorization Code + PKCE)+ Google IdP
- IaC: TerraformでUserPool/Domain/AppClient/Google IdPを作成
- シークレット:
TF_VAR_*
環境変数(シェル)で注入
参考(公式)
- Amplify Hosting(Terraformリソース): aws_amplify_app
- Cognito User Pool: cognito_user_pool
- Cognito User Pool Domain: cognito_user_pool_domain
- Cognito Identity Provider (Google): cognito_identity_provider
- React × Amplify × Cognito ガイド: Create a React app and add Cognito auth
手順
1) Terraform でCognitoを構築
- 作業ディレクトリ例:
<PROJECT_ROOT>/infra
変数(infra/variables.tf
)
variable "aws_region" { type = string, default = "<AWS_REGION>" } # 例: ap-northeast-1
variable "cognito_domain_prefix" { type = string } # 例: "<COGNITO_DOMAIN_PREFIX>"
variable "google_client_id" { type = string, sensitive = true }
variable "google_client_secret" { type = string, sensitive = true }
メイン(infra/main.tf
)
terraform {
required_version = ">= 1.13.1"
required_providers {
aws = { source = "hashicorp/aws", version = "~> 5.0" }
random = { source = "hashicorp/random", version = "~> 3.6" }
}
}
provider "aws" { region = var.aws_region }
data "aws_region" "current" {}
resource "random_string" "suffix" { length = 6, upper = false, special = false }
resource "aws_cognito_user_pool" "pool" { name = "<PRODUCT_NAME>-user-pool" }
resource "aws_cognito_user_pool_domain" "domain" {
domain = "${var.cognito_domain_prefix}-${random_string.suffix.result}"
user_pool_id = aws_cognito_user_pool.pool.id
}
resource "aws_cognito_identity_provider" "google" {
user_pool_id = aws_cognito_user_pool.pool.id
provider_name = "Google"
provider_type = "Google"
provider_details = {
client_id = var.google_client_id
client_secret = var.google_client_secret
authorize_scopes = "openid email profile"
}
attribute_mapping = { email = "email" }
}
resource "aws_cognito_user_pool_client" "web" {
name = "web-client"
user_pool_id = aws_cognito_user_pool.pool.id
generate_secret = false
allowed_oauth_flows_user_pool_client = true
allowed_oauth_flows = ["code"]
allowed_oauth_scopes = ["openid","email","profile"]
callback_urls = ["http://localhost:<DEV_PORT>/"] # 例: 5173
logout_urls = ["http://localhost:<DEV_PORT>/"]
supported_identity_providers = ["Google"]
depends_on = [aws_cognito_identity_provider.google]
}
output "cognito_hosted_ui_domain" {
value = "https://${aws_cognito_user_pool_domain.domain.domain}.auth.${data.aws_region.current.name}.amazoncognito.com"
}
output "google_authorized_redirect_uri" {
value = "https://${aws_cognito_user_pool_domain.domain.domain}.auth.${data.aws_region.current.name}.amazoncognito.com/oauth2/idpresponse"
}
output "user_pool_id" { value = aws_cognito_user_pool.pool.id }
output "user_pool_web_client_id" { value = aws_cognito_user_pool_client.web.id }
infra/terraform.tfvars
(例)
aws_region = "<AWS_REGION>" # 例: ap-northeast-1
cognito_domain_prefix = "<COGNITO_DOMAIN_PREFIX>"
2) シークレットはシェルで安全に管理
infra/set-google-creds.sh
#!/bin/bash
# 使い方: source set-google-creds.sh
export TF_VAR_google_client_id="<GOOGLE_CLIENT_ID>"
export TF_VAR_google_client_secret="<GOOGLE_CLIENT_SECRET>"
echo "Google OAuth環境変数を設定しました。"
.gitignore
(抜粋)
*.tfstate
.terraform/
*.tfvars
infra/set-google-creds.sh
3) Terraform 適用
cd <PROJECT_ROOT>/infra
source set-google-creds.sh
terraform init
terraform apply -auto-approve
terraform output
出力例(プレースホルダ)
cognito_hosted_ui_domain = "https://<COGNITO_DOMAIN_PREFIX>-<RAND>.auth.<AWS_REGION>.amazoncognito.com"
google_authorized_redirect_uri = "https://<COGNITO_DOMAIN_PREFIX>-<RAND>.auth.<AWS_REGION>.amazoncognito.com/oauth2/idpresponse"
user_pool_id = "<USER_POOL_ID>"
user_pool_web_client_id = "<USER_POOL_WEB_CLIENT_ID>"
4) Google 側の設定
- OAuthクライアント(Web)で以下を登録
- Authorized redirect URIs:
https://<COGNITO_SUBDOMAIN>.auth.<AWS_REGION>.amazoncognito.com/oauth2/idpresponse
- Authorized JavaScript origins:
-
http://localhost:<DEV_PORT>
(例: 5173)
-
- Authorized redirect URIs:
- 組織内限定にしたい場合は「同意画面」を“Internal”
5) フロント(Vite + Amplify)を作成
cd <PROJECT_ROOT>
npm create vite@latest <FRONTEND_DIR> -- --template react-ts
cd <FRONTEND_DIR>
npm i
npm i aws-amplify @aws-amplify/ui-react
src/amplifyClient.ts
import { Amplify } from 'aws-amplify';
Amplify.configure({
Auth: {
Cognito: {
userPoolId: '<USER_POOL_ID>',
userPoolClientId: '<USER_POOL_WEB_CLIENT_ID>',
loginWith: {
oauth: {
domain: '<COGNITO_DOMAIN>',
scopes: ['openid', 'email', 'profile'],
redirectSignIn: ['http://localhost:5173/'],
redirectSignOut: ['http://localhost:5173/'],
responseType: 'code',
},
},
},
},
});
src/main.tsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import '@aws-amplify/ui-react/styles.css'
import './AmplifyClient'
import App from './App.tsx'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)
src/App.tsx
import { Authenticator } from '@aws-amplify/ui-react';
export default function App() {
const components = {
SignIn: {
FormFields() { return null }, // ユーザー名/パスワード非表示
Footer() { return null }, // 「Forgot...」非表示
},
};
return (
<div style={{ minHeight:'100vh', display:'flex', alignItems:'center', justifyContent:'center' }}>
<Authenticator components={components} socialProviders={['google']} hideSignUp>
{({ signOut, user }) => (
<div style={{ padding:24 }}>
<h1>Hello</h1>
<p>Signed in as: {user?.username}</p>
<button onClick={signOut}>Sign out</button>
</div>
)}
</Authenticator>
</div>
);
}
src/index.css
/* AuthenticatorでGoogleボタン以外を非表示(区切り線も消す) */
[data-amplify-form] > :not(.federated-sign-in-container),
.federated-sign-in-container > hr { display: none; }
.federated-sign-in-container { padding: 0 !important; }
起動
npm run dev
# http://localhost:<DEV_PORT>
未ログインならHosted UI → Googleへ遷移、ログイン後に「Hello」を表示。
UI周りの調整
セキュリティの要点
- SPAは「Authorization Code + PKCE」、クライアントシークレットは使わない(ブラウザに出さない)
- GoogleのAuthorized Redirect URIはCognitoの
/oauth2/idpresponse
(取り違え注意) - 機密値(Googleクレデンシャル)は
TF_VAR_*
環境変数で注入し、リポジトリに含めない
Discussion