AWS AppSync を CORS 対応させる [Amazon CloudFront 使用]

こんにちは、広野です。

AWS AppSync はレスポンスが速くてサブスクリプションを手軽に作れて便利なのですが、ネイティブに CORS 対応はしていません。CORS が必要になった場合には、現時点では Amazon CloudFront をかぶせて CORS ヘッダーをオーバーライドするのが一番スマートかな、と思います。

と、思ってたら簡単には行かなかったので、気付いたことを残しておきます。

実装したアーキテクチャ

冒頭で説明したように、AWS AppSync はネイティブに CORS をサポートしていません。レスポンスで返ってくる Access-Control-Allow-Origin ヘッダーは “*” 固定になっています。そのため、これを Amazon CloudFront distribution でレスポンスヘッダーを上書きします。

CORS は https:// から始まる宛先への通信をサポートしています。AWS AppSync への Query は https の POST メソッドを使用しているようで、Amazon CloudFront を介して CORS を有効化させられます。ところが Subscription (WebSocket) は wss:// から始まる宛先になるので、ブラウザは CORS をサポートしていません。一般的に、WebSocket で同様のセキュリティ対策をしようと思ったら Origin ヘッダーのチェックをするようです。ということなので、Subscription 通信については Amazon CloudFront を通さずに行きます。他にもそのようにした理由がありますが、細かすぎるので最後の方で補足します。

さて、Query の CORS 有効化に話を戻します。レスポンスヘッダーを上書きするだけじゃん?と思うのですが、今回の構成では AWS AppSync が Amazon Cognito 認証になっています。その場合、リクエストの Authorization ヘッダーに Amazon Cognito から受け取ったトークンを格納して送りますので、Amazon CloudFront が AWS AppSync にそれを転送する必要があります。

ここで気を付けないといけない点があり、Authorization ヘッダーをオリジン (ここでは AWS AppSync) に転送するには、特定の設定方法でしか実装できません。それが以下の AWS ドキュメントに書いてあります。

まるっとビューワーヘッダー全体指定で転送するか、キャッシュポリシーに Authorization を明記するか、の大まかに 2 択です。私は今回のケースでは Amazon CloudFront にキャッシュをさせたくなかった (通信をパススルーさせたい) ので、キャッシュポリシーをマネージドの CachingDisabled を選択することにしていました。そのため、まるっとビューワーヘッダー全体指定の転送を選択しています。ただし、それだけでは転送されず、レスポンスヘッダーのオーバーライドで使用予定だった Access-Control-Allow-Headers に Authorization を追加すると転送されるようになりました。

今回の一番の目的である CORS 有効化ですが、この設定自体は簡単です。具体的には後述の設定の章を見て欲しいですが、レスポンスヘッダーポリシーに CORS 用設定があるので、そこに CORS 用のヘッダー情報を入れるだけです。また、オーバーライドする設定を有効にします。ここよりも、他のヘッダー設定の方が苦労しました。

 

React アプリ側は、以下のコードで AWS AppSync を呼び出すクライアントの設定をしています。※値は変えてます

Amplify.configure(
  {
    Auth: {
      Cognito: {
        userPoolId: import.meta.env.VITE_USERPOOLID,
        userPoolClientId: import.meta.env.VITE_USERPOOLWEBCLIENTID,
        identityPoolId: import.meta.env.VITE_IDPOOLID
      }
    },
    API: {
      GraphQL: {
        // AppSync の標準のエンドポイント
        endpoint: 'https://example1234567890000.appsync-api.ap-northeast-1.amazonaws.com/graphql',
        region: 'ap-northeast-1'
        defaultAuthMode: 'userPool',
        // 独自ドメインの CloudFront に置き換えたエンドポイント
        customEndpoint: 'https://xxx.hironoenterprise.com/graphql',
        customEndpointRegion: 'ap-northeast-1'
      }
    }
  }
);

CORS 有効化のため AWS AppSync に手作りカスタムドメインを追加構築したようなものなので、カスタムドメインの設定を追加したところ Query 通信にはカスタムドメイン (Amazon CloudFront のエンドポイント) に、Subscription 通信は AWS AppSync のエンドポイントにアクセスするようになりました。

 

さて、、、ここまでで CORS 有効化設定そのものは整いましたが、周辺のアーキテクチャについても次章で説明します。

追加のアーキテクチャ

もう 1 回同じ図を再掲します。

