Skip to content

Conversation

@ebma
Copy link
Member

@ebma ebma commented Oct 31, 2025

Closes #882.

This pull request introduces Supabase-based authentication to the API, including OTP (one-time password) flows, token verification, and user session management. It adds new authentication endpoints, middleware for extracting and verifying user identity from requests, and propagates the authenticated userId throughout the business logic and data models. Several routes now optionally or mandatorily require authentication, and the system is prepared for future endpoint protection. Additionally, the codebase is updated to store and utilize the userId in key models and service layers.

Supabase Authentication Integration

  • Added Supabase configuration variables to .env.example and included @supabase/supabase-js as a dependency in package.json. [1] [2]
  • Implemented SupabaseAuthService with methods for checking user existence, sending and verifying OTPs, verifying and refreshing tokens, and retrieving user profiles. [1] [2]
  • Created AuthController with endpoints for checking email registration, requesting OTP, verifying OTP, refreshing tokens, and verifying tokens. [1] [2]
  • Added /v1/auth routes for authentication endpoints and registered them in the main API router. [1] [2] [3]

Authentication Middleware and Route Protection

  • Added requireAuth and optionalAuth middleware to extract and verify user identity from Bearer tokens, attaching userId to the request.
  • Updated BRLA, Quote, and Ramp routes to use optionalAuth middleware, allowing endpoints to access userId if provided. [1] [2] [3] [4] [5] [6] [7]

User Identity Propagation

  • Modified controllers (brla.controller.ts, quote.controller.ts, ramp.controller.ts) to store userId from the request in database records and service calls. [1] [2] [3] [4] [5] [6]
  • Updated service and type definitions to support userId as part of the request context and data models. [1] [2]

These changes collectively enable secure, user-specific flows across the API, laying the groundwork for robust authentication and authorization using Supabase.

@netlify
Copy link

netlify bot commented Oct 31, 2025

Deploy Preview for vortexfi ready!

Name Link
🔨 Latest commit 2ee1d77
🔍 Latest deploy log https://app.netlify.com/projects/vortexfi/deploys/696cf94b7a04c700088b6e02
😎 Deploy Preview https://deploy-preview-912--vortexfi.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link

netlify bot commented Oct 31, 2025

Deploy Preview for vortex-sandbox ready!

Name Link
🔨 Latest commit 2ee1d77
🔍 Latest deploy log https://app.netlify.com/projects/vortex-sandbox/deploys/696cf94b9d8ba70008c2a2a5
😎 Deploy Preview https://deploy-preview-912--vortex-sandbox.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

type: DataTypes.UUID
});

await queryInterface.sequelize.query(`
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we keep the userId relation for this one (and any that allows for null) empty instead of using the dummy? Or do you think there is a benefit of using the dummy user here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, well maybe we should just allow it to be null on each table. You are right that the dummy user is not great. I used it so we can enforce the non-null constraint that is logical on some of the tables but now that I think of it, it might be better if it can be null and we just add it later..

Copy link
Contributor

@gianfra-t gianfra-t Jan 13, 2026

Choose a reason for hiding this comment

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

Yes I dont have a strong opinion on either as they both serve the same purpose. null seems a bit easier to deal with after than the dummy.

Oh but if I remember correctly, those fields allow for a null relationship so no need to change that constraint itself.

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe to also make the migration easier to follow. Not much more

Copy link
Member Author

Choose a reason for hiding this comment

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

Some of them did, some didn't. But I adjusted it so that the userId is nullable on all the other relations and the code checks if necessary.

@Sharqiewicz
Copy link
Member

@ebma Please let me know when it's ready for review again 🙏

@ebma ebma requested review from Sharqiewicz and gianfra-t January 13, 2026 19:28
@ebma
Copy link
Member Author

ebma commented Jan 13, 2026

@Sharqiewicz I consider the changes ready now :)

@Sharqiewicz
Copy link
Member

@ebma Ok, thanks. I'll review it today

@Sharqiewicz
Copy link
Member

@ebma I need some more time. I'll post summary tomorrow

@Sharqiewicz
Copy link
Member

I reviewed the new auth components.

🔴 Accessibility (High Priority)
AuthOTPStep: The 6 OTP inputs have no accessible names - screen readers will just say "text input" 6 times. Add:
aria-label={t("components.authOTP.digitLabel", { position: index + 1 })}
Also wrap the inputs with role="group" and aria-labelledby.
AuthEmailStep: Error messages aren't associated with inputs. Add aria-describedby="email-error" and aria-invalid={!!error} to the email input.

Consider shadcn/ui InputOTP
Instead of manually handling all this, we should use shadcn's InputOTP component. Here's why:

  • WCAG compliance out-of-the-box - handles aria-label, role, focus management, screen reader announcements automatically
  • Eliminates ~50 lines of custom code - no more manual inputRefs, handleKeyDown, handlePaste logic
  • Battle-tested edge cases - handles mobile keyboards, autocomplete from SMS, browser autofill properly
  • Consistent with industry standard - built on Radix primitives that Stripe/Vercel use
    Usage would be as simple as:
<InputOTP maxLength={6} onComplete={(code) => rampActor.send({ code, type: "VERIFY_OTP" })}>  <InputOTPGroup>    <InputOTPSlot index={0} />    // ...  </InputOTPGroup></InputOTP>

🟡 Hardcoded Strings
Both components have untranslated text:
"Enter Your Email", "Continue", "Sending..." (AuthEmailStep)
"Enter Verification Code", "Verifying...", "Use a different email" (AuthOTPStep)
Move these to en.json/pt.json since we already have i18n set up.

🟡 Inconsistent Styling
The buttons use custom Tailwind classes:
className="w-full rounded-lg bg-blue-600 px-4 py-3..."
Should use our design system class btn-vortex-primary btn like other components (see RampSubmitButton).

🟢 Minor Issues
AuthOTPStep 67-72: The useEffect that clears OTP on error could be handled in the error callback instead
AuthEmailStep: Uses controlled useState for email instead of react-hook-form like our other forms. Not blocking, but loses validation consistency.

Let me know if you want me to push fixes or if you want to handle it! (

@ebma
Copy link
Member Author

ebma commented Jan 15, 2026

🟡 Hardcoded Strings

@Sharqiewicz maybe you found that in an earlier review but I actually moved the translations in a recent commit already so this shouldn't be the case anymore.

For the other suggested improvements, I agree we can/should add them. Feel free to add the respective changes yourself.

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.

Account creation on Vortex

4 participants