Skip to content

Chore/remove step up from session flow - #168

Merged
harshlocham merged 2 commits into
mainfrom
chore/remove-step-up-from-session-flow
Aug 10, 2026
Merged

Chore/remove step up from session flow#168
harshlocham merged 2 commits into
mainfrom
chore/remove-step-up-from-session-flow

Conversation

@harshlocham

@harshlocham harshlocham commented Aug 9, 2026

Copy link
Copy Markdown
Owner

This pull request introduces several Phase 2 features and hardening improvements, with a focus on Work Inbox, execution approval flows, and improved handling of suggest-only execution modes. It also removes the automatic step-up OTP challenge from session refresh, cleans up related dead code, and updates documentation. The changes include new APIs, UI surfaces, policy gate helpers, and robust test coverage.

Session and Authorization Changes:

  • Removed automatic step-up OTP challenges from session refresh and bootstrap; cleaned up dead code and updated documentation to reflect the new session contract.
  • Improved internal web API bridge to properly handle structured authorization denials and ensure socket authorization never targets step-up challenge endpoints, with corresponding tests. [1] [2] [3]

Work Inbox & Approvals:

  • Added a feature-flagged read-only /inbox Work Inbox UI, listing authorized WorkSuggestions with server-side pagination.
  • Introduced a feature-flagged /inbox/approvals UI, reusing the existing task-approvals API in the Work Inbox.
  • Made Work Inbox suggestions actionable (Accept / Assign / Dismiss) using mutation APIs.

Execution Approval and Suggest-Only Mode:

  • Added explicit manager “Allow AI tools”/request-execution path, distinct from WorkSuggestion accept, reusing TaskAction approvals.
  • Implemented helpers and logic to handle suggest_only mode exceptions for explicit manager requests and human-approved re-entry, with comprehensive unit tests. [1] [2] [3] [4] [5] [6] [7] [8] [9]

Backend and Observability Hardening:

  • Emitted new outbox events for work suggestion accept/dismiss, added triage metrics and latency tracking, and documented the authorization matrix.
  • Improved database connection handling and added a timeout for more robust worker startup.

Work Suggestion Mutations:

  • Accept/dismiss/assign WorkSuggestion mutations now create coordination Tasks without enqueuing execution, with linkage via Task.suggestionId.

These changes collectively improve the product’s security, auditability, and user experience, especially around work suggestions and execution approval flows.

Summary by CodeRabbit

  • Changes
    • Session refresh and startup recovery now complete silently without step-up verification challenges.
    • Removed password- and OTP-based step-up challenge screens, endpoints, and related notifications.
    • OTP verification remains available for account registration and email verification.
    • Device and network metadata no longer block session refresh.
    • Legacy sessions are normalized during token rotation and remain usable.
  • Bug Fixes
    • Improved database startup warmup reliability.
  • Documentation
    • Updated authentication, architecture, operations, and security documentation to reflect the revised session behavior.

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

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

Next review available in: 5 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

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 Plus

Run ID: 3d7df0b8-dbbc-4a51-8dad-f0d260af34d5

📥 Commits

Reviewing files that changed from the base of the PR and between f8776b0 and 3e23dda.

📒 Files selected for processing (1)
  • apps/socket/tests/internal-web-bridge.test.ts
📝 Walkthrough

Walkthrough

Changes

The PR removes automatic step-up authentication from session refresh, bootstrap, middleware, API, and socket authorization. It deletes challenge services, routes, UI, and the StepUpChallenge model. Device fingerprints remain session metadata. OTP remains for account verification.

Session authentication simplification