AWS AppSync への Query 通信を CORS 有効化できただけでも 1 つの進歩なのですが、まだ少々懸念が残っています。

  1. AWS AppSync に Amazon CloudFront をかぶせただけでは、AWS AppSync にダイレクトにアクセスできる経路が残っている。
    つまり、Amazon S3 で言う OAC (Origin Access Control) のようなことをした方が良い。
  2. WebSocket 通信については、CORS 同等の対策ができていない。アクセス元アプリを、ここでは hironoenterprise.com に限定するような設定が必要。Origin ヘッダーをチェックさせたい。

これらについて、完璧ではないですが今時点できることを実装してみます。

AWS AppSync エンドポイントへのダイレクトアクセス拒否

これについては、AWS ブログで方法が紹介されています。原始的と言っては失礼ですが、今できることを他の AWS サービスを駆使して組み上げた感じです。

私の図をベースに説明しますと。

  1. Amazon CloudFront から AWS AppSync にリクエストを転送するときに、「私は許可された CloudFront ですよー」と証明するためのキーをカスタムヘッダーに追加します。
  2. リクエストを受け取った AWS AppSync は、リクエストを AWS WAF にチェックしてもらいます。カスタムヘッダーの値が、あらかじめ口裏合わせしておいたキーと同じであれば、アクセスを許可します。
  3. (今回の私の例には含めていませんが) Amazon CloudFront と AWS WAF に持たせる口裏合わせのキーは AWS Secrets Manager で定期的に自動ローテーションします。

なんちゃって OAC AppSync 版、って感じですが、十分な機能ですね。私は今回はバッドプラクティスですが、キーはベタに書きました。そこは参考にしないでください。

Subscription (WebSocket) 通信の Origin チェック

WebSocket は特殊な通信で、今回、AWS WAF のログやブラウザの開発者コンソールを見て、通信の仕組みがよくわからないことがわかりました。目的の Origin チェックは簡単に実装できるのですが、前述の AppSync 通信ダイレクトアクセス拒否が意味を失います。長くなりますが、説明します。

まず、Origin のチェックは AWS WAF Web ACL で、Origin ヘッダーがここでは https://hironoenterprise.com であることをチェックすればよいです。ただし、それは以下のように条件分けする必要があります。

  1. Query 通信はカスタムヘッダーをチェックする。
  2. Subscription 通信は Origin ヘッダーをチェックする。

では、AWS WAF がどの情報で Query か Subscription かを識別するかです。

まず、私は host ヘッダーの利用を考えました。host は通信の宛先の FQDN で、今回のケースですと AWS AppSync のエンドポイントになります。実は AWS AppSync は Query と Subscription でエンドポイントが異なります。

Query 用のエンドポイント (GraphQL エンドポイント)
例:https://example1234567890000.appsync-api.us-east-1.amazonaws.com/graphql

SubScription 用のエンドポイント (リアルタイムエンドポイント)
例:wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql

FQDN が異なることが確認できると思います。そのため、AWS WAF に残る host ヘッダーが変わり、それぞれの通信を識別できるだろうと考えました。

結論から言いますが、実際には host 情報がまさかの同じ!だったので、識別できませんでした。

Query 通信も SubScription 通信ともに、AWS WAF のログを見る限り、どちらも GraphQL エンドポイントの FQDN が host ヘッダーに残っていました。それ以外の情報では通信を絶対というレベルで識別できるものは見つけられませんでした。

ログを見ていて、SubScription の通信の動きでわかったことは。

  1. まず、wss:// から始まるリアルタイムエンドポイントに GET の通信をしに行く。
    これが AWS WAF には残らない!(WAF を通過しない???)
  2. その後、GraphQL エンドポイントに POST の通信をしに行く。これは AWS WAF に残る。でもエンドポイントが Query と同じなので Subscription の通信なのかがわからない。
  3. WebSocket のセッションが張れた後は、AWS WAF に通信の記録は残らない。

Subscription 用にリアルタイムエンドポイントなるものが用意されてはいるものの、目に見えるのは GraphQL エンドポイントの情報のみなので、いかんともしがたいです。

しかしながら、2 の POST の通信がブロックされると WebSocket セッション確立が失敗するのと、Origin ヘッダーはあったので Origin チェックは意味を成します。

Origin チェックを Subscription 通信限定で行うことができず、AWS WAF を通る全通信を対象にするしかないため、ダイレクトアクセス拒否の設定は意味なくなります。ただし、Subscription 通信を全くしない構成であれば機能します。

