Skip to content

Frontend/auth updates#1727

Open
jsandoval81 wants to merge 20 commits into
csg-org:mainfrom
InspiringApps:frontend/auth-updates
Open

Frontend/auth updates#1727
jsandoval81 wants to merge 20 commits into
csg-org:mainfrom
InspiringApps:frontend/auth-updates

Conversation

@jsandoval81

@jsandoval81 jsandoval81 commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Requirements List

  • Deploy backend updates if testing pre-merge

Description List

  • Moved auth-related helper functions into their own utility module
  • Separated the AuthCallback pages into separate pages per userType/compact
  • Updated the login state parameter to be a random-value CSRF protection
  • Added PKCE to the login flows
  • Updated the Cognito app clients to accept the new separate redirect_uri landing pages

Testing List

  • yarn test:unit:all should run without errors or warnings
  • yarn serve should run without errors or warnings
  • yarn build should run without errors or warnings
  • Code review
  • Testing
    • Deploy the backend updates to your sandbox / test environment
    • Login to each compact, ensuring that correct logins are successful with no regressions
    • Ensure user sessions remain valid in the browser, with no regressions to page refreshes or auto logout

Closes #1641

Summary by CodeRabbit

  • New Features
    • Added dedicated OAuth callback routes/pages for staff (JCC, Cosmetology, Social Work) and licensee.
    • OAuth redirect/callback URLs now support role- and app-specific callback paths.
    • Hosted sign-in and password-reset flows now include CSRF validation and PKCE protection, with redirect error handling to return to the dashboard.
  • Bug Fixes
    • Improved callback URL generation for both hosted and local UI environments.
  • Tests
    • Updated and expanded callback/OAuth assertions, plus Postman flow redirect URIs.

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@jsandoval81, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 42 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9a26fbcb-e902-4164-b3c4-f2252cc6f898

📥 Commits

Reviewing files that changed from the base of the PR and between 3336cca and fc2a646.

📒 Files selected for processing (5)
  • backend/common-cdk/common_constructs/user_pool.py
  • backend/common-cdk/tests/test_user_pool.py
  • backend/compact-connect/tests/app/base.py
  • backend/cosmetology-app/tests/app/base.py
  • backend/social-work-app/tests/app/base.py
📝 Walkthrough

Walkthrough

OAuth authentication is reorganized around shared CSRF and PKCE utilities. Cognito clients, hosted-login URLs, and frontend routes now use application-specific callback paths with dedicated callback pages and shared token exchange handling.

Changes

OAuth callback flow

Layer / File(s) Summary
Backend callback contracts
backend/common-cdk/..., backend/*/stacks/..., backend/*/tests/..., backend/*/docs/postman/...
Cognito UI clients accept configurable callback paths, and application-specific redirect URLs are applied across stacks, tests, and Postman collections.
Authentication utility foundation
webroot/src/app.config.ts, webroot/src/utils/auth.ts, webroot/tests/helpers/setup.ts, webroot/tsconfig.json, webroot/vue.config.js, webroot/src/styles.common/...
Authentication exports move into @utils/auth, which provides CSRF state, PKCE, Cognito configuration, hosted-login URI generation, and auto-logout configuration.
Authentication import migration
webroot/src/components/..., webroot/src/models/..., webroot/src/network/..., webroot/src/router/..., webroot/src/store/...
Frontend modules and tests import authentication values from the new utility module while retaining application configuration imports for non-auth constants.
PKCE-protected login initiation
webroot/src/pages/PublicDashboard/..., webroot/src/pages/MfaReset*/*
Login and licensee MFA reset flows generate CSRF and PKCE values before constructing hosted-login URLs.
Callback handling and routing
webroot/src/pages/AuthCallback/..., webroot/src/router/routes.ts
A shared callback mixin validates state, exchanges authorization codes with PKCE, and supports dedicated callback pages for staff and licensee contexts.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant PublicDashboard
  participant Cognito
  participant AuthCallback
  PublicDashboard->>Browser: Generate CSRF state and PKCE challenge
  Browser->>Cognito: Request hosted login with state and code challenge
  Cognito-->>AuthCallback: Redirect with authorization code and state
  AuthCallback->>Cognito: Exchange code with PKCE verifier
  Cognito-->>AuthCallback: Return tokens
