【小ネタ】AWS Cognito + CloudFrontで最低限の認証機能付きWebブラウザを実装しよう

いつも記事を読んでいただきありがとうございます！
モブエンジニア（[@mob-engineer](https://qiita.com/mob_engineer))です！

社内開発を行うなかで**Webブラウザの動作確認**を行うことがあると思います。そのうえで、**セキュリティを考慮したうえで認証設定を行いたいが、めんどくさい実装は行いたくない**といった悩みはあるかと思います。

私個人も社内開発で、**最低限の設定だけで認証機能付きWebブラウザ**を実装したいと思っていました。そのうえで、AWS Cognito + CloudFrontを利用したら**とりあえず社内検証用であれば問題ないレベル**の認証機能付きWebブラウザが実装できそうだと分かりました。

今後、使う機会があると思いましたので記事として残しておきたいと思います。

# 目次
- 対象読者
- 構成イメージ
- やってみたこと
- 所感

## 対象読者

次のような課題を抱えている方向けに本記事をお役立ていただければ幸いです。

- とりあえず、楽に認証機能付きWebブラウザを実装したい方
- 社内検証用のWebブラウザの管理方法を模索している方
- サービス間連携をキャッチアップしたい方

## 構成イメージ

ざっくりですが、次のような構成をゴールとします。

![Cognito＋CloudFront.drawio (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3846878/ef6a7f37-d655-497f-9c78-5eb1a987da8e.png)

## やってみたこと

今回構築するうえで、あらかじめテストページを作成しておきました。

```html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>株式会社シャーク | FinOps サメ - 革新的な金融ソリューション</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Helvetica Neue', Arial, 'Hiragino Sans', 'Hiragino Kaku Gothic ProN', Meiryo, sans-serif;
            line-height: 1.6;
            color: #333;
        }

        .container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 0 20px;
        }

        /* ヘッダー */
        header {
            background: linear-gradient(135deg, #1a237e, #283593);
            color: white;
            padding: 1rem 0;
            position: fixed;
            width: 100%;
            top: 0;
            z-index: 1000;
            box-shadow: 0 2px 5px rgba(0,0,0,0.1);
        }

        nav {
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .logo {
            font-size: 1.8rem;
            font-weight: bold;
            display: flex;
            align-items: center;
        }

        .logo-icon {
            width: 40px;
            height: 40px;
            margin-right: 10px;
            background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><path d="M50 15 L80 40 L80 70 L50 85 L20 70 L20 40 Z" fill="%233949ab" stroke="white" stroke-width="3"/><path d="M40 50 L60 30 L60 70 Z" fill="white"/></svg>') no-repeat center;
            background-size: contain;
        }

        .nav-links {
            display: flex;
            list-style: none;
        }

        .nav-links li {
            margin-left: 2rem;
        }

        .nav-links a {
            color: white;
            text-decoration: none;
            transition: opacity 0.3s;
        }

        .nav-links a:hover {
            opacity: 0.8;
        }

        /* ヒーローセクション */
        .hero {
            background: linear-gradient(135deg, #283593, #3949ab);
            color: white;
            padding: 8rem 0 4rem;
            margin-top: 5rem;
            position: relative;
            overflow: hidden;
        }

        .hero::before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320"><path fill="%23ffffff" fill-opacity="0.1" d="M0,192L48,197.3C96,203,192,213,288,229.3C384,245,480,267,576,250.7C672,235,768,181,864,181.3C960,181,1056,235,1152,234.7C1248,235,1344,181,1392,154.7L1440,128L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z"></path></svg>') no-repeat bottom;
            background-size: cover;
        }

        .hero-content {
            display: flex;
            justify-content: space-between;
            align-items: center;
            position: relative;
            z-index: 1;
        }

        .hero-text {
            flex: 1;
            max-width: 600px;
        }

        .hero h1 {
            font-size: 3.5rem;
            margin-bottom: 1rem;
            line-height: 1.2;
        }

        .hero p {
            font-size: 1.3rem;
            margin-bottom: 2rem;
            opacity: 0.9;
        }

        .cta-button {
            display: inline-block;
            padding: 1rem 2.5rem;
            background: #00bcd4;
            color: white;
            text-decoration: none;
            border-radius: 30px;
            font-weight: bold;
            transition: transform 0.3s, box-shadow 0.3s;
        }

        .cta-button:hover {
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(0,188,212,0.3);
        }

        .hero-image {
            flex: 1;
            text-align: center;
        }

        .shark-animation {
            width: 400px;
            height: 300px;
            background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 300"><path d="M50 150 Q200 80 350 150 Q200 220 50 150" fill="%231e88e5" stroke="white" stroke-width="3"/><circle cx="100" cy="130" r="8" fill="white"/><path d="M350 150 L380 130 L380 170 Z" fill="%231e88e5" stroke="white" stroke-width="2"/></svg>') no-repeat center;
            background-size: contain;
            animation: swim 3s ease-in-out infinite;
        }

        @keyframes swim {
            0%, 100% { transform: translateY(0); }
            50% { transform: translateY(-20px); }
        }

        /* 特徴セクション */
        .features {
            padding: 5rem 0;
            background: #f8f9fa;
        }

        .section-title {
            text-align: center;
            font-size: 2.5rem;
            margin-bottom: 3rem;
            color: #283593;
        }

        .features-grid {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 2rem;
        }

        .feature-card {
            background: white;
            padding: 2rem;
            border-radius: 10px;
            box-shadow: 0 5px 15px rgba(0,0,0,0.1);
            text-align: center;
            transition: transform 0.3s;
        }

        .feature-card:hover {
            transform: translateY(-5px);
        }

        .feature-icon {
            font-size: 3rem;
            margin-bottom: 1rem;
            color: #00bcd4;
        }

        .feature-card h3 {
            color: #283593;
            margin-bottom: 1rem;
        }

        /* サービスセクション */
        .services {
            padding: 5rem 0;
        }

        .services-grid {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 2rem;
            margin-top: 3rem;
        }

        .service-item {
            display: flex;
            align-items: center;
            padding: 2rem;
            background: #f8f9fa;
            border-radius: 10px;
            transition: background 0.3s;
        }

        .service-item:hover {
            background: #eceff1;
        }

        .service-icon {
            font-size: 2.5rem;
            margin-right: 1.5rem;
            color: #283593;
        }

        /* 統計セクション */
        .stats {
            background: linear-gradient(135deg, #283593, #3949ab);
            color: white;
            padding: 4rem 0;
        }

        .stats-grid {
            display: grid;
            grid-template-columns: repeat(4, 1fr);
            gap: 2rem;
            text-align: center;
        }

        .stat-item h3 {
            font-size: 3rem;
            margin-bottom: 0.5rem;
        }

        /* CTA セクション */
        .cta-section {
            background: #00bcd4;
            color: white;
            padding: 4rem 0;
            text-align: center;
        }

        .cta-section h2 {
            font-size: 2.5rem;
            margin-bottom: 1rem;
        }

        .cta-section p {
            font-size: 1.2rem;
            margin-bottom: 2rem;
        }

        .cta-secondary {
            background: white;
            color: #00bcd4;
            padding: 1rem 2.5rem;
            border-radius: 30px;
            text-decoration: none;
            font-weight: bold;
            transition: transform 0.3s;
        }

        .cta-secondary:hover {
            transform: translateY(-2px);
        }

        /* フッター */
        footer {
            background: #1a237e;
            color: white;
            padding: 3rem 0 1rem;
        }

        .footer-content {
            display: grid;
            grid-template-columns: repeat(4, 1fr);
            gap: 2rem;
            margin-bottom: 2rem;
        }

        .footer-section h4 {
            margin-bottom: 1rem;
            font-size: 1.2rem;
        }

        .footer-section ul {
            list-style: none;
        }

        .footer-section ul li {
            margin-bottom: 0.5rem;
        }

        .footer-section ul li a {
            color: #ccc;
            text-decoration: none;
            transition: color 0.3s;
        }

        .footer-section ul li a:hover {
            color: white;
        }

        .footer-bottom {
            text-align: center;
            padding-top: 2rem;
            border-top: 1px solid #283593;
            color: #ccc;
        }

        /* レスポンシブ対応 */
        @media (max-width: 768px) {
            .hero h1 {
                font-size: 2.5rem;
            }

            .hero-content {
                flex-direction: column;
                text-align: center;
            }

            .shark-animation {
                width: 300px;
                height: 200px;
                margin-top: 2rem;
            }

            .features-grid,
            .services-grid {
                grid-template-columns: 1fr;
            }

            .stats-grid {
                grid-template-columns: repeat(2, 1fr);
            }

            .footer-content {
                grid-template-columns: repeat(2, 1fr);
            }
        }
    </style>
</head>
<body>
    <header>
        <nav class="container">
            <div class="logo">
                <div class="logo-icon"></div>
                株式会社シャーク
            </div>
            <ul class="nav-links">
                <li><a href="#services">サービス</a></li>
                <li><a href="#features">特徴</a></li>
                <li><a href="#about">会社概要</a></li>
                <li><a href="#contact">お問い合わせ</a></li>
            </ul>
        </nav>
    </header>

    <section class="hero">
        <div class="container">
            <div class="hero-content">
                <div class="hero-text">
                    <h1>FinOps サメ</h1>
                    <p>スピードと精度を極めた金融ソリューション。<br>ビジネスの成長を鋭く加速させます。</p>
                    <a href="#contact" class="cta-button">無料相談を申し込む</a>
                </div>
                <div class="hero-image">
                    <div class="shark-animation"></div>
                </div>
            </div>
        </div>
    </section>

    <section class="features" id="features">
        <div class="container">
            <h2 class="section-title">シャークの特徴</h2>
            <div class="features-grid">
                <div class="feature-card">
                    <div class="feature-icon">⚡</div>
                    <h3>高速処理</h3>
                    <p>AIを活用した高速データ処理により、リアルタイムで財務状況を把握。迅速な意思決定をサポートします。</p>
                </div>
                <div class="feature-card">
                    <div class="feature-icon">🔒</div>
                    <h3>セキュリティ</h3>
                    <p>銀行レベルのセキュリティで、お客様の大切なデータを徹底的に保護。安心してご利用いただけます。</p>
                </div>
                <div class="feature-card">
                    <div class="feature-icon">📈</div>
                    <h3>成長支援</h3>
                    <p>データ分析に基づく的確なアドバイスで、ビジネスの成長を強力にバックアップします。</p>
                </div>
            </div>
        </div>
    </section>

    <section class="services" id="services">
        <div class="container">
            <h2 class="section-title">サービス内容</h2>
            <div class="services-grid">
                <div class="service-item">
                    <div class="service-icon">💰</div>
                    <div>
                        <h3>財務コンサルティング</h3>
                        <p>専門家による財務分析と最適化提案で、収益性の向上を実現します。</p>
                    </div>
                </div>
                <div class="service-item">
                    <div class="service-icon">🔄</div>
                    <div>
                        <h3>自動化ソリューション</h3>
                        <p>会計処理の自動化により、業務効率を大幅に改善します。</p>
                    </div>
                </div>
                <div class="service-item">
                    <div class="service-icon">📊</div>
                    <div>
                        <h3>リアルタイム分析</h3>
                        <p>常に最新の財務状況を把握し、迅速な経営判断を可能にします。</p>
                    </div>
                </div>
                <div class="service-item">
                    <div class="service-icon">🎯</div>
                    <div>
                        <h3>戦略立案支援</h3>
                        <p>データに基づく戦略立案で、確実な成長を支援します。</p>
                    </div>
                </div>
            </div>
        </div>
    </section>

    <section class="stats">
        <div class="container">
            <div class="stats-grid">
                <div class="stat-item">
                    <h3>500+</h3>
                    <p>導入企業数</p>
                </div>
                <div class="stat-item">
                    <h3>35%</h3>
                    <p>平均コスト削減率</p>
                </div>
                <div class="stat-item">
                    <h3>24/7</h3>
                    <p>サポート体制</p>
                </div>
                <div class="stat-item">
                    <h3>99.9%</h3>
                    <p>システム稼働率</p>
                </div>
            </div>
        </div>
    </section>

    <section class="cta-section" id="contact">
        <div class="container">
            <h2>今すぐFinOps サメを体験しませんか？</h2>
            <p>無料相談で、貴社に最適なソリューションをご提案いたします。</p>
            <a href="#" class="cta-secondary">無料相談を申し込む</a>
        </div>
    </section>

    <footer>
        <div class="container">
            <div class="footer-content">
                <div class="footer-section">
                    <h4>株式会社シャーク</h4>
                    <p>革新的な金融ソリューションで<br>ビジネスの成長を加速</p>
                </div>
                <div class="footer-section">
                    <h4>サービス</h4>
                    <ul>
                        <li><a href="#">財務コンサルティング</a></li>
                        <li><a href="#">自動化ソリューション</a></li>
                        <li><a href="#">リアルタイム分析</a></li>
                        <li><a href="#">戦略立案支援</a></li>
                    </ul>
                </div>
                <div class="footer-section">
                    <h4>企業情報</h4>
                    <ul>
                        <li><a href="#">会社概要</a></li>
                        <li><a href="#">採用情報</a></li>
                        <li><a href="#">ニュース</a></li>
                        <li><a href="#">お問い合わせ</a></li>
                    </ul>
                </div>
                <div class="footer-section">
                    <h4>法的情報</h4>
                    <ul>
                        <li><a href="#">利用規約</a></li>
                        <li><a href="#">プライバシーポリシー</a></li>
                        <li><a href="#">特定商取引法に基づく表記</a></li>
                    </ul>
                </div>
            </div>
            <div class="footer-bottom">
                <p>&copy; 2025 株式会社シャーク All Rights Reserved.</p>
            </div>
        </div>
    </footer>
</body>
</html>
```

テストページは以下の通りです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3846878/0e20dcc2-9b07-4c07-a40c-dfc73f602dd3.png)

#### S3設定

Webサイトホスティングを有効にしたS3バケットを用意しておきます。
バケット内にテストページを格納しておきます。
（それ以外はデフォルト設定です）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3846878/d4fad5b8-143d-401e-8c7e-86f2f9c5443d.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3846878/a1909d8b-4d2d-4bcd-8f85-d3c8d070ff65.png)