さらに補足

他の方法として、CloudFront Functions や Lambda@Edge でヘッダーを書き換える方法も検討しましたが、セキュリティの根幹となるヘッダーを書き換えることはできない仕様だったのであきらめました。(そりゃそうだ、それができたら Amazon CloudFront 使って不正アクセスできるようになりますわw)

AWS WAF のログは必ず残しましょう。通信がなぜブロックされたのか、また許可された正常な通信はどのようなヘッダー情報を持っているのか確認できるので。

そもそも Subscription (WebSocket) 通信を Amazon CloudFront 経由に統合しなかったのか?と思われる方もいらっしゃるかもしれません。試しましたが、私はできませんでした。おそらく前述した、WebSocket 通信がトリッキーなことが原因だと思います。wss:// から始まる通信、GET から始まり途中から POST に変わる、そしてエンドポイントも変わる、host は GraphQL エンドポイント、、、などなど、通信仕様が理解できず、Amazon CloudFront にヘッダー処理をうまく組み込めませんでした。AWS AppSync はネイティブにカスタムドメインはサポートしており、それを設定すると AWS 側で管理する Amazon CloudFront distribution が立ち上がります。当然その構成では機能するはずなので、勝手な想像ですが Lambda@Edge 等も活用して通信が正常に通るように作り込んでいるのだと思います。ただし CORS はできませんが。

最後になってしまいましたが気になる AWS AppSync のレスポンスは、Amazon CloudFront を介しても体感的には変わらなかったです。さすがエッジロケーション。どうでもいいですが、使用されたエッジロケーションの所在地を意味するようなヘッダーがあって、xx県xx市からのアクセスだとあそこに誘導されるんだー、って一人で感動してました。

思った以上にアーキテクチャ説明が長くなってしまいました。次章で設定情報を紹介します。

具体的な設定 (AWS CloudFormation テンプレート)

すみません、AWS CloudFormation テンプレートで失礼します。インラインで補足をコメントします。

AWS AppSync の設定については説明のテーマではないので割愛します。

AWSTemplateFormatVersion: 2010-09-09
Description: The CloudFormation template that creates S3 Buckets, AppSync API with a Web acl, a CloudFront distribution with CORS and relevant IAM roles.

# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------#
Parameters:
  SubName:
    Type: String
    Description: System sub name that is used for all deployed resources. (e.g. prod or dev)
    Default: dev
    MaxLength: 10
    MinLength: 1

  DomainName:
    Type: String
    Description: Domain name for URL. xxxxx.xxx (e.g. hironoenterprise.com)
    Default: hironoenterprise.com
    MaxLength: 40
    MinLength: 5
    AllowedPattern: "[^\\s@]+\\.[^\\s@]+"

  CertificateId:
    Type: String
    Description: ACM certificate ID. CloudFront only supports ACM certificates in us-east-1 region.
    Default: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
    MaxLength: 36
    MinLength: 36

  CloudFrontOriginVerificationKey:
    Type: String
    Description: The random string key that AppSync verifies it is sent from the exact CloudFront. !Bad Practice! Use Secrets Manager instead.
    Default: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    MaxLength: 256
    MinLength: 8

  LogRetentionDays:
    Type: Number
    Description: The retention period (days) for AWS WAF logs. Enter an integer between 35 to 540.
    Default: 365
    MaxValue: 540
    MinValue: 35

Resources:
# ------------------------------------------------------------#
# S3
# ------------------------------------------------------------#
  S3BucketWafLogs:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub aws-waf-logs-hironoenterprise-${SubName}
      LifecycleConfiguration:
        Rules:
          - Id: AutoDelete
            Status: Enabled
            ExpirationInDays: !Ref LogRetentionDays
      OwnershipControls:
        Rules:
          - ObjectOwnership: BucketOwnerEnforced
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      Tags:
        - Key: Cost
          Value: !Sub hironoenterprise-${SubName}

