Skip to content

feat(wabe): enhance security on 2FA and auth#299

Merged
coratgerl merged 4 commits intomainfrom
enhance-security-on-2fa-and-auth
Feb 20, 2026
Merged

feat(wabe): enhance security on 2FA and auth#299
coratgerl merged 4 commits intomainfrom
enhance-security-on-2fa-and-auth

Conversation

@coratgerl
Copy link
Collaborator

@coratgerl coratgerl commented Feb 20, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Added rate-limiting for authentication flows (sign-in, sign-up, challenge verification) to prevent brute-force attacks
    • Added Multi-Factor Authentication (MFA) challenge support for enhanced security
    • Added file upload security with validation for file size, MIME type, and extensions
    • Disabled bucket route in production by default for security
  • Improvements

    • Enhanced cookie security handling for cross-domain authentication scenarios

@coratgerl coratgerl self-assigned this Feb 20, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 20, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR introduces authentication rate limiting, multi-factor authentication (MFA) with challenge tokens, file upload security validation, dynamic cookie SameSite configuration, and production-mode safety features (disabling bucket route). The changes span authentication providers, session handling, file processing, and server configuration.

Changes

Cohort / File(s) Summary
Authentication Security Configuration
packages/wabe/src/authentication/interface.ts, packages/wabe/src/authentication/cookies.ts
Adds AuthenticationRateLimitConfig and AuthenticationSecurityConfig interfaces to configure rate limiting per operation (signIn, signUp, verifyChallenge) with thresholds and durations. Introduces getSessionCookieSameSite() helper to dynamically set cookie SameSite based on domain configuration.
Rate Limiting Implementation
packages/wabe/src/authentication/security.ts, packages/wabe/src/authentication/providers/EmailPassword.ts, packages/wabe/src/authentication/providers/EmailPasswordSRP.ts, packages/wabe/src/authentication/providers/EmailOTP.ts, packages/wabe/src/authentication/providers/PhonePassword.ts, packages/wabe/src/authentication/providers/QRCodeOTP.ts
New security module exports rate limiting utilities (isRateLimited, registerRateLimitFailure, clearRateLimit). Each authentication provider integrates rate limiting by normalizing user identifiers (email/phone), checking limits before operations, registering failures on invalid attempts, and clearing limits on success.
Rate Limiting Tests
packages/wabe/src/authentication/providers/EmailPassword.test.ts, packages/wabe/src/authentication/providers/PhonePassword.test.ts
Adds production-mode tests verifying rate limiting blocks repeated sign-in/sign-up attempts with appropriate error messages and prevents further database lookups after the block is reached.
MFA Challenge Flow
packages/wabe/src/authentication/security.ts, packages/wabe/src/authentication/resolvers/signInWithResolver.ts, packages/wabe/src/authentication/resolvers/verifyChallenge.ts, packages/wabe/src/schema/Schema.ts
Implements MFA challenge management with token generation (createMfaChallenge), validation (consumeMfaChallenge), and requirement checking (shouldRequireMfaChallenge). Adds PendingAuthenticationChallenge object to User schema and challengeToken fields to authentication resolvers. Manages MFA challenge tokens with TTL expiration and database persistence.
MFA Challenge Tests
packages/wabe/src/authentication/resolvers/signInWithResolver.test.ts, packages/wabe/src/authentication/resolvers/verifyChallenge.test.ts
Adds tests for MFA challenge creation on sign-in and validation during challenge verification, including production-mode flows requiring challenge tokens and proper error handling for invalid tokens.
Dynamic Cookie SameSite
packages/wabe/src/authentication/resolvers/signInWithResolver.ts, packages/wabe/src/authentication/resolvers/signUpWithResolver.ts, packages/wabe/src/authentication/resolvers/verifyChallenge.ts, packages/wabe/src/server/defaultSessionHandler.ts, packages/wabe/src/server/routes/authHandler.ts
Replaces hardcoded cookie SameSite values ('Strict' or 'None') with dynamic computation via getSessionCookieSameSite(), returning 'None' when front and back domains differ, otherwise 'Strict'.
File Upload Security
packages/wabe/src/file/interface.ts, packages/wabe/src/file/security.ts, packages/wabe/src/file/hookUploadFile.ts, packages/wabe/src/file/index.test.ts
Adds FileUploadSecurityConfig with constraints (max file size, allowed MIME types/extensions, filename randomization). New security module validates uploads via MIME signature detection, size/extension checks, and optional filename randomization. Integrates validation into upload flow. Tests verify extension blocking and filename randomization in production.
Production Safety
packages/wabe/src/server/index.ts, packages/wabe/src/server/routes/index.ts, packages/wabe/src/server/index.test.ts
Adds enableBucketRoute flag (true in dev, false in production) to conditionally disable the /bucket route in production. Updates defaultRoutes() signature to accept config object instead of plain string parameter.
Utilities
packages/wabe/src/utils/database.ts, packages/wabe/src/utils/export.ts
Introduces getDatabaseController() utility for type-safe database controller access with runtime validation. Exports new database utilities publicly.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant SignInResolver
    participant AuthProvider
    participant RateLimiter
    participant MFAManager
    participant Database

    Client->>SignInResolver: signIn(credentials)
    SignInResolver->>RateLimiter: isRateLimited(email)
    alt Rate Limited
        RateLimiter-->>SignInResolver: true
        SignInResolver-->>Client: Error - Invalid credentials
    else Not Limited
        RateLimiter-->>SignInResolver: false
        SignInResolver->>AuthProvider: validate credentials
        alt Invalid Credentials
            AuthProvider-->>SignInResolver: false
            SignInResolver->>RateLimiter: registerRateLimitFailure(email)
            SignInResolver-->>Client: Error - Invalid credentials
        else Valid Credentials
            AuthProvider-->>SignInResolver: userId
            alt MFA Required
                SignInResolver->>MFAManager: createMfaChallenge(userId, provider)
                MFAManager->>Database: save pendingChallenges
                Database-->>MFAManager: saved
                MFAManager-->>SignInResolver: challengeToken
                SignInResolver->>RateLimiter: clearRateLimit(email)
                SignInResolver-->>Client: { user, challengeToken }
            else MFA Not Required
                SignInResolver->>RateLimiter: clearRateLimit(email)
                SignInResolver-->>Client: { accessToken, refreshToken, user }
            end
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 With tokens and limits, we fortify the gate,
MFA challenges ensure safety's weight,
Rate limits defend from the brute-force attack,
Files now validated, none slip through the cracks,
In production we're prudent, in dev we explore,
Wabe's defenses now stronger than before! 🔐

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(wabe): enhance security on 2FA and auth' accurately describes the main changes in this PR, which introduce rate limiting for authentication flows, MFA challenge validation, and file upload security.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch enhance-security-on-2fa-and-auth

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