Layer / File(s) Summary
Refresh service and legacy session handling
packages/auth/services/refresh.service.ts, packages/auth/repositories/session.*, packages/auth/session/fingerprint.ts
Refresh no longer checks fingerprint drift or creates step-up challenges. Token rotation restores legacy step_up_pending sessions to active.
Web authentication flow cleanup
apps/web/lib/utils/auth/client-session.ts, apps/web/lib/utils/api.ts, apps/web/middleware.ts, apps/web/lib/auth/authBootstrap.ts, apps/web/app/api/auth/refresh/route.ts, apps/web/app/login/page.tsx
Web authentication no longer detects, queries, redirects to, or displays step-up challenges.
Removed challenge surface and package exports
apps/web/app/api/auth/challenge/**, apps/web/app/auth/challenge/**, apps/web/app/api/internal/auth/step-up-status/**, packages/auth/services/step-up-*, packages/auth/errors/auth-errors.ts, packages/auth/index.ts, packages/db/models/StepUpChallenge.ts
Challenge APIs, UI, services, model, error, and related exports are deleted.
Authentication regression coverage
apps/web/test/*, apps/socket/tests/*, packages/auth/__tests__/*
Tests cover refresh recovery and verify that normal authentication traffic does not call challenge endpoints or redirect to the challenge page.
Authentication contract documentation
docs/ARCHITECTURE.md, docs/architecture/*, docs/operations/*, .changeset/*
Documentation describes silent refresh, account-verification-only OTP, legacy session compatibility, and historical step-up audit events.

Web database warmup

Layer / File(s) Summary
Node database warmup
apps/web/instrumentation.ts, apps/web/instrumentation.node.mjs
Instrumentation dynamically loads a Node-only MongoDB warmup module and logs successful connection initialization.

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

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant WebAuth
  participant refreshService
  participant SessionRepository
  Client->>WebAuth: request authenticated resource
  WebAuth->>refreshService: refresh expired access token
  refreshService->>SessionRepository: validate and rotate session token
  SessionRepository-->>refreshService: active session
  refreshService-->>WebAuth: refreshed tokens
  WebAuth-->>Client: retry authenticated request
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.77% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: removing step-up authentication from the session flow.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/remove-step-up-from-session-flow

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.

@harshlocham

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 6

🤖 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 `@apps/socket/tests/internal-web-bridge.test.ts`:
- Around line 23-39: Update the test around both postToInternalWebApi calls to
store their returned values, then assert each result equals { allowed: true,
role: "user" }. Keep the existing call-count and requested-path assertions
unchanged.

In `@apps/web/instrumentation.node.mjs`:
- Around line 1-3: Update the module-loading comment in the file header to state
that apps/web/instrumentation.ts loads this module via new Function, removing
the inaccurate webpackIgnore claim while preserving the explanation that this
prevents Next/webpack from bundling mongoose.

In `@apps/web/instrumentation.ts`:
- Around line 1-6: The nodeModuleFileUrl function in apps/web/instrumentation.ts
must stop deriving module paths from process.cwd(); use a stable startup root or
module-relative resolution so the warmup import consistently targets the sibling
module regardless of the launch directory. Apply the corresponding resolution
update in apps/web/instrumentation.node.mjs at lines 8-10; both sites must use
the same CWD-independent behavior.

In `@docs/ARCHITECTURE.md`:
- Around line 164-167: In the “Internal — socket → web authorization” section,
update the sentence beginning “These are how” to “These endpoints let the
stateless socket server make trust decisions,” leaving the endpoint list and
surrounding documentation unchanged.

In `@docs/architecture/authentication-and-session-model.md`:
- Around line 157-175: Update the new Markdown code fences around the
access-token and invalid-refresh flows to include the text language identifier,
ensuring both opening fences use ```text and the document passes MD040.
- Line 456: Restore the route-level CSRF caveat in the authentication and
session model documentation, explicitly noting that cookie-authenticated API
POST/PUT/PATCH/DELETE handlers lack demonstrated CSRF token validation or
equivalent origin checks.
🪄 Autofix

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 Plus

Run ID: aed28d4f-07bd-4afa-8cd1-b035aaf8868f

📥 Commits

Reviewing files that changed from the base of the PR and between 1a62da9 and f8776b0.

📒 Files selected for processing (46)
  • .changeset/remove-step-up-from-session-flow.md
  • apps/socket/tests/internal-web-bridge.test.ts
  • apps/web/app/api/auth/challenge/otp/send/route.ts
  • apps/web/app/api/auth/challenge/otp/verify/route.ts
  • apps/web/app/api/auth/challenge/password/route.ts
  • apps/web/app/api/auth/refresh/route.ts
  • apps/web/app/api/internal/auth/step-up-status/route.ts
  • apps/web/app/auth/challenge/ChallengeForm.tsx
  • apps/web/app/auth/challenge/page.tsx
  • apps/web/app/login/page.tsx
  • apps/web/instrumentation.node.mjs
  • apps/web/instrumentation.ts
  • apps/web/lib/auth/authBootstrap.ts
  • apps/web/lib/utils/api.ts
  • apps/web/lib/utils/auth/client-session.ts
  • apps/web/middleware.ts
  • apps/web/test/auth-bootstrap.no-step-up.test.ts
  • apps/web/test/authorize-conversation-access.route.test.ts
  • apps/web/test/client-session.refresh.test.ts
  • apps/web/test/refresh-flow.integration.test.ts
  • docs/ARCHITECTURE.md
  • docs/architecture/authentication-and-session-model.md
  • docs/architecture/shared-package-design.md
  • docs/operations/PRODUCTION_REQUIREMENTS.md
  • docs/operations/SECURITY_AUDIT.md
  • packages/auth/__tests__/e2e/auth-lifecycle.e2e.test.ts
  • packages/auth/__tests__/helpers/factories/request-context.factory.ts
  • packages/auth/__tests__/helpers/factories/session.factory.ts
  • packages/auth/__tests__/helpers/factories/step-up-challenge.factory.ts
  • packages/auth/__tests__/integration/repositories/session.repo.test.ts
  • packages/auth/__tests__/integration/services/refresh.service.test.ts
  • packages/auth/__tests__/integration/services/step-up-otp.service.test.ts
  • packages/auth/__tests__/integration/services/step-up-password.service.test.ts
  • packages/auth/__tests__/step-up-flow.integration.test.ts
  • packages/auth/errors/auth-errors.ts
  • packages/auth/index.ts
  • packages/auth/repositories/authEventModel.ts
  • packages/auth/repositories/session.repo.ts
  • packages/auth/repositories/sessionModel.ts
  • packages/auth/services/list-auth-events.service.ts
  • packages/auth/services/refresh.service.ts
  • packages/auth/services/security-event-logger.ts
  • packages/auth/services/step-up-otp.service.ts
  • packages/auth/services/step-up-password.service.ts
  • packages/auth/session/fingerprint.ts
  • packages/db/models/StepUpChallenge.ts
💤 Files with no reviewable changes (21)
  • packages/auth/errors/auth-errors.ts
  • apps/web/app/api/internal/auth/step-up-status/route.ts
  • apps/web/app/api/auth/challenge/otp/verify/route.ts
  • packages/auth/tests/integration/services/step-up-password.service.test.ts
  • apps/web/app/api/auth/challenge/password/route.ts
  • apps/web/app/api/auth/refresh/route.ts
  • packages/auth/services/step-up-otp.service.ts
  • packages/auth/tests/integration/services/step-up-otp.service.test.ts
  • packages/auth/services/step-up-password.service.ts
  • apps/web/app/api/auth/challenge/otp/send/route.ts
  • packages/auth/tests/helpers/factories/step-up-challenge.factory.ts
  • apps/web/app/auth/challenge/ChallengeForm.tsx
  • packages/db/models/StepUpChallenge.ts
  • apps/web/app/auth/challenge/page.tsx
  • packages/auth/services/security-event-logger.ts
  • packages/auth/index.ts
  • packages/auth/tests/step-up-flow.integration.test.ts
  • apps/web/lib/utils/api.ts
  • packages/auth/tests/helpers/factories/session.factory.ts
  • apps/web/app/login/page.tsx
  • apps/web/lib/auth/authBootstrap.ts

Comment on lines +23 to +39
await postToInternalWebApi({
path: "/api/internal/socket/authorize-identity",
body: { userId: "u1", tokenVersion: 0 },
timeoutMs: 1_000,
});
await postToInternalWebApi({
path: "/api/internal/socket/authorize-conversation-access",
body: { userId: "u1", conversationId: "c1" },
timeoutMs: 1_000,
});

assert.equal(fetchMock.mock.callCount(), 2);
for (const url of requestedPaths) {
assert.equal(url.includes("step-up"), false);
assert.equal(url.includes("/auth/challenge"), false);
assert.equal(url.includes("/api/auth/challenge"), false);
}

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert both authorization responses.

postToInternalWebApi returns null when the bridge cannot produce a usable response. Lines 23-32 discard both results. The test can pass after two failed authorization calls if the bridge returns null.

Store both results and assert the expected { allowed: true, role: "user" } response.

Proposed fix
-        await postToInternalWebApi({
+        const identityResult = await postToInternalWebApi({
             path: "/api/internal/socket/authorize-identity",
             body: { userId: "u1", tokenVersion: 0 },
             timeoutMs: 1_000,
         });
-        await postToInternalWebApi({
+        const conversationResult = await postToInternalWebApi({
             path: "/api/internal/socket/authorize-conversation-access",
             body: { userId: "u1", conversationId: "c1" },
             timeoutMs: 1_000,
         });
 
+        assert.deepEqual(identityResult, { allowed: true, role: "user" });
+        assert.deepEqual(conversationResult, { allowed: true, role: "user" });
         assert.equal(fetchMock.mock.callCount(), 2);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await postToInternalWebApi({
path: "/api/internal/socket/authorize-identity",
body: { userId: "u1", tokenVersion: 0 },
timeoutMs: 1_000,
});
await postToInternalWebApi({
path: "/api/internal/socket/authorize-conversation-access",
body: { userId: "u1", conversationId: "c1" },
timeoutMs: 1_000,
});
assert.equal(fetchMock.mock.callCount(), 2);
for (const url of requestedPaths) {
assert.equal(url.includes("step-up"), false);
assert.equal(url.includes("/auth/challenge"), false);
assert.equal(url.includes("/api/auth/challenge"), false);
}
const identityResult = await postToInternalWebApi({
path: "/api/internal/socket/authorize-identity",
body: { userId: "u1", tokenVersion: 0 },
timeoutMs: 1_000,
});
const conversationResult = await postToInternalWebApi({
path: "/api/internal/socket/authorize-conversation-access",
body: { userId: "u1", conversationId: "c1" },
timeoutMs: 1_000,
});
assert.deepEqual(identityResult, { allowed: true, role: "user" });
assert.deepEqual(conversationResult, { allowed: true, role: "user" });
assert.equal(fetchMock.mock.callCount(), 2);
for (const url of requestedPaths) {
assert.equal(url.includes("step-up"), false);
assert.equal(url.includes("/auth/challenge"), false);
assert.equal(url.includes("/api/auth/challenge"), false);
}
🤖 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 `@apps/socket/tests/internal-web-bridge.test.ts` around lines 23 - 39, Update
the test around both postToInternalWebApi calls to store their returned values,
then assert each result equals { allowed: true, role: "user" }. Keep the
existing call-count and requested-path assertions unchanged.

Comment thread apps/web/instrumentation.node.mjs
Comment on lines +1 to +6
function nodeModuleFileUrl(relativeToCwd: string): string {
const absolute = `${process.cwd()}/${relativeToCwd}`.replace(/\\/g, "/");
return absolute.startsWith("/")
? `file://${absolute}`
: `file:///${absolute}`;
}

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.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 3 \
  'next (dev|start)|standalone/server\.js|WORKDIR|process\.cwd|outputFileTracing' \
  --glob 'package.json' \
  --glob 'Dockerfile*' \
  --glob '*.yml' \
  --glob '*.yaml' \
  --glob '*.ts' \
  --glob '*.mjs' . || true

for path in apps/web/instrumentation.node.mjs packages/db/dist/db.js; do
  if [ -f "$path" ]; then
    echo "present: $path"
  else
    echo "missing: $path"
  fi
done

Repository: harshlocham/semantask

Length of output: 3499


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== tracked Docker/workflow deploy start files =="
git ls-files | rg '(^|/)(Dockerfile.*|[.]?(github/workflows/.*\.ya?ml))|server\.js|start.*\.tsx|standalone|package-lock\.json|pnpm-lock\.yaml|bun\.lockb|yarn\.lock|\.next' || true

echo
echo "== next config files =="
for f in $(git ls-files '*/next.config.*' 'next.config.*'); do
  echo "--- $f"
  sed -n '1,180p' "$f"