# ------------------------------------------------------------#
# AppSync 大部分を省略、Cognito の設定は外部テンプレートを参照しているのでこのままでは動きません
# ------------------------------------------------------------#
  AppSyncApi:
    Type: AWS::AppSync::GraphQLApi
    Description: AppSync API for hironoenterprise
    Properties:
      Name: !Sub hironoenterprise-${SubName}
      AuthenticationType: AMAZON_COGNITO_USER_POOLS
      AdditionalAuthenticationProviders:
        - AuthenticationType: AWS_IAM
      UserPoolConfig:
        UserPoolId:
          Fn::ImportValue:
            !Sub CognitoUserPoolID-hironoenterprise-${SubName}
        AwsRegion: !Sub ${AWS::Region}
        DefaultAction: "ALLOW"
      IntrospectionConfig: DISABLED
      LogConfig:
        CloudWatchLogsRoleArn: !GetAtt AppSyncCloudWatchLogsPushRole.Arn
        ExcludeVerboseContent: true
        FieldLogLevel: ALL
      Visibility: GLOBAL
      XrayEnabled: true
      Tags:
        - Key: Cost
          Value: !Sub hironoenterprise-${SubName}
    DependsOn:
      - AppSyncCloudWatchLogsPushRole

# ------------------------------------------------------------#
# AppSync CloudWatch Invocation Role (IAM)
# ------------------------------------------------------------#
  AppSyncCloudWatchLogsPushRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub hironoenterprise-AppSyncCloudWatchLogsPushRole-${SubName}
      Description: This role allows AppSync to push logs to CloudWatch Logs.
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
              - appsync.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSAppSyncPushToCloudWatchLogs

# ------------------------------------------------------------#
# WAF Web Acl for AppSync
# ------------------------------------------------------------#
  WebAclAppSync:
    Type: AWS::WAFv2::WebACL
    Properties:
      Name: !Sub hironoenterprise-${SubName}-appsync
      Description: !Sub WAFv2 WebACL for AppSync hironoenterprise-${SubName}
      Scope: REGIONAL
      DefaultAction:
        Block: {}
      Rules:
        # Query 通信はカスタムヘッダーが正しく入っているかチェックする、ただし Origin チェックを併用すると意味はない
        - Name: !Sub AllowAppSyncGraphqlEndpoint-hironoenterprise-${SubName}
          Priority: 1
          Action:
            Allow: {}
          Statement:
            AndStatement:
              Statements:
                - ByteMatchStatement:
                    FieldToMatch:
                      SingleHeader:
                        Name: host
                    PositionalConstraint: EXACTLY
                    # GraphQL エンドポイント宛ての通信であることをチェック
                    SearchString: !GetAtt AppSyncApi.GraphQLDns
                    TextTransformations:
                      - Priority: 0
                        Type: NONE
                # x-origin-verify というカスタムヘッダーをチェックする
                - ByteMatchStatement:
                    FieldToMatch:
                      SingleHeader:
                        Name: x-origin-verify
                    PositionalConstraint: EXACTLY
                    SearchString: !Ref CloudFrontOriginVerificationKey
                    TextTransformations:
                      - Priority: 0
                        Type: NONE
          VisibilityConfig:
            CloudWatchMetricsEnabled: true
            MetricName: !Sub AllowAppSyncGraphqlEndpoint-hironoenterprise-${SubName}
            SampledRequestsEnabled: true
        # Subscription 通信はという条件にしたかったが識別できず、全体に対してチェックしている
        - Name: !Sub AllowAppSyncRealtimeEndpoint-hironoenterprise-${SubName}
          Priority: 2
          Action:
            Allow: {}
          Statement:
            AndStatement:
              Statements:
                - ByteMatchStatement:
                    FieldToMatch:
                      SingleHeader:
                        # React アプリから呼び出したことを条件にしている、無くてもよい
                        Name: x-amz-user-agent
                    PositionalConstraint: STARTS_WITH
                    SearchString: aws-amplify
                    TextTransformations:
                      - Priority: 0
                        Type: NONE
                - ByteMatchStatement:
                    FieldToMatch:
                      SingleHeader:
                        # origin ヘッダーをチェックする、ここでは hironoenterprise.com であること
                        Name: origin
                    PositionalConstraint: EXACTLY
                    SearchString: !Sub https://${DomainName}
                    TextTransformations:
                      - Priority: 0
                        Type: NONE
          VisibilityConfig:
            CloudWatchMetricsEnabled: true
            MetricName: !Sub AllowAppSyncRealtimeEndpoint-hironoenterprise-${SubName}
            SampledRequestsEnabled: true
      VisibilityConfig:
        CloudWatchMetricsEnabled: true
        MetricName: !Sub hironoenterprise-${SubName}-appsync
        SampledRequestsEnabled: true
      Tags:
        - Key: Cost
          Value: !Sub hironoenterprise-${SubName}
    DependsOn:
      - AppSyncApi

  WebACLAssociationAppSync:
    Type: AWS::WAFv2::WebACLAssociation
    Properties:
      ResourceArn: !GetAtt AppSyncApi.Arn
      WebACLArn: !GetAtt WebAclAppSync.Arn
    DependsOn:
      - WebAclAppSync

  WebAclLoggingConfigurationAppSync:
    Type: AWS::WAFv2::LoggingConfiguration
    Properties:
      ResourceArn: !GetAtt WebAclAppSync.Arn
      LogDestinationConfigs:
        - !GetAtt S3BucketWafLogs.Arn
    DependsOn:
      - S3BucketWafLogs
      - WebAclAppSync