@coratgerl
Copy link
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 20, 2026

✅ Actions performed

Review triggered.

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (1)
packages/wabe/src/authentication/security.ts (1)

48-75: Consider shared storage for rate-limit state in multi-instance deployments.

The module-level Map is per-process, so horizontally scaled instances won’t share limits.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/wabe/src/authentication/security.ts` around lines 48 - 75, The
current module-level Map rateLimitStorage (storing RateLimitState) is
process-local and won't enforce limits across multiple instances; replace it
with a pluggable shared store interface (e.g., IRateLimitStore with
get/set/increment/expire) and update code that references rateLimitStorage to
use that interface, wiring a Redis-backed implementation (or existing app cache
in the WabeContext) when available; keep getRateLimitOptions as-is but ensure
the rate-limiter uses the new shared store so signIn/signUp/verifyChallenge
scopes share state across instances.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/wabe/src/authentication/providers/EmailOTP.ts`:
- Line 10: The EmailOTP provider's verifyChallenge currently pre-checks rate
limits with isRateLimited but never calls registerRateLimitFailure, so failed
attempts aren't counted; update EmailOTP.verifyChallenge to call
registerRateLimitFailure (same helper used by QRCodeOTP) whenever the user is
missing or the OTP is invalid before returning null, and call clearRateLimit on
successful verification; reference the EmailOTP class and its verifyChallenge
method and the registerRateLimitFailure/isRateLimited/clearRateLimit helpers
when making the change.