Loading

Possibly related PRs

Suggested reviewers: rmolinares, jlkravitz, isabeleliassen, carlsims

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is related but too generic to describe the main auth, PKCE, and callback-routing changes. Use a more specific title like "Split auth callbacks and add PKCE/CSRF login flow updates".
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description covers the main requirements, changes, testing, and issue reference, so it matches the repository template well.
Linked Issues check ✅ Passed The changes add CSRF state, PKCE, separate callback pages, shared handlers, and updated callback URIs, matching #1641.
Out of Scope Changes check ✅ Passed The diff stays focused on auth flow refactors, callback routes, Cognito config, tests, and related docs, with no clear unrelated additions.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (6)
webroot/src/pages/AuthCallback/StaffCosmo/StaffCosmo.vue (1)

8-20: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting shared error markup.

This template is duplicated verbatim across StaffCosmo.vue, StaffJcc.vue, and (per the stack outline) StaffSocialWork.vue/LicenseeJcc.vue. Each new page only needs to render this same error card, so extracting a shared AuthCallbackError component (or a slot in a common wrapper) would reduce duplication and centralize future error-copy changes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webroot/src/pages/AuthCallback/StaffCosmo/StaffCosmo.vue` around lines 8 -
20, Extract the duplicated error card markup from StaffCosmo.vue, StaffJcc.vue,
StaffSocialWork.vue, and LicenseeJcc.vue into a shared AuthCallbackError
component or common wrapper. Preserve the existing isError conditional,
translations, styling classes, and DashboardPublic router link, then replace
each page’s inline markup with the shared component.
backend/social-work-app/docs/internal/postman/postman-collection.json (1)

164-167: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Exercise the PKCE and CSRF flow in these requests.

The authorize requests omit state, code_challenge, and code_challenge_method; the token request omits code_verifier. Update the collection’s pre-request setup and exchange request so Postman validates the security contract introduced here.

Also applies to: 240-247, 312-319

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/social-work-app/docs/internal/postman/postman-collection.json` around
lines 164 - 167, Update the Postman authorization-flow requests and shared
pre-request setup to generate and send a CSRF state and PKCE code challenge with
code_challenge_method, then persist the matching code_verifier for the flow. Add
the stored state, challenge parameters, and verifier to the corresponding token
exchange requests, including the sections identified by the repeated
authorize/token request blocks.
webroot/src/pages/AuthCallback/_mixins/handler.mixin.ts (3)

138-138: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove commented-out toNative export.

The commented-out // export default toNative(MixinAuthCallbackHandler); is dead code. The direct class export on line 140 is correct for use with mixins() in subclasses like StaffCosmo.ts.

🧹 Proposed cleanup
-
-// export default toNative(MixinAuthCallbackHandler);
-
 export default MixinAuthCallbackHandler;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webroot/src/pages/AuthCallback/_mixins/handler.mixin.ts` at line 138, Remove
the commented-out toNative export near MixinAuthCallbackHandler, keeping the
active direct class export unchanged for mixins() consumers.

84-84: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add type annotations for cognitoAuthDomain and cognitoClientId parameters.

Both getTokens and fetchCognitoTokens leave cognitoAuthDomain and cognitoClientId untyped, defaulting to any. Adding string annotations would improve type safety and consistency with the typed authType parameter.

🔧 Proposed fix
-    async getTokens(appMode: AppModes, authType: AuthTypes, cognitoAuthDomain, cognitoClientId): Promise<void> {
+    async getTokens(appMode: AppModes, authType: AuthTypes, cognitoAuthDomain: string, cognitoClientId: string): Promise<void> {
-    async fetchCognitoTokens(authType: AuthTypes, cognitoAuthDomain, cognitoClientId): Promise<void> {
+    async fetchCognitoTokens(authType: AuthTypes, cognitoAuthDomain: string, cognitoClientId: string): Promise<void> {

Also applies to: 99-99

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webroot/src/pages/AuthCallback/_mixins/handler.mixin.ts` at line 84, Add
explicit string type annotations to the cognitoAuthDomain and cognitoClientId
parameters in both getTokens and fetchCognitoTokens, preserving the existing
appMode and authType types.