# ------------------------------------------------------------#
# CloudFront ログの設定は外部テンプレートを参照しているのでこのままでは動かない
# ------------------------------------------------------------#
  CloudFrontDistributionAppSync:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Enabled: true
        Comment: !Sub CloudFront distribution for hironoenterprise-${SubName}-appsync
        Aliases:
          - !Sub hironoenterprise-${SubName}-appsync.${DomainName}
        HttpVersion: http2
        IPV6Enabled: true
        PriceClass: PriceClass_200
        Logging:
          Bucket:
            Fn::ImportValue:
              !Sub hironoenterprise-S3BucketDomainName-Logs-${SubName}
          IncludeCookies: false
          Prefix: cloudfrontAccesslogAppsync/
        DefaultCacheBehavior:
          TargetOriginId: !Sub AppSyncOrigin-hironoenterprise-${SubName}-https
          ViewerProtocolPolicy: redirect-to-https
          # 以下の AllowdMethods と CachedMethods の設定は書き方に制約があるのでこの通りに書く
          AllowedMethods:
            - GET
            - HEAD
            - OPTIONS
            - PUT
            - PATCH
            - POST
            - DELETE
          CachedMethods:
            - HEAD
            - GET
          # キャッシュポリシーはAWSマネージドのキャッシュを全くしないポリシーを指定
          CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad
          OriginRequestPolicyId: !Ref CloudFrontOriginRequestPolicy
          ResponseHeadersPolicyId: !Ref CloudFrontResponseHeadersPolicy
          Compress: false
          SmoothStreaming: false
        Origins:
          - Id: !Sub AppSyncOrigin-hironoenterprise-${SubName}-https
            # オリジンには AppSync の GraphQL エンドポイントを指定、FQDN 形式でないとエラーになる
            DomainName: !GetAtt AppSyncApi.GraphQLDns
            CustomOriginConfig:
              HTTPPort: 80
              HTTPSPort: 443
              OriginProtocolPolicy: https-only
              OriginSSLProtocols:
                - TLSv1.2
            # ここで、オリジンに送るカスタムヘッダーを追加している
            OriginCustomHeaders: 
              - HeaderName: x-origin-verify
                HeaderValue: !Ref CloudFrontOriginVerificationKey
            ConnectionAttempts: 3
            ConnectionTimeout: 10
        ViewerCertificate:
          AcmCertificateArn: !Sub "arn:aws:acm:us-east-1:${AWS::AccountId}:certificate/${CertificateId}"
          MinimumProtocolVersion: TLSv1.2_2021
          SslSupportMethod: sni-only
      Tags:
        - Key: Cost
          Value: !Sub hironoenterprise-${SubName}
    DependsOn:
      - CloudFrontOriginRequestPolicy
      - CloudFrontResponseHeadersPolicy

  CloudFrontOriginRequestPolicy:
    Type: AWS::CloudFront::OriginRequestPolicy
    Properties:
      OriginRequestPolicyConfig:
        Name: !Sub OriginRequestPolicy-hironoenterprise-${SubName}-appsync
        Comment: !Sub CloudFront Origin Request Policy for hironoenterprise-${SubName}-appsync
        CookiesConfig:
          CookieBehavior: none
        # オリジンに転送するヘッダーは host 以外全てにした
        HeadersConfig:
          HeaderBehavior: allExcept
          Headers:
            - Host
        QueryStringsConfig:
          QueryStringBehavior: all

  CloudFrontResponseHeadersPolicy:
    Type: AWS::CloudFront::ResponseHeadersPolicy
    Properties:
      ResponseHeadersPolicyConfig:
        Name: !Sub ResponseHeadersPolicy-hironoenterprise-${SubName}-appsync
        Comment: !Sub CloudFront Response Headers Policy for hironoenterprise-${SubName}-appsync
        # CORS の設定は
        CorsConfig:
          AccessControlAllowCredentials: false
          # ここに Authorization を追加しないと動かなかった
          AccessControlAllowHeaders:
            Items:
              - Origin
              - Content-Type
              - Authorization
              - x-amz-user-agent
          # Query は POST メソッド。プリフライトチェックが行われるので OPTIONS を必ず入れる。
          AccessControlAllowMethods:
            Items:
              - POST
              - OPTIONS
          # アクセスを許可するアプリの URL を入れる。ここでは hironoenterprise.com
          AccessControlAllowOrigins:
            Items:
              - !Sub https://${DomainName}
          # 必ず CORS ヘッダーをオーバーライドすること。
          OriginOverride: true
        # セキュリティヘッダーはおまけ。一般的な設定。
        SecurityHeadersConfig:
          ContentSecurityPolicy:
            ContentSecurityPolicy: !Sub "default-src 'self' *.${DomainName} ${DomainName}"
            Override: true
          ContentTypeOptions:
            Override: true
          FrameOptions:
            FrameOption: DENY
            Override: true
          ReferrerPolicy:
            Override: true
            ReferrerPolicy: strict-origin-when-cross-origin
          StrictTransportSecurity:
            AccessControlMaxAgeSec: 31536000
            IncludeSubdomains: true
            Override: true
            Preload: true
          XSSProtection:
            ModeBlock: true
            Override: true
            Protection: true
        CustomHeadersConfig:
          Items:
            - Header: Cache-Control
              Value: no-store
              Override: true

