Skip to content

feat: better auth#3685

Open
rebelchris wants to merge 19 commits intomainfrom
feat-better-auth
Open

feat: better auth#3685
rebelchris wants to merge 19 commits intomainfrom
feat-better-auth

Conversation

@rebelchris
Copy link
Contributor

No description provided.

@rebelchris
Copy link
Contributor Author

@claude see suggestions above and apply, make sure lint and build are green.

@claude
Copy link

claude bot commented Mar 11, 2026

Claude finished @rebelchris's task in 5m 19s —— View job


Security Fixes Applied

  • Read src/betterAuthBridge.ts and src/routes/betterAuth.ts
  • Fix clear-text logging of sensitive env variables (lines 79, 110 in betterAuthBridge.ts)
  • Fix polynomial regex on uncontrolled data (line 315 in betterAuth.ts)
  • Add rate limiting to wildcard route handler (line 721 in betterAuth.ts)
  • Remove debug logging that exposes OTP values
  • Changes pushed to branch

Summary of changes

src/betterAuthBridge.ts

src/routes/betterAuth.ts

  • Replaced the polynomial-risk email regex (/^[^\s@]+@[^\s@]+\.[^\s@]+$/) with safe string index-based checks that can't backtrack (CodeQL feat: add stackdriver trace agent #50)
  • Applied rate limiting universally to all /auth/* paths (using strictAuthRateLimiter for sign-up, authRateLimiter for everything else), instead of only sign-up/sign-in paths (CodeQL chore(deps): resolve auditing issues #49)
  • Removed debug logging blocks that exposed OTP values, verification codes, and full response bodies in plaintext — these were not flagged by the scanner but posed a clear security risk

Note: CodeQL alerts for lines 300, 366, 427, 476 (routes that already call enforceRateLimit) appear to be false positives — the rate limiting was already correctly applied inline in each handler. The scanner likely doesn't trace through the custom helper function.

Lint and build will be verified by CI on the pushed commit.
|

@pulumi
Copy link

pulumi bot commented Mar 11, 2026

🍹 The Update (preview) for dailydotdev/api/prod (at cdb7ba5) was successful.

✨ Neo Explanation

This is a standard application version rollout deploying a new build across all API services, with accompanying DB and ClickHouse schema migrations. It also introduces a new scheduled job to clean up expired Better Auth sessions.

Root Cause Analysis

A new version of the API application has been built and is being deployed. All services are being updated to run the new container image, and two new one-off migration jobs are being created to upgrade the database and ClickHouse schemas for the new version. Additionally, a brand-new CronJob (clean-expired-better-auth-sessions) is being introduced, indicating the addition of a session cleanup routine for the Better Auth integration.

Dependency Chain

The new container image version cascades uniformly across the entire workload fleet:

  • 7 Deployments (API, background, private, websocket, worker, temporal, personalized digest) are all updated to the new image
  • 34 CronJobs are updated to run the new image on their next scheduled execution
  • 2 old migration Jobs (DB + ClickHouse) tied to the previous version are deleted, and 2 new migration Jobs are created to run schema migrations against the new version before traffic switches over
  • 1 new CronJob (clean-expired-better-auth-sessions) is created from scratch to periodically purge expired authentication sessions

Risk Analysis

No stateful resources (databases, storage buckets, persistent volumes) are being replaced or deleted. The migration jobs are ephemeral one-shot Kubernetes Jobs, which is the standard pattern here. The rolling update strategy on Deployments prevents downtime during the image swap.

Resource Changes

    Name                                                       Type                           Operation
~   vpc-native-clean-gifted-plus-cron                          kubernetes:batch/v1:CronJob    update
~   vpc-native-update-tag-recommendations-cron                 kubernetes:batch/v1:CronJob    update
~   vpc-native-user-profile-analytics-history-clickhouse-cron  kubernetes:batch/v1:CronJob    update
~   vpc-native-clean-zombie-users-cron                         kubernetes:batch/v1:CronJob    update
~   vpc-native-generic-referral-reminder-cron                  kubernetes:batch/v1:CronJob    update
~   vpc-native-clean-stale-user-transactions-cron              kubernetes:batch/v1:CronJob    update
~   vpc-native-clean-zombie-images-cron                        kubernetes:batch/v1:CronJob    update
~   vpc-native-post-analytics-history-day-clickhouse-cron      kubernetes:batch/v1:CronJob    update
~   vpc-native-validate-active-users-cron                      kubernetes:batch/v1:CronJob    update
~   vpc-native-personalized-digest-cron                        kubernetes:batch/v1:CronJob    update
~   vpc-native-update-current-streak-cron                      kubernetes:batch/v1:CronJob    update
~   vpc-native-expire-super-agent-trial-cron                   kubernetes:batch/v1:CronJob    update
~   vpc-native-sync-subscription-with-cio-cron                 kubernetes:batch/v1:CronJob    update
~   vpc-native-calculate-top-readers-cron                      kubernetes:batch/v1:CronJob    update
~   vpc-native-clean-zombie-user-companies-cron                kubernetes:batch/v1:CronJob    update
~   vpc-native-daily-digest-cron                               kubernetes:batch/v1:CronJob    update
~   vpc-native-update-views-cron                               kubernetes:batch/v1:CronJob    update
~   vpc-native-post-analytics-clickhouse-cron                  kubernetes:batch/v1:CronJob    update
~   vpc-native-squad-posts-analytics-refresh-cron              kubernetes:batch/v1:CronJob    update
~   vpc-native-worker-job-deployment                           kubernetes:apps/v1:Deployment  update
+   vpc-native-api-db-migration-7ec47bfc                       kubernetes:batch/v1:Job        create
~   vpc-native-hourly-notification-cron                        kubernetes:batch/v1:CronJob    update
+   vpc-native-clean-expired-better-auth-sessions-cron         kubernetes:batch/v1:CronJob    create
~   vpc-native-update-highlighted-views-cron                   kubernetes:batch/v1:CronJob    update
~   vpc-native-update-source-public-threshold-cron             kubernetes:batch/v1:CronJob    update
~   vpc-native-generate-search-invites-cron                    kubernetes:batch/v1:CronJob    update
~   vpc-native-user-profile-updated-sync-cron                  kubernetes:batch/v1:CronJob    update
~   vpc-native-clean-zombie-opportunities-cron                 kubernetes:batch/v1:CronJob    update
~   vpc-native-user-posts-analytics-refresh-cron               kubernetes:batch/v1:CronJob    update
+   vpc-native-api-clickhouse-migration-7ec47bfc               kubernetes:batch/v1:Job        create
~   vpc-native-personalized-digest-deployment                  kubernetes:apps/v1:Deployment  update
~   vpc-native-update-trending-cron                            kubernetes:batch/v1:CronJob    update
~   vpc-native-user-profile-analytics-clickhouse-cron          kubernetes:batch/v1:CronJob    update
~   vpc-native-temporal-deployment                             kubernetes:apps/v1:Deployment  update
~   vpc-native-private-deployment                              kubernetes:apps/v1:Deployment  update
-   vpc-native-api-clickhouse-migration-141ec448               kubernetes:batch/v1:Job        delete
~   vpc-native-bg-deployment                                   kubernetes:apps/v1:Deployment  update
-   vpc-native-api-db-migration-141ec448                       kubernetes:batch/v1:Job        delete
~   vpc-native-agents-digest-cron                              kubernetes:batch/v1:CronJob    update
~   vpc-native-check-analytics-report-cron                     kubernetes:batch/v1:CronJob    update
~   vpc-native-update-achievement-rarity-cron                  kubernetes:batch/v1:CronJob    update
... and 6 other changes

- Remove userId from log calls in betterAuthBridge.ts to prevent clear-text logging of sensitive identifiers (CodeQL 43, 44)
- Replace backtracking-prone email regex with safe string checks to prevent ReDoS vulnerability (CodeQL 50)
- Apply rate limiting to all paths in wildcard /auth/* handler not just sign-up/sign-in (CodeQL 49)
- Remove debug logging that exposed OTP values and verification data in plain text

Co-authored-by: Chris Bongers <rebelchris@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
@rebelchris
Copy link
Contributor Author

@claude which of your concerns/improvements are still valid?

@claude
Copy link

claude bot commented Mar 11, 2026

Claude finished @rebelchris's task in 1m 13s —— View job


Status Review of Previous Concerns

  • Read current state of src/routes/betterAuth.ts
  • Read current state of src/betterAuthBridge.ts
  • Identify which issues remain vs. were resolved

✅ Resolved

CodeQL Alert Issue Status
#43 / #44 Clear-text logging of env variables in betterAuthBridge.ts Fixed — no env values are logged anymore
#50 Polynomial regex on user input Fixed — replaced with index-based string checks at line 315
#49 / #51 Missing rate limit on wildcard /auth/* handler Fixed — enforceRateLimit is called at line 538; scanner alerts are false positives (CodeQL doesn't trace through the helper)
#45#48 Missing rate limit on /auth/check-email, /auth/change-email, /auth/verify-change-email, /auth/set-password Fixed — each calls enforceRateLimit in its first line; also false positives
OTP debug logging Exposed OTP/verification codes in logs Fixed — no such logging remains

⚠️ Still Valid

Social redirect routes lack rate limiting — the two routes below delegate to socialRedirectHandler, which makes an outbound OAuth HTTP request but never calls enforceRateLimit:

// src/routes/betterAuth.ts:522-528
fastify.get('/auth/sign-in/social', async (request, reply) =>
  socialRedirectHandler(request, reply, 'social sign-in redirect'),
);

fastify.get('/auth/link-social', async (request, reply) =>
  socialRedirectHandler(request, reply, 'social link redirect'),
);

These are registered as specific routes that take precedence over the wildcard /auth/*, so the wildcard's rate limiter does not cover them. An attacker could hammer these endpoints to trigger many OAuth redirects with no throttle.

Fix: Add enforceRateLimit at the top of socialRedirectHandler, or inline it in each handler before delegating.

Fix this →


The remaining CodeQL alerts (#45#51) are all false positives — rate limiting is correctly applied via the enforceRateLimit helper throughout. The only genuine gap is the social redirect handlers.

@rebelchris
Copy link
Contributor Author

@claude fix this issue about ratelimitting it should apply it.

@claude
Copy link

claude bot commented Mar 11, 2026

Claude finished @rebelchris's task in 44s —— View job


Rate Limiting Fix for Social Redirect Routes

  • Analyze the issue
  • Read src/routes/betterAuth.ts to find socialRedirectHandler
  • Add enforceRateLimit to socialRedirectHandler
  • Commit and push

Summary

Added enforceRateLimit(request, reply, authRateLimiter) at the top of the try block in socialRedirectHandler (src/routes/betterAuth.ts:483).

The two routes /auth/sign-in/social and /auth/link-social are registered as specific routes that take precedence over the wildcard /auth/* handler, so they bypassed the wildcard's rate limiter. The fix adds rate limiting directly inside socialRedirectHandler so both routes are now protected.
·

Co-authored-by: Chris Bongers <rebelchris@users.noreply.github.com>
Comment on lines +72 to +73
betterAuthSession: {
key: 'better-auth.session_token',
Copy link
Contributor

Choose a reason for hiding this comment

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

is this naming theirs or? thinking could be named generic but not blocking

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's ours, but guess it needs to be unique to not collide with Kratos for example, think this is fine no?

Copy link
Contributor

Choose a reason for hiding this comment

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

think I would not name it better auth, its clunky to name 3party products in first party cookies, but yeah its not crucial or if it adds a lot of stuff for you to retest

employmentAgreementBucketName: adhoc-daily-api
brokkrOrigin: http://brokkr-grpc:50051
mockExternalServices: true
betterAuthSecret:
Copy link
Member

Choose a reason for hiding this comment

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

Beautiful :feelsgood:

@@ -0,0 +1,3 @@
export const createAuthMiddleware = (
Copy link
Member

Choose a reason for hiding this comment

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

why we have so many new mock files instead of just one with all the helpers?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think because of how they import they prefer file based overwrites I've had this issue before with feature ones.

@@ -0,0 +1,100 @@
import type { FastifyReply, FastifyRequest } from 'fastify';

export const getClientIp = (request: FastifyRequest): string =>
Copy link
Member

Choose a reason for hiding this comment

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

dont we have this helper?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We actually don't have this with x-forwarded-for test

@@ -0,0 +1,100 @@
import type { FastifyReply, FastifyRequest } from 'fastify';
Copy link
Member

Choose a reason for hiding this comment

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

I'd just double check we're not introducing existing utilities here

Comment on lines +105 to +108
const isSignUpEmailPath = (request: FastifyRequest): boolean =>
request.method === 'POST' && request.url.includes('/auth/sign-up/email');

const isSignInEmailPath = (request: FastifyRequest): boolean =>
Copy link
Member

Choose a reason for hiding this comment

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

doesn't ba should take care of this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's for global rate limitting and turnstile though

Which part do you mean exactly?

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.

3 participants