Elysia OIDC/OAuth proxy#101
Conversation
- Refactor AuthHandler to use openid-client for OIDC integration - Add OIDC providers table to manage client configurations - Add auth routes to API for login/callback flows - Add frontend auth hooks (useAuth) and protected route components - Update project dependencies with elysia-oauth2 and openid-client - Improve type safety for SQLite wrapper index creation
- Added `jose` to handle JWT creation for secure user session transmission - Updated AuthHandler to include state, nonce, and PKCE verification cookies - Refactored frontend auth callback to process JWT tokens instead of direct API response - Added `FRONTEND_URL` environment variable for cross-origin redirects - Enhanced logging in the auth flow for improved debugging - Updated SignInPage UI to support dynamic provider discovery and selection - Refactored ProtectedRoute to use updated auth logic
- Added `/auth/:providerId/logout` endpoint to `@dockstat/auth` to support OIDC end-session redirection - Enhanced `useAuth` hook to manage provider ID tracking and handle logout redirects - Updated `Navbar` and `Sidebar` components to display the authenticated user's identifier - Added logout action to the UI sidebar - Refactored `AuthHandler` to store `logout_url` and improve environment variable handling for JWT secrets
- Move configuration logic to ConfigService - Move route definitions to separate file - Update API entry point to use AuthHandler.routes property - Bump turbo to 2.9.6 and bun-types to 1.3.13 - Add version field to @dockstat/auth package.json
…mprove plugin management - Add missing OpenAPI summaries and descriptions to various API routes - Clean up unused parameters in Docker client routes - Refactor plugin installation type casting and enhance unloadPlugins documentation - Implement `useAuth` React hook in `@dockstat/auth` - Update auth route handlers to use Elysia `redirect` utility - Add missing documentation to auth routes
- Add ProtectedRoute component for route guarding - Reorganize @dockstat/auth/client exports - Update layout to handle auth state and user display in navbar - Refactor system stats route logic for better maintainability - Add DOM lib to auth package for browser-based auth utilities - Minor cleanup of route documentation summaries and internal code formatting
…ntext provider - Add `createAuthMiddleware` to `@dockstat/auth` for JWT-based request authentication in ElysiaJS. - Integrate authentication middleware into `DockStatAPI` via `apps/api`. - Introduce `AuthProvider` in `@dockstat/auth/client` for improved React state management, including cross-tab sync and automatic token refresh. - Add comprehensive documentation for new authentication middleware and React integration in `packages/auth/README.md`. - Deprecate legacy hook-based authentication approach in favor of the new Context API. - Add utility functions for WebSocket authentication and protected route handling.
…middleware - Remove manual middleware usage in API routes and move to a centralized `.guard(authenticated(), ...)` pattern. - Consolidate auth middleware into `middleware/auth.ts`. - Update `DockStatAPI` to enforce authentication globally for core services via guards. - Remove deprecated accounts UI components. - Fix TS path mapping for `@dockstat/auth` package and adjust client-side routing to use the new `CreateRoutes` wrapper. - Upgrade `elysia` to 1.4.28 and bump `@dockstat/auth` dependency versioning.
…lient [DOCK-123] - Add auth_token support to Eden Treaty API client via Authorization header - Persist auth_token to localStorage during sign-in - Configure TanStack Query default options with staleTime, gcTime, and retry strategies - Cleanup SignInPage logic to improve state handling and remove redundant user redirection logic
- Refactor `AuthHandler` to expose middleware via `getMiddlewareFunctions` - Update API and Elysia plugins to use factory-provided middleware and proper OpenAPI security schemes - Clean up `packages/auth` exports and deprecate old `useAuth` API - Update JWT expiration time to 1 day - Improve authorization header handling in `lib/api` - Added comprehensive logging to the authentication middleware flow
- Reorder properties in Elysia openapi configuration - Correct indentation and spacing in QueryClient, React Router, and auth middleware - Improve readability of exported middleware functions - Add missing whitespace in api.ts and other utility files
- Replace standard string concatenation with template literals in SignIn.tsx - Remove unused imports in router.tsx and AuthProvider.tsx - Update property access and prefix unused variable with underscore in middleware.ts for better linting compliance
Reviewer's GuideIntroduce a full OIDC/OAuth proxy/authentication system (@dockstat/auth) and integrate it end‑to‑end with the API and frontend, enforcing JWT‑based auth, adding React auth context/hooks, protected routing, and related documentation and typing updates. Sequence diagram for OAuth login and callback flowsequenceDiagram
actor User
participant Browser
participant Frontend as DockStatFrontend
participant AuthClient as AuthProviderClient
participant API as DockStatAPI
participant AuthRoutes as AuthRoutes_/auth
participant OIDC as OIDCProvider
User->>Browser: Navigate to protected route
Browser->>Frontend: Request /protected
Frontend->>AuthClient: useAuth() in ProtectedRoute
AuthClient-->>Frontend: isAuthenticated = false
Frontend-->>Browser: Redirect to /login
User->>Browser: Click provider button
Browser->>AuthClient: login(providerId)
AuthClient->>Browser: window.location = API_BASE/auth/providerId/login
Browser->>API: GET /auth/:providerId/login
API->>AuthRoutes: Route handling
AuthRoutes->>AuthRoutes: ConfigService.getConfig(providerId)
AuthRoutes->>AuthRoutes: Generate state, nonce, PKCE
AuthRoutes->>Browser: Set cookies state, nonce, pkce
AuthRoutes-->>Browser: 302 Redirect to OIDC authorization_url
Browser->>OIDC: GET authorization_url (user login)
OIDC-->>Browser: Redirect to BASE_URL/auth/providerId/callback?code&state
Browser->>API: GET /auth/:providerId/callback?code&state
API->>AuthRoutes: Route handling
AuthRoutes->>AuthRoutes: Validate cookies and state
AuthRoutes->>OIDC: authorizationCodeGrant(meta, callbackUrl,...)
OIDC-->>AuthRoutes: tokens
AuthRoutes->>OIDC: fetchUserInfo(access_token, sub)
OIDC-->>AuthRoutes: userInfo
AuthRoutes->>AuthRoutes: createAuthToken(userInfo) (JWT)
AuthRoutes-->>Browser: 302 Redirect to FRONTEND_URL/auth/providerId/callback?token=jwt
Browser->>Frontend: GET /auth/providerId/callback?token=jwt
Frontend->>AuthClient: AuthCallback processes token
AuthClient->>Browser: Store auth_token and user in localStorage
AuthClient-->>Browser: Redirect to original path
Browser->>API: Subsequent API calls with Authorization: Bearer jwt
API->>API: Middleware.verifyAuthToken
API-->>Browser: Protected data
ER diagram for oidc_providers tableerDiagram
OIDC_PROVIDERS {
string id PK
string issuer_url
string client_id
string client_secret
string scopes
date created_at
string logout_url
}
Class diagram for @dockstat/auth backend componentsclassDiagram
class AuthHandler {
+QueryBuilder~ProvidersTable~ table
+Logger logger
+ConfigService configService
+Elysia routes
+AuthMiddlewareBundle middleware
+AuthHandler(DB db, Logger logger)
}
class ConfigService {
+QueryBuilder~ProvidersTable~ table
+Logger logger
+Map~string, Configuration~ issuerCache
+ConfigService(QueryBuilder~ProvidersTable~ table, Logger logger)
+getConfig(string providerId) Promise~OAuthConfig~
}
class OAuthConfig {
+ClientMetadata config
+Configuration meta
+string scopes
}
class AuthMiddlewareBundle {
+createAuthMiddleware() Elysia
+authenticated(options) AuthRouteDecorator
+createAuthenticatedWsHandler(options) WsHandler
+getWsUser(ElysiaWS ws) AuthUser
+handleWsAuthentication(ElysiaWS ws, string token) Promise~boolean~
+isAuthenticatedUser(AuthUser user) boolean
+withAuth(handler) Function
}
class AuthUser {
+string sub
+string email
+string name
+string picture
+number iat
+number exp
+[key: string]: unknown
}
class ProvidersTable {
+string id
+string issuer_url
+string client_id
+string client_secret
+string scopes
+Date created_at
+string logout_url
}
class JWTHelpers {
+createAuthToken(userInfo) Promise~string~
+verifyAuthToken(string token) Promise~JWTPayload | null~
}
class JWTPayload {
+AuthUser user
+number iat
+number exp
}
class EnvConfig {
+string BASE_URL
+string FRONTEND_URL
+Uint8Array JWT_SECRET
}
class AuthRoutesFactory {
+createAuthRoutes(QueryBuilder~ProvidersTable~, Logger, ConfigService) Elysia
}
class Logger {
+Logger spawn(string name) Logger
+info(string msg)
+warn(string msg)
+error(string msg)
}
class DB {
+createTable~T~(string name, schema, options) QueryBuilder~T~
}
class QueryBuilder~T~ {
+insertAndGet(object row) T
+select(string[] cols) QueryBuilder~T~
+where(object where) QueryBuilder~T~
+first() T | null
+all() T[]
}
AuthHandler --> ConfigService : creates
AuthHandler --> ProvidersTable : manages
AuthHandler --> AuthMiddlewareBundle : uses
AuthHandler --> AuthRoutesFactory : uses
ConfigService --> ProvidersTable : reads
ConfigService --> EnvConfig : uses BASE_URL
AuthMiddlewareBundle --> AuthUser : produces
AuthMiddlewareBundle --> JWTHelpers : uses verifyAuthToken
JWTHelpers --> EnvConfig : uses JWT_SECRET
AuthRoutesFactory --> ConfigService : uses
AuthRoutesFactory --> JWTHelpers : uses createAuthToken
AuthRoutesFactory --> ProvidersTable : reads
AuthRoutesFactory --> EnvConfig : uses BASE_URL, FRONTEND_URL
DB --> QueryBuilder~ProvidersTable~ : creates table
Class diagram for React auth client (AuthProvider and ProtectedRoute)classDiagram
class AuthProvider {
+AuthState state
+AuthProviderProps props
+AuthProvider(AuthProviderProps props)
+login(string providerId) void
+logout() void
+refreshToken() Promise~void~
+clearError() void
}
class AuthState {
+User user
+string token
+boolean loading
+string error
+boolean isAuthenticated
}
class AuthProviderProps {
+ReactNode children
+string apiBase
+string tokenStorageKey
+string userStorageKey
+() => void onTokenExpired
}
class User {
+string sub
+string email
+string name
+string picture
+[key: string]: unknown
}
class AuthContextType {
+User user
+string token
+boolean loading
+string error
+boolean isAuthenticated
+login(string providerId) void
+logout() void
+refreshToken() Promise~void~
+clearError() void
}
class ProtectedRoute {
+ReactNode children
+ReactNode loadingComponent
+string redirectTo
}
class AuthHooks {
+useAuth() AuthContextType
+useUser() User | null
+useIsAuthenticated() boolean
+useIsLoading() boolean
+useAuthError() string | null
}
class LegacyUseAuth {
<<deprecated>>
+useAuth() AuthContextType
}
AuthProvider --> AuthContextType : provides
AuthProvider --> User : stores
AuthProvider --> AuthHooks : used by
AuthProvider --> AuthState : manages
ProtectedRoute --> AuthHooks : uses useAuth
AuthHooks --> AuthContextType : returns
LegacyUseAuth --> AuthHooks : wraps useAuth
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Hey - I've found 7 issues, and left some high level feedback:
- In
apps/dockstat/src/router.tsx, the route forConfigureClientsPagechanged from/clients/configureto/client/configure; if this isn’t intentional it will break existing navigation/bookmarks and should be corrected back to the plural path. - In
apps/dockstat/src/lib/api.ts,getHeaders()logs the rawauth_tokento the console and the headers are computed only once when creating the Treaty client; consider removing the token logging and switching to a per-request interceptor or dynamic header injection so updated tokens are picked up without recreating the client. - Authentication state is now managed both by
AuthProvider(which parses tokens from the URL/localStorage) and theAuthCallbackcomponent / localuseAuthhook underapps/dockstat, which also decode/store the token; consolidating on the context-based@dockstat/auth/clientflow and removing duplicate token parsing/storage logic will reduce the risk of subtle inconsistencies.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `apps/dockstat/src/router.tsx`, the route for `ConfigureClientsPage` changed from `/clients/configure` to `/client/configure`; if this isn’t intentional it will break existing navigation/bookmarks and should be corrected back to the plural path.
- In `apps/dockstat/src/lib/api.ts`, `getHeaders()` logs the raw `auth_token` to the console and the headers are computed only once when creating the Treaty client; consider removing the token logging and switching to a per-request interceptor or dynamic header injection so updated tokens are picked up without recreating the client.
- Authentication state is now managed both by `AuthProvider` (which parses tokens from the URL/localStorage) and the `AuthCallback` component / local `useAuth` hook under `apps/dockstat`, which also decode/store the token; consolidating on the context-based `@dockstat/auth/client` flow and removing duplicate token parsing/storage logic will reduce the risk of subtle inconsistencies.
## Individual Comments
### Comment 1
<location path="apps/dockstat/src/layout/index.tsx" line_range="34-38" />
<code_context>
theme,
} = useLayout()
+ const { user, logout } = useAuth()
+
const heading = useContext(PageHeadingContext).heading
</code_context>
<issue_to_address>
**issue (bug_risk):** Layout is using a different auth hook than the new AuthProvider, which will desync auth state and redirects.
Layout imports `useAuth` from `@/hooks/useAuth`, but the app is wrapped with `AuthProvider` from `@dockstat/auth/client`. As a result, Navbar/Sidebar use a different `user/logout` source than the context, and `ProtectedRoute` ends up with a separate `user/isAuthenticated` state that can fall out of sync (e.g. logout in one place not updating the other). Please switch Layout to use `useAuth` from `@dockstat/auth/client` so all components share the same auth state.
</issue_to_address>
### Comment 2
<location path="apps/dockstat/src/lib/api.ts" line_range="4-7" />
<code_context>
-import { type Treaty, treaty } from "@elysiajs/eden"
+import { treaty } from "@elysiajs/eden"
+
+const getHeaders = () => {
+ const token = localStorage.getItem("auth_token")
+ console.log(`Found auth token: ${token}`)
+ return token ? { authorization: `Bearer ${token}` } : {}
+}
</code_context>
<issue_to_address>
**🚨 issue (security):** Leaking tokens via console.log and using a static Authorization header snapshot are security/behavioral issues.
Two issues:
1) `console.log("Found auth token: ${token}")` prints the raw bearer token, which is a credential leak via logs/devtools. Remove this log or redact the token.
2) `headers: getHeaders()` is only evaluated when the client is created, so header values won’t track login/logout/refresh. Ensure the latest token is read per request (e.g., a wrapper `fetch`, recreating the client on auth changes, or using per-call header overrides), otherwise auth will behave inconsistently.
</issue_to_address>
### Comment 3
<location path="apps/dockstat/src/router.tsx" line_range="24" />
<code_context>
+ { element: <DockNodePage />, path: "/node" },
+ { element: <NodeStacksPage />, path: "/node/stacks" },
+ { element: <ClientsPage />, path: "/clients" },
+ { element: <ConfigureClientsPage />, path: "/client/configure" },
+ { element: <PluginIdPage />, path: "/p/:pluginId/*" },
+ { element: <ExtensionsIndex />, path: "/extensions" },
</code_context>
<issue_to_address>
**issue (bug_risk):** The ConfigureClients route path changed from `/clients/configure` to `/client/configure`, which looks unintended.
This change will break any existing navigation or deep links that still use `/clients/configure`. If that path is still expected to work, either revert to the plural route or add a redirect/alias so both paths remain valid.
</issue_to_address>
### Comment 4
<location path="packages/auth/src/routes.ts" line_range="18-21" />
<code_context>
+ return new Elysia({ detail: { tags: ["Auth"] }, prefix: "/auth" })
+ .post(
+ "/providers",
+ ({ body }) =>
+ table.insertAndGet({
+ ...(body as object),
+ scopes: (body as { scopes: string }).scopes ? (body as { scopes: string }).scopes : null,
+ }),
+ {
</code_context>
<issue_to_address>
**issue (bug_risk):** Overriding `scopes` to `null` on provider creation defeats the DB default and may cause runtime issues.
The insert currently forces `scopes` to `null` when not provided:
```ts
scopes: (body as { scopes: string }).scopes ? body.scopes : null
```
This overrides the table’s default (`"openid profile email"`) so `scopes` can be `null` even though downstream code (e.g. `configService`) expects a string.
Instead, omit `scopes` when it’s not supplied so the DB default is used, e.g.:
```ts
const { scopes, ...rest } = body
return table.insertAndGet({
...rest,
...(scopes ? { scopes } : {}),
})
```
This preserves the default and avoids unexpected `null` handling downstream.
</issue_to_address>
### Comment 5
<location path="packages/logger/src/utils.ts" line_range="70" />
<code_context>
}
export function colorByReqID(rawReqId: string) {
- let reqId = rawReqId
+ let reqId = rawReqId.length >= 3 ? rawReqId : ""
let from = ""
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Silently discarding short request IDs can make correlation harder and may hide `from` information.
Clearing `reqId` when `rawReqId.length < 3` means valid short IDs are dropped and any `from` segment in them (e.g. `"1|ui"`) is no longer parsed. If the goal is to reject invalid IDs, consider validating by structure (e.g. requiring `|`) instead of length, or at least logging when an ID is discarded so unexpected input can be detected.
Suggested implementation:
```typescript
export function colorByReqID(rawReqId: string) {
let reqId = rawReqId
let from = ""
// If the ID is very short *and* doesn't contain a `|` segment separator, treat it as invalid.
// We log this in non-production so unexpected input can be investigated without affecting prod noise.
if (reqId.length < 3 && !reqId.includes("|")) {
if (process.env.NODE_ENV !== "production") {
console.warn(`[logger] Discarding short request id without source: "${rawReqId}"`)
}
reqId = ""
}
```
If this package already has a central logging utility (instead of using `console` directly), you should:
1. Replace `console.warn` with the appropriate logger call (e.g. `logger.warn` or similar).
2. Ensure the chosen logger call respects the current log level configuration so these warnings can be filtered in production if desired.
</issue_to_address>
### Comment 6
<location path="apps/dockstat/src/pages/SignIn.tsx" line_range="52" />
<code_context>
+ }
+}
+
+export function AuthCallback() {
+ const [searchParams] = useSearchParams()
+ const navigate = useNavigate()
</code_context>
<issue_to_address>
**issue (complexity):** Consider delegating auth callback, redirect handling, and file structure to the shared auth client so this page remains a thin UI wrapper instead of re‑implementing authentication logic locally.
You’ve effectively re‑implemented parts of the new auth client in this file, which does increase complexity and creates two sources of truth.
### 1. Delegate callback handling to the auth client
Instead of decoding the JWT, touching `localStorage`, and dealing with redirect in `AuthCallback`, expose that logic from `@dockstat/auth/client` (or re‑use existing API if it already exists).
For example, in `@dockstat/auth/client`:
```ts
// auth/client.ts
export function useAuth() {
// ...
const handleAuthCallback = async (params: URLSearchParams) => {
const token = params.get("token");
if (!token) throw new Error("Missing token");
// existing shared logic:
// - decode token
// - extract user
// - write to localStorage
// - manage auth_redirect (if still needed) or central redirect
};
return { login, handleAuthCallback, /* ... */ };
}
```
Then simplify `AuthCallback` to a pure UI shell that delegates:
```tsx
// AuthCallbackPage.tsx
import { useAuth } from "@dockstat/auth/client";
import { useEffect, useState } from "react";
import { useNavigate, useSearchParams } from "react-router";
export function AuthCallbackPage() {
const { handleAuthCallback } = useAuth();
const [searchParams] = useSearchParams();
const navigate = useNavigate();
const [error, setError] = useState<string | null>(null);
useEffect(() => {
(async () => {
try {
await handleAuthCallback(searchParams);
navigate("/"); // or let handleAuthCallback decide redirect
} catch (err) {
console.error("Auth callback error:", err);
setError("Failed to process authentication. Please try again.");
}
})();
}, [searchParams, handleAuthCallback, navigate]);
// keep your existing loading/error UI
}
```
This removes the manual JWT parsing and localStorage access from the page and guarantees a single place where token semantics live.
### 2. Centralize redirect handling
Right now, `AuthCallback` uses `auth_redirect` in `localStorage` and `ProtectedRoute` / `AuthProvider` also handle redirects. That’s two redirect systems.
Once `handleAuthCallback` lives in the auth client, you can also centralize redirect there:
```ts
// inside handleAuthCallback
const redirect = localStorage.getItem("auth_redirect") || "/";
localStorage.removeItem("auth_redirect");
navigate(redirect); // or update auth state so ProtectedRoute handles it
```
Then the page no longer knows about `auth_redirect`:
```tsx
// in AuthCallbackPage effect
await handleAuthCallback(searchParams);
// navigate only if the auth client doesn't already do it
```
And the login page (`SignInPage`) can just call `login(providerId)` as you already do, without worrying about redirect concerns.
### 3. Split callback vs sign‑in UI
`AuthCallback` and `SignInPage` are distinct concerns; splitting them will make each file easier to reason about and keeps this file closer to a pure UI layer.
```tsx
// src/pages/auth/AuthCallbackPage.tsx
export function AuthCallbackPage() {
// callback-only logic + UI
}
// src/pages/auth/SignInPage.tsx
export function SignInPage() {
// your existing sign-in UI only
}
export default SignInPage;
```
Then update your routes to use the new `AuthCallbackPage` component for the callback path.
This keeps all existing behavior (login providers list, callback flow, redirects) but pushes the complex/auth‑specific logic into the shared auth client, leaving these pages as thin UI wrappers.
</issue_to_address>
### Comment 7
<location path="apps/dockstat/src/hooks/useAuth.ts" line_range="11" />
<code_context>
+ [key: string]: unknown
+}
+
+export function useAuth() {
+ const [user, setUser] = useState<User | null>(null)
+ const [loading, setLoading] = useState(true)
</code_context>
<issue_to_address>
**issue (complexity):** Consider removing this custom `useAuth` implementation and instead re-exporting or thinly wrapping the existing shared auth hook to avoid duplicating logic.
You can reduce complexity here by removing this custom `useAuth` and delegating to the existing context-based auth API instead of reimplementing it.
Since `@dockstat/auth/client` already exposes a canonical `useAuth`, you can:
1. **Delete this local hook** and
2. **Re-export** the shared hook (or adapt it with a thin wrapper if the call sites depend on this exact shape).
For example, instead of:
```ts
// src/hooks/useAuth.ts
import { useEffect, useState } from "react"
interface User {
sub: string
email?: string
name?: string
picture?: string
[key: string]: unknown
}
export function useAuth() {
const [user, setUser] = useState<User | null>(null)
const [loading, setLoading] = useState(true)
const [error] = useState<string | null>(null)
// ... localStorage, login/logout redirects ...
return { error, loading, login, logout, user }
}
```
use a re-export:
```ts
// src/hooks/useAuth.ts
export { useAuth } from "@dockstat/auth/client"
// or from "packages/auth/src/client/useAuth"
```
If consumers expect specific properties (e.g. `user`, `loading`, `error`, `login`, `logout`), keep a very thin adapter that simply forwards to the shared hook:
```ts
// src/hooks/useAuth.ts
import { useAuth as useSharedAuth } from "@dockstat/auth/client"
export function useAuth() {
const { user, isLoading, error, login, logout } = useSharedAuth()
return {
user,
loading: isLoading,
error,
login,
logout,
}
}
```
This keeps behavior aligned with `AuthProvider` (tokens, refresh, redirects) while preserving the existing call sites’ shape, avoiding two divergent auth flows.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
- Add `cryptr` dependency to encrypt/decrypt OAuth client secrets. - Update auth routes and config service to handle secret transformation. - Introduce `CRYPTO_SECRET` environment variable for encryption key management. - Refactor `authenticated` middleware type hint. - Replace manual SVG with `ArrowRight` icon in `SignIn` page.
Reordered imports in config.ts and routes.ts to maintain consistency and updated type casting syntax in routes.ts for improved readability.
There was a problem hiding this comment.
Actionable comments posted: 11
Note
Due to the large number of review comments, Critical severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/logger/src/utils.ts (1)
70-77:⚠️ Potential issue | 🟡 MinorValidate ID length after splitting
id|from, not before.Line 70 applies the minimum-length rule to the raw combined string. Cases like
"a|proxy"still pass and produce a 1-char request ID. Apply the length check after extracting the actual ID segment.🔧 Proposed fix
export function colorByReqID(rawReqId: string) { - let reqId = rawReqId.length >= 3 ? rawReqId : "" + let reqId = rawReqId let from = "" if (reqId.includes("|")) { const parts = reqId.split("|") reqId = String(parts[0]) from = String(parts[1]) } + reqId = reqId.length >= 3 ? reqId : "" const hash = stringToHash(reqId)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/logger/src/utils.ts` around lines 70 - 77, The current logic applies the minimum-length check to rawReqId before splitting, allowing short IDs like "a|proxy"; change the flow in the function handling rawReqId so you first split rawReqId on "|" when present (use reqId, from variables), then validate the length of the extracted request ID segment (reqId) and only keep it if it meets the minimum-length requirement; update any assignments that previously set reqId = "" based on rawReqId to instead re-evaluate after the split so the actual ID is validated.
🟠 Major comments (18)
apps/dockstat/src/pages/settings.tsx-49-49 (1)
49-49:⚠️ Potential issue | 🟠 MajorRestore actionable Accounts content or remove the Accounts slide for now.
Line 49 currently renders a static placeholder (
<div>Accounts</div>), which leaves users at a dead-end while the UI still advertises account management (Line 30). This is a user-facing regression; either wire a minimal functional Accounts panel or temporarily remove the Accounts slide/description until it’s ready.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/dockstat/src/pages/settings.tsx` at line 49, The "Accounts" slide currently renders a static placeholder ("Accounts" key with <div>Accounts</div>), leaving a dead-end; either remove this slide/entry from the settings slides/descriptions array or replace the placeholder with a minimal actionable Accounts panel: render current account state (e.g., "No accounts connected" or list of connected accounts), add a "Connect account" and optionally "Disconnect" button, and wire those buttons to your existing account handlers (e.g., connectAccount / disconnectAccount or handleConnectAccount / handleDisconnectAccount) so the UI actually performs actions rather than showing a static div.packages/auth/tsconfig.json-24-25 (1)
24-25:⚠️ Potential issue | 🟠 MajorReconsider disabling
strictNullChecksin auth-critical code.Setting
strictNullChecks: falsewhilestrict: trueis enabled explicitly overrides one of the most valuable strict checks. This is particularly concerning in authentication code where null/undefined values (e.g., missing tokens, uninitialized user state) can lead to security vulnerabilities or runtime errors.Consider fixing the underlying type issues instead of disabling the check. If there are specific areas causing friction, you could use explicit type assertions or optional chaining locally rather than globally weakening type safety.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/auth/tsconfig.json` around lines 24 - 25, The tsconfig overrides disable a key safety check—remove the "strictNullChecks": false setting so "strict": true enforces null/undefined checks; then fix the resulting type errors in auth-related modules (e.g., authentication token parsing, user state initialization, and functions handling optional values) by adding precise types, optional chaining, non-null assertions only where proven safe, or explicit type guards, targeting the files that fail compilation after re-enabling strictNullChecks and updating functions that handle tokens/user to return properly typed values instead of allowing null/undefined.apps/dockstat/src/providers/index.tsx-11-11 (1)
11-11:⚠️ Potential issue | 🟠 MajorHardcoded localhost URL will break in non-development environments.
The
apiBaseis hardcoded tohttp://localhost:3030/api/v2, which will fail in staging, production, or any non-local deployment. This should be configurable via an environment variable.🔧 Proposed fix
- <AuthProvider apiBase="http://localhost:3030/api/v2"> + <AuthProvider apiBase={import.meta.env.VITE_API_BASE_URL || "http://localhost:3030/api/v2"}>Ensure
VITE_API_BASE_URL(or equivalent) is set in your environment configuration for each deployment target.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/dockstat/src/providers/index.tsx` at line 11, Replace the hardcoded apiBase on AuthProvider with a configurable env var: read VITE_API_BASE_URL (or a fallback like process.env.API_BASE_URL) and pass that value into the AuthProvider apiBase prop instead of "http://localhost:3030/api/v2"; update any initialization code that references AuthProvider to use the resolved env value and ensure VITE_API_BASE_URL is documented for each deployment environment.apps/dockstat/src/lib/api.ts-4-8 (1)
4-8:⚠️ Potential issue | 🟠 MajorRemove token logging - security risk.
Logging the JWT token to the console exposes sensitive credentials in browser dev tools. This should be removed before production.
🔒 Proposed fix
const getHeaders = () => { const token = localStorage.getItem("auth_token") - console.log(`Found auth token: ${token}`) return token ? { authorization: `Bearer ${token}` } : {} }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/dockstat/src/lib/api.ts` around lines 4 - 8, The getHeaders function currently logs the raw JWT from localStorage (localStorage.getItem("auth_token")) which is a security risk; remove the console.log call so the token is never printed, and ensure getHeaders still returns the same header shape (authorization: `Bearer ${token}`) when token exists; update only the getHeaders function to eliminate any logging of the token or sensitive data.apps/api/src/index.ts-26-26 (1)
26-26:⚠️ Potential issue | 🟠 MajorRequest logging only covers authenticated requests.
RequestLoggeris inside the authentication guard, so auth failures and requests to unguarded endpoints (likeAuthHandler.routes) won't be logged. This creates observability gaps for debugging authentication issues and monitoring all API traffic.Consider moving
RequestLoggerbefore the guard to capture all requests.♻️ Proposed fix
export const DockStatAPI = new Elysia({ precompile: false, prefix: "/api/v2" }) .use(Middleware) .use(DockStatElysiaPlugins) + .use(RequestLogger) .guard(authenticated(), (app) => { return app - .use(RequestLogger) .use(MetricsMiddleware)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/api/src/index.ts` at line 26, Request logging is currently registered after the authentication guard so unauthenticated requests and routes like AuthHandler.routes aren't logged; move the RequestLogger middleware registration so it is applied before the authentication guard is mounted (i.e., register .use(RequestLogger) prior to the code that attaches the auth guard/middleware and before mounting AuthHandler.routes) to ensure all incoming requests are captured.apps/dockstat/src/hooks/useAuth.ts-40-47 (1)
40-47:⚠️ Potential issue | 🟠 Major
logout()may construct invalid URL whenproviderIdis null.If
auth_provider_idwas never stored in localStorage,providerIdwill benull, resulting in a request to/auth/null/logoutwhich will fail. Add a guard or fallback handling.🐛 Proposed fix
const logout = () => { localStorage.removeItem("user") const providerId = localStorage.getItem("auth_provider_id") localStorage.removeItem("auth_provider_id") - const loc = window.location.href setUser(null) - window.location.href = `${API_BASE}/auth/${providerId}/logout?redirectUri=${loc}` + if (providerId) { + const loc = encodeURIComponent(window.location.href) + window.location.href = `${API_BASE}/auth/${providerId}/logout?redirectUri=${loc}` + } else { + // Fallback: just redirect to login if no provider known + window.location.href = "/login" + } }Note: The
redirectUrishould also be URL-encoded to handle special characters.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/dockstat/src/hooks/useAuth.ts` around lines 40 - 47, logout currently reads auth_provider_id from localStorage and builds window.location.href using `${API_BASE}/auth/${providerId}/logout?redirectUri=${loc}`, which can produce `/auth/null/logout` if providerId is missing and may break with unencoded redirectUri; update the logout function to check providerId (from localStorage.getItem("auth_provider_id")) and if missing either use a safe default provider id or perform a local sign-out flow (remove user, setUser(null) and navigate to a generic post-logout route) instead of calling the API with "null", and ensure the redirectUri (loc) is URL-encoded (encodeURIComponent) before appending; keep the removals of "user" and "auth_provider_id" and use API_BASE and setUser as currently referenced.packages/auth/src/index.ts-28-28 (1)
28-28:⚠️ Potential issue | 🟠 Major
logout_urlconstraint mismatch with API schema.The column is defined as
notNull: true, but the route inpackages/auth/src/routes.tsdefineslogout_url: t.MaybeEmpty(t.String()), allowing empty/null values. This will cause database INSERT failures when clients don't provide alogout_url.Either make the column nullable or require
logout_urlin the API schema.🐛 Option 1: Make column nullable (if logout_url is optional)
- logout_url: column.text({ notNull: true }), + logout_url: column.text({ notNull: false }),🐛 Option 2: Require logout_url in API (in routes.ts)
- logout_url: t.MaybeEmpty(t.String()), + logout_url: t.String(),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/auth/src/index.ts` at line 28, The DB column definition for logout_url in packages/auth/src/index.ts is declared with notNull: true but the API schema in packages/auth/src/routes.ts uses logout_url: t.MaybeEmpty(t.String()), causing a mismatch; fix by either making the column nullable (change logout_url: column.text({ notNull: true }) to logout_url: column.text() or column.text({ notNull: false })) so empty/null values can be persisted, or update the route/type to require the field (change logout_url: t.MaybeEmpty(t.String()) to logout_url: t.String() or appropriate non-empty validator) so the API always supplies a value—pick one approach and apply it consistently in the logout_url declaration in index.ts and the corresponding route/schema in routes.ts.packages/auth/src/routes.ts-19-22 (1)
19-22:⚠️ Potential issue | 🟠 MajorNormalize
logout_urlthe same way asscopes.
logout_urlis optional here, but empty strings are passed through unchanged. The logout route later treats any non-nullvalue as a URL, so a provider saved withlogout_url: ""will fail onnew URL(logoutUrl).Also applies to: 231-233
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/auth/src/routes.ts` around lines 19 - 22, Normalize the optional logout_url the same way scopes are normalized: when calling table.insertAndGet (and the other create/update block that mirrors lines 231-233), coerce logout_url to null if it's an empty string instead of passing the empty string through; i.e., check (body as { logout_url: string }).logout_url and set logout_url to that value or null, similar to how scopes is handled, so the downstream logout route's new URL(logoutUrl) call never receives an empty string.packages/auth/README.md-69-80 (1)
69-80:⚠️ Potential issue | 🟠 MajorThe schema docs no longer match the implementation.
The README describes an
oidc-providerstable withlogout_urlrequired, butpackages/auth/src/index.tscreates aproviderstable andlogout_urlis nullable. These setup instructions will send migrations in the wrong direction.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/auth/README.md` around lines 69 - 80, The README schema is out of sync with the code: the implementation in packages/auth/src/index.ts creates a table named providers and makes logout_url nullable, while README documents an oidc-providers table with logout_url REQUIRED; update packages/auth/README.md to match the actual implementation by changing the table name to "providers" and marking logout_url as nullable (remove NOT NULL), and ensure other field names/types (id, issuer_url, client_id, client_secret, scopes, created_at) match the definitions used in packages/auth/src/index.ts so migrations/instructions remain correct.packages/auth/src/middleware.ts-217-239 (1)
217-239:⚠️ Potential issue | 🟠 Major
createAuthenticatedWsHandler()rejects every socket by default.The implementation only reads a token when
options.extractTokenis supplied, so callingcreateAuthenticatedWsHandler()without arguments always ends inverifyWsToken(null)and a1008close.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/auth/src/middleware.ts` around lines 217 - 239, The createAuthenticatedWsHandler implementation currently only calls options.extractToken and passes null to verifyWsToken when no extractor is provided, causing every socket to be rejected; update createAuthenticatedWsHandler so it falls back to a default extractor (e.g., call a local default function or existing extractor like extractTokenFromWs) when options.extractToken is undefined, assign its result to token before calling verifyWsToken(token), and keep the rest of the flow (closing with 1008 if verifyWsToken returns falsy) unchanged so unauthenticated sockets are only rejected when no valid token is found.apps/dockstat/src/pages/SignIn.tsx-262-284 (1)
262-284:⚠️ Potential issue | 🟠 MajorDon't let one bad
issuer_urlcrash the whole sign-in page.
getProviderInfo()already tolerates invalid URLs, but the card body callsnew URL(provider.issuer_url)unguarded. A single malformed provider record will take down the entire provider grid.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/dockstat/src/pages/SignIn.tsx` around lines 262 - 284, The provider card currently calls new URL(provider.issuer_url) unguarded which can throw for malformed issuer_url and crash the page; update the provider rendering (inside the filteredProviders.map callback that uses getProviderInfo(provider.issuer_url) and handleLogin(provider.id)) to safely derive the hostname by either validating/try-catch wrapping new URL(provider.issuer_url) or using a helper (e.g., safeHostnameFromUrl) that returns a fallback (empty string or provider.issuer_url) when parsing fails, and render that fallback in the <p> instead of calling new URL(...) directly so a single bad provider record cannot break the whole grid.packages/auth/src/client/AuthProvider.tsx-212-216 (1)
212-216:⚠️ Potential issue | 🟠 MajorDecode the callback token as base64url, not plain base64.
atob(parts[1])fails on valid JWT payload segments containing-,_, or omitted padding. Normalize and pad the segment before parsing so legitimate callbacks don't get rejected.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/auth/src/client/AuthProvider.tsx` around lines 212 - 216, The JWT payload is being decoded with atob on a base64url segment which fails for '-' '_' and missing padding; in AuthProvider.tsx where token is split and parts[1] is parsed (see variables token, parts, payload, user/User), normalize the middle segment from base64url to base64 (replace '-'→'+' and '_'→'/'), add '=' padding until length % 4 == 0, then atob the normalized string and JSON.parse the result to obtain payload.user; update that decode logic accordingly so valid JWTs with base64url encoding are accepted.packages/auth/src/client/AuthProvider.tsx-139-171 (1)
139-171:⚠️ Potential issue | 🟠 MajorThis refresh loop will log users out after ~4 minutes.
The client polls
${apiBase}/auth/verify, but this PR's auth router does not define that endpoint, so every interval becomes a non-OK response that falls intologout(). It also assumes a 5-minute token lifetime whilecreateAuthToken()now issues1dJWTs.Also applies to: 193-199
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/auth/src/client/AuthProvider.tsx` around lines 139 - 171, The token-check loop in AuthProvider.tsx calls a non-existent `${apiBase}/auth/verify` every interval causing false non-OK responses and logout; update the logic in the token-validation/refresh routine to (1) stop polling a hardcoded non-existent endpoint—either call an existing endpoint (e.g. `/auth/me` or your real refresh route) instead of `/auth/verify`, or remove the fetch entirely and decode the current JWT (use a jwt-decode helper) to read exp and schedule refresh only when near expiry, (2) only treat 401 responses as token-expired (do not logout on 404/other errors), and (3) make the poll/refresh interval derive from the token TTL (from createAuthToken() / exp) or make it configurable rather than assuming 5 minutes; touch the functions/blocks around AuthProvider, the token validation/fetch call, onTokenExpired, and logout to implement these changes.packages/auth/README.md-11-18 (1)
11-18:⚠️ Potential issue | 🟠 MajorThe documented JWT lifetime is stale.
This README still says tokens expire in 5 minutes and should be refreshed every 4 minutes, but
packages/auth/src/utils/jwt.tsnow issues1dtokens. That mismatch will push integrators toward an unnecessary refresh loop.Also applies to: 158-163, 443-447
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/auth/README.md` around lines 11 - 18, Update the README to match the actual token lifetime emitted by the code: replace the stale "5 minutes / refresh every 4 minutes" wording with the current "1d" expiry and remove the unnecessary refresh recommendation; specifically update all occurrences called out in the comment and any other mentions; cross-check against the token issuance in src/utils/jwt.ts (the function that signs/creates JWTs, e.g., signJwt/generateToken) which sets the TTL to "1d" so the README wording and examples reflect that exact value.packages/auth/src/client/AuthProvider.tsx-114-117 (1)
114-117:⚠️ Potential issue | 🟠 MajorURL-encode
redirectUribefore concatenating it.
window.location.hrefoften contains its own query string. Building/logout?redirectUri=${currentLocation}will truncate or corrupt the redirect target at the first&or#.Possible fix
- const logoutUrl = `${apiBase}/auth/${providerId}/logout?redirectUri=${currentLocation}` + const logoutUrl = `${apiBase}/auth/${providerId}/logout?redirectUri=${encodeURIComponent(currentLocation)}`🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/auth/src/client/AuthProvider.tsx` around lines 114 - 117, The logout URL is built by concatenating currentLocation directly into logoutUrl which can break if the location contains query params or fragments; update the code that constructs logoutUrl (the block using providerId, logoutUrl, and currentLocation in AuthProvider.tsx—likely inside the logout handler) to URL-encode the redirect target using encodeURIComponent(currentLocation) before concatenation (i.e., use ?redirectUri=${encodeURIComponent(currentLocation)}) so the redirectUri query parameter is safe.apps/dockstat/src/pages/SignIn.tsx-67-74 (1)
67-74:⚠️ Potential issue | 🟠 MajorHandle JWT payloads as base64url before decoding.
atob()expects padded base64, but JWT segments are base64url and often omit padding. This will throw for valid tokens from some providers and break the callback flow.Possible fix
- const base64Url = token.split(".")[1] - const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/") + const base64Url = token.split(".")[1] + const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/") + const paddedBase64 = base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), "=") const jsonPayload = decodeURIComponent( - atob(base64) + atob(paddedBase64) .split("") .map((c) => `%${(`00${c.charCodeAt(0).toString(16)}`).slice(-2)}`) .join("") )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/dockstat/src/pages/SignIn.tsx` around lines 67 - 74, The JWT payload decoding assumes standard base64 and may fail for base64url without padding; update the SignIn.tsx decoding flow that computes base64Url/base64/jsonPayload (where token is the JWT) to first convert the base64url segment to base64 by replacing "-"->"+" and "_"->"/" and then add the required "=" padding to make the length a multiple of 4 before calling atob; ensure the padded base64 is what you pass into atob so valid tokens from all providers decode correctly.apps/dockstat/src/pages/SignIn.tsx-159-173 (1)
159-173:⚠️ Potential issue | 🟠 MajorClear the stale error state before retrying providers.
After the first failure,
errorstays truthy forever. A later successfulapi.auth.providers.get()call updatesproviders, but the page keeps rendering the error card because success never resetserror.Possible fix
const fetchProviders = async () => { try { + setError(null) setProvidersLoading(true) const response = await api.auth.providers.get() if (response.status === 200 && response.data) { setProviders(response.data) + setError(null) } else { setError("Failed to load authentication providers") }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/dockstat/src/pages/SignIn.tsx` around lines 159 - 173, In fetchProviders, clear the previous error before attempting the network call so a later successful response removes the error UI: call setError(null) (or setError("")) right before or immediately after setProvidersLoading(true) in the fetchProviders function so on success the providers UI renders without the stale error; ensure you reference the existing state setters setError and setProviders in fetchProviders and do not remove the existing try/catch/finally logic.packages/auth/src/routes.ts-69-76 (1)
69-76:⚠️ Potential issue | 🟠 MajorUse
scope, notscopes, in the authorization request parameters.The
buildAuthorizationUrl()function expects the standard OAuth 2.0 authorization parameterscope(singular) as a space-delimited string, per OpenID Connect specification. The code currently passesscopesas a key, which causes the scope parameter to be omitted from the authorization URL sent to the provider. This can result in providers rejecting the request or failing to include requested claims (such asopenid) in the token response.Change line 74 from
scopes,toscope: scopes,(or rename the variable if needed).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/auth/src/routes.ts` around lines 69 - 76, The params object for the authorization request is using the wrong key: change the parameter key from "scopes" to "scope" so the authorization URL includes the space-delimited scope string expected by buildAuthorizationUrl; update the params declaration (the object containing code_challenge, code_challenge_method, nonce, redirect_uri, scopes, state) to use scope: scopes (or rename the variable from scopes to scope and pass scope) so buildAuthorizationUrl receives the proper OAuth/OpenID Connect parameter.
🟡 Minor comments (7)
packages/auth/src/client/protectedRoute.tsx-22-30 (1)
22-30:⚠️ Potential issue | 🟡 MinorQuery parameters and hash are not preserved on redirect.
Only
window.location.pathnameis stored. If a user accesses/settings?tab=security#advancedwhile unauthenticated, they'll be redirected to just/settingsafter login, losing the?tab=security#advancedportion.🔧 Proposed fix
if (!isAuthenticated || !user) { // Save current location for post-login redirect - localStorage.setItem("auth_redirect", window.location.pathname) + localStorage.setItem("auth_redirect", window.location.pathname + window.location.search + window.location.hash) return (🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/auth/src/client/protectedRoute.tsx` around lines 22 - 30, The redirect currently saves only window.location.pathname so query string and hash are dropped; update the logic in the ProtectedRoute (where isAuthenticated/user are checked and localStorage.setItem("auth_redirect", ... ) is called) to save the full relative URL by concatenating window.location.pathname + window.location.search + window.location.hash (or otherwise constructing the full path) so users are redirected back including query parameters and fragment after login.packages/ui/src/components/Sidebar/Sidebar.tsx-148-155 (1)
148-155:⚠️ Potential issue | 🟡 MinorAdd
aria-labelfor accessibility.The logout button only contains an icon without any text or accessible label. Screen reader users won't know what this button does.
♿ Proposed fix
<Button + aria-label="Logout" className="h-8 w-8 p-0 mr-2" onClick={auth.logout} size="sm" variant="ghost" > <LogOut size={16} /> </Button>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/ui/src/components/Sidebar/Sidebar.tsx` around lines 148 - 155, The logout Button in Sidebar (the Button with onClick={auth.logout} that renders the <LogOut /> icon) lacks an accessible name; add an aria-label prop (e.g. aria-label="Log out" or an i18n string) to the Button so screen readers announce its purpose; update the Button component where <LogOut /> is rendered to include aria-label and ensure it remains visually unchanged.apps/dockstat/src/contexts/queryClient.ts-9-12 (1)
9-12:⚠️ Potential issue | 🟡 MinorMisleading comment contradicts the actual configuration.
The comment states "Don't refetch on window focus, mount, or reconnect" but
refetchOnMountandrefetchOnReconnectare both set totrue. Update the comment to accurately reflect the behavior.📝 Proposed fix
refetchOnMount: true, refetchOnReconnect: true, - // Don't refetch on window focus, mount, or reconnect - only when explicitly invalidated + // Don't refetch on window focus - refetch on mount and reconnect refetchOnWindowFocus: false,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/dockstat/src/contexts/queryClient.ts` around lines 9 - 12, The inline comment incorrectly states "Don't refetch on window focus, mount, or reconnect" while the config sets refetchOnMount: true and refetchOnReconnect: true; update the comment above refetchOnWindowFocus/refetchOnMount/refetchOnReconnect to accurately describe current behavior (e.g., "Refetch on mount and reconnect, but not on window focus") or change the boolean flags to match the original intent—modify the comment to reference refetchOnMount, refetchOnReconnect, and refetchOnWindowFocus so it clearly reflects the actual configuration.apps/dockstat/src/lib/protectedRoute.tsx-17-39 (1)
17-39:⚠️ Potential issue | 🟡 MinorMissing
keyprops on dynamically rendered Route elements.React requires unique
keyprops when rendering arrays of elements. BothprotectedRoutesandroutesmaps are missing keys, which will cause React warnings and may lead to unexpected re-rendering behavior.💡 Proposed fix
{(protectedRoutes ?? []).map((r) => { return ( <Route + key={r.path} element={ <PRoute loadingComponent={r.loadingComponent} redirectTo={"/login"} > {r.element} </PRoute> } path={r.path} /> ) })} {(routes ?? []).map((r) => { return ( <Route + key={r.path} element={r.element} path={r.path} /> ) })}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/dockstat/src/lib/protectedRoute.tsx` around lines 17 - 39, The two array renders mapping over protectedRoutes and routes are missing React key props on the Route elements; update both map callbacks that return <Route ... /> to include a unique key (e.g., key={r.path} or key={r.key} and fall back to the array index if necessary) so each Route rendered by the protectedRoute component receives a stable unique key; apply this to the Route inside the protectedRoutes map (the one wrapping PRoute) and the Route inside the routes map.packages/auth/README.md-245-253 (1)
245-253:⚠️ Potential issue | 🟡 MinorSeveral examples won't render or compile as written.
The guard example has invalid chaining syntax, the legacy section opens nested fences, and the callback section starts with a doubled code fence. Those issues are already surfacing in markdownlint and make the examples unsafe to copy-paste.
Also applies to: 450-460, 557-565
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/auth/README.md` around lines 245 - 253, The README examples contain invalid chaining and malformed fenced-code blocks; fix the guard snippet to use valid callback chaining by calling guard(authenticated(), (app) => app.get("/protected", ({ user }) => `Hello, ${user.name}!`)) (referencing Elysia, createAuthMiddleware, guard, authenticated and get), close and not nest triple-backtick fences in the legacy section, and remove the doubled code-fence at the start of the callback section so all fenced blocks open and close exactly once; apply the same cleanup to the other occurrences noted (lines ~450-460 and ~557-565).apps/dockstat/src/pages/SignIn.tsx-288-293 (1)
288-293:⚠️ Potential issue | 🟡 MinorAdd an accessible name or mark the arrow SVG decorative.
This is the current Biome a11y failure. If the icon is decorative, add
aria-hidden="true"; otherwise provide a<title>.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/dockstat/src/pages/SignIn.tsx` around lines 288 - 293, The SVG inside the SignIn component is missing an accessible name; either mark it decorative by adding aria-hidden="true" to the <svg> element (the one with className "flex-shrink-0 w-5 h-5 text-secondary-text") or provide an accessible name by adding a <title> element inside the same <svg> and ensure the svg has role="img" and aria-labelledby referencing that title; update the SVG accordingly in SignIn.tsx.packages/auth/src/middleware.ts-91-99 (1)
91-99:⚠️ Potential issue | 🟡 MinorReplace the
anytype with the explicitAuthContextshape.The
anytype at line 98 is a lint blocker and loses type safety. TheAuthContextinterface is already defined in this file (lines 16-19) with the requiredisAuthenticatedanduserproperties. Type the context parameter asAuthContext & { set: any }or define a more specific type that includes Elysia'ssetproperty to restore type safety for destructured values.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/auth/src/middleware.ts` around lines 91 - 99, Replace the loose any in the authenticated middleware's beforeHandle parameter with the explicit AuthContext shape: change the parameter type used in beforeHandle to AuthContext & { set: any } (or create a small interface that extends AuthContext with the set method) so the destructured { isAuthenticated, set } uses the defined AuthContext types; update the beforeHandle signature in the authenticated function accordingly and import/ reference the existing AuthContext interface defined earlier in this file.
🧹 Nitpick comments (9)
packages/auth/package.json (1)
21-25: Consider addingreacttopeerDependenciesMetaas optional.The
./clientexport uses React components (AuthProvider, hooks), butreactisn't listed inpeerDependenciesor marked as optional. If a server-only consumer imports from the root.export, they shouldn't need React installed. Consider adding React as an optional peer dependency for clarity.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/auth/package.json` around lines 21 - 25, The package.json should mark React as an optional peer so server-only consumers importing the root export don't need React installed; add an entry for "react": { "optional": true } under "peerDependenciesMeta" in the package.json and ensure this aligns with the fact that the ./client export exposes React items like AuthProvider and hooks (so only clients will require React). Update the peerDependenciesMeta section to include "react" as optional and verify the consumer-facing ./client exports remain unchanged.apps/dockstat/tsconfig.json (1)
14-14: Consider adopting the two-pattern approach for consistency withapps/api/tsconfig.json.All imports of
@dockstat/authin the app use subpath imports (@dockstat/auth/client), so the current wildcard pattern works correctly. However, for consistency withapps/api/tsconfig.jsonand to support potential bare imports in the future, consider splitting the pattern:♻️ Suggested pattern
"paths": { "@/*": ["./src/*"], "@dockstat/api": ["../api/src/*"], - "@dockstat/auth": ["../../packages/auth/src/*"], + "@dockstat/auth": ["../../packages/auth/src/index.ts"], + "@dockstat/auth/*": ["../../packages/auth/src/*"], "@WSS": ["./src/lib/websocketEffects/index.ts"],🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/dockstat/tsconfig.json` at line 14, Update the tsconfig path mapping for the "@dockstat/auth" entry to use the two-pattern approach: keep the existing wildcard mapping for subpath imports and add an additional pattern that points to the package root to support bare imports; modify the "@dockstat/auth" paths in apps/dockstat/tsconfig.json so both the client subpath (e.g., client/*) and the package root are covered (mirror the approach used in apps/api/tsconfig.json).apps/api/src/elysia-plugins.ts (1)
8-10: Consider explicitly setting CORS origin for clarity.Line 9 enables credentialed CORS. The
@elysiajs/corslibrary by default reflects the request Origin header whencredentials: true, which safely supports cross-origin requests. However, explicitly setting the origin improves clarity and avoids relying on default behavior.Suggested approach (optional)
.use( cors({ credentials: true, + origin: Bun.env.FRONTEND_URL ?? "http://localhost:3000", }) )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/api/src/elysia-plugins.ts` around lines 8 - 10, The cors plugin call currently only sets credentials: true (cors({ credentials: true })) which relies on default origin reflection; update the cors configuration to explicitly set the allowed origin(s) instead of relying on the default—e.g., provide a static origin string or a origin-check function that reads allowed origins from env/config and returns the request Origin when allowed; modify the cors(...) invocation in elysia-plugins.ts to include this explicit origin setting alongside credentials to make allowed origins deterministic and clear.apps/dockstat/src/router.tsx (1)
23-24: Inconsistent path naming:/clientsvs/client/configure.The clients index route uses plural
/clientsbut the configure route uses singular/client/configure. Consider aligning these for consistency and predictable URL structure.♻️ Suggested fix
{ element: <ClientsPage />, path: "/clients" }, - { element: <ConfigureClientsPage />, path: "/client/configure" }, + { element: <ConfigureClientsPage />, path: "/clients/configure" },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/dockstat/src/router.tsx` around lines 23 - 24, Route paths are inconsistent: the index route uses plural "/clients" while ConfigureClientsPage is mounted at singular "/client/configure"; update the route definition for ConfigureClientsPage to use a consistent plural path (e.g., "/clients/configure") so URLs align with ClientsPage, changing the path string in the router where ConfigureClientsPage is referenced.packages/auth/src/utils/jwt.ts (1)
4-11: Consider typinguserInfoparameter.While the
biome-ignoreis acceptable for flexibility, consider defining at minimum an interface for the expected user properties (e.g.,sub,name) to improve maintainability and catch integration issues early.💡 Suggested improvement
+interface UserInfo { + sub?: string + email?: string + name?: string + [key: string]: unknown +} + -// biome-ignore lint/suspicious/noExplicitAny: a -export async function createAuthToken(userInfo: any): Promise<string> { +export async function createAuthToken(userInfo: UserInfo): Promise<string> {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/auth/src/utils/jwt.ts` around lines 4 - 11, Define a minimal typed interface for the expected user payload (e.g., AuthUser with properties like sub: string, email?: string, name?: string) and change the createAuthToken signature from userInfo: any to userInfo: AuthUser; update usages of SignJWT/SignJWT payload typing if applicable so the token payload is strongly typed (referencing createAuthToken, SignJWT and JWT_SECRET to locate the code) to improve type safety and maintainability.apps/api/src/routes/plugins/index.ts (1)
69-69: Unnecessary type cast bypasses validation.The cast
body as DBPluginShemaTis redundant ifPluginModel.installPluginBodyalready matchesDBPluginShemaT. If they differ, this cast hides a potential runtime mismatch between the validated body and whatsavePluginexpects. Consider aligning the body schema withDBPluginShemadirectly or removing the cast.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/api/src/routes/plugins/index.ts` at line 69, The handler for the POST /install route is casting the request body to DBPluginShemaT which bypasses validation; update the route to pass the validated body type instead of forcing a cast: either remove the cast and pass body directly to PluginHandler.savePlugin if PluginModel.installPluginBody already produces DBPluginShemaT, or adjust PluginModel.installPluginBody to match DBPluginShemaT and then pass the typed result; ensure the call to PluginHandler.savePlugin uses the actual validated object (refer to .post("/install", ({ body }) => PluginHandler.savePlugin(...)), PluginModel.installPluginBody, and DBPluginShemaT).apps/dockstat/src/hooks/useAuth.ts (1)
14-14: Unusederrorstate.The
errorstate is declared butsetErroris never called anywhere in the hook. Either remove the unused state or implement error handling for failed operations.♻️ Remove unused state or implement error handling
- const [error] = useState<string | null>(null) + const [error, setError] = useState<string | null>(null) + + // Then use setError in try/catch blocks, e.g.: + // } catch (e) { + // setError(e instanceof Error ? e.message : "Authentication failed") + // }Or simply remove if not needed:
- const [error] = useState<string | null>(null) ... - return { error, loading, login, logout, user } + return { loading, login, logout, user }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/dockstat/src/hooks/useAuth.ts` at line 14, The hook declares an unused state "error" (const [error] = useState<string | null>(null)) inside useAuth but never updates it via a setError, so either remove the unused state or add proper error handling: either delete the declaration entirely to avoid dead state, or change it to const [error, setError] = useState<string | null>(null) and call setError(...) in catch blocks or failure branches inside useAuth (where async ops like login, fetchUser, or token refresh occur) and expose error from the hook return so callers can consume it.packages/auth/src/config.ts (2)
32-43: OIDC configuration cache has no expiration.The
issuerCachestores discovered OIDC configurations indefinitely. OIDC providers may rotate keys or update endpoints, and stale cached metadata could cause authentication failures. Consider adding a TTL or periodic cache invalidation.♻️ Example: Add TTL to cache entries
- issuerCache = new Map<string, client.Configuration>() + issuerCache = new Map<string, { config: client.Configuration; cachedAt: number }>() + private readonly CACHE_TTL_MS = 60 * 60 * 1000 // 1 hour // In getConfig: - let meta = this.issuerCache.get(row.issuer_url) + const cached = this.issuerCache.get(row.issuer_url) + let meta = cached && (Date.now() - cached.cachedAt < this.CACHE_TTL_MS) + ? cached.config + : undefined // When caching: - this.issuerCache.set(row.issuer_url, meta) + this.issuerCache.set(row.issuer_url, { config: meta, cachedAt: Date.now() })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/auth/src/config.ts` around lines 32 - 43, The issuerCache currently holds OIDC metadata indefinitely, which can become stale; update the caching logic around issuerCache and client.discovery so each cache entry stores a timestamp (or TTL) and meta object, check the age before using cached meta in the block that reads this. If the cached entry is expired, call client.discovery(new URL(row.issuer_url), row.client_id, row.client_secret) again, replace the cache entry (including new timestamp/expiry), and log that discovery was refreshed (you can still log token_endpoint via meta.serverMetadata().token_endpoint); ensure the expiry/TTL value is configurable or set to a sensible default.
25-30: Verbose logging on every config fetch may impact performance and clutter logs.These
INFO-level logs fire on everygetConfig()call, which happens per authentication request. Consider usingDEBUGlevel for the detailed configuration dump, keeping only essential info atINFO.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/auth/src/config.ts` around lines 25 - 30, The current getConfig() implementation logs full OAuth details at INFO every call (lines showing this.logger.info and fields like providerId, BASE_URL, row.issuer_url, row.client_id, row.client_secret, row.scopes); change these verbose calls to use DEBUG level instead (e.g., this.logger.debug) and leave only essential high-level info (if any) at INFO to avoid per-request log noise, keeping the existing redaction of client_secret unchanged; update calls in the function/method that contains getConfig() and any related logger.info invocations for providerId/BASE_URL to use the debug log level.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 2263e21b-1a57-4678-85cd-fcc1cd93e336
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (62)
apps/api/package.jsonapps/api/src/auth.tsapps/api/src/auth/db.tsapps/api/src/auth/index.tsapps/api/src/database/index.tsapps/api/src/elysia-plugins.tsapps/api/src/env.d.tsapps/api/src/index.tsapps/api/src/models/plugins.tsapps/api/src/routes/docker/client.tsapps/api/src/routes/metrics/prometheus.tsapps/api/src/routes/misc/index.tsapps/api/src/routes/plugins/frontend.tsapps/api/src/routes/plugins/index.tsapps/api/src/routes/repositories/index.tsapps/api/tsconfig.jsonapps/dockstat/package.jsonapps/dockstat/src/components/settings/accounts/index.tsxapps/dockstat/src/components/settings/accounts/sections/adminModal.tsxapps/dockstat/src/components/settings/accounts/sections/apiKeys.tsxapps/dockstat/src/components/settings/accounts/sections/oauthProviders.tsxapps/dockstat/src/components/settings/accounts/sections/useAccounts.tsxapps/dockstat/src/components/settings/accounts/sections/users.tsxapps/dockstat/src/contexts/queryClient.tsapps/dockstat/src/hooks/useAuth.tsapps/dockstat/src/layout/index.tsxapps/dockstat/src/lib/api.tsapps/dockstat/src/lib/protectedRoute.tsxapps/dockstat/src/pages/SignIn.tsxapps/dockstat/src/pages/settings.tsxapps/dockstat/src/providers/index.tsxapps/dockstat/src/router.tsxapps/dockstat/tsconfig.jsonpackage.jsonpackages/auth/README.mdpackages/auth/package.jsonpackages/auth/src/client/AuthProvider.tsxpackages/auth/src/client/index.tspackages/auth/src/client/index.tsxpackages/auth/src/client/protectedRoute.tsxpackages/auth/src/client/useAuth.tsxpackages/auth/src/config.tspackages/auth/src/env.d.tspackages/auth/src/index.tspackages/auth/src/middleware.tspackages/auth/src/routes.tspackages/auth/src/server/db/index.tspackages/auth/src/server/db/types.tspackages/auth/src/server/db/utils.tspackages/auth/src/server/handler/index.tspackages/auth/src/server/index.tspackages/auth/src/types.tspackages/auth/src/utils/env.tspackages/auth/src/utils/jwt.tspackages/auth/tsconfig.jsonpackages/logger/src/Logger.tspackages/logger/src/utils.tspackages/sqlite-wrapper/src/index.tspackages/sqlite-wrapper/src/lib/index/createIndex.tspackages/sqlite-wrapper/src/types.tspackages/ui/src/components/Navbar/Navbar.tsxpackages/ui/src/components/Sidebar/Sidebar.tsx
💤 Files with no reviewable changes (14)
- apps/api/src/auth/db.ts
- apps/api/src/database/index.ts
- packages/auth/src/server/handler/index.ts
- packages/auth/src/server/db/index.ts
- packages/auth/src/server/db/utils.ts
- apps/dockstat/src/components/settings/accounts/sections/users.tsx
- packages/auth/src/server/db/types.ts
- apps/dockstat/src/components/settings/accounts/sections/oauthProviders.tsx
- apps/api/src/auth/index.ts
- apps/dockstat/src/components/settings/accounts/sections/adminModal.tsx
- apps/dockstat/src/components/settings/accounts/sections/apiKeys.tsx
- apps/dockstat/src/components/settings/accounts/sections/useAccounts.tsx
- apps/dockstat/src/components/settings/accounts/index.tsx
- apps/api/src/routes/docker/client.ts
| declare module "bun" { | ||
| interface Env { | ||
| BASE_URL: string | ||
| JWT_SECRET: string | ||
| FRONTEND_URL: string | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check how JWT_SECRET is used and if there's startup validation
cat packages/auth/src/utils/env.tsRepository: Its4Nik/DockStat
Length of output: 372
🏁 Script executed:
# Find all files using JWT_SECRET to understand how it's used
rg "JWT_SECRET" --type ts --type js -B 2 -A 2Repository: Its4Nik/DockStat
Length of output: 1580
🏁 Script executed:
# Check for any startup/initialization files that might validate environment variables
fd -e ts -e js | xargs rg "throw|error|validate" | grep -i "env\|secret" | head -20Repository: Its4Nik/DockStat
Length of output: 2086
🏁 Script executed:
# Look for any package.json scripts or configuration that might indicate env validation
cat packages/auth/package.json 2>/dev/null || echo "File not found"Repository: Its4Nik/DockStat
Length of output: 628
🏁 Script executed:
# Check the structure of packages/auth to understand the context better
find packages/auth -type f -name "*.ts" -o -name "*.js" | head -20Repository: Its4Nik/DockStat
Length of output: 290
Type declaration mismatches runtime behavior; JWT_SECRET fallback is a critical security risk.
Two issues:
-
Type/runtime mismatch: These are declared as required
string, butpackages/auth/src/utils/env.tsprovides fallback defaults for all three via the||operator. TypeScript won't warn about missing environment variables at compile time, and the fallbacks silently take effect at runtime. -
Critical security risk: The
JWT_SECRETfallback is"your-secret-key-change-in-production". This hardcoded weak secret is used directly inSignJWT.sign()andjwtVerify(). IfJWT_SECRETis not set in the environment, tokens become trivially forgeable in production. The type declaration falsely suggests this is required.
Fix: Either throw at startup if JWT_SECRET is not set (recommended), or make the type string | undefined and conditionally fail/reject token operations if the proper secret is not available.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/auth/src/env.d.ts` around lines 1 - 7, The Env declaration currently
marks BASE_URL, JWT_SECRET, FRONTEND_URL as required strings but runtime code in
packages/auth/src/utils/env.ts provides fallbacks and a hardcoded JWT_SECRET;
change the contract and runtime to eliminate the unsafe fallback: update the
module "bun" interface Env to make JWT_SECRET string | undefined (and optionally
BASE_URL/FRONTEND_URL as undefined if they also have fallbacks), then modify the
codepaths that use JWT_SECRET (notably where SignJWT.sign and jwtVerify are
called) to explicitly check for presence of process.env.JWT_SECRET and
throw/exit at startup (or reject token operations) with a clear error if it's
missing instead of using "your-secret-key-change-in-production". Ensure all
references to JWT_SECRET are updated to the non-null-checked flow so there are
no implicit fallbacks.
| .post( | ||
| "/providers", | ||
| ({ body }) => | ||
| table.insertAndGet({ | ||
| ...(body as object), | ||
| scopes: (body as { scopes: string }).scopes ? (body as { scopes: string }).scopes : null, | ||
| }), | ||
| { | ||
| body: t.Object({ | ||
| client_id: t.String(), | ||
| client_secret: t.String(), | ||
| issuer_url: t.String(), | ||
| logout_url: t.MaybeEmpty(t.String()), | ||
| scopes: t.MaybeEmpty(t.String()), | ||
| }), | ||
| detail: { | ||
| description: "Create a new OAuth/OIDC provider", | ||
| summary: "Create Provider", | ||
| }, | ||
| } | ||
| ) |
There was a problem hiding this comment.
Protect provider management separately from the public auth flow.
As written, anyone who can reach this router can enumerate providers and register new ones. Because login/callback/logout must stay public, these management endpoints need their own guard or a separate protected router.
Also applies to: 206-215
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/auth/src/routes.ts` around lines 16 - 36, The provider management
endpoints (e.g., the POST handler for "/providers" that calls table.insertAndGet
and the similar routes around lines 206-215) are currently exposed on the public
auth router; move or protect them behind an admin/auth guard by either mounting
them on a separate protected router or wrapping the handlers with an
authorization middleware/check (e.g., requireAdmin or ensureAuthenticated) so
only authorized users can call the provider management handlers; update the
route definitions (the "/providers" POST and the provider-listing endpoints) to
use that guard and return 401/403 when unauthorized.
- Introduce `LocalUsersTable` and implement database storage for local accounts - Add registration, login, and exists check endpoints to the Auth service - Implement password hashing using argon2id and secure token generation - Update encryption utility to support asynchronous operations - Add UI for local login on the SignIn page, conditionally displayed if local users exist - Update AuthHandler to manage user repository and provide routes - Refactor authentication middleware to use compatible schema types
- Add `api-keys` table to `AuthHandler` for storing hashed API keys - Implement API key validation middleware in `packages/auth` - Add support for `Authorization: Api-Key <key>` and `X-API-Key` headers - Add `/api-keys` CRUD routes for key generation, listing, and revocation - Create `SignInPage` and `AuthCallback` pages in `apps/dockstat` - Add UI logic to hide sidebar/navbar on the login route - Update `CommandResult` type definition in `apps/docknode`
…[DS-000] - Remove unused CommandResult import in docknode/index.ts. - Define explicit ApiClient type for the Elysia eden treaty in dockstat/lib/api.ts. - Replace label elements with p tags in SignIn.tsx to resolve hydration/DOM nesting warnings. - Remove unused redirect parameter from the login auth route handler.
- Introduced a modernized, dark-themed login interface with animated background orbs and glassmorphism effects. - Integrated brand logo into the authentication flow. - Refactored layout to handle conditional padding for the login page specifically. - Enhanced the visual feedback for loading, error, and provider-selection states. - Cleaned up redundant comments and improved input field styling.
- Add new `SignInPage` component with `AnimatedIconBackground` and `HeroPanel` - Add `FeaturePill`, `LocalLoginForm`, and `ProviderList` components for modular auth UI - Integrate `react-simple-icons` for provider branding - Remove legacy `apps/dockstat/src/pages/auth/SignIn.tsx` - Add `DOCKSTAT_API_PORT` to API env and update `auth` package env keys to `DOCKSTAT_AUTH_*` - Clean up Dockerfile and minor layout component styles
- Introduce `getAuthHeaders` utility to retrieve and format Authorization headers. - Update `eden` query and mutation hooks to support passing custom `opts` (headers). - Refactor all API service calls and mutation definitions to include auth headers. - Remove redundant/deprecated Eden helper files in favor of unified `opts` support. - Improve error feedback in `AddClient` component. - Fix client secret decryption issue in OIDC config.
…ogging [#DS-001] - Refactor RequestLogger into a factory function to support per-request state tracking. - Add comprehensive error logging with duration tracking for API requests. - Introduce `LocalRegistration` component and user mutation hooks for the sign-in page. - Add `/auth/local/register` and `/auth/local/allow-guest` endpoints to the AuthHandler. - Add administrative control to toggle guest registration. - Optimize `SignInBg` component by switching from Framer Motion to pure CSS animations for improved performance. - Update `AuthHandler` to manage guest registration status dynamically. - Update database default configuration schema to include registration settings.
…grade Eden client - Refactored `requestLogger` to use a `WeakMap` for per-request state tracking (`startTime` and `reqId`). - Updated `AuthHandler` and middleware to inject the state map, enabling context-aware logging and auth checks. - Overhauled `EdenClient` to handle automatic authorization headers and unified toast notifications for mutations. - Updated `onError` handler to globalize error processing. - Enhanced API registration logic to include automated onboarding for the first user.
…AUTH-001] - Replace manual header injection and `eden` global object usage with `EdenClientContext` throughout the codebase. - Implement token management via `EdenClientContext` in `Layout`, `AuthCallback`, and authentication-related hooks. - Remove redundant `useAuth` hook and consolidate header management logic. - Standardize `eden.query` and `eden.mutate` calls to consume the client from context.
* Adjust dev scripts and turbo config - Use `docker-compose` syntax in docker-up script - Silence docker output when running API dev server - Add global passthrough for DOCKSTAT_LOGGER_IGNORE_MESSAGES * Update tooling and clean up code - Switch to `docker compose` v2 - Expand globalPassThroughEnv in turbo.json - Remove unused variables and imports - Use template literals for strings - Fix void return in AuthHandler - Add Biome ignore comments
This prevents token leakage through browser history, referrer headers, and server logs. Also adds auth requirements to provider endpoints, improves JWT secret validation, fixes CPU calculation units, and cleans up excessive OAuth logging.
* Pass OAuth token via HttpOnly cookie instead of URL This prevents token leakage through browser history, referrer headers, and server logs. Also adds auth requirements to provider endpoints, improves JWT secret validation, fixes CPU calculation units, and cleans up excessive OAuth logging. * Migrate to bun-types and update Bun configuration * Prefix auth env vars and refactor timeout --------- Signed-off-by: ItsNik <info@itsnik.de>
Combine login and registration into tabs Inline form components into SignIn page Update ProviderList styling and empty state Allow unauthenticated access to providers endpoint Update auth package dependencies and environment variables
Replace framer-motion and complex animations with lightweight CSS. Add more provider icons and update sign-in page logic.
- Add name and icon fields to providers table and API - Add delete endpoints for providers and users - Add list users endpoint - Improve auth callback page with animated background - Replace accounts settings placeholder with component - Fix Tailwind important syntax for v4 compatibility - Treat auth callback routes as login pages in layout
Adds UI sections for managing local users, API keys, and OAuth providers. Includes query and mutation hooks for account data operations.
Add server-side token verification via /auth/verify endpoint, replacing client-side JWT decoding. Also auto-select the register tab when no local users exist and add API key security scheme to OpenAPI docs.
Replace hardcoded URL-based provider icon detection with database-driven icon mapping. Centralize provider types in the shared @dockstat/auth package.
- Add enableRegistration setting with live toggle in general settings - Pass allowGuestRegistration to AuthHandler and support runtime updates - Add create local user dialog with validation in accounts section - Fix auth cookie path and send token via Authorization header in callback verification - Extract reusable AdditionalSettingsCard component - Memoize active/revoked API key filtering - Add null check for OAuth provider icons
Display dynamic error messages in HeroPanel with auto-dismiss. Add parseApiDate utility to handle ISO strings, Unix timestamps, and Date objects. Serialize Unix timestamps to ISO format in backend responses. Enhance background icon animations with multi-axis drifting. Prevent users from deleting their own account in settings.
- Add error handler middleware with request ID logging - Refactor auth forms: remove Card wrapper, centralize error handling - Move SignIn page to separate component - Add minimal development script - Update error display utilities and imports
Summary by Sourcery
Introduce a unified OIDC/OAuth authentication system for DockStat, exposing backend auth routes and middleware, React client context and protected routing, and integrating them into the API server and frontend app, while enhancing API documentation and type safety.
New Features:
Bug Fixes:
Enhancements:
Build:
Documentation:
Summary by CodeRabbit
Release Notes
New Features
Documentation
Refactor