Labels / Complexity: bug · High — 600 points
Problem
The frontend auth layer is wired to an email/password + refresh-token API that the backend does not implement. lib/api/auth.ts calls endpoints such as /auth/login, /auth/register, /auth/refresh-token, /auth/social-login, /auth/me, and /users/forgot-password. The actual backend (MilestoneX-Backend) exposes none of these; its auth module contains only GET /auth/challenge (src/auth/auth-challenge.controller.ts), POST /auth/verify (src/auth/auth-verify.controller.ts), and POST /auth/logout (src/auth/auth-logout.controller.ts), and POST /auth/verify returns { accessToken, tokenType: 'Bearer' } rather than the frontend's expected LoginResponse { user, token, refreshToken? }:
// lib/api/auth.ts
login: async (credentials: LoginCredentials) => {
const response = await apiClient.post<ApiResponse<LoginResponse>>("/auth/login", credentials);
...
},
refreshToken: async () => {
const response = await apiClient.post<ApiResponse<LoginResponse>>("/auth/refresh-token");
...
},
Consequence: the wallet challenge-response flow that is the backend's only sign-in method is never invoked from the frontend (a search finds no call to /auth/challenge or /auth/verify). Login, signup, social login, and token refresh therefore fail against the real backend, and the entire sessionExpiry.ts refresh mechanism is built around a /auth/refresh-token endpoint that does not exist. The frontend has two incompatible auth worlds — NextAuth OAuth in lib/auth/authOptions.ts and a dead email/password client — and neither produces a backend accessToken the API actually accepts.
Root cause
// lib/api/auth.ts
const response = await apiClient.post<ApiResponse<LoginResponse>>("/auth/login", credentials);
// ← no such endpoint; the backend only offers wallet challenge-response
// src/auth/auth-verify.controller.ts (backend) — the only real sign-in response shape
return { accessToken, tokenType: 'Bearer' };
Why this is architecturally hard
- The fix must implement the Stellar wallet challenge-response on the frontend: request
/auth/challenge, sign the returned challenge with the connected wallet (the app already manages wallets in store/walletStore.ts and lib/stellar/), then POST /auth/verify with { walletAddress, signedChallenge, challenge }. This replaces, not patches, the email/password login/register methods.
- The backend issues a single 15-minute
accessToken with no refresh token, so lib/auth/sessionTimeout.ts, lib/auth/sessionExpiry.ts, and store/authStore.ts — all built around a refreshToken — must be reworked to handle re-authentication via a fresh wallet signature instead of a refresh call.
types/api.ts defines LoginCredentials/RegisterRequest/LoginResponse (user, token, refreshToken) that do not match the backend's { accessToken, tokenType }; the User type is email-based while the backend user is wallet-based (walletAddress, displayName, role). Aligning types means touching every component that reads user.email.
Proposed design
Add a wallet-auth client that calls /auth/challenge then /auth/verify, store the returned accessToken as the bearer token, and derive the user from the backend's walletAddress/role (or from GET /users/me). Replace the LoginResponse/LoginCredentials types with backend-accurate shapes and rework session expiry to prompt a re-sign instead of a refresh. Keep OAuth (authOptions.ts) as a separate, clearly-marked concern until the backend adds social login.
Downstream impact
This is the highest-leverage cross-repo issue in the batch: the frontend must implement the backend's exact auth ABI (GET /auth/challenge, POST /auth/verify, { accessToken, tokenType }). The sibling MilestoneX-Backend repo is the authority for that contract; the frontend cannot ship a working login without matching it. No generated bindings; plain REST + JWT claim shape.
Acceptance criteria
Auth flow
Types
Tests
Out of scope
Adding social/OAuth login to the backend is a separate backend issue; this one makes wallet challenge-response the frontend's working auth path.
Getting started
Files in scope: lib/api/auth.ts, lib/auth/sessionTimeout.ts, lib/auth/sessionExpiry.ts, store/authStore.ts, types/api.ts.
npm run type-check
npm run lint
Good first files to read: lib/api/auth.ts, store/walletStore.ts, lib/stellar/connection.ts, and (in the backend) src/auth/auth-verify.controller.ts.
Labels / Complexity: bug · High — 600 points
Problem
The frontend auth layer is wired to an email/password + refresh-token API that the backend does not implement.
lib/api/auth.tscalls endpoints such as/auth/login,/auth/register,/auth/refresh-token,/auth/social-login,/auth/me, and/users/forgot-password. The actual backend (MilestoneX-Backend) exposes none of these; its auth module contains onlyGET /auth/challenge(src/auth/auth-challenge.controller.ts),POST /auth/verify(src/auth/auth-verify.controller.ts), andPOST /auth/logout(src/auth/auth-logout.controller.ts), andPOST /auth/verifyreturns{ accessToken, tokenType: 'Bearer' }rather than the frontend's expectedLoginResponse { user, token, refreshToken? }:Consequence: the wallet challenge-response flow that is the backend's only sign-in method is never invoked from the frontend (a search finds no call to
/auth/challengeor/auth/verify). Login, signup, social login, and token refresh therefore fail against the real backend, and the entiresessionExpiry.tsrefresh mechanism is built around a/auth/refresh-tokenendpoint that does not exist. The frontend has two incompatible auth worlds — NextAuth OAuth inlib/auth/authOptions.tsand a dead email/password client — and neither produces a backendaccessTokenthe API actually accepts.Root cause
Why this is architecturally hard
/auth/challenge, sign the returned challenge with the connected wallet (the app already manages wallets instore/walletStore.tsandlib/stellar/), then POST/auth/verifywith{ walletAddress, signedChallenge, challenge }. This replaces, not patches, the email/passwordlogin/registermethods.accessTokenwith no refresh token, solib/auth/sessionTimeout.ts,lib/auth/sessionExpiry.ts, andstore/authStore.ts— all built around arefreshToken— must be reworked to handle re-authentication via a fresh wallet signature instead of a refresh call.types/api.tsdefinesLoginCredentials/RegisterRequest/LoginResponse(user,token,refreshToken) that do not match the backend's{ accessToken, tokenType }; theUsertype is email-based while the backend user is wallet-based (walletAddress,displayName,role). Aligning types means touching every component that readsuser.email.Proposed design
Add a wallet-auth client that calls
/auth/challengethen/auth/verify, store the returnedaccessTokenas the bearer token, and derive the user from the backend'swalletAddress/role(or fromGET /users/me). Replace theLoginResponse/LoginCredentialstypes with backend-accurate shapes and rework session expiry to prompt a re-sign instead of a refresh. Keep OAuth (authOptions.ts) as a separate, clearly-marked concern until the backend adds social login.Downstream impact
This is the highest-leverage cross-repo issue in the batch: the frontend must implement the backend's exact auth ABI (
GET /auth/challenge,POST /auth/verify,{ accessToken, tokenType }). The siblingMilestoneX-Backendrepo is the authority for that contract; the frontend cannot ship a working login without matching it. No generated bindings; plain REST + JWT claim shape.Acceptance criteria
Auth flow
/auth/challengechallenge and completing/auth/verify, and the returnedaccessTokenis used for subsequent API calls./auth/login,/auth/register,/auth/refresh-token,/auth/social-login, and/auth/mecalls are removed or replaced with backend-backed equivalents.Types
LoginResponseand theUsertype match the backend's{ accessToken, tokenType }and wallet-based user shape.Tests
Out of scope
Adding social/OAuth login to the backend is a separate backend issue; this one makes wallet challenge-response the frontend's working auth path.
Getting started
Files in scope:
lib/api/auth.ts,lib/auth/sessionTimeout.ts,lib/auth/sessionExpiry.ts,store/authStore.ts,types/api.ts.Good first files to read:
lib/api/auth.ts,store/walletStore.ts,lib/stellar/connection.ts, and (in the backend)src/auth/auth-verify.controller.ts.