You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Frontend changes to enable faster upstream to internal repo (#126)
* refactor(auth): pluggable provider architecture via providers/index.ts
Replace inline commented-out providers and hardcoded signIn('oauth')
calls with a clean plugin pattern where providers/index.ts is the sole
swap-point for enabling authentication.
- Add providers/types.ts with AuthProviderConfig interface contract
- Rewrite providers/index.ts as the auth registry (returns null by default)
- Uncomment internal-auth.ts as a working, type-checked reference
- Refactor config.ts to import from providers and derive AUTH_PROVIDER_ID
- Add authProviderId to AppConfig so session.ts and signin/page.tsx
adapt dynamically to any provider (no more hardcoded 'oauth')
- Expand README.md Authentication section with step-by-step enablement
guide targeting AI agent consumption
Default-user (no auth) flow is unchanged: getAuthProviderConfig()
returns null, session.ts returns DEFAULT_USER, proxy.ts clears cookies.
Made-with: Cursor
* fix(auth): ephemeral secret fallback, aligned lifetimes, configurable refresh buffer
Address three security/correctness issues from PR #121 review:
1. Replace hardcoded 'disabled-auth-secret' with crypto.randomUUID()
so the disabled-auth fallback is ephemeral and unpredictable. When
auth is required with a real provider, undefined is passed so
NextAuth throws a loud error if NEXTAUTH_SECRET is missing.
2. Create SESSION_MAX_AGE_SECONDS (configurable via SESSION_MAX_AGE_HOURS,
default 24h) and use it in both config.ts session.maxAge and proxy.ts
cookie maxAge. Eliminates the 30-day cookie / 24-hour session mismatch
that left stale idToken cookies after session expiry.
3. Make TOKEN_REFRESH_BUFFER_SECONDS configurable via
TOKEN_REFRESH_BUFFER_MINUTES env var (default 5 min). Operators with
long-running deep research jobs set TOKEN_REFRESH_BUFFER_MINUTES=30
to prevent mid-job 401 errors.
Both new env vars documented in .env.example and README.
Made-with: Cursor
* fix(ui): add missing HITL respond route, fix auth token, guard URL parsing
Three P0 bug fixes:
1. Add /api/generate/respond route handler so HITL prompt responses
(approve/reject) no longer silently 404. Mirrors the existing
generate route's auth and error patterns as a non-streaming REST
proxy.
2. Change accessToken to idToken in use-load-job-data.ts (13 sites).
Every other hook already uses idToken; the mismatch caused 401s
when loading deep research job data with OIDC providers where
accessToken and idToken differ.
3. Wrap parse(req.url) in server.js with try/catch in both the HTTP
handler (returns 400) and WebSocket upgrade handler (destroys
socket) to prevent crashes on malformed request URLs.
Made-with: Cursor
* refactor(ui): remove unused NEXT_PUBLIC_WORKFLOW_ID / workflowId plumbing
Multi-workflow routing is not used. Remove the workflowId prop chain
(page → MainLayout → InputArea → useChat → chat-client) and the
workflow_id field from ChatCompletionRequestSchema.
Made-with: Cursor
* fix(auth): type-safe providers, rename internal-auth to auth-example, fix test fixtures
- Add type assertion for providers ternary to satisfy AuthOptions type
- Rename internal-auth.ts to auth-example.ts (doc-only reference)
- Add authProviderId to test AppConfig fixtures
Made-with: Cursor
* fix(auth): use node:crypto import for test compat, fix stale file references
Replace global crypto.randomUUID() with an explicit node:crypto import
so the ephemeral secret works in vitest jsdom/happy-dom environments
where the Web Crypto global does not expose randomUUID().
Update four references from the renamed internal-auth.ts to
auth-example.ts (config.ts, providers/index.ts, README.md).
Made-with: Cursor
* fix(auth): address PR #126 review comments
- Fix perpetual token refresh when OAuth provider omits expires_at:
guard refresh logic so undefined expiresAt no longer falls back to 0,
which caused refreshAccessToken on every JWT callback invocation
- Fix stale internal-auth.ts references → auth-example.ts in types.ts
and auth-example.ts docstrings
- Align client-side session polling interval with configurable
TOKEN_REFRESH_BUFFER_SECONDS via new AppConfig.sessionRefreshIntervalSeconds,
replacing the hardcoded 4-minute setInterval in useAuth
- Update test fixtures with sessionRefreshIntervalSeconds field
Made-with: Cursor
* fix(auth): isolate server-only auth config from client imports
Prevent the auth barrel from pulling NextAuth config into the client bundle so Turbopack no longer evaluates server-only crypto code in browser paths.
Made-with: Cursor
* fix(ui): clear stale deep research state after missing jobs
Sync persisted deep research tracking state when jobs fail, expire, or disappear so sessions stop showing as active and archived report loads resolve to a terminal failure instead of lingering in progress.
Made-with: Cursor
* fix(test): stub server-only for vitest auth imports
Teach the Vitest resolver to treat Next's server-only marker as a no-op shim so auth config specs can import server-only modules without breaking the UI unit test suite.
Made-with: Cursor
* fix(ui): address remaining PR #126 review comments
Clarify the auth provider example as a template, harden auth timing env parsing, remove dead chat hook options, and deduplicate deep research missing-job error handling while keeping the full UI test suite green.
Made-with: Cursor
* fix(auth): avoid unknown-expiry refresh churn
Keep tokens stable when providers omit expires_at, align callback idToken cookie lifetime with the shared session max age, and make cancelled deep research recovery map explicitly to interrupted status.
Made-with: Cursor
* fix(test): satisfy auth config callback types
Pass a minimal typed user object into the auth config JWT callback test so the UI type-check stays green in CI.
Made-with: Cursor
* fix(ui): remove workflowId references from README and schemas
Eliminate workflowId from the chat streaming example in README.md and the WebSocket connection schema in schemas.ts to streamline the API and reduce unnecessary complexity.
* fix(chat): enhance file update handling in useDeepResearch hook
Add tests to verify that the status is set to 'writing' when 'report.md' is received, and ensure that non-report files do not trigger this status. Remove unused reference to allTodosCompletedRef to streamline the hook's logic. Update the onFileUpdate function to set the writing status based on the filename.
---------
Co-authored-by: Ajay Thorve <athorve@nvidia.com>
> **Note:** When `OAUTH_ISSUER` is set, the app uses OIDC auto-discovery to resolve authorization, token, and userinfo endpoints automatically. No additional endpoint URLs are needed for standard OIDC providers.
309
+
Provider-specific env vars depend on your provider implementation. See `src/adapters/auth/providers/auth-example.ts` for a template/checklist and the [Authentication](#authentication) section for setup steps.
314
310
315
311
316
312
## API Communication
@@ -325,7 +321,7 @@ OpenAI-compatible chat completions via `/chat/stream`:
325
321
import { streamChat } from '@/adapters/api'
326
322
327
323
await streamChat(
328
-
{ messages, sessionId, workflowId },
324
+
{ messages, sessionId },
329
325
{
330
326
onChunk: (content) => console.log(content),
331
327
onComplete: () => console.log('Done'),
@@ -343,7 +339,6 @@ import { createWebSocketClient } from '@/adapters/api'
343
339
344
340
const ws = createWebSocketClient({
345
341
sessionId: 'abc123',
346
-
workflowId: 'researcher',
347
342
callbacks: {
348
343
onAgentText: (content, isFinal) => {},
349
344
onStatus: (status, message) => {},
@@ -358,23 +353,133 @@ ws.sendMessage('Hello!')
358
353
359
354
## Authentication
360
355
361
-
Authentication is **disabled by default**. All users are assigned a "Default User" identity with no login required.
356
+
Authentication is **disabled by default**. All users are assigned a "Default User" identity with no login required. The auth system uses a **plugin architecture** where `src/adapters/auth/providers/index.ts` is the sole file that controls whether auth is enabled and which provider is active.
- `providers/index.ts` exports `getAuthProviderConfig()` which returns the active provider configuration. By default it returns `{ provider: null }` (auth disabled).
373
+
- `config.ts` imports from `providers/index.ts` and wires the provider into NextAuth. It never needs to be edited when adding a new provider.
374
+
- `session.ts` provides the `useAuth()` hook that components use. It reads `authProviderId` from `AppConfig` and adapts dynamically.
375
+
376
+
### Provider Contract
377
+
378
+
Every auth provider must conform to the `AuthProviderConfig` interface defined in `providers/types.ts`:
To enable OAuth/OIDC authentication, follow these steps:
398
+
399
+
#### Step 1: Create a provider file
400
+
401
+
Create a new file in `src/adapters/auth/providers/` (e.g. `my-sso.ts`). See `auth-example.ts` in the same directory for a template/checklist. Your file should export:
402
+
403
+
1. A NextAuth-compatible provider object (OAuth/OIDC config)
404
+
2. A token refresh function matching the `TokenRefreshResult` return type
# Provider-specific (names depend on your provider file)
471
+
MY_SSO_ISSUER=https://sso.example.com
472
+
MY_SSO_CLIENT_ID=<your-client-id>
473
+
MY_SSO_CLIENT_SECRET=<your-client-secret>
474
+
MY_SSO_TOKEN_URL=https://sso.example.com/token
376
475
```
377
476
477
+
That's it. No other files need to change -- `config.ts`, `session.ts`, `proxy.ts`, and all components automatically adapt to the new provider via `getAuthProviderConfig()`.
478
+
479
+
### Disabling Authentication
480
+
481
+
To disable auth (the default), ensure `providers/index.ts` returns `{ provider: null }` and either unset `REQUIRE_AUTH` or set `REQUIRE_AUTH=false`. The app will use a "Default User" identity with no login required.
482
+
378
483
### Using the Auth Hook
379
484
380
485
```typescript
@@ -386,16 +491,11 @@ const MyComponent = () => {
386
491
if (isLoading) return <Spinner />
387
492
if (!isAuthenticated) return <ButtononClick={signIn}>SignIn</Button>
388
493
389
-
// Use idToken for backend API calls
390
-
await fetch('/api/data', {
391
-
headers: { 'Authorization': `Bearer ${idToken}` }
392
-
})
393
-
394
494
return <Text>Welcome, {user?.name}</Text>
395
495
}
396
496
```
397
497
398
-
>**NOTE:** Above Authentication docs are reference only and implementation depends on environment specifics.
498
+
When auth is disabled, `useAuth()` returns `isAuthenticated: true` with a default user -- no sign-in flow is triggered.
0 commit comments