feat: Add PIN-Based Authentication for LAN Clients#139
Conversation
📝 WalkthroughWalkthroughThis PR implements a PIN-based authentication system for WebSocket connections. It introduces a new Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Server
participant SessionStore
participant PinAuth
Client->>Server: GET /api/auth/pin (localhost only)
Server->>PinAuth: getPin()
PinAuth-->>Server: PIN string
Server-->>Client: PIN display
Client->>Client: User enters PIN
Client->>Server: POST /api/auth/pin {pin}
Server->>PinAuth: validatePin(pin)
PinAuth-->>Server: true/false
alt PIN Valid
Server->>SessionStore: createSession(ip?)
SessionStore-->>Server: token (UUID)
Server-->>Client: {status: "authenticated", token}
Client->>Client: Store token in localStorage
else PIN Invalid
Server-->>Client: {status: "error", error: "Invalid PIN"}
end
Client->>Server: POST /api/auth/verify {token}
Server->>SessionStore: isValidSession(token)
SessionStore-->>Server: true/false
Server-->>Client: {valid: true/false}
Client->>Server: WebSocket upgrade {token in query}
alt Token Valid
Server->>SessionStore: touchSession(token)
SessionStore-->>Server: success
Server-->>Client: WebSocket connected
else Token Invalid
Server-->>Client: 401 Unauthorized
end
sequenceDiagram
participant User
participant TrackpadUI
participant usePinAuth
participant useRemoteConnection
participant WebSocketServer
User->>TrackpadUI: Visit trackpad (non-localhost)
TrackpadUI->>usePinAuth: Hook initialization
usePinAuth-->>TrackpadUI: status="unauthenticated"
TrackpadUI->>TrackpadUI: Render PIN entry UI
User->>TrackpadUI: Submit PIN
TrackpadUI->>usePinAuth: submitPin(pin)
usePinAuth->>WebSocketServer: POST /api/auth/pin
WebSocketServer-->>usePinAuth: token
usePinAuth-->>TrackpadUI: status="authenticated", token
TrackpadUI->>useRemoteConnection: Initialize with {token, enabled: true}
useRemoteConnection->>WebSocketServer: WebSocket upgrade {token}
WebSocketServer-->>useRemoteConnection: Connected
useRemoteConnection-->>TrackpadUI: status="connected"
TrackpadUI->>TrackpadUI: Render Trackpad UI
User->>TrackpadUI: Control trackpad
TrackpadUI->>WebSocketServer: Send commands via WebSocket
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/routes/settings.tsx (1)
125-128:⚠️ Potential issue | 🟡 MinorInconsistent localhost check — misses
127.0.0.1and::1.Line 127 only checks
window.location.hostname !== 'localhost', while the PIN fetch effect (lines 65-68) andtrackpad.tsx(lines 17-21) also handle127.0.0.1and::1. If a user accesses the settings page viahttp://127.0.0.1:port/settings, the LAN IP auto-detection won't run.Proposed fix
- if (window.location.hostname !== 'localhost') return; + const hostname = window.location.hostname; + if (hostname !== 'localhost' && hostname !== '127.0.0.1' && hostname !== '::1') return;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/routes/settings.tsx` around lines 125 - 128, The hostname guard inside the useEffect in settings.tsx currently only skips when hostname !== 'localhost' so it misses requests from '127.0.0.1' and '::1'; update the check used in that useEffect to allow the same localhost variants as the PIN fetch effect and trackpad.tsx (i.e. treat 'localhost', '127.0.0.1' and '::1' as local) by replacing the single-string comparison with a whitelist or regex check that matches those three hostnames so the LAN IP auto-detection runs for all local-access variants.
🧹 Nitpick comments (5)
src/server/sessionStore.ts (1)
39-42: O(n) linear scan withtimingSafeEqualon every validation.Each call to
isValidSessionandtouchSessioniterates all sessions with a crypto comparison. This is fine for few LAN clients but consider using aMap<string, SessionEntry>keyed by token for O(1) lookups if the session count could grow. The timing-safe comparison guards against remote timing attacks, but since these are self-generated UUIDs (high entropy, not user-supplied secrets), a plainMaplookup would be acceptable here.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/server/sessionStore.ts` around lines 39 - 42, Replace the array-based sessions store and linear scans with a Map keyed by token to get O(1) lookups: change the sessions collection to a Map<string, SessionEntry>, update isValidSession to purgeExpired then use sessions.has(token) (no timingSafeEqual), and update touchSession to lookup by token via Map.get(token) and update its lastSeen; also update any session creation/removal code to use Map.set/delete and ensure purgeExpired iterates Map entries and deletes expired ones.src/server/pinAuth.ts (1)
4-6: Variable PIN length (4–6) may cause user confusion.The PIN length changes randomly at each server restart. The client form hints "4–6 digit PIN" but the server rejects anything that doesn't match the exact length. Users may need multiple attempts to guess the correct length unless they check the settings page. Consider fixing the length (e.g., always 6 digits) for a more predictable UX, or displaying the expected length in the client prompt.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/server/pinAuth.ts` around lines 4 - 6, The server currently chooses PIN_LENGTH randomly on each restart (variables PIN_LENGTH, PIN_MAX, PIN) causing the server to accept only that exact length while the client says "4–6 digit PIN"; change PIN_LENGTH to a fixed value (e.g., 6) and compute PIN_MAX and PIN from that fixed length so the server consistently expects a stable PIN length, and optionally update any client prompt to match the chosen fixed length.src/hooks/usePinAuth.ts (2)
27-48: Token verification runs on every mount even when token is known-invalid.The
verifyasync function is defined but there's no abort mechanism if the component unmounts before the fetch completes. Consider using anAbortControllerto cancel the in-flight request on cleanup to avoid state updates on an unmounted component.Proposed fix
useEffect(() => { if (typeof window === 'undefined') return; if (bypass) { setStatus('authenticated'); return; } const stored = localStorage.getItem(STORAGE_KEY); if (!stored) { setStatus('unauthenticated'); return; } + const controller = new AbortController(); const verify = async () => { try { const res = await fetch('/api/auth/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token: stored }), + signal: controller.signal, }); if (!res.ok) throw new Error('Verification failed'); const data = await res.json(); if (data?.valid) { setToken(stored); setStatus('authenticated'); } else { localStorage.removeItem(STORAGE_KEY); setStatus('unauthenticated'); } } catch { + if (!controller.signal.aborted) { + localStorage.removeItem(STORAGE_KEY); + } setStatus('unauthenticated'); } }; verify(); + return () => controller.abort(); }, [bypass]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/usePinAuth.ts` around lines 27 - 48, The verify function in usePinAuth performs a fetch('/api/auth/verify') without an abort mechanism, causing potential state updates after unmount; fix by creating an AbortController inside useEffect, pass controller.signal to fetch, call controller.abort() in the effect cleanup, and guard setToken/setStatus so they only run when the request wasn't aborted (e.g., check !controller.signal.aborted or catch and ignore AbortError). Reference verify, setToken, setStatus, STORAGE_KEY and the fetch('/api/auth/verify') call when making the changes.
43-45: Stale token left in localStorage on network/verification errors.When the
fetchcall throws (network error, non-OK response), the stored token remains in localStorage. On every subsequent mount, the hook will re-attempt verification of a potentially invalid token. Consider removing the token here to avoid repeated futile verification attempts.Proposed fix
} catch { + localStorage.removeItem(STORAGE_KEY); setStatus('unauthenticated'); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/usePinAuth.ts` around lines 43 - 45, In usePinAuth's catch block (where it calls setStatus('unauthenticated')), also clear the stale auth token from localStorage to prevent repeated failed verification attempts; add a localStorage.removeItem('pinAuthToken') (or removeItem using the exact key the hook uses to store the token) before or alongside setStatus('unauthenticated') so the hook won't repeatedly try to verify a stale token on subsequent mounts.src/hooks/useRemoteConnection.ts (1)
56-61: Unconditional reconnect on close may cause infinite retry loops.If the server rejects the token (401 during upgrade), the socket closes immediately and the hook retries every 3 seconds indefinitely. Consider adding exponential backoff or a maximum retry count, or checking the close code to distinguish server rejection from transient network issues.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/useRemoteConnection.ts` around lines 56 - 61, The socket.onclose handler in useRemoteConnection.ts unconditionally schedules reconnects which can cause infinite retry loops on auth rejection; modify socket.onclose to inspect the CloseEvent.code (and/or reason) and abort retries for authentication/upgrade failures, add a retryCount state (e.g., retryCount) with a configurable maxRetries and an exponential backoff (baseBackoffMs doubling each attempt) when calling connect, ensure reconnectTimer is cleared on unmount or successful connect, and update setStatus appropriately; touch the socket.onclose block, the connect function, and the reconnectTimer/retryCount bookkeeping to implement these changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/routes/trackpad.tsx`:
- Around line 226-228: The handler silently returns when the PIN check against
/^\d{4,6}$/ fails (using rawPin), so set the visible error state instead of
returning silently: update the submit handler to call the existing authError
setter (e.g., setAuthError or the local authError state used for display) with a
clear message like "PIN must be 4–6 digits" when the regex test fails;
optionally also add HTML-level validation by adding pattern="^\d{4,6}$" and
required to the PIN input so the browser shows native validation before the
handler runs.
In `@src/server/websocket.ts`:
- Around line 62-79: Add per-IP rate limiting to the POST /api/auth/pin handler
in websocket.ts: before validating the pin (before validatePin is called), track
attempts for req.socket.remoteAddress (or fallback) and enforce a limit (e.g.,
max 5 attempts per minute), returning a 429 with sendJson when exceeded; on each
failed PIN validation increment the counter and apply an escalating
timeout/temporary lockout (or exponential backoff) for that IP, and on
successful createSession reset/clear that IP’s counters; ensure the rate-store
is safe for concurrent use (in-memory map with timestamps or a small LRU cache)
and include logic to expire old entries.
- Around line 107-117: Current code attaches the auth handler directly via
server.on('request', using AUTH_HANDLER_FLAG and handleAuthRequest), which
bypasses Vite's middleware pipeline; change this to register the auth routes
inside the plugin's configureServer hook using server.middlewares.use(...) so
requests go through Vite's middleware chain and ensure the AUTH_HANDLER_FLAG is
still set only once. Also add request throttling for the POST /api/auth/pin
route (e.g., per-IP rate limiting or exponential backoff after failures) inside
the auth middleware so repeated PIN attempts are limited; update
handleAuthRequest (or the new middleware wrapper) to enforce and log
rate-limited responses.
---
Outside diff comments:
In `@src/routes/settings.tsx`:
- Around line 125-128: The hostname guard inside the useEffect in settings.tsx
currently only skips when hostname !== 'localhost' so it misses requests from
'127.0.0.1' and '::1'; update the check used in that useEffect to allow the same
localhost variants as the PIN fetch effect and trackpad.tsx (i.e. treat
'localhost', '127.0.0.1' and '::1' as local) by replacing the single-string
comparison with a whitelist or regex check that matches those three hostnames so
the LAN IP auto-detection runs for all local-access variants.
---
Nitpick comments:
In `@src/hooks/usePinAuth.ts`:
- Around line 27-48: The verify function in usePinAuth performs a
fetch('/api/auth/verify') without an abort mechanism, causing potential state
updates after unmount; fix by creating an AbortController inside useEffect, pass
controller.signal to fetch, call controller.abort() in the effect cleanup, and
guard setToken/setStatus so they only run when the request wasn't aborted (e.g.,
check !controller.signal.aborted or catch and ignore AbortError). Reference
verify, setToken, setStatus, STORAGE_KEY and the fetch('/api/auth/verify') call
when making the changes.
- Around line 43-45: In usePinAuth's catch block (where it calls
setStatus('unauthenticated')), also clear the stale auth token from localStorage
to prevent repeated failed verification attempts; add a
localStorage.removeItem('pinAuthToken') (or removeItem using the exact key the
hook uses to store the token) before or alongside setStatus('unauthenticated')
so the hook won't repeatedly try to verify a stale token on subsequent mounts.
In `@src/hooks/useRemoteConnection.ts`:
- Around line 56-61: The socket.onclose handler in useRemoteConnection.ts
unconditionally schedules reconnects which can cause infinite retry loops on
auth rejection; modify socket.onclose to inspect the CloseEvent.code (and/or
reason) and abort retries for authentication/upgrade failures, add a retryCount
state (e.g., retryCount) with a configurable maxRetries and an exponential
backoff (baseBackoffMs doubling each attempt) when calling connect, ensure
reconnectTimer is cleared on unmount or successful connect, and update setStatus
appropriately; touch the socket.onclose block, the connect function, and the
reconnectTimer/retryCount bookkeeping to implement these changes.
In `@src/server/pinAuth.ts`:
- Around line 4-6: The server currently chooses PIN_LENGTH randomly on each
restart (variables PIN_LENGTH, PIN_MAX, PIN) causing the server to accept only
that exact length while the client says "4–6 digit PIN"; change PIN_LENGTH to a
fixed value (e.g., 6) and compute PIN_MAX and PIN from that fixed length so the
server consistently expects a stable PIN length, and optionally update any
client prompt to match the chosen fixed length.
In `@src/server/sessionStore.ts`:
- Around line 39-42: Replace the array-based sessions store and linear scans
with a Map keyed by token to get O(1) lookups: change the sessions collection to
a Map<string, SessionEntry>, update isValidSession to purgeExpired then use
sessions.has(token) (no timingSafeEqual), and update touchSession to lookup by
token via Map.get(token) and update its lastSeen; also update any session
creation/removal code to use Map.set/delete and ensure purgeExpired iterates Map
entries and deletes expired ones.
| if (!/^\d{4,6}$/.test(rawPin)) { | ||
| return; | ||
| } |
There was a problem hiding this comment.
Silent rejection when PIN format is invalid — no user feedback.
When the regex test fails, the form handler returns without any indication to the user. Consider setting an error message so the user knows what went wrong.
Proposed fix
const rawPin = String(data.get('pin') || '').trim();
if (!/^\d{4,6}$/.test(rawPin)) {
+ // Optionally surface an error to the user
return;
}You could reuse the existing authError display by calling a local setter or relying on the HTML pattern attribute for native validation. At minimum, adding required to the input would trigger native form validation before reaching this code.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/routes/trackpad.tsx` around lines 226 - 228, The handler silently returns
when the PIN check against /^\d{4,6}$/ fails (using rawPin), so set the visible
error state instead of returning silently: update the submit handler to call the
existing authError setter (e.g., setAuthError or the local authError state used
for display) with a clear message like "PIN must be 4–6 digits" when the regex
test fails; optionally also add HTML-level validation by adding
pattern="^\d{4,6}$" and required to the PIN input so the browser shows native
validation before the handler runs.
| if (url.pathname === '/api/auth/pin' && req.method === 'POST') { | ||
| try { | ||
| const body = await readJsonBody(req); | ||
| const pin = typeof body?.pin === 'string' ? body.pin : ''; | ||
| if (!validatePin(pin)) { | ||
| sendJson(res, 401, { error: 'Invalid PIN.' }); | ||
| return true; | ||
| } | ||
|
|
||
| const token = createSession(req.socket.remoteAddress || undefined); | ||
| sendJson(res, 200, { token }); | ||
| return true; | ||
| } catch (error: any) { | ||
| const message = error?.message === 'Payload too large' ? 'Payload too large.' : 'Invalid request.'; | ||
| sendJson(res, 400, { error: message }); | ||
| return true; | ||
| } | ||
| } |
There was a problem hiding this comment.
No rate limiting on PIN submission — 4-digit PINs are brute-forceable.
POST /api/auth/pin has no rate limiting. With PIN lengths of 4–6 digits (10,000–1,000,000 combinations), a LAN attacker can brute-force a 4-digit PIN in minutes. Consider adding per-IP rate limiting (e.g., max 5 attempts per minute with exponential backoff or temporary lockout).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/server/websocket.ts` around lines 62 - 79, Add per-IP rate limiting to
the POST /api/auth/pin handler in websocket.ts: before validating the pin
(before validatePin is called), track attempts for req.socket.remoteAddress (or
fallback) and enforce a limit (e.g., max 5 attempts per minute), returning a 429
with sendJson when exceeded; on each failed PIN validation increment the counter
and apply an escalating timeout/temporary lockout (or exponential backoff) for
that IP, and on successful createSession reset/clear that IP’s counters; ensure
the rate-store is safe for concurrent use (in-memory map with timestamps or a
small LRU cache) and include logic to expire old entries.
| if (!(server as any)[AUTH_HANDLER_FLAG]) { | ||
| (server as any)[AUTH_HANDLER_FLAG] = true; | ||
| server.on('request', (req: IncomingMessage, res: ServerResponse) => { | ||
| handleAuthRequest(req, res).catch((error) => { | ||
| logger.error(`Auth handler error: ${String(error)}`); | ||
| if (!res.headersSent) { | ||
| sendJson(res, 500, { error: 'Server error.' }); | ||
| } | ||
| }); | ||
| }); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check how createWsServer is invoked and how the server object is obtained
rg -n 'createWsServer' --type=ts -C5
# Check Vite plugin setup
rg -n 'configureServer' --type=ts -C10
# Look for how the HTTP server is created / accessed
rg -n 'httpServer' --type=ts -C3Repository: AOSSIE-Org/Rein
Length of output: 3753
🏁 Script executed:
#!/bin/bash
# Check Nitro configuration in vite.config
cat -n vite.config.ts
# Check if there are any middleware registrations related to API routes
rg -n 'server\.middlewares' --type=ts -C5
# Check the full websocket.ts to see if there's middleware registration
wc -l src/server/websocket.ts
# Look for any API route definitions or middleware setup
rg -n 'middlewares\.use\|server\.use' --type=ts -C3Repository: AOSSIE-Org/Rein
Length of output: 1660
🏁 Script executed:
#!/bin/bash
# Check for API routes - Nitro typically uses routes/ or server/ directories
fd -t f '\.(ts|js)$' server/routes/ api/ routes/ 2>/dev/null | head -20
# Check if there's a server/ directory with Nitro routes
find src/server -name '*.ts' -type f | head -20
# Look at the full websocket.ts to understand request flow
cat -n src/server/websocket.ts | head -120
# Check the auth handler section specifically
cat -n src/server/websocket.ts | sed -n '50,120p'Repository: AOSSIE-Org/Rein
Length of output: 8718
🏁 Script executed:
#!/bin/bash
# Check if there's rate limiting on PIN validation attempts
rg -n 'validatePin\|rateLimit\|attempt' --type=ts -C3
# Check the pinAuth.ts file to see PIN validation logic
cat -n src/server/pinAuth.ts
# Look for session tracking or attempt limiting
cat -n src/server/sessionStore.tsRepository: AOSSIE-Org/Rein
Length of output: 2637
🏁 Script executed:
#!/bin/bash
# Check if there are Nitro API routes handling /api/auth
find src -name '*.ts' -path '*/routes/*' | xargs grep -l 'auth' 2>/dev/null
# Check the server directory structure more carefully
find src/server -type f -name '*.ts' | sort
# Look for any API route files in common Nitro locations
ls -la src/routes/ 2>/dev/null || ls -la src/api/ 2>/dev/null || echo "No routes/ or api/ directory found"
# Check if there's a server/routes directory
ls -la src/server/ 2>/dev/nullRepository: AOSSIE-Org/Rein
Length of output: 1051
🏁 Script executed:
#!/bin/bash
# Check if there's any mention of /api handling in Vite config or comments
grep -n 'api\|middleware\|route' vite.config.ts
# Look for tanstackStart documentation or usage patterns
rg -n 'tanstackStart' --type=ts -C5
# Check if there's any test or error handling related to this
rg -n 'write after\|headersSent\|already sent' --type=tsRepository: AOSSIE-Org/Rein
Length of output: 1057
🏁 Script executed:
#!/bin/bash
# Let me check if there are any error handling tests or existing issues with this pattern
rg -n 'POST.*auth/pin\|validatePin' --type=ts -B5 -A5
# Check if there's any rate limiting elsewhere
rg -n 'limit\|throttle\|bucket\|attempt' --type=ts
# Verify handleAuthRequest is actually called without awaiting by checking the full context
cat -n src/server/websocket.ts | sed -n '107,125p'Repository: AOSSIE-Org/Rein
Length of output: 969
🏁 Script executed:
#!/bin/bash
# Check if there's special handling in TanstackStart or if API routes are supposed to be handled this way
grep -rn 'createServer\|middleware' src/ --type=ts | grep -v node_modules | head -10
# Verify the exact request flow - check if there are tests or documentation
find . -name '*.md' -o -name '*.test.ts' | xargs grep -l 'auth\|api' 2>/dev/null | head -5
# Check the complete websocket handler to see if there's error handling we missed
cat -n src/server/websocket.ts | sed -n '49,95p'Repository: AOSSIE-Org/Rein
Length of output: 2320
🏁 Script executed:
#!/bin/bash
# Let me check the full websocket.ts to understand the complete flow and how the handler integrates
cat -n src/server/websocket.ts | tail -100
# Check if there's any documentation about the server setup
cat README.md 2>/dev/null | head -50Repository: AOSSIE-Org/Rein
Length of output: 5824
🏁 Script executed:
#!/bin/bash
# Check if there are any error logs or tests documenting request handling issues
find . -name '*.test.*' -o -name '*.spec.*' | xargs grep -l 'auth\|request' 2>/dev/null | head -3
# Check the TanStack Start / Vite integration - does it have middlewares property?
rg 'server\.middlewares' . --type=ts 2>/dev/null | head -5
# Verify the exact plugin order and if configureServer is called before or after other plugins
grep -n 'configureServer\|plugins:' vite.config.tsRepository: AOSSIE-Org/Rein
Length of output: 109
Use Vite middleware pattern for auth routes instead of raw 'request' listeners.
The auth handler is registered via server.on('request', ...) on the raw HTTP server, which bypasses Vite's standard middleware pipeline. This can cause unpredictable behavior with Vite's dev server or preview server, which have their own request handling. The idiomatic approach is to register auth routes as Vite middleware via server.middlewares.use(...) in the plugin's configureServer hook, which ensures proper ordering relative to Vite's internal handlers.
Additionally, add rate limiting to POST /api/auth/pin. The endpoint currently has no attempt throttling, making the 4-digit PIN (10,000 combinations) brute-forceable in ~17 minutes at a modest request rate. Implement per-IP rate limiting or exponential backoff after failed attempts.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/server/websocket.ts` around lines 107 - 117, Current code attaches the
auth handler directly via server.on('request', using AUTH_HANDLER_FLAG and
handleAuthRequest), which bypasses Vite's middleware pipeline; change this to
register the auth routes inside the plugin's configureServer hook using
server.middlewares.use(...) so requests go through Vite's middleware chain and
ensure the AUTH_HANDLER_FLAG is still set only once. Also add request throttling
for the POST /api/auth/pin route (e.g., per-IP rate limiting or exponential
backoff after failures) inside the auth middleware so repeated PIN attempts are
limited; update handleAuthRequest (or the new middleware wrapper) to enforce and
log rate-limited responses.
|
Hey @Muneerali199, see the discord chats. This was not planned. I was also going with this approach but then we decided to go with the token method with an expiry. |
|
Ok sir @Rozerxshashank thank you for your response |
|
@Rozerxshashank Thanks for the review and feedback. I wasn’t aware that we had finalized on the token-with-expiry approach instead of PIN-based verification. Since the direction has changed, I’ll align with the updated plan and rework the implementation accordingly. Let me confirm the exact token lifecycle and expiry behavior before updating the PR. |
Closes #138
Summary
/trackpadand display current PIN on/settings(localhost only).Changes
POST /api/auth/pinPOST /api/auth/verifyTesting
Ran
npm installRan
npm testvitest runNotes
Summary by CodeRabbit