2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS SAM:Cognito認証付きアプリを構築してみたハンズオンやってみた!

Posted at

はじめに

AWS SAM(Serverless Application Model)を使って、Cognito認証付きのサーバーレスアプリケーションを構築してみました。

昨夜のうちに検証して投稿しようと頑張っていたのですが、検証でうまくいかない部分があり、最終的にまとめ終わったのは翌朝になってしまいました…!(思った以上に手こずりました...Cognito久しぶりだったから

今回は、S3によるWebホスティング、CloudFrontによるHTTPS配信、API Gateway + Lambda + DynamoDB のバックエンド、そしてCognitoによるユーザー認証を統合した構成です。

AWS SAM 本当に便利で連日、感動中です...!!

書こうと思ったきっかけ

個人的に「SAMでCognitoを使ってみたい!!」という気持ちがずっとありました。普段は手動でCognitoを設定していましたが、インフラをすべてコード化することで理解が深まると思い、試してみることにしました...!

前回の記事の追加検証になります!

構成図はこんな感じになります(CloudFrontとCognitoを追加)

Screenshot 2025-06-11 at 7.23.32.png

※内容に不備などがございましたら、お手数ですが優しくご指摘いただけますと幸いです。

ディレクトリ構成

本構成のディレクトリ構成は以下のようになっています。

sam-cognito-api-example/
├── template.yaml         # SAMテンプレート(Cognito追加あり)
├── samconfig.toml        # デプロイ設定ファイル
├── hello_world/
│   ├── app.py            # Lambda関数(DynamoDB読み取り)
│   └── __init__.py       # 空でOK
└── frontend/
    ├── index.html        # Cognitoログインボタンあり
    ├── callback.html     # トークン取得・API呼び出し
    └── script.js         # トークン処理&APIアクセス

※今回は、フロントエンドやバックエンドのコードには触れず、SAM テンプレートに焦点を当てて、デプロイ手順を中心に解説していきます。

テンプレート内容

今回の構成に含まれるリソースは以下の通りです:

  • S3バケットindex.html をホスティングする静的ウェブサイト
  • CloudFront:HTTPSでの配信対応(redirect-to-https
  • DynamoDBItemsTable に保存されたデータをAPI経由で取得
  • Cognito User Pool / Client / Domain:ログイン機能を提供
  • API Gateway + Lambda:APIとして/itemsエンドポイントを提供し、DynamoDBの内容を返す
  • Cognito Authorizer:API Gateway によるCognito認証

このテンプレートでは、CloudFormationテンプレートでCognito認証付きの構成全体を自動展開できるようになっています。

ユーザーは、CloudFront経由で配信されるWebアプリにアクセスし、Cognitoログイン後にAPIを呼び出してDynamoDBのデータを取得するという流れです。

以下は、実際に使用したテンプレートコードです:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: API Gateway + Lambda + DynamoDB + S3 + Cognito Auth

Globals:
  Function:
    Timeout: 10

Resources:

  WebHostingBucket:
    Type: AWS::S3::Bucket
    Properties:
      WebsiteConfiguration:
        IndexDocument: index.html
      OwnershipControls:
        Rules:
          - ObjectOwnership: ObjectWriter
      PublicAccessBlockConfiguration:
        BlockPublicAcls: false
        BlockPublicPolicy: false
        IgnorePublicAcls: false
        RestrictPublicBuckets: false

  BucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref WebHostingBucket
      PolicyDocument:
        Statement:
          - Action: s3:GetObject
            Effect: Allow
            Resource: !Sub "${WebHostingBucket.Arn}/*"
            Principal: "*"

  ItemsTable:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: ItemsTable
      AttributeDefinitions:
        - AttributeName: id
          AttributeType: S
      KeySchema:
        - AttributeName: id
          KeyType: HASH
      BillingMode: PAY_PER_REQUEST

  MyUserPool:
    Type: AWS::Cognito::UserPool
    Properties:
      UserPoolName: MyUserPool
      AutoVerifiedAttributes:
        - email
      UsernameAttributes:
        - email

  # CloudFront Distribution for HTTPS
  CloudFrontDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Origins:
          - DomainName: !Select [2, !Split ["/", !GetAtt WebHostingBucket.WebsiteURL]]
            Id: S3Origin
            CustomOriginConfig:
              HTTPPort: 80
              OriginProtocolPolicy: http-only
        Enabled: true
        DefaultRootObject: index.html
        DefaultCacheBehavior:
          TargetOriginId: S3Origin
          ViewerProtocolPolicy: redirect-to-https
          AllowedMethods: [GET, HEAD]
          CachedMethods: [GET, HEAD]
          ForwardedValues:
            QueryString: false
            Cookies:
              Forward: none
        Comment: !Sub "${AWS::StackName} CloudFront Distribution"

  MyUserPoolClient:
    Type: AWS::Cognito::UserPoolClient
    Properties:
      ClientName: MyUserPoolClient
      UserPoolId: !Ref MyUserPool
      GenerateSecret: false
      AllowedOAuthFlowsUserPoolClient: true
      AllowedOAuthFlows:
        - implicit
      AllowedOAuthScopes:
        - email
        - openid
      CallbackURLs:
        - !Sub "https://${CloudFrontDistribution.DomainName}/callback.html"
      SupportedIdentityProviders:
        - COGNITO

  # Cognito User Pool Domain(標準ドメイン)
  MyUserPoolDomain:
    Type: AWS::Cognito::UserPoolDomain
    Properties:
      Domain: !Sub "myapp-${AWS::StackName}-${AWS::AccountId}"
      UserPoolId: !Ref MyUserPool

  MyApi:
    Type: AWS::Serverless::Api
    Properties:
      Name: MyApi
      StageName: Prod
      Cors:
        AllowMethods: "'GET,OPTIONS'"
        AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'"
        AllowOrigin: "'*'"
      Auth:
        DefaultAuthorizer: CognitoAuthorizer
        AddDefaultAuthorizerToCorsPreflight: false
        Authorizers:
          CognitoAuthorizer:
            UserPoolArn: !GetAtt MyUserPool.Arn

  GetItemsFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.12
      Environment:
        Variables:
          TABLE_NAME: !Ref ItemsTable
      Policies:
        - DynamoDBReadPolicy:
            TableName: !Ref ItemsTable
  Events:
    GetItemsApi:
      Type: Api
      Properties:
        RestApiId: !Ref MyApi
        Path: /items
        Method: get

    OptionsItemsApi:
      Type: Api
      Properties:
        RestApiId: !Ref MyApi
        Path: /items
        Method: options

Outputs:
  WebURL:
    Value: !GetAtt WebHostingBucket.WebsiteURL
    Description: "S3 Static Website URL (HTTP)"

  CloudFrontURL:
    Value: !Sub "https://${CloudFrontDistribution.DomainName}"
    Description: "CloudFront Distribution URL (HTTPS)"

  ApiURL:
    Value: !Sub "https://${MyApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/items"

  UserPoolId:
    Value: !Ref MyUserPool

  UserPoolClientId:
    Value: !Ref MyUserPoolClient

  CognitoDomainURL:
    Value: !Sub "https://${MyUserPoolDomain}.auth.${AWS::Region}.amazoncognito.com"
    Description: "Cognito認証用ドメインURL"

  CallbackURL:
    Value: !Sub "https://${CloudFrontDistribution.DomainName}/callback.html"
    Description: "Cognito Callback URL (HTTPS)"

AWS環境にデプロイしてみた

上記の構成を sam deploy でデプロイしたところ、以下のような流れでサーバーレスアプリが立ち上がりました。

  • sam build && sam deploy で全リソースを一括作成

Screenshot 2025-06-11 at 7.38.22.png

  • S3バケットに frontend/index.html などをアップロード

→実際に使ったコマンド

aws s3 cp frontend/ s3://バケット名 --recursive

問題なくアップロードされていることが確認できました!

Screenshot 2025-06-11 at 7.38.43.png

  • CloudFrontのURLにアクセスすると、Cognitoログインボタン付きのトップページが表示される

Screenshot 2025-06-11 at 7.40.19.png

ログイン画面

Screenshot 2025-06-11 at 7.41.57.png

すでにCognito側でユーザーを作成済みとなりますので、そのユーザーでログインを試していきます。

Screenshot 2025-06-11 at 7.44.19.png

問題なくログインできることが確認できましたので、今回の検証はこれで以上とします!

Screenshot 2025-06-11 at 7.47.12.png

CognitoのOAuth連携やトークンの扱いも callback.html + script.js で処理しており、かなり実践的な構成になったと思います...!(少し難しかったです!)

まとめ

ここまでお読みいただき、ありがとうございました!

AWS SAMを使うことで、サーバーレスアプリの複雑な構成もテンプレート1枚で管理でき、Cognito認証との統合もスムーズに行えました。

今後はこの構成をベースに、ユーザー登録・データ投稿機能の追加などを行っていきたいと考えています。

Cognito連携の自動化に挑戦したい方にとっても、良い学びの機会になる構成だと思います...!!

不要になったリソースは以下のコマンドで削除できますので、適宜削除しておくことをおすすめします。

aws cloudformation delete-stack --stack-name sam-app

今後も応用的な構成に対応できるよう、引き続きキャッチアップを進めていきたいと思います...!

参考文献

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?