Skip to content

Commit a861ee0

Browse files
committed
Fix Google sign-in loop: restore Amplify env vars, fix DynamoDB table keys, fix permanent dynamoFailed flag
- Restored all 16 Amplify env vars (update-app --environment-variables replaces, not appends) - Recreated autisense-users table with PK=id (was userId) - Recreated autisense-auth-sessions table with PK=token (was sessionToken) - Changed dynamoFailed from permanent boolean to 30s cooldown timer - Updated SETUP_GUIDE.md with warnings and changelog
1 parent 054636d commit a861ee0

2 files changed

Lines changed: 102 additions & 22 deletions

File tree

SETUP_GUIDE.md

Lines changed: 93 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,9 @@ If you skip step 2, the variable will be available during `npm run build` but **
7373
| `POLLY_REGION` | Amplify + next.config.ts | Polly voice region (ap-south-1) |
7474
| `S3_MODELS_BUCKET` | Amplify + next.config.ts | S3 bucket for ONNX models |
7575
| `DYNAMODB_*` (7 tables) | Amplify + next.config.ts | DynamoDB table names |
76-
| `AWS_REGION` | Auto (Lambda) | Lambda execution region |
77-
| `AWS_ACCESS_KEY_ID` | Auto (Lambda IAM) | IAM role credentials |
78-
| `AWS_SECRET_ACCESS_KEY` | Auto (Lambda IAM) | IAM role credentials |
79-
| `AWS_SESSION_TOKEN` | Auto (Lambda IAM) | IAM role session token |
76+
| `APP_ACCESS_KEY_ID` | Amplify + next.config.ts | IAM user access key (Amplify blocks `AWS_*`) |
77+
| `APP_SECRET_ACCESS_KEY` | Amplify + next.config.ts | IAM user secret key |
78+
| `APP_REGION` | Amplify + next.config.ts | AWS region for SDK clients |
8079

8180
### Google OAuth Setup
8281

