Authentication callback 403 using CloudFront + S3 and Cognito ReactJS SPA

0

I'm piloting a ReactJS (16.14.0) web site with CloudFront and S3 to store the ReactJS app resources. For authentication I use Cognito and an external idP, which redirects back to the React App authentication callback. I get a 403 during the redirect, which I assume is because the URL doesn't represent a physical resource in the bucket. How do I resolve this? In my research suggestions include S3 redirection rules for 403 & 404 with "ReplaceKeyPrefixWith": "#!/".

Details:

  • Access the web site at https://xxxxx.cloudfront.net/
  • Redirect to the login page: https://xxxxx.cloudfront.net/login and click the login button
  • Application issues a GET https://xxxxx.us-east-1.amazoncognito.com/oauth2/authorize?response_type=code&client_id=xxxxxxxxx&state=xxxxx&scope=profile%20openid%20email%20aws.cognito.signin.user.admin&redirect_uri=https%3A%2F%xxxxx.cloudfront.net%2Fauthentication%2Fcallback&code_challenge=xxxxxxxxxxxxxx&code_challenge_method=S256
  • Log in through the iDP and Cognito redirects to the authentication callback https://xxxxx.cloudfront.net/authentication/callback?code=xxxxxxx&state=xxxxxx
  • Result is a 403 and the payload is XML with Code and Message "Access Denied"

CloudFront configuration:

  • General: Price Class:NA & Europe, Default root object: index.html
  • Security: Defaults. WAF is disabled
  • Origins: (S3) xxxxxxxx.s3.us-east-1.amazonaws.com
    • Origin access control settings, type: S3, sign requests, Origin Shield disabled
  • Behaviors: Redirect HTTP to HTTPS
  • Error pages: none
  • Invalidations: none

S3 configuration

  • Encryption SSE-S3
  • Static website hosting: Enabled
    • Hosting type: Host a static website
    • Index document: index.html, Error document: index.html
    • Redirection rules:
[
    {
        "Condition": {
            "HttpErrorCodeReturnedEquals": "403"
        },
        "Redirect": {
            "HostName": "xxxxx.cloudfront.net",
            "Protocol": "https",
            "ReplaceKeyPrefixWith": "#!/"
        }
    },
    {
        "Condition": {
            "HttpErrorCodeReturnedEquals": "404"
        },
        "Redirect": {
            "HostName": "xxxxx.cloudfront.net",
            "Protocol": "https",
            "ReplaceKeyPrefixWith": "#!/"
        }
    }
]
  • Block all public access: On
  • Bucket policy:
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowCloudFrontServicePrincipal",
            "Effect": "Allow",
            "Principal": {
                "Service": "cloudfront.amazonaws.com"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::xxxx-bucket-name-xxxx/*",
            "Condition": {
                "StringEquals": {
                    "AWS:SourceArn": "arn:aws:cloudfront::xxxxxx:distribution/something"
                }
            }
        },
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                    "arn:aws:iam:: xxxxxx:root"
                ]
            },
            "Action": [
                "s3:DeleteObject*",
                "s3:GetBucket*",
                "s3:List*",
                "s3:PutBucketPolicy"
            ],
            "Resource": [
                "arn:aws:s3::: xxxx-bucket-name-xxxx",
                "arn:aws:s3::: xxxx-bucket-name-xxxx/*"
            ]
        }
    ]
}
2 Answers
0

Hi, there are 3 things to keep in mind while hosting an SPA with Cognito auth behind CloudFront:

  • how to generate CloudFront redirects to index.html for non-existing S3 paths,
  • how to managing the logged-in state on the client side (in the browser),
  • how to get an auth token that can be validated by CloudFront for protected resources and authenticated users.

To generate redirects to /index.html please refer to the Error pages tab of your distribution in the AWS Console. Configure redirects for non-existing resources on S3 with response code override like shown on the attached screenshot. You may want to configure only one redirect (just for 403), because S3 uses this status code for non-existing objects.

Error pages on CloudFront

To handle Cognito integration, please refer to our battle-tested solutions, like Amplify Authentication to manage user session state in your React application. Similarly for backend/ CloudFront redirections to the IdP login screen, you can have a look at our library cognito-at-edge. It leverages Lambda@Edge to validate the token on every request and generate redirections if needed (when the auth token is not present in the request).

AWS
Piotrek
answered 4 months ago
  • Thank you. I'll look into this and reply back with the results. For the sake of simplicity I left out some details which are relevant to the third bullet point in your answer.

    • There are no protected resources for anything served from S3 (via CloudFront)
    • The SPA uses the authentication token strictly to communicate with the back-end API
    • The authentication token is validated by API Gateway through a custom Lambda authorizer before sending to the back-end, which also verifies the signature and ensures the principal exists in the back-end system.
0

Hello

From your Details section, Step 4, When Cognito redirects back to your Application Callback, I am assuming you validate your token, what is your next step ? Does the API call go on the same call, I am assuming no, so how do you save you token ? I think there is some gap in getting the token and revalidating when actual api call happens

Anand
answered 2 months ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions