Skip to content

Fix SSO redirect: use 302, trailing slash, remove S3#118

Merged
Alexanderamiri merged 1 commit into
mainfrom
fix/sso-redirect-302
Mar 30, 2026
Merged

Fix SSO redirect: use 302, trailing slash, remove S3#118
Alexanderamiri merged 1 commit into
mainfrom
fix/sso-redirect-302

Conversation

@Alexanderamiri
Copy link
Copy Markdown
Member

Summary

  • 301 → 302: Prevents browsers permanently caching the redirect URL. Stale cached 301s were likely causing users to end up at aws.amazon.com.
  • /start/start/: Saves an extra 302 hop (AWS adds trailing slash automatically).
  • cache-control: no-cache: Instead of 86400s cache, ensures fresh redirects.
  • Remove S3 bucket: The bucket, website config, and public access block were dead weight — the CloudFront Function intercepts all requests before reaching the origin. Replaced with invalid.invalid dummy origin.

Net result: 3 fewer AWS resources, simpler stack, same functionality.

Root cause of "ends up at aws.amazon.com"

The 301 with max-age=86400 was cached by browsers. The original redirect went to /start (no trailing slash) which then got another 302 from AWS. Browser caching of these intermediate redirects likely caused the portal SPA authentication flow to break.

Note: The Identity Center portal is an AWS-hosted JavaScript SPA. If Google SAML authentication fails or cookies are blocked, users will still end up at aws.amazon.com — that's on AWS's side, not ours.

Test plan

  • CI plan shows S3 resources destroyed, CloudFront function updated
  • curl -sI https://aws.javabin.no returns 302 with location: https://javabin.awsapps.com/start/
  • Clear browser cache and visit aws.javabin.no — should reach SSO login page

…ucket

- Switch from 301 to 302 to prevent browsers permanently caching the
  redirect. Stale cached 301s cannot be cleared remotely.
- Add trailing slash to /start/ to avoid an extra 302 hop from AWS.
- Set cache-control: no-cache instead of max-age=86400.
- Remove the S3 bucket, website config, and public access block — they
  were dead weight since the CloudFront Function handles everything.
  Replace with dummy origin (invalid.invalid).
@Alexanderamiri Alexanderamiri requested a review from a team as a code owner March 30, 2026 22:08
@Alexanderamiri Alexanderamiri enabled auto-merge (squash) March 30, 2026 22:09
@github-actions
Copy link
Copy Markdown

Terraform Plan

🚧 Changes detected — Plan: 0 to add, 2 to change, 3 to destroy.

Plan output

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  ~ update in-place
  - destroy

Terraform will perform the following actions:

  # module.dns.aws_cloudfront_distribution.sso_redirect will be updated in-place
  ~ resource "aws_cloudfront_distribution" "sso_redirect" {
        id                             = "E2F04LHEI6J2CL"
        tags                           = {
            "Name" = "aws.javabin.no-redirect"
        }
        # (20 unchanged attributes hidden)

      - origin {
          - connection_attempts = 3 -> null
          - connection_timeout  = 10 -> null
          - domain_name         = "aws.javabin.no.s3-website.eu-central-1.amazonaws.com" -> null
          - origin_id           = "dummy" -> null

          - custom_origin_config {
              - http_port                = 80 -> null
              - https_port               = 443 -> null
              - origin_keepalive_timeout = 5 -> null
              - origin_protocol_policy   = "http-only" -> null
              - origin_read_timeout      = 30 -> null
              - origin_ssl_protocols     = [
                  - "TLSv1.2",
                ] -> null
            }
        }
      + origin {
          + connection_attempts = 3
          + connection_timeout  = 10
          + domain_name         = "invalid.invalid"
          + origin_id           = "dummy"

          + custom_origin_config {
              + http_port                = 80
              + https_port               = 443
              + origin_keepalive_timeout = 5
              + origin_protocol_policy   = "https-only"
              + origin_read_timeout      = 30
              + origin_ssl_protocols     = [
                  + "TLSv1.2",
                ]
            }
        }

        # (3 unchanged blocks hidden)
    }

  # module.dns.aws_cloudfront_function.sso_redirect will be updated in-place
  ~ resource "aws_cloudfront_function" "sso_redirect" {
      ~ code                         = <<-EOT
            function handler(event) {
              return {
          -     statusCode: 301,
          -     statusDescription: 'Moved Permanently',
          +     statusCode: 302,
          +     statusDescription: 'Found',
                headers: {
          -       location: { value: 'https://javabin.awsapps.com/start' },
          -       'cache-control': { value: 'max-age=86400' }
          +       location: { value: 'https://javabin.awsapps.com/start/' },
          +       'cache-control': { value: 'no-cache' }
                }
              };
            }
        EOT
        id                           = "javabin-sso-redirect"
        name                         = "javabin-sso-redirect"
        # (7 unchanged attributes hidden)
    }

  # module.dns.aws_s3_bucket.sso_redirect will be destroyed
  # (because aws_s3_bucket.sso_redirect is not in configuration)
  - resource "aws_s3_bucket" "sso_redirect" {
      - arn                         = "arn:aws:s3:::aws.javabin.no" -> null
      - bucket                      = "aws.javabin.no" -> null
      - bucket_domain_name          = "aws.javabin.no.s3.amazonaws.com" -> null
      - bucket_regional_domain_name = "aws.javabin.no.s3.eu-central-1.amazonaws.com" -> null
      - force_destroy               = false -> null
      - hosted_zone_id              = "Z21DNDUVLTQW6Q" -> null
      - id                          = "aws.javabin.no" -> null
      - object_lock_enabled         = false -> null
      - region                      = "eu-central-1" -> null
      - request_payer               = "BucketOwner" -> null
      - tags                        = {
          - "Name" = "aws.javabin.no-redirect"
        } -> null
      - tags_all                    = {
          - "Name"        = "aws.javabin.no-redirect"
          - "environment" = "production"
          - "managed-by"  = "terraform"
          - "repo"        = "javaBin/platform"
          - "service"     = "platform"
          - "team"        = "platform"
        } -> null
      - website_domain              = "s3-website.eu-central-1.amazonaws.com" -> null
      - website_endpoint            = "aws.javabin.no.s3-website.eu-central-1.amazonaws.com" -> null

      - grant {
          - id          = "224bb804033bf32b757e937fa53de39dea27ba15c59d10a5dafadce3202a9921" -> null
          - permissions = [
              - "FULL_CONTROL",
            ] -> null
          - type        = "CanonicalUser" -> null
        }

      - server_side_encryption_configuration {
          - rule {
              - bucket_key_enabled = false -> null

              - apply_server_side_encryption_by_default {
                  - sse_algorithm = "AES256" -> null
                }
            }
        }

      - versioning {
          - enabled    = false -> null
          - mfa_delete = false -> null
        }

      - website {
          - redirect_all_requests_to = "https://javabin.awsapps.com" -> null
        }
    }

  # module.dns.aws_s3_bucket_public_access_block.sso_redirect will be destroyed
  # (because aws_s3_bucket_public_access_block.sso_redirect is not in configuration)
  - resource "aws_s3_bucket_public_access_block" "sso_redirect" {
      - block_public_acls       = true -> null
      - block_public_policy     = true -> null
      - bucket                  = "aws.javabin.no" -> null
      - id                      = "aws.javabin.no" -> null
      - ignore_public_acls      = true -> null
      - restrict_public_buckets = true -> null
    }

  # module.dns.aws_s3_bucket_website_configuration.sso_redirect will be destroyed
  # (because aws_s3_bucket_website_configuration.sso_redirect is not in configuration)
  - resource "aws_s3_bucket_website_configuration" "sso_redirect" {
      - bucket           = "aws.javabin.no" -> null
      - id               = "aws.javabin.no" -> null
      - website_domain   = "s3-website.eu-central-1.amazonaws.com" -> null
      - website_endpoint = "aws.javabin.no.s3-website.eu-central-1.amazonaws.com" -> null

      - redirect_all_requests_to {
          - host_name = "javabin.awsapps.com" -> null
          - protocol  = "https" -> null
        }
    }

