feat: Add auth#67
Conversation
|
Warning Rate limit exceeded
To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (5)
📝 WalkthroughWalkthroughThis PR adds an authentication flow and app wiring: typed auth models, axios API client with secure token storage and interceptors, AuthProvider + useAuth hook, login/register screens and styles, ErrorBoundary and PaperProvider wiring in App, navigation routes for login/register, Expo config update, new deps, and expanded i18n files. ChangesAuthentication + App Bootstrap
Sequence DiagramsequenceDiagram
actor User
participant LoginScreen as Login Screen
participant AuthProvider as AuthProvider / useAuth
participant ApiClient as Api Client (axios)
participant SecureStore as SecureStore (expo-secure-store)
participant Backend as Backend API
User->>LoginScreen: Fill credentials & tap Login
LoginScreen->>AuthProvider: login(email, password)
AuthProvider->>ApiClient: POST /api/auth/login
ApiClient->>Backend: HTTP request
Backend-->>ApiClient: 200 { token: "jwt..." }
ApiClient-->>AuthProvider: response.data
AuthProvider->>SecureStore: store jwt
SecureStore-->>AuthProvider: stored
AuthProvider->>AuthProvider: decodeToken -> set user
AuthProvider-->>LoginScreen: resolve/login success
LoginScreen->>Navigation: navigate("home")
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. 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. Comment |
We skip the addition of the auth header for register/login to avoid old sessions/tokens
There was a problem hiding this comment.
Actionable comments posted: 18
🧹 Nitpick comments (3)
frontend/src/components/auth/register/Body.style.tsx (1)
1-79: ⚡ Quick winNear-complete duplication with
login/Body.style.tsx— extract a shared auth style factory.Every style value in this file is identical to its counterpart in
frontend/src/components/auth/login/Body.style.tsx. The only differences are renamed style keys (loginContainer↔registerContainer,loginButton↔registerButton, etc.). Any future design change must be applied to both files, and they're already diverging via naming.Extract the shared definitions into a single factory:
♻️ Proposed refactor
// frontend/src/components/auth/shared/AuthBody.style.tsx import { StyleSheet } from 'react-native'; export const createAuthStyles = (theme: any) => StyleSheet.create({ backgroundImage: { flex: 1, width: '100%', height: '100%' }, container: { flex: 1 }, scrollContainer: { flexGrow: 1, justifyContent: 'flex-end' }, title: { fontSize: 32, fontWeight: 'bold', color: '#1A1A1A', marginBottom: 8 }, highlight: { color: theme.colors.primary }, subtitle: { fontSize: 15, color: theme.colors.secondary, marginBottom: 32 }, inputContainer: { gap: 16, marginBottom: 24 }, input: { backgroundColor: '#F9FAFB', borderRadius: 16 }, errorText: { color: '#EF4444', fontSize: 13, marginTop: -12, marginLeft: 4, fontWeight: '500' }, authContainer: { width: '100%', backgroundColor: theme.colors.surface + '70', borderTopLeftRadius: 32, borderTopRightRadius: 32, paddingHorizontal: 28, paddingTop: 40, paddingBottom: 40, shadowColor: '#000', shadowOffset: { width: 0, height: -4 }, shadowOpacity: 0.1, shadowRadius: 12, elevation: 8, }, authButton: { borderRadius: 16, backgroundColor: theme.colors.primary, paddingVertical: 8, marginBottom: 12, }, linkContainer: { paddingVertical: 12, alignItems: 'center' as const }, linkText: { fontSize: 14, color: '#6B7280', textAlign: 'center' as const }, linkHighlight: { color: theme.colors.primary, fontWeight: '700' as const, textDecorationLine: 'underline' as const }, });Both
login/Body.style.tsxandregister/Body.style.tsxcan then re-export from this, adding only screen-specific aliases if needed.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/auth/register/Body.style.tsx` around lines 1 - 79, The two style modules duplicate the same StyleSheet object (createStyles) with only key-name differences; extract the shared styles into a new factory createAuthStyles(theme) (e.g., AuthBody.style) that contains the common keys (backgroundImage, container, scrollContainer, title, highlight, subtitle, inputContainer, input, errorText, authContainer, authButton, linkContainer, linkText, linkHighlight), then update the existing createStyles in both login and register modules to import createAuthStyles(theme) and return it (or return an object that re-maps authContainer→registerContainer/loginContainer and authButton→registerButton/loginButton) so both screens reuse the single source of truth. Ensure you keep references to the original exported function name createStyles and preserve theme usage (theme.colors.primary/secondary/surface) when moving styles.frontend/src/navigation/Navigation.tsx (1)
20-24: No authentication guard — unauthenticated users land onhomeand can access all registered routesThe navigator always starts at
"home"regardless of auth state. There is no conditional stack (e.g., showing onlylogin/registerwhenuser === null) or redirect-to-login logic. Ifhomeand other routes require authentication, verify that they individually checkuseAuth().userand redirect accordingly, or consider splitting into an authenticated stack and an unauthenticated stack driven by theuservalue fromAuthContext.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/navigation/Navigation.tsx` around lines 20 - 24, The navigator currently always shows the unauthenticated "home" stack (Stack.Navigator / Stack.Screen entries for "home", "login", "register") regardless of auth state; update Navigation.tsx to branch on the AuthContext/useAuth() user value and render two separate stacks: an AuthenticatedStack that registers protected screens (e.g., "home", any other protected components) and an UnauthenticatedStack that only registers "login" and "register", or alternatively keep a single navigator but add a redirect-to-login when useAuth().user is null; ensure Home, Login, Register references remain unchanged and that you call useAuth() in Navigation to decide which stack to mount so unauthenticated users cannot land on protected routes.frontend/src/components/navigation/Header.tsx (1)
2-3: ⚡ Quick win
useNavigationand its type import are unused — consider a global navigator type declaration instead.
navigationis declared (Line 11) but never called anywhere in the component's JSX. Additionally, annotatinguseNavigationwith a manual type parameter is not type-safe since it can't be statically verified; the React Navigation docs recommend keeping manual annotations to a minimum and using default types instead. The idiomatic approach for v7 is a global type declaration:// e.g. in navigation/types.ts or Navigation.tsx declare global { namespace ReactNavigation { interface RootParamList extends RootStackParamList {} } }This removes the need for explicit
NativeStackNavigationProp<RootStackParamList>annotations across all components.Also applies to: 11-11
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/navigation/Header.tsx` around lines 2 - 3, Remove the unused imports and local navigation typing: delete the useNavigation and NativeStackNavigationProp imports and the unused navigation variable in Header.tsx; instead add a global ReactNavigation RootParamList declaration (e.g., in a new navigation/types.ts) that extends your RootStackParamList so components can use useNavigation without manual type parameters—this centralizes RootParamList and lets you drop explicit NativeStackNavigationProp annotations across the codebase.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/app.json`:
- Around line 32-34: The apiUrl in frontend/app.json is hardcoded to
"http://localhost:8080/" which fails on real devices and is blocked by Android
cleartext rules; update the "extra": {"apiUrl": ...} entry to source its value
from an environment-aware variable (e.g., EXPO_PUBLIC_API_URL) or platform-aware
logic (use 10.0.2.2 for Android emulator, LAN IP for physical devices) so builds
can vary by environment and avoid cleartext issues; ensure any Android release
builds either use https endpoints or apply
network-security-config/usesCleartextTraffic if you must allow http.
In `@frontend/package.json`:
- Line 19: The package.json lists expo-secure-store and expo-localization at SDK
55 versions (expo-secure-store@^55.0.13) which are incompatible with this
project’s Expo SDK 54; run npx expo install expo-secure-store expo-localization
from the frontend workspace to have Expo resolve and update package.json to the
correct SDK 54-compatible versions, then reinstall node_modules (npm/yarn) and
rebuild the app to ensure native modules match the SDK.
- Line 13: The package.json currently pins
"@react-native-async-storage/async-storage": "^3.0.2", which is incompatible
with Expo SDK 54; change that dependency to the Expo-compatible release (e.g.,
"2.2.0") in package.json, update your lockfile by running npm/yarn install, and
re-run your EAS build to confirm the "No matching variant" error is resolved;
look for the dependency entry "@react-native-async-storage/async-storage" to
make this change.
In `@frontend/src/assets/translate/bg.json`:
- Line 6: The Bulgarian locale entry for the key "logout" is currently English;
update the value for the "logout" key in the BG translation file to the correct
Bulgarian translation (for example "Изход" or "Излез") so the locale file uses
Bulgarian text instead of "Logout".
In `@frontend/src/components/auth/login/Body.style.tsx`:
- Line 18: The backgroundColor assignment using string concatenation
(theme.colors.surface + "70") is fragile and can produce invalid colors; update
the style in Body.style.tsx (and the same pattern in register/Body.style.tsx) to
compute a proper semi-transparent color using a safe helper instead of
concatenation — e.g., add/use a utility like hexToRgba or the 'color' library to
convert theme.colors.surface to an rgba/hex with alpha, validate short hex/#rgb
and rgb(...) inputs, and replace the concatenation in the style object's
backgroundColor with a call to that helper (refer to the backgroundColor
property in the style object to locate the change).
In `@frontend/src/components/auth/login/Body.tsx`:
- Line 102: In Body.tsx replace hardcoded strings ('Logging in...' and "Don't
have an account?") with i18n keys via t('login-logging-in') and
t('login-no-account') (or similar) to match existing usage; then add these keys
with appropriate English and Bulgarian values to both en.json and bg.json so
locales render correctly; update any tests or snapshots that assert exact text
where necessary.
- Line 19: The formErrors state is never populated so client-side validation UI
never appears; update the Body component's handleLogin to perform synchronous
validation of the email and password fields before calling login(), and when
validation fails call setFormErrors with a map of field keys to human-readable
messages (e.g., { email: 'Invalid email', password: 'Password required' }) and
return early to prevent the API call; also ensure the existing per-field
onChange handlers still clear their respective keys via setFormErrors({
...formErrors, email: undefined }) or similar so errors disappear as the user
types, or alternatively remove the unused formErrors, setFormErrors, and the
per-field error UI blocks if you prefer server-only validation.
In `@frontend/src/components/auth/register/Body.tsx`:
- Line 102: The component Body.tsx contains hardcoded English strings for the
button label and the helper text (the ternary using loading showing
'Registering...' and the static "Already have an account?"), which bypass i18n;
replace those literals with t(...) calls (e.g., t('registering') and
t('already-have-account')) in the JSX (where the current loading conditional and
the helper text render), and add matching keys to the locale files (en.json and
bg.json) with appropriate translations so the t() calls resolve.
- Line 19: The component never runs client-side validation so formErrors is
never set and per-field error UI in Body.tsx is unused; before calling
register() (the submit handler in this component) add synchronous validation for
email and password (same rules as login Body: required, email format, password
min length) and call setFormErrors with a map of field->message when validation
fails, preventing the API call; if validation passes, clear errors via
setFormErrors({}) and then invoke register(); update the submit handler and any
helper validate functions used by this component to mirror the fixes applied in
frontend/src/components/auth/login/Body.tsx.
In `@frontend/src/components/navigation/Header.tsx`:
- Around line 31-33: The Pressable logout button (Pressable with
styles.logoutButton and MaterialIcons) has no onPress handler and does not call
logout(); import or access the logout function from your authentication context
(e.g., AuthContext or useAuth hook) and wire it to the Pressable's onPress so
tapping the icon invokes logout(); ensure you reference the same logout symbol
used across the app (logout()) and handle any async/Promise result or errors
appropriately.
In `@frontend/src/services/ApiClient.tsx`:
- Around line 10-16: There are two incompatible auth flows: authService.ts
stores tokens in SecureStore under key "authToken" and posts to absolute paths
like "/auth/register", while AuthContext.tsx uses AsyncStorage key "jwt" and
relative "api/..." paths; as a fix, pick the AuthContext flow (recommended),
update or remove authService.ts so it either (a) uses
AsyncStorage.getItem/setItem with key "jwt" instead of SecureStore "authToken"
and posts to relative endpoints like "api/auth/register", or (b) if you keep
authService, change the request interceptor (customAPI.interceptors.request) to
read from SecureStore "authToken" and normalize all authService endpoints to use
the baseURL-relative "api/..." form; ensure customAPI, authService (functions in
authService.ts) and AuthContext.tsx agree on the same storage key ("jwt" or
"authToken"), storage API (AsyncStorage vs SecureStore) and consistent endpoint
paths ("api/...").
- Around line 5-8: The current creation of customAPI uses apiUrl from
Constants.expoConfig?.extra?.apiUrl which can be undefined and breaks axios;
update the ApiClient initialization so apiUrl is validated and never passed as
undefined to axios.create: check Constants.expoConfig?.extra?.apiUrl and fall
back to a safe value (e.g., process.env.API_URL or an explicit default) or
throw/log a clear error during startup, then pass that ensured string into
axios.create({ baseURL: apiUrl }); reference the apiUrl variable and
customAPI/axios.create initialization to locate and fix the code.
In `@frontend/src/services/AuthContext.tsx`:
- Line 58: Remove the console.log(decoded.id) statements that expose user
identifiers: delete the console.log call in the AuthContext login flow where
decoded is used and the similar console.log in the register function (both
occurrences referencing decoded.id), ensuring no PII is written to device logs;
if you need runtime tracing, replace with an environment-gated debug logger or
redact the identifier before logging.
- Line 48: The call to loadUser() in AuthContext.tsx can throw and currently has
no .catch, so an outer exception will prevent setLoading(false) from running and
leave the app stuck; update the invocation of loadUser (in the AuthContext
component where loadUser() is called) to handle rejections—either append
.catch(err => { process/log error; }) and ensure setLoading(false) is invoked in
a .finally(), or wrap the call in an async IIFE with try/catch/finally so that
any error is caught and setLoading(false) is always executed.
- Around line 51-60: The login and register functions are storing the raw JSON
string instead of the JWT and using a wrong endpoint path; remove the custom
transformResponse override so axios parses JSON normally, use
response.data.token (not response.data) when calling AsyncStorage.setItem('jwt',
...) and when decoding with decodeToken, and change the request path from
'api/auth/login' and 'api/auth/register' to '/auth/login' and '/auth/register'
respectively; update both functions (login, register) to setUser(decoded) after
decoding the actual token so ApiClient's request interceptor receives a proper
"Bearer <jwt>" header.
- Around line 26-29: decodeToken currently calls atob on the raw JWT payload
which fails for base64url-encoded tokens; update decodeToken to normalize
base64url by replacing '-' with '+' and '_' with '/', then add '=' padding to
make length a multiple of 4 before calling atob and JSON.parse, and also ensure
the login() path that invokes decodeToken is wrapped in a try/catch (or handle
errors the same way loadUser() does) so token parse failures are caught;
reference functions: decodeToken, login, loadUser.
In `@frontend/src/services/authService.ts`:
- Around line 5-25: authService is writing tokens to expo-secure-store under key
"authToken" while ApiClient reads AsyncStorage key "jwt", and UI components use
AuthContext's own AsyncStorage-based methods, so tokens never reach request
headers; fix by consolidating auth into one layer: either have AuthContext
delegate to authService (update AuthContext.login/register/logout to call
authService.register/login/logout and use SecureStore 'authToken') OR remove
authService and update ApiClient to read AsyncStorage 'jwt' and have AuthContext
continue writing that same key; ensure the chosen storage backend and key
(referenced by authService.register/login, ApiClient request interceptor, and
AuthContext/login) are identical and that UI login/register components call the
unified implementation (useAuth or authService) consistently.
In `@frontend/src/types/auth.ts`:
- Around line 10-13: The exported User interface is dead/misleading; either
update it to match the decoded JWT payload used in AuthContext.tsx or remove it.
If you choose to keep it, change the User interface to the JWT payload shape
(id: number; username: string; sub: string; roles: Role[]) and update
AuthContext.tsx to import and use this exported User type instead of its local
type; otherwise delete the User export to avoid confusion. Ensure the Role type
referenced by User remains consistent with the existing Role definition.
---
Nitpick comments:
In `@frontend/src/components/auth/register/Body.style.tsx`:
- Around line 1-79: The two style modules duplicate the same StyleSheet object
(createStyles) with only key-name differences; extract the shared styles into a
new factory createAuthStyles(theme) (e.g., AuthBody.style) that contains the
common keys (backgroundImage, container, scrollContainer, title, highlight,
subtitle, inputContainer, input, errorText, authContainer, authButton,
linkContainer, linkText, linkHighlight), then update the existing createStyles
in both login and register modules to import createAuthStyles(theme) and return
it (or return an object that re-maps
authContainer→registerContainer/loginContainer and
authButton→registerButton/loginButton) so both screens reuse the single source
of truth. Ensure you keep references to the original exported function name
createStyles and preserve theme usage (theme.colors.primary/secondary/surface)
when moving styles.
In `@frontend/src/components/navigation/Header.tsx`:
- Around line 2-3: Remove the unused imports and local navigation typing: delete
the useNavigation and NativeStackNavigationProp imports and the unused
navigation variable in Header.tsx; instead add a global ReactNavigation
RootParamList declaration (e.g., in a new navigation/types.ts) that extends your
RootStackParamList so components can use useNavigation without manual type
parameters—this centralizes RootParamList and lets you drop explicit
NativeStackNavigationProp annotations across the codebase.
In `@frontend/src/navigation/Navigation.tsx`:
- Around line 20-24: The navigator currently always shows the unauthenticated
"home" stack (Stack.Navigator / Stack.Screen entries for "home", "login",
"register") regardless of auth state; update Navigation.tsx to branch on the
AuthContext/useAuth() user value and render two separate stacks: an
AuthenticatedStack that registers protected screens (e.g., "home", any other
protected components) and an UnauthenticatedStack that only registers "login"
and "register", or alternatively keep a single navigator but add a
redirect-to-login when useAuth().user is null; ensure Home, Login, Register
references remain unchanged and that you call useAuth() in Navigation to decide
which stack to mount so unauthenticated users cannot land on protected routes.
🪄 Autofix (Beta)
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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 8d3e68ef-8070-4a47-9fc2-c64f27e5772b
⛔ Files ignored due to path filters (2)
frontend/package-lock.jsonis excluded by!**/package-lock.jsonfrontend/src/assets/images/background.jpgis excluded by!**/*.jpg
📒 Files selected for processing (23)
frontend/App.tsxfrontend/app.jsonfrontend/package.jsonfrontend/src/assets/translate/bg.jsonfrontend/src/assets/translate/en.jsonfrontend/src/components/auth/login/Body.style.tsxfrontend/src/components/auth/login/Body.tsxfrontend/src/components/auth/register/Body.style.tsxfrontend/src/components/auth/register/Body.tsxfrontend/src/components/header/Header.tsxfrontend/src/components/home/Body.style.tsxfrontend/src/components/home/Body.tsxfrontend/src/components/navigation/Header.style.tsxfrontend/src/components/navigation/Header.tsxfrontend/src/navigation/.gitkeepfrontend/src/navigation/Navigation.tsxfrontend/src/screens/auth/Login.tsxfrontend/src/screens/auth/Register.tsxfrontend/src/screens/home/Home.tsxfrontend/src/services/ApiClient.tsxfrontend/src/services/AuthContext.tsxfrontend/src/services/authService.tsfrontend/src/types/auth.ts
💤 Files with no reviewable changes (2)
- frontend/src/navigation/.gitkeep
- frontend/src/components/header/Header.tsx
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (1)
frontend/src/components/navigation/Header.tsx (1)
17-20: ⚡ Quick winMake logout navigation resilient to logout failures.
If
logout()rejects,handleLogoutcan fail and leave the user stranded. Navigate in afinallypath (or handle the error explicitly) so the flow remains deterministic.🔧 Proposed resilient handler
const handleLogout = async () => { - await logout(); - navigation.navigate("login"); + try { + await logout(); + } finally { + navigation.navigate("login"); + } };🤖 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 `@frontend/src/components/navigation/Header.tsx` around lines 17 - 20, handleLogout currently awaits logout() and only navigates to "login" on success, which can leave the UI stuck if logout() rejects; update handleLogout to ensure navigation.navigate("login") runs regardless by moving the navigation call into a finally block (or by catching errors and then navigating), keeping the async signature intact and preserving any logging or error handling for logout() inside the catch; refer to the handleLogout function and the logout() call and ensure navigation.navigate("login") executes in the finally path.
🤖 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 `@frontend/src/components/auth/login/Body.tsx`:
- Around line 27-39: The login flow currently calls navigation.navigate("home")
inside handleLogin which pushes Home onto the existing stack and leaves the auth
screens reachable; change this to either navigation.replace("home") or use
navigation.reset({ index: 0, routes: [{ name: "home" }] }) so the auth stack is
cleared/replaced after successful login (update the call in handleLogin where
navigation.navigate("home") appears and ensure types from RootStackParamList
still match).
In `@frontend/src/components/auth/register/Body.tsx`:
- Around line 27-39: handleRegister currently calls navigation.navigate("home")
after a successful register, which leaves auth screens on the back stack; update
handleRegister to call navigation.reset({ index: 0, routes: [{ name: 'home' }]
}) instead of navigation.navigate("home") so the auth stack is cleared; modify
the success branch in the handleRegister function (the code that calls
register(email, password)) to use navigation.reset and keep the existing
error/loading handling intact.
In `@frontend/src/components/ErrorBoundary.tsx`:
- Around line 28-47: The ErrorBoundary currently exposes full error and stack in
render; update componentDidCatch / render in the ErrorBoundary class so that
only a generic fallback message is shown in production and the detailed error
and componentStack are shown only when in development (e.g., check __DEV__ or
process.env.NODE_ENV === 'development'). Concretely, keep setting error and
errorInfo in componentDidCatch, but change the render fallback to conditionally
render this.state.error and this.state.errorInfo?.componentStack only when in
dev; otherwise render a non-sensitive generic message and optionally a
lightweight error id or contact link for production.
In `@frontend/src/services/ApiClient.tsx`:
- Around line 5-10: The current apiUrl assignment uses a hardcoded fallback
'http://localhost:8080' so the subsequent guard never triggers; change the logic
in ApiClient.tsx so that Constants.expoConfig?.extra?.apiUrl ||
process.env.API_URL is used for production/non-dev builds and only fall back to
'http://localhost:8080' when running in development (e.g., __DEV__ or
process.env.NODE_ENV === 'development'), then keep the existing if (!apiUrl)
check to log and throw an Error to fail-fast when apiUrl is missing in non-dev
builds; update references to apiUrl and the sources
(Constants.expoConfig?.extra?.apiUrl, process.env.API_URL) accordingly.
In `@frontend/src/services/AuthContext.tsx`:
- Around line 13-33: Remove or guard verbose console logs that print token/user
details in AuthContext's loadUser flow; specifically wrap or remove
console.log/console.warn/console.error calls around authService.getStoredToken,
authService.decodeToken, setUser, and authService.logout so token and decoded
payloads are not logged in production (use a __DEV__ check or equivalent), and
apply the same change to the other log at the referenced location (around line
42).
- Around line 18-23: The decoded JWT is accepted without checking expiration;
update the boot logic in AuthContext where you call
authService.decodeToken(token) to verify the token's exp claim before calling
setUser. After decoding (in the same try block around authService.decodeToken),
check that decoded.exp exists and decoded.exp > Math.floor(Date.now()/1000); if
expired, remove/clear the stored token and do not call setUser (optionally
log/debug the expiration), otherwise proceed to setUser(decoded). Also ensure
any decode errors still fall through to the existing error handling path.
In `@frontend/src/services/authService.ts`:
- Around line 5-14: decodeToken currently assumes a well-formed JWT and will
throw cryptic errors for malformed input; update decodeToken to first validate
that token is a non-empty string and that token.split('.') yields exactly 3
segments and a non-empty payload before doing replace/atob/JSON.parse, then wrap
the atob+JSON.parse in a try/catch to return a clear error or null on failure;
reference the decodeToken function and ensure the input checks and try/catch are
implemented there so malformed tokens are rejected early and parsing errors are
handled gracefully.
---
Nitpick comments:
In `@frontend/src/components/navigation/Header.tsx`:
- Around line 17-20: handleLogout currently awaits logout() and only navigates
to "login" on success, which can leave the UI stuck if logout() rejects; update
handleLogout to ensure navigation.navigate("login") runs regardless by moving
the navigation call into a finally block (or by catching errors and then
navigating), keeping the async signature intact and preserving any logging or
error handling for logout() inside the catch; refer to the handleLogout function
and the logout() call and ensure navigation.navigate("login") executes in the
finally path.
🪄 Autofix (Beta)
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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 18aa322b-7b57-40ce-a7e0-ac9ef8182f89
⛔ Files ignored due to path filters (1)
frontend/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (13)
frontend/App.tsxfrontend/app.jsonfrontend/package.jsonfrontend/src/components/ErrorBoundary.tsxfrontend/src/components/auth/login/Body.tsxfrontend/src/components/auth/register/Body.tsxfrontend/src/components/navigation/Header.tsxfrontend/src/hooks/.gitkeepfrontend/src/hooks/useAuth.tsfrontend/src/services/ApiClient.tsxfrontend/src/services/AuthContext.tsxfrontend/src/services/authService.tsfrontend/src/types/auth.ts
💤 Files with no reviewable changes (1)
- frontend/src/hooks/.gitkeep
✅ Files skipped from review due to trivial changes (1)
- frontend/app.json
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/package.json
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
frontend/src/services/AuthContext.tsx (1)
21-23: ⚡ Quick win
decodedis untypedany—setUserhas no runtime type safety guarantee.
authService.decodeTokenreturnsJSON.parse(...)which TypeScript types asany. Becauseanyis assignable toUser | null, the threesetUser(decoded)calls (lines 23, 42, 47) compile silently regardless of the actual payload shape. If the JWT payload doesn't match theUserinterface, the app ends up with silently corrupt auth state.Add a runtime type guard before calling
setUser:♻️ Proposed type guard approach
// In authService.ts or a shared types file: +import type { User } from '../types/auth'; + +export const isUser = (obj: unknown): obj is User => + typeof obj === 'object' && + obj !== null && + 'id' in obj && + 'email' in obj; // extend with all required User fields// In AuthContext.tsx loadUser: - const decoded = authService.decodeToken(token); - console.log('[AuthContext] Token decoded successfully:', decoded); - setUser(decoded); + const decoded = authService.decodeToken(token); + if (!authService.isUser(decoded)) { + throw new Error('Token payload does not match User shape'); + } + setUser(decoded);Apply the same guard in the
login(line 40–42) andregister(line 46–47) methods.🤖 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 `@frontend/src/services/AuthContext.tsx` around lines 21 - 23, The decoded JWT payload is typed as any so call sites can silently set invalid auth state; add a runtime type guard (e.g., function isUser(obj): obj is User that checks required User properties and their types) and use it before calling setUser wherever you decode a token: the decode path shown (authService.decodeToken → setUser) and inside the login and register flows (the login and register functions where setUser(decoded) is invoked). If the guard fails, do not call setUser — instead log the invalid payload and handle it safely (leave user null or trigger logout/error flow).
🤖 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 `@frontend/src/utils/errorParser.ts`:
- Around line 6-11: The current construction that builds errorMessages from
Object.values(errors) filters only strings and drops array-valued field errors
(e.g., ["Invalid email."]), causing fallback to the generic message; update the
logic that builds errorMessages (the const errorMessages assignment that
iterates over Object.values(errors) and the surrounding function in
errorParser.ts) to normalize each value: if a value is a string use it, if it's
an array/map over it and extract string entries (recursively flatten if nested),
then join all collected strings with ', ' into a single errorMessages string and
return that when non-empty so field-level array errors are preserved.
---
Nitpick comments:
In `@frontend/src/services/AuthContext.tsx`:
- Around line 21-23: The decoded JWT payload is typed as any so call sites can
silently set invalid auth state; add a runtime type guard (e.g., function
isUser(obj): obj is User that checks required User properties and their types)
and use it before calling setUser wherever you decode a token: the decode path
shown (authService.decodeToken → setUser) and inside the login and register
flows (the login and register functions where setUser(decoded) is invoked). If
the guard fails, do not call setUser — instead log the invalid payload and
handle it safely (leave user null or trigger logout/error flow).
🪄 Autofix (Beta)
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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: f4837a26-5448-484c-bf82-394c70c47f5f
📒 Files selected for processing (6)
frontend/app.jsonfrontend/src/components/auth/login/Body.tsxfrontend/src/components/auth/register/Body.tsxfrontend/src/services/AuthContext.tsxfrontend/src/services/authService.tsfrontend/src/utils/errorParser.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- frontend/app.json
- frontend/src/components/auth/register/Body.tsx
- frontend/src/services/authService.ts
- Resolved package-lock.json by regenerating - Removed old header directory (renamed to navigation) - Added .env.example and updated .gitignore
|



Summary by CodeRabbit
New Features
UI / Accessibility
Localization
Configuration
Security