# ------------------------------------------------------------#
# Route 53 ※独自ドメインを使用するときは CloudFront にエイリアスレコードを登録する
# ------------------------------------------------------------#
  Route53RecordA:
    Type: AWS::Route53::RecordSet
    Properties:
      HostedZoneName: !Sub ${DomainName}.
      Name: !Sub hironoenterprise-${SubName}-appsync.${DomainName}.
      Type: A
      AliasTarget:
        HostedZoneId: Z2FDTNDATAQYW2
        DNSName: !GetAtt CloudFrontDistributionAppSync.DomainName
    DependsOn:
      - CloudFrontDistributionAppSync

  Route53RecordAAAA:
    Type: AWS::Route53::RecordSet
    Properties:
      HostedZoneName: !Sub ${DomainName}.
      Name: !Sub hironoenterprise-${SubName}-appsync.${DomainName}.
      Type: AAAA
      AliasTarget:
        HostedZoneId: Z2FDTNDATAQYW2
        DNSName: !GetAtt CloudFrontDistributionAppSync.DomainName
    DependsOn:
      - CloudFrontDistributionAppSync

まとめ

いかがでしたでしょうか。

いろいろ調べて非常に疲れました。HTTP ヘッダーの勉強になりました。同じ問題で困っている方、もし解決できたようでしたら、世の中に公開してくれると嬉しいです。または、AWS さんが AWS AppSync の CORS サポートを追加してくれればよいのですが。。。

本記事が皆様のお役に立てれば幸いです。

著者について
広野 祐司

AWS サーバーレスアーキテクチャを駆使して社内クラウド人材育成アプリとコンテンツづくりに勤しんでいます。React で SPA を書き始めたら快適すぎて、他の言語には戻れなくなりました。サーバーレス & React 仲間を増やしたいです。AWSは好きですが、それよりもフロントエンド開発の方が好きでして、バックエンド構築を簡単にしてくれたAWSには感謝の気持ちの方が強いです。
取得資格:AWS 認定は13資格、ITサービスマネージャ、ITIL v3 Expert 等
2020 - 2024 Japan AWS Top Engineer 受賞
2022 - 2024 AWS Ambassador 受賞
2023 当社初代フルスタックエンジニア認定
好きなAWSサービス:AWS Amplify / AWS AppSync / Amazon Cognito / AWS Step Functions / AWS CloudFormation

広野 祐司をフォローする

クラウドに強いによるエンジニアブログです。

SCSKクラウドサービス(AWS)は、企業価値の向上につながるAWS 導入を全面支援するオールインワンサービスです。AWS最上位パートナーとして、多種多様な業界のシステム構築実績を持つSCSKが、お客様のDX推進を強力にサポートします。

AWSアプリケーション開発クラウドソリューション
シェアする
タイトルとURLをコピーしました