@@ -92,23 +91,21 @@ The redirect URI is constructed from `NEXT_PUBLIC_APP_URL` + `/api/auth/callback
9291

9392
## AWS SDK Credential Handling
9493

95-
**NEVER pass explicit credentials to AWS SDK clients in API routes.**
94+
Amplify WEB_COMPUTE does **not** expose IAM role credentials to the SSR Lambda runtime. We use custom-named env vars (`APP_ACCESS_KEY_ID`, `APP_SECRET_ACCESS_KEY`, `APP_REGION`) because Amplify reserves the `AWS_*` prefix.
95+
96+
All SDK clients use the shared helper in `app/lib/aws/credentials.ts`:
9697

9798
```ts
98-
// WRONG — breaks on Amplify Lambda (missing sessionToken)
99+
import { getAppCredentials, getAppRegion } from "@/app/lib/aws/credentials";
100+
101+
const credentials = getAppCredentials();
99102
new DynamoDBClient({
100-
region: "ap-south-1",
101-
credentials: {
102-
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
103-
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
104-
},
103+
region: getAppRegion("ap-south-1"),
104+
...(credentials && { credentials }),
105105
});
106-
107-
// CORRECT — SDK auto-detects IAM role credentials (including session token)
108-
new DynamoDBClient({ region: "ap-south-1" });
109106
```
110107

111-
On Amplify Lambda, the IAM execution role provides **temporary STS credentials** that include `AWS_SESSION_TOKEN`. The default SDK credential provider chain handles this automatically. Passing explicit `accessKeyId`/`secretAccessKey` without `sessionToken` will cause auth failures.
108+
This falls back to the SDK default provider chain when `APP_*` vars are not set (e.g., local dev with `AWS_*` in `.env.local`).
112109

113110
---
114111

@@ -151,3 +148,84 @@ Then redeploy.
151148
**Cause:** React 19 strict lint rules (`react-hooks/*`) introduced in `eslint-config-next`.
152149

153150
**Fix:** Rules are configured in `eslint.config.mjs`. If new violations appear after updating Next.js, check if hooks ordering or ref patterns need adjustment.
151+
152+
### Google sign-in enters a redirect loop
153+
154+
**Cause:** DynamoDB auth tables have wrong primary key names, or env vars were wiped.
155+
156+
**Fix:** Verify table schemas match what the code expects:
157+
- `autisense-users`: PK = `id` (string), GSI = `email-index` on `email`
158+
- `autisense-auth-sessions`: PK = `token` (string), TTL on `expiresAt`
159+
160+
Also verify all 16 Amplify env vars are set (see below).
161+
162+
---
163+
164+
## Critical Warnings
165+
166+
### `aws amplify update-app --environment-variables` REPLACES ALL vars
167+
168+
**The `--environment-variables` flag REPLACES the entire env var map.** It does not append.
169+
170+
If you run:
171+
```bash
172+
aws amplify update-app --environment-variables NEW_VAR=value
173+
```
174+
This **deletes every other env var** and sets only `NEW_VAR`. Always include ALL existing vars when updating.
175+
176+
**Safe pattern:**
177+
```bash
178+
aws amplify get-app --app-id d2n7pu2vtgi8yc --region ap-south-1 \
179+
--query "app.environmentVariables"
180+
# Copy all existing vars, add your new one, then update with the full list
181+
```
182+
183+
### DynamoDB Table Key Schema Must Match Code
184+
185+
The auth code in `app/lib/auth/dynamodb.ts` uses these exact key names:
186+
- `autisense-users` table: PK must be `id` (NOT `userId`)
187+
- `autisense-auth-sessions` table: PK must be `token` (NOT `sessionToken`)
188+
189+
If tables are created with different key names, all DynamoDB operations fail silently and auth falls back to in-memory (which doesn't persist across Lambda instances).
190+
191+
### All 16 Required Amplify Environment Variables
192+
193+
```
194+
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, NEXT_PUBLIC_APP_URL,
195+
BEDROCK_REGION, POLLY_REGION, S3_MODELS_BUCKET,
196+
DYNAMODB_SESSIONS_TABLE, DYNAMODB_BIOMARKERS_TABLE,
197+
DYNAMODB_USERS_TABLE, DYNAMODB_AUTH_SESSIONS_TABLE,
198+
DYNAMODB_CHILD_PROFILES_TABLE, DYNAMODB_SESSION_SUMMARIES_TABLE,
199+
DYNAMODB_FEED_POSTS_TABLE,
200+
APP_ACCESS_KEY_ID, APP_SECRET_ACCESS_KEY, APP_REGION
201+
```
202+
203+
---
204+
205+
## Changelog
206+
207+
### 2026-03-06 — Fix Google sign-in loop
208+
209+
**Root causes found:**
210+
1. `aws amplify update-app --environment-variables` replaced ALL env vars (wiped `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, `NEXT_PUBLIC_APP_URL`, etc.)
211+
2. DynamoDB `autisense-users` table had PK `userId` but code uses `id`
212+
3. DynamoDB `autisense-auth-sessions` table had PK `sessionToken` but code uses `token`
213+
4. `dynamoFailed` flag was permanent — one transient error disabled DynamoDB forever
214+
215+
**Fixes applied:**
216+
- Restored all 16 Amplify env vars
217+
- Recreated `autisense-users` table (PK=`id`, GSI=`email-index`)
218+
- Recreated `autisense-auth-sessions` table (PK=`token`, TTL on `expiresAt`)
219+
- Changed `dynamoFailed` from permanent boolean to 30-second cooldown timer
220+
221+
### 2026-03-05 — Fix AWS SDK credentials for Amplify
222+
223+
- Created shared credential helper (`app/lib/aws/credentials.ts`)
224+
- Updated all 8 SDK client locations to use `APP_*` env vars
225+
- Added `APP_ACCESS_KEY_ID`, `APP_SECRET_ACCESS_KEY`, `APP_REGION` to Amplify
226+
- Polly TTS confirmed working on deployed site
227+
228+
### 2026-03-05 — Fix COEP header for map tiles
229+
230+
- Changed `Cross-Origin-Embedder-Policy` from `require-corp` to `credentialless`
231+
- Updated both `next.config.ts` and Amplify custom headers

app/lib/auth/dynamodb.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,9 @@ export interface AuthSession {
3434
// On Amplify Lambda, credentials come via IAM role (SDK auto-detects).
3535
// Only fall back to in-memory when DynamoDB actually fails or in local dev
3636
// without any AWS config.
37-
let dynamoFailed = false;
37+
let dynamoFailedUntil = 0;
3838

3939
function shouldUseDynamo(): boolean {
40-
if (dynamoFailed) return false;
4140
// In local dev without any AWS config, skip DynamoDB
4241
if (
4342
process.env.NODE_ENV === "development" &&
@@ -47,12 +46,13 @@ function shouldUseDynamo(): boolean {
4746
) {
4847
return false;
4948
}
50-
// In production (Amplify), always try DynamoDB
49+
// After a failure, wait 30 s before retrying DynamoDB
50+
if (Date.now() < dynamoFailedUntil) return false;
5151
return true;
5252
}
5353

5454
/**
55-
* Try a DynamoDB operation; on failure, set dynamoFailed = true and
55+
* Try a DynamoDB operation; on failure, cool down for 30 s and
5656
* transparently retry against the in-memory adapter.
5757
*/
5858
async function withFallback<T>(
@@ -64,10 +64,12 @@ async function withFallback<T>(
6464
return memoryFn();
6565
}
6666
try {
67-
return await dynamoFn();
67+
const result = await dynamoFn();
68+
dynamoFailedUntil = 0; // reset on success
69+
return result;
6870
} catch (err) {
69-
console.warn(`[auth/dynamodb] ${operation} failed, falling back to in-memory:`, err);
70-
dynamoFailed = true;
71+
console.error(`[auth/dynamodb] ${operation} failed, falling back to in-memory:`, err);
72+
dynamoFailedUntil = Date.now() + 30_000;
7173
return memoryFn();
7274
}
7375
}

0 commit comments

Comments
 (0)