### CloudFront設定

先ほど作成したS3バケットを紐づけるように設定しておきます。
セキュリティを考慮してOACの設定も行っておきます。

:::note warn
OAC設定後、S3バケットポリシーを更新する必要があります。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3846878/216754d9-dc96-4447-9e7c-999f61c6ffbd.png)

:::note info
ビューワープロトコルポリシーとして**Redirect HTTP to HTTPS**を選択しないとHTTPS接続してくれません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3846878/69487d8a-01d0-4e58-9674-d2a6a3f6447e.png)

:::

:::note warn
実際にアクセスするパスは**ディストリビューションドメイン名/index.html**となります。
:::

### Cognito設定

ユーザープールとログインユーザの設定を行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3846878/7477fc08-2cd9-481f-9d77-6677596e1638.png)

**ログインページを表示**ボタンをクリックすることで、ログイン画面が表示されます。
あとは、ログイン情報を入力するだけでWebブラウザが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3846878/6dbbd744-7448-4c73-919f-48e28795824b.png)

:::note info
CloudFrontいらないのではと思われるかもしれませんが、Cognitoを利用するうえでHTTPS認証が必須となるため、今回の構成の場合CloudFront経由でのアクセスが必要となります。
:::

## 所感

検証環境で利用するレベル感であれば、S3＋CloudFront＋Cognitoがお手軽に利用できると思います。そのうえで、パイプライン設定やSignerによるコード署名など幅広く組み合わせられると思います。

最後まで、記事をお読みいただきありがとうございました。