In `@packages/wabe/src/authentication/providers/EmailPassword.test.ts`:
- Around line 140-181: The test currently expects the third signIn to still
result in one more DB call; update it to assert that with
signInRateLimit.maxAttempts set to 2 the third attempt is blocked and does not
invoke the DB. Specifically, keep using emailPassword.onSignIn and
mockGetObjects, record mockGetObjects.mock.calls.length after the two failing
attempts into callsBeforeBlockedAttempt, then call emailPassword.onSignIn a
third time and assert it rejects (same error if applicable) and that
mockGetObjects.mock.calls.length === callsBeforeBlockedAttempt (i.e., no
additional DB call), verifying onSignIn short-circuits before calling
mockGetObjects.

In `@packages/wabe/src/authentication/providers/EmailPassword.ts`:
- Line 8: In EmailPassword.signIn, when credentials are invalid you must call
registerRateLimitFailure with the same key used for isRateLimited/clearRateLimit
before throwing the authentication error; update the invalid-credentials branch
to invoke registerRateLimitFailure(... ) (matching the rate-limit key logic used
earlier in signIn and the pattern from signUp and PhonePassword.ts) so failed
sign-ins are tracked by the rate limiter.

In `@packages/wabe/src/authentication/providers/PhonePassword.ts`:
- Around line 77-81: In onSignUp, the rate-limiting and database checks are
inconsistent because the rateLimitKey uses normalizedPhone but subsequent
queries use input.phone; update all usages in onSignUp (including the DB
count/query and any comparisons around lines referencing the phone check) to use
the same normalizedPhone value (normalized via input.phone.trim().toLowerCase())
so the rate-limit and existence checks match; ensure you replace input.phone
with normalizedPhone in the onSignUp function and keep the same normalization
logic used in onSignIn.
- Around line 24-28: The rate-limiting uses normalizedPhone but the DB lookup
still uses raw input.phone; update the authentication flow to use
normalizedPhone everywhere so normalization is consistent: replace uses of
input.phone in the sign-in user lookup with normalizedPhone and do the same in
the onSignUp flow (where the rateLimit key is built) so rate limiting and
database queries reference the same normalized value; ensure the symbols
affected are normalizedPhone, rateLimitKey, isRateLimited(... 'signIn' ...), and
the sign-up handler onSignUp.

In `@packages/wabe/src/authentication/resolvers/signInWithResolver.ts`:
- Line 62: The MFA branch in signInWithResolver returns { accessToken,
refreshToken, user, challengeToken } but omits the srp field, causing
inconsistent response shapes; modify the MFA return to include srp (e.g., srp:
null or the appropriate SRP object) so the resolver always returns the same keys
(accessToken, refreshToken, user, challengeToken, srp), ensuring clients can
rely on a uniform response structure.

---

Nitpick comments:
In `@packages/wabe/src/authentication/security.ts`:
- Around line 48-75: The current module-level Map rateLimitStorage (storing
RateLimitState) is process-local and won't enforce limits across multiple
instances; replace it with a pluggable shared store interface (e.g.,
IRateLimitStore with get/set/increment/expire) and update code that references
rateLimitStorage to use that interface, wiring a Redis-backed implementation (or
existing app cache in the WabeContext) when available; keep getRateLimitOptions
as-is but ensure the rate-limiter uses the new shared store so
signIn/signUp/verifyChallenge scopes share state across instances.

@coratgerl coratgerl merged commit aca2d8d into main Feb 20, 2026
3 checks passed
@coratgerl coratgerl deleted the enhance-security-on-2fa-and-auth branch February 20, 2026 16:49
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