99-117: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Add a timeout and error logging to the Cognito token exchange.

The axios.post() call has no explicit timeout — if Cognito is slow or unresponsive, the user is stuck on a blank callback page until the browser's default timeout fires. Additionally, the .catch() on line 88 silently swallows the error with no logging, making production debugging difficult.

⏱️ Proposed fix: add timeout and error logging
         params.append('code_verifier', consumePkceCodeVerifier() || '');

-        const { data } = await axios.post(`${cognitoAuthDomain}/oauth2/token`, params);
+        const { data } = await axios.post(`${cognitoAuthDomain}/oauth2/token`, params, {
+            timeout: 15000,
+        });

And in getTokens():

-        await this.fetchCognitoTokens(authType, cognitoAuthDomain, cognitoClientId).catch(() => {
+        await this.fetchCognitoTokens(authType, cognitoAuthDomain, cognitoClientId).catch((err) => {
+            console.error('Auth callback token exchange failed:', err);
             this.isError = true;
         });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webroot/src/pages/AuthCallback/_mixins/handler.mixin.ts` around lines 99 -
117, Update fetchCognitoTokens to pass an explicit timeout option to the
axios.post token request, using the project’s standard request timeout if
available. In getTokens, replace the silent catch with error logging that
includes the caught error and relevant Cognito token-exchange context before
preserving the existing failure handling.
webroot/src/pages/AuthCallback/StaffSocialWork/StaffSocialWork.spec.ts (1)

12-19: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Smoke test only covers mounting; consider testing auth callback behavior.

The test verifies the component mounts but doesn't exercise the CSRF state verification, token exchange, or error display paths that are the core purpose of this callback page. The mixin's created() will set isError = true when verifyCsrfState() fails (no stored state in test), which is untested.

💡 Suggested additional test cases
it('should display error when CSRF state verification fails', async () => {
    const wrapper = await mountShallow(StaffSocialWork);
    await wrapper.vm.$nextTick();
    expect(wrapper.findComponent(StaffSocialWork).exists()).to.equal(true);
    // Assert isError is true and error UI is rendered
});

it('should verify CSRF state and exchange tokens on valid callback', async () => {
    // Set up sessionStorage with matching state, route query with code+state
    // Assert token exchange was called and redirect occurred
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webroot/src/pages/AuthCallback/StaffSocialWork/StaffSocialWork.spec.ts`
around lines 12 - 19, Expand the StaffSocialWork tests beyond mounting by
covering the auth callback behavior in the component’s created lifecycle and
mixin. Add a test for failed verifyCsrfState() that waits for $nextTick and
asserts isError plus the rendered error UI, and a valid callback test that
configures matching sessionStorage state and route code/state, then verifies
token exchange and redirect behavior using the relevant mocked methods.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@webroot/src/pages/AuthCallback/_mixins/mixins.spec.ts`:
- Around line 43-50: Update the “should successfully get tokens” test to stub
the axios.post token exchange with a successful response and spy on the router
navigation method used by getTokens, then assert it is called with the expected
route instead of checking history.state.replaced. Ensure no real request to
http://localhost/oauth2/token can occur.

In `@webroot/src/pages/MfaResetConfirmLicensee/MfaResetConfirmLicensee.ts`:
- Around line 51-61: Handle failures from createPkceChallenge() within
MfaResetConfirmLicensee.created() using try/catch, preserve the existing CSRF
initialization, and surface a clear user-facing error or disable login when PKCE
generation fails so navigation cannot occur with an empty code_challenge.
- Line 92: Update the getHostedLoginUri call in MfaResetConfirmLicensee to pass
AppModes.JCC instead of this.appMode, ensuring the licensee callback path always
targets the supported JCC route.

In `@webroot/src/utils/auth.ts`:
- Around line 133-218: Resolve the LICENSEE callback mismatch in
getHostedLoginUri: either add the missing LicenseeCosmo and LicenseeSocialWork
callback pages and router entries for /auth/callback/licensee/cosmo and
/auth/callback/licensee/socialwork, or restrict callback-path generation so
LICENSEE only produces supported routes such as /auth/callback/licensee/jcc.

---

Nitpick comments:
In `@backend/social-work-app/docs/internal/postman/postman-collection.json`:
- Around line 164-167: Update the Postman authorization-flow requests and shared
pre-request setup to generate and send a CSRF state and PKCE code challenge with
code_challenge_method, then persist the matching code_verifier for the flow. Add
the stored state, challenge parameters, and verifier to the corresponding token
exchange requests, including the sections identified by the repeated
authorize/token request blocks.

In `@webroot/src/pages/AuthCallback/_mixins/handler.mixin.ts`:
- Line 138: Remove the commented-out toNative export near
MixinAuthCallbackHandler, keeping the active direct class export unchanged for
mixins() consumers.
- Line 84: Add explicit string type annotations to the cognitoAuthDomain and
cognitoClientId parameters in both getTokens and fetchCognitoTokens, preserving
the existing appMode and authType types.
- Around line 99-117: Update fetchCognitoTokens to pass an explicit timeout
option to the axios.post token request, using the project’s standard request
timeout if available. In getTokens, replace the silent catch with error logging
that includes the caught error and relevant Cognito token-exchange context
before preserving the existing failure handling.

In `@webroot/src/pages/AuthCallback/StaffCosmo/StaffCosmo.vue`:
- Around line 8-20: Extract the duplicated error card markup from
StaffCosmo.vue, StaffJcc.vue, StaffSocialWork.vue, and LicenseeJcc.vue into a
shared AuthCallbackError component or common wrapper. Preserve the existing
isError conditional, translations, styling classes, and DashboardPublic router
link, then replace each page’s inline markup with the shared component.

In `@webroot/src/pages/AuthCallback/StaffSocialWork/StaffSocialWork.spec.ts`:
- Around line 12-19: Expand the StaffSocialWork tests beyond mounting by
covering the auth callback behavior in the component’s created lifecycle and
mixin. Add a test for failed verifyCsrfState() that waits for $nextTick and
asserts isError plus the rendered error UI, and a valid callback test that
configures matching sessionStorage state and route code/state, then verifies
token exchange and redirect behavior using the relevant mocked methods.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 85950346-9adc-4ed8-8df5-ef52b99643b1

📥 Commits

Reviewing files that changed from the base of the PR and between a8102ac and 3a1a9d2.

📒 Files selected for processing (80)
  • backend/common-cdk/common_constructs/user_pool.py
  • backend/common-cdk/tests/test_user_pool.py
  • backend/compact-connect/docs/internal/postman/postman-collection.json
  • backend/compact-connect/docs/postman/postman-collection.json
  • backend/compact-connect/stacks/persistent_stack/staff_users.py
  • backend/compact-connect/stacks/provider_users/provider_users.py
  • backend/compact-connect/tests/app/base.py
  • backend/cosmetology-app/docs/internal/postman/postman-collection.json
  • backend/cosmetology-app/docs/postman/postman-collection.json
  • backend/cosmetology-app/docs/search-internal/postman/postman-collection.json
  • backend/cosmetology-app/stacks/persistent_stack/staff_users.py
  • backend/cosmetology-app/tests/app/base.py
  • backend/social-work-app/docs/internal/postman/postman-collection.json
  • backend/social-work-app/docs/postman/postman-collection.json
  • backend/social-work-app/docs/search-internal/postman/postman-collection.json
  • backend/social-work-app/stacks/persistent_stack/staff_users.py
  • backend/social-work-app/tests/app/base.py
  • webroot/src/app.config.ts
  • webroot/src/components/App/App.spec.ts
  • webroot/src/components/App/App.ts
  • webroot/src/components/AutoLogout/AutoLogout.ts
  • webroot/src/components/ChangePassword/ChangePassword.ts
  • webroot/src/components/MilitaryAffiliationInfoBlock/MilitaryAffiliationInfoBlock.ts
  • webroot/src/components/Page/PageMainNav/PageMainNav.ts
  • webroot/src/components/StateSettingsList/StateSettingsList.ts
  • webroot/src/components/UserAccount/UserAccount.ts
  • webroot/src/models/LicenseeUser/LicenseeUser.model.spec.ts
  • webroot/src/models/LicenseeUser/LicenseeUser.model.ts
  • webroot/src/models/StaffUser/StaffUser.model.spec.ts
  • webroot/src/models/StaffUser/StaffUser.model.ts
  • webroot/src/models/User/User.model.ts
  • webroot/src/network/licenseApi/data.api.ts
  • webroot/src/network/licenseApi/interceptors.ts
  • webroot/src/network/searchApi/interceptors.ts
  • webroot/src/network/stateApi/interceptors.ts
  • webroot/src/network/userApi/interceptors.ts
  • webroot/src/pages/AuthCallback/AuthCallback.spec.ts
  • webroot/src/pages/AuthCallback/AuthCallback.ts
  • webroot/src/pages/AuthCallback/LicenseeJcc/LicenseeJcc.less
  • webroot/src/pages/AuthCallback/LicenseeJcc/LicenseeJcc.spec.ts
  • webroot/src/pages/AuthCallback/LicenseeJcc/LicenseeJcc.ts
  • webroot/src/pages/AuthCallback/LicenseeJcc/LicenseeJcc.vue
  • webroot/src/pages/AuthCallback/StaffCosmo/StaffCosmo.less
  • webroot/src/pages/AuthCallback/StaffCosmo/StaffCosmo.spec.ts
  • webroot/src/pages/AuthCallback/StaffCosmo/StaffCosmo.ts
  • webroot/src/pages/AuthCallback/StaffCosmo/StaffCosmo.vue
  • webroot/src/pages/AuthCallback/StaffJcc/StaffJcc.less
  • webroot/src/pages/AuthCallback/StaffJcc/StaffJcc.spec.ts
  • webroot/src/pages/AuthCallback/StaffJcc/StaffJcc.ts
  • webroot/src/pages/AuthCallback/StaffJcc/StaffJcc.vue
  • webroot/src/pages/AuthCallback/StaffSocialWork/StaffSocialWork.less
  • webroot/src/pages/AuthCallback/StaffSocialWork/StaffSocialWork.spec.ts
  • webroot/src/pages/AuthCallback/StaffSocialWork/StaffSocialWork.ts
  • webroot/src/pages/AuthCallback/StaffSocialWork/StaffSocialWork.vue
  • webroot/src/pages/AuthCallback/_mixins/handler.mixin.ts
  • webroot/src/pages/AuthCallback/_mixins/mixins.spec.ts
  • webroot/src/pages/CompactSettings/CompactSettings.ts
  • webroot/src/pages/Home/Home.ts
  • webroot/src/pages/Logout/Logout.ts
  • webroot/src/pages/MfaResetConfirmLicensee/MfaResetConfirmLicensee.ts
  • webroot/src/pages/MfaResetStartLicensee/MfaResetStartLicensee.ts
  • webroot/src/pages/PrivilegeDetail/PrivilegeDetail.ts
  • webroot/src/pages/PublicDashboard/PublicDashboard.spec.ts
  • webroot/src/pages/PublicDashboard/PublicDashboard.ts
  • webroot/src/pages/StateSettings/StateSettings.ts
  • webroot/src/router/index.ts
  • webroot/src/router/routes.ts
  • webroot/src/store/global/global.mutations.ts
  • webroot/src/store/global/global.spec.ts
  • webroot/src/store/global/global.state.ts
  • webroot/src/store/user/user.actions.ts
  • webroot/src/store/user/user.mutations.ts
  • webroot/src/store/user/user.spec.ts
  • webroot/src/store/user/user.state.ts
  • webroot/src/styles.common/_mixins.less
  • webroot/src/styles.common/mixins/auth-error.less
  • webroot/src/utils/auth.ts
  • webroot/tests/helpers/setup.ts
  • webroot/tsconfig.json
  • webroot/vue.config.js
💤 Files with no reviewable changes (2)
  • webroot/src/pages/AuthCallback/AuthCallback.spec.ts
  • webroot/src/pages/AuthCallback/AuthCallback.ts

Comment thread webroot/src/pages/AuthCallback/_mixins/mixins.spec.ts
Comment thread webroot/src/pages/MfaResetConfirmLicensee/MfaResetConfirmLicensee.ts Outdated
Comment thread webroot/src/utils/auth.ts
@jsandoval81 jsandoval81 requested a review from rmolinares July 13, 2026 20:01

@landonshumway-ia landonshumway-ia left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The backend changes for this look clean, the only thing missing is we need to keep the legacy callback URL in place to avoid downtime for JCC.

Comment thread backend/common-cdk/common_constructs/user_pool.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/common-cdk/common_constructs/user_pool.py (1)

259-281: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Avoid adding duplicate or unexpected callback URLs.

When callback_path uses its default /auth/callback, lines 274 and 276 append the same URL twice. For custom paths, the implementation adds a third legacy URL, while backend/common-cdk/tests/test_user_pool.py expects only the custom domain and localhost URLs. This will break the supplied callback URL assertions and may complicate Cognito client configuration.

Only append the legacy URL when callback_path != '/auth/callback', or update the contract and tests deliberately.

Proposed fix
         if ui_domain_name is not None:
             callback_urls.append(f'https://{ui_domain_name}{callback_path}')
-            # TODO - remove after cutover to custom callback paths is deployed `#noqa`: FIX002
-            callback_urls.append(f'https://{ui_domain_name}/auth/callback')
+            if callback_path != '/auth/callback':
+                # TODO - remove after cutover to custom callback paths is deployed `#noqa`: FIX002
+                callback_urls.append(f'https://{ui_domain_name}/auth/callback')
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/common-cdk/common_constructs/user_pool.py` around lines 259 - 281,
The callback URL construction in the app-client creation method duplicates the
default URL and adds an unexpected legacy URL for custom paths. Update the logic
around callback_path so the legacy /auth/callback URL is appended only when
callback_path differs from /auth/callback, preserving the custom-domain and
localhost URLs expected by the existing contract.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@backend/common-cdk/common_constructs/user_pool.py`:
- Around line 259-281: The callback URL construction in the app-client creation
method duplicates the default URL and adds an unexpected legacy URL for custom
paths. Update the logic around callback_path so the legacy /auth/callback URL is
appended only when callback_path differs from /auth/callback, preserving the
custom-domain and localhost URLs expected by the existing contract.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5efa48dd-8038-4063-b301-8fd64ea9b5b8

📥 Commits

Reviewing files that changed from the base of the PR and between 18286cc and 3336cca.

📒 Files selected for processing (1)
  • backend/common-cdk/common_constructs/user_pool.py

@jsandoval81 jsandoval81 requested a review from jlkravitz July 14, 2026 15:58
@jsandoval81

Copy link
Copy Markdown
Collaborator Author

@jlkravitz This is ready for your review.

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.

[Security/Medium] Cognito Hosted UI OAuth flow lacks PKCE and state CSRF validation

3 participants