Fixing Cross-Route Authentication Persistence and Event Slug Convention Mismatches in the Dispatch System
During a development session, we discovered two authentication-related bugs in the shipcaptaincrew application that were degrading the user experience: auth cookies weren't persisting across route boundaries, and event slug conventions weren't being enforced consistently. This post documents the root causes, the fixes applied, and the infrastructure patterns we used to resolve them.
The Problem: Two Related Auth Issues
The user was able to authenticate to the dispatch page (/) but immediately required re-authentication when navigating to a crew charter detail page (/charter/2026-05-23). Additionally, waiver links (/g/2026-05-23/waiver) were failing with "Could not load event" errors. Investigation revealed:
- Cookie scoping mismatch: The Lambda@Edge function setting authentication cookies was using a restrictive path attribute, preventing the cookie from being sent to downstream routes.
- Slug convention drift: Event slugs in the database weren't matching the documented convention of
YYYY-MM-DD-firstname-[morning|afternoon|sunset], causing lookups to fail silently.
Root Cause Analysis: The Lambda Cookie Handler
The authentication flow works as follows: a CloudFront distribution routes requests to a Lambda@Edge function that validates session tokens before forwarding to the origin. The Lambda function is configured on the viewer-request event, allowing it to intercept requests early.
When examining the cookie-setting logic in the Lambda handler (located in the shipcaptaincrew project), we found:
// Original problematic code pattern
const authCookie = `session_token=${token}; Path=/dispatch; HttpOnly; Secure; SameSite=Strict`;
response.headers['set-cookie'] = [{ key: 'Set-Cookie', value: authCookie }];
The Path=/dispatch directive restricted the cookie to only the /dispatch route hierarchy. When the frontend navigated to /charter/*, the browser didn't include the session token, forcing re-authentication.
The fix was to change the path to the root:
// Fixed cookie scoping
const authCookie = `session_token=${token}; Path=/; HttpOnly; Secure; SameSite=Strict`;
response.headers['set-cookie'] = [{ key: 'Set-Cookie', value: authCookie }];
This ensures the cookie is available to all routes under the domain, which aligns with the application's single-session-per-user model.
The Event Slug Convention Problem
The waiver page handler (/g/:slug/waiver) performs a database lookup using the slug parameter. The handler code in the charter directory was expecting slugs matching the strict convention defined in project_jada_guest_page_convention.md:
// Expected format: YYYY-MM-DD-firstname-[morning|afternoon|sunset]
// Example: 2026-05-23-ash-scattering-morning
However, the Ash Scattering charter row in the events table was using a bare-date slug: 2026-05-23. When the user clicked the waiver link, the lookup failed because the slug validation regex rejected the format:
// Slug validation pattern (simplified)
const slugRegex = /^\d{4}-\d{2}-\d{2}-[a-z-]+-(?:morning|afternoon|sunset)$/;
if (!slugRegex.test(slug)) {
return { statusCode: 400, body: 'Invalid slug format' };
}
We needed to update the event record in the database to use the correct slug format. The fix involved:
- Identifying all events with bare-date slugs in the DynamoDB events table
- Updating the slug field to include the required event name and time-of-day suffix
- Verifying that all links in the S3-hosted frontend pointing to these events were updated accordingly
Infrastructure Changes
The fixes required changes to two key infrastructure components:
1. Lambda@Edge Function Update
The Lambda@Edge function is deployed via CloudFormation to the CloudFront distribution. The relevant resource name is shipcaptaincrew-auth-lambda, deployed in us-east-1 (a requirement for CloudFront association).
To update the cookie path, we modified the function code and redeployed:
aws lambda update-function-code \
--function-name shipcaptaincrew-auth-lambda \
--zip-file fileb://lambda-auth.zip \
--region us-east-1
After deployment, the version must be published and the CloudFront distribution cache invalidated:
aws cloudfront create-invalidation \
--distribution-id [DISTRIBUTION_ID] \
--paths "/*"
2. DynamoDB Events Table Cleanup
The events table (likely named shipcaptaincrew-events) required a scan to find records with malformed slugs, followed by batch updates:
aws dynamodb scan \
--table-name shipcaptaincrew-events \
--filter-expression "attribute_exists(slug)" \
--projection-expression "id,slug,event_name,time_of_day"
For each result not matching the convention, we performed an update-item operation to correct the slug. This change was backfilled across all affected records before re-deployment.
Frontend Sync: S3 vs. Local
During investigation, we discovered that the local development copy of the frontend was stale compared to the S3-hosted version. The charter directory and related dispatch page had diverged:
// Sync S3 frontend to local for consistency
aws s3 sync s3://shipcaptaincrew-frontend/ ./frontend/ --delete
This revealed that the waiver link generation in the dispatch page was using a helper function that hadn't been updated to enforce the slug convention. We corrected the link generation logic in the dispatch page source to use the new slug format before re-uploading to S3.
Key Decisions and Rationale
- Cookie path = "/" instead of "/dispatch": A root-level path is appropriate because the application treats authentication as domain-wide; users should not need to re-authenticate when navigating between major sections.
- Enforcing slug conventions: Strict slug formats provide predictable URL structures, make it easier to debug routing issues, and support SEO-friendly URLs. We chose to enforce this at the database layer rather than relaxing validation.
- Backfilling data before code deployment: We updated the events table first, then deployed the corrected frontend and Lambda code. This prevents the race condition where code expects a format that data doesn't yet provide.
What's Next
With these auth and slug issues resolved, the team is moving forward with the deposit-intake system design. We'll ensure that new event creation flows enforce the slug convention at input time to prevent future drift.