done

echo
echo "== root scripts/config =="
for f in package.json pnpm-workspace.yaml turbo.json .github/workflows/*.yml .github/workflows/*.yaml; do
  [ -f "$f" ] || continue
  echo "--- $f"
  sed -n '1,220p' "$f"
done

echo
echo "== files importing instrumentation.node.mjs or instrumenting warmup =="
rg -n -C 3 'instrumentation\.node\.mjs|nodeModuleFileUrl|node --import|--import|node --experimental-import-meta-resolve|export async function register|register' --glob '*.ts' --glob '*.mjs' --glob '*.js' --glob '*.tsx' . || true

echo
echo "== relevant app/web files =="
sed -n '1,220p' apps/web/instrumentation.ts
wc -l apps/web/instrumentation.node.mjs
sed -n '1,80p' apps/web/instrumentation.node.mjs

Repository: harshlocham/semantask

Length of output: 50378


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== all non-test references to apps/web/instrumentation file/name =="
git ls-files | grep -vE '(/tests/|tests/)' | xargs -r rg -n 'apps/web/instrumentation|instrumentation\.ts|nodeModuleFileUrl|\.\././\.\./instrumentation\.node\.mjs' || true

echo
echo "== all direct references to relativeWarmup path =="
rg -n -C 3 'relativeWarmup|\.\/instrumentation\.node\.mjs|node --import|--import' --glob '*.ts' --glob '*.mjs' --glob '*.js' --glob '*.tsx' --exclude-dir node_modules --exclude-dir .next . || true

echo
echo "== deterministic cwd resolution for root and web working dirs =="
node - <<'JS'
const path = require('node:path');
const fs = require('node:fs');

function nodeModuleFileUrl(relativeToCwd) {
    const absolute = `${process.cwd()}/${relativeToCwd}`.replace(/\\/g, "/");
    return absolute.startsWith("/")
        ? `file://${absolute}`
        : `file:///${absolute}`;
}

const cases = {
    root: [
        process.cwd(),
        path.resolve(process.cwd(), 'apps/web/instrumentation.ts'),
        path.resolve(process.cwd(), '../../packages/db/dist/db.js'),
    ],
    appsWeb: [
        path.resolve(process.cwd(), 'apps/web'),
        path.resolve(process.cwd(), 'apps/web/instrumentation.ts'),
        path.resolve(process.cwd(), '../../packages/db/dist/db.js'),
    ],
};

for (const [name, root] of Object.entries(cases)) {
    process.chdir(root);
    console.log(name, JSON.stringify({
        cwd: process.cwd(),
        nodeModuleUrl: nodeModuleFileUrl('instrumentation.ts'),
        dbEntry: `file://${path.resolve(process.cwd(), '../../packages/db/dist/db.js')}`
    }));
}
JS

Repository: harshlocham/semantask

Length of output: 1464


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== deterministic cwd resolution for root and web working dirs =="
node - <<'JS'
const path = require('node:path');
const fs = require('node:fs');

function nodeModuleFileUrl(relativeToCwd) {
    const absolute = `${process.cwd()}/${relativeToCwd}`.replace(/\\/g, "/");
    return absolute.startsWith("/")
        ? `file://${absolute}`
        : `file:///${absolute}`;
}

const root = process.cwd();
const appRoot = path.resolve(root, 'apps/web');
const cases = {
    root: {
        cwd: root,
        nodeModuleUrl: nodeModuleFileUrl('instrumentation.ts'),
        dbEntry: `file://${path.resolve(root, '../../packages/db/dist/db.js')}`,
    },
    appsWeb: {
        cwd: appRoot,
        nodeModuleUrl: nodeModuleFileUrl('instrumentation.ts'),
        dbEntry: `file://${path.resolve(appRoot, '../../packages/db/dist/db.js')}`,
    },
};
for (const name of ['root', 'appsWeb']) {
    process.chdir(cases[name].cwd);
    console.log(name, JSON.stringify({
        cwd: process.cwd(),
        nodeModuleUrl: nodeModuleFileUrl('instrumentation.ts'),
        dbEntry: cases[name].dbEntry,
    }));
}
process.chdir(root);
JS

echo
echo "== inspect apps/web/instrumentation.ts relevant import path =="
sed -n '30,55p' apps/web/instrumentation.ts

echo
echo "== check whether packages/db/dist/db.js exists in committed tree =="
node - <<'JS'
const fs = require('node:fs');
const root = require('node:process').cwd();
for (const p of [
  'packages/db/dist/db.js',
  'packages/db/package.json',
  'apps/web/instrumentation.ts'
]) {
  const abs = require('node:path').resolve(root, p);
  console.log(p, require('node:fs').existsSync(abs) ? 'exists' : 'missing');
}
JS

Repository: harshlocham/semantask

Length of output: 1567


Remove the working-directory dependency from apps/web/instrumentation.ts.

The warmup import is only reached from this instrumentation entry point and currently resolves only when Node starts in apps/web; another CWD makes it resolve to apps/web/../instrumentation.ts and miss the sibling warmup module. Use a known startup root or module-relative resolution instead of process.cwd().

📍 Affects 2 files
  • apps/web/instrumentation.ts#L1-L6 (this comment)
  • apps/web/instrumentation.node.mjs#L8-L10
🤖 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 `@apps/web/instrumentation.ts` around lines 1 - 6, The nodeModuleFileUrl
function in apps/web/instrumentation.ts must stop deriving module paths from
process.cwd(); use a stable startup root or module-relative resolution so the
warmup import consistently targets the sibling module regardless of the launch
directory. Apply the corresponding resolution update in
apps/web/instrumentation.node.mjs at lines 8-10; both sites must use the same
CWD-independent behavior.

Comment thread docs/ARCHITECTURE.md
Comment on lines 164 to +167
- **Internal — socket → web authorization** (`api/internal/socket/*`):
`authorize-identity`, `authorize-conversation-access`, `authorize-message-action`; plus
`api/internal/auth/step-up-status`. These are how the stateless socket server makes trust decisions.
`authorize-identity`, `authorize-conversation-access`, `authorize-message-action`,
`presence-peers`. These are how the stateless socket server makes trust decisions (no
step-up-status bridge).

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the sentence on Line 165.

“These are how the stateless socket server makes trust decisions” is ungrammatical. Use “These endpoints let the stateless socket server make trust decisions.”

🧰 Tools
🪛 LanguageTool

[grammar] ~165-~165: Ensure spelling is correct
Context: ...on-access, authorize-message-action, presence-peers`. These are how the stateless socket ser...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🤖 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 `@docs/ARCHITECTURE.md` around lines 164 - 167, In the “Internal — socket → web
authorization” section, update the sentence beginning “These are how” to “These
endpoints let the stateless socket server make trust decisions,” leaving the
endpoint list and surrounding documentation unchanged.

Source: Linters/SAST tools

Comment on lines +157 to 175
```
access token expires / missing
→ valid refresh token
→ verifySession + user/tokenVersion checks
→ rotate refresh token hash (state → active)
→ issue new access + refresh tokens
→ continue
```

**Invalid / revoked / expired refresh**

```
invalid or revoked refresh token
→ authentication failure (401)
→ client clears session / redirects to login
→ login recovery
```

```

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add language identifiers to the new Markdown fences.

markdownlint-cli2 reports MD040 on Lines 157, 168, and 175. Use text for the flow diagrams so the document passes the configured Markdown checks.

🧰 Tools
🪛 markdownlint-cli2 (0.23.2)

[warning] 157-157: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 168-168: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 175-175: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 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 `@docs/architecture/authentication-and-session-model.md` around lines 157 -
175, Update the new Markdown code fences around the access-token and
invalid-refresh flows to include the text language identifier, ensuring both
opening fences use ```text and the document passes MD040.

Source: Linters/SAST tools

user-facing route handlers in `apps/web/app/api/auth/*` add cookie
handling, CSRF, and rate limiting (rate limiting was not
exhaustively traced).
handling and rate limiting (rate limiting was not exhaustively traced).

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.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

fd -t f -e ts -e tsx . apps/web/app/api -0 |
  xargs -0 rg -n -C 3 \
    'export async function (POST|PUT|PATCH|DELETE)|cookies\(|csrf|CSRF|Origin|Referer|sameSite'

Repository: harshlocham/semantask

Length of output: 28089


🏁 Script executed:

#!/usr/bin/env bash1
set -euo pipefail

echo "== markdown line =="
sed -n '440,462p' docs/architecture/authentication-and-session-model.md | cat -n -v

echo
echo "== route-level auth/origin checks near relevant APIs =="
for f in \
  apps/web/app/api/tasks/route.ts \
  apps/web/app/api/tasks/[id]/route.ts \
  apps/web/app/api/tasks/[id]/request-execution/route.ts \
  apps/web/app/api/tasks/[id]/cancel/route.ts \
  apps/web/app/api/messages/route.ts \
  apps/web/app/api/messages/[id]/edit/route.ts \
  apps/web/app/api/messages/[id]/delete/route.ts \
  apps/web/app/api/messages/[id]/delivered/route.ts \
  apps/web/app/api/messages/[id]/semantic/route.ts \
  apps/web/app/api/conversations/route.ts \
  apps/web/app/api/organizations/route.ts \
  apps/web/app/api/organizations/[id]/route.ts \
  apps/web/app/api/organizations/[id]/members/route.ts \
  apps/web/app/api/organizations/[id]/policy/route.ts \
  apps/web/app/api/organizations/[id]/quota/route.ts \
  apps/web/app/api/work-suggestions/[id]/accept/route.ts \
  apps/web/app/api/work-suggestions/[id]/assign/route.ts \
  apps/web/app/api/work-suggestions/[id]/dismiss/route.ts \
  apps/web/app/api/auth/logout/route.ts \
  apps/web/app/api/auth/revoke-all-tokens/route.ts \
  apps/web/app/api/admin/tool-grants/route.ts \
  apps/web/app/api/admin/tool-grants/[id]/route.ts \
  apps/web/app/api/admin/toggleban/route.ts \
  apps/web/app/api/admin/changeRoal/route.ts \
  apps/web/app/api/admin/assignments/route.ts \
  apps/web/app/api/auth/refresh/route.ts \
  apps/web/app/api/updateImage/route.ts
do
  [ -f "$f" ] || continue
  echo "--- $f ---"
  rg -n -C 3 \
    'export async function (POST|PUT|PATCH|DELETE|GET|HEAD|OPTIONS|POST|PUT)$\
export async function GET\
export async function HEAD\
export async function OPTIONS\
csrf|CSRF\
Origin|Origin:"\s*\
OriginHeader\
Origin |Referer|sameSite|cookies\(|requireAuthUser|requireAdminUser|AuthorizationError' \
    "$f" || true
done

echo
echo "== auth guard and origin helpers =="
rg -n -C 4 \
  'export (async function|function) (requireAuthUser|requireAdminUser|isSameSite|checkOrigin|verifyCsrf|Origin|origin|csrf|CSRF|sameSite)' \
  apps/web/lib apps/web/models apps/web/app/api || true

Repository: harshlocham/semantask

Length of output: 9392


🏁 Script executed:

#!/usr/bin/env bash
set -edu pipefail

echo "== changed files/stat =="
git status --short
git diff --stat || true

echo
echo "== docs line context =="
sed -n '448,462p' docs/architecture/authentication-and-session-model.md | cat -n -v

echo
echo "== changed docs lines =="
git diff HEAD -- docs/architecture/authentication-and-session-model.md | sed -n '/authentication-and-session-model.md/{p}; /Uncertain/{p;N;p}' || true

echo
echo "== require guards/source =="
for f in \
  apps/web/lib/utils/auth/requireAuthUser.ts \
  apps/web/lib/utils/auth/requireAdminUser.ts \
  apps/web/lib/utils/auth/getAuthUser.ts \
  apps/web/lib/utils/auth/validateAuthUser.ts
do
  [ -f "$f" ] || continue
  echo "--- $f ---"
  cat -n -v "$f"
done

echo
echo "== API handler auth usage, excluding internal secret routes and google callback state handling =="
python3 - <<'PY'
import os, re
root="apps/web/app/api"
files=[]
for dirpath, dirnames, filenames in os.walk(root):
    for fn in filenames:
        if fn == "route.ts":
            files.append(os.path.join(dirpath, fn))
for path in files:
    text=open(path, "r", encoding='utf-8', errors='replace').read()
    if re.search(r'export async function (POST|PUT|PATCH|DELETE)', text):
        # include relevant tokens, safe context
        for token in ['requireAuthUser', 'requireAdminUser', 'AuthError', 'AuthorizationError', 'cookies(', 'get(' , 'csrf', 'CSRF', 'Origin', 'Referer', 'sameSite']:
            if token in text:
                print(f"--- {path} has {token!r} ---")
                for i,line in enumerate(text.splitlines(), 1):
                    if token in line:
                        lo=max(1,i-4); hi=min(len(text.splitlines()), i+4)
                        for j in range(lo, hi+1):
                            print(f"{j}: {text.splitlines()[j-1]}")
                        break
PY

Repository: harshlocham/semantask

Length of output: 38957


Keep the route-level CSRF caveat.

The removed item still matters because the API cookie-authenticated POST/PUT/PATCH/DELETE routes do not show route-level CSRF token validation or equivalent origin checks in the handler implementations.

🤖 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 `@docs/architecture/authentication-and-session-model.md` at line 456, Restore
the route-level CSRF caveat in the authentication and session model
documentation, explicitly noting that cookie-authenticated API
POST/PUT/PATCH/DELETE handlers lack demonstrated CSRF token validation or
equivalent origin checks.

@harshlocham
harshlocham force-pushed the chore/remove-step-up-from-session-flow branch from f8776b0 to cdd46de Compare August 10, 2026 09:52
@harshlocham
harshlocham force-pushed the chore/remove-step-up-from-session-flow branch 2 times, most recently from 0c3d916 to 749f03c Compare August 10, 2026 11:25
@harshlocham
harshlocham force-pushed the chore/remove-step-up-from-session-flow branch from 749f03c to 22bc766 Compare August 10, 2026 12:14
@harshlocham
harshlocham force-pushed the chore/remove-step-up-from-session-flow branch from 22bc766 to d9fd9b0 Compare August 10, 2026 12:23
@harshlocham
harshlocham force-pushed the chore/remove-step-up-from-session-flow branch from d9fd9b0 to d410220 Compare August 10, 2026 12:31
@harshlocham
harshlocham force-pushed the chore/remove-step-up-from-session-flow branch 2 times, most recently from 6db5541 to fc6bf87 Compare August 10, 2026 12:50
@harshlocham
harshlocham force-pushed the chore/remove-step-up-from-session-flow branch from fc6bf87 to 071fe5e Compare August 10, 2026 12:58
Base automatically changed from feat/v2-p2-hardening to main August 10, 2026 13:08
- Eliminated automatic step-up OTP challenges during session refresh and bootstrap processes.
- Removed related UI/API components and the StepUpChallenge model as they are no longer needed.
- Updated tests to ensure that the socket authorization bridge does not target step-up challenge endpoints.
- Adjusted middleware and API routes to reflect the removal of step-up logic, enhancing overall code clarity and maintainability.
- Eliminated automatic step-up OTP challenges from session refresh and bootstrap processes, streamlining the authentication flow.
- Removed the StepUpChallenge model and related UI/API components as they are no longer necessary.
- Updated documentation to reflect the new MVP session contract, clarifying that session maintenance does not involve step-up challenges.
- Introduced a Node-only Mongo warmup script to optimize database connection handling during application startup.
- Adjusted various components and services to ensure compatibility with the updated authentication logic, enhancing overall code clarity and maintainability.
@harshlocham
harshlocham force-pushed the chore/remove-step-up-from-session-flow branch from 071fe5e to 3e23dda Compare August 10, 2026 13:08
@harshlocham
harshlocham merged commit 45d3e43 into main Aug 10, 2026
8 checks passed
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