Plan: 0 to add, 2 to change, 3 to destroy.

─────────────────────────────────────────────────────────────────────────────

Saved the plan to: tfplan

To perform exactly these actions, run the following command to apply:
    terraform apply "tfplan"

LLM Review

Risk: 🟢 LOW

Plan removes S3 website redirect infrastructure and updates CloudFront distribution to use a placeholder origin with modified redirect behavior.

  • [routine] CloudFront function updated: HTTP status code changed from 301 (permanent) to 302 (temporary), cache-control changed from 86400s to no-cache, and redirect URL modified to include trailing slash. These are configuration updates to redirect behavior.
  • 💥 [destruction] S3 bucket 'aws.javabin.no' and related resources (public access block, website configuration) are being destroyed. This bucket was used for SSO redirect via S3 website hosting. No data loss risk as it only contained redirect configuration.
  • [routine] CloudFront origin configuration changed from S3 website endpoint (aws.javabin.no.s3-website.eu-central-1.amazonaws.com) to placeholder domain (invalid.invalid) and protocol changed from http-only to https-only. Origin protocol policy change is a security improvement.
  • 💰 [cost] Removal of S3 bucket will reduce storage costs, though the impact is minimal as this was a redirect-only bucket with no actual content.
  • [routine] No IAM, security group, or permission changes. No new billable resources being created. This is a refactoring of the SSO redirect mechanism.

@Alexanderamiri Alexanderamiri merged commit 7ed63a7 into main Mar 30, 2026
3 checks passed
@Alexanderamiri Alexanderamiri deleted the fix/sso-redirect-302 branch March 30, 2026 22:09
Alexanderamiri added a commit that referenced this pull request May 9, 2026
## Summary
- **301 → 302**: Prevents browsers permanently caching the redirect URL.
Stale cached 301s were likely causing users to end up at aws.amazon.com.
- **`/start` → `/start/`**: Saves an extra 302 hop (AWS adds trailing
slash automatically).
- **`cache-control: no-cache`**: Instead of 86400s cache, ensures fresh
redirects.
- **Remove S3 bucket**: The bucket, website config, and public access
block were dead weight — the CloudFront Function intercepts all requests
before reaching the origin. Replaced with `invalid.invalid` dummy
origin.

Net result: 3 fewer AWS resources, simpler stack, same functionality.

## Root cause of "ends up at aws.amazon.com"
The 301 with `max-age=86400` was cached by browsers. The original
redirect went to `/start` (no trailing slash) which then got another 302
from AWS. Browser caching of these intermediate redirects likely caused
the portal SPA authentication flow to break.

Note: The Identity Center portal is an AWS-hosted JavaScript SPA. If
Google SAML authentication fails or cookies are blocked, users will
still end up at aws.amazon.com — that's on AWS's side, not ours.

## Test plan
- [ ] CI plan shows S3 resources destroyed, CloudFront function updated
- [ ] `curl -sI https://aws.javabin.no` returns `302` with `location:
https://javabin.awsapps.com/start/`
- [ ] Clear browser cache and visit aws.javabin.no — should reach SSO
login page
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant