Skip to content

Move OIDC request handlers into @eventuras/fides-auth/server#17

Merged
losolio merged 1 commit into
mainfrom
extract-oidc-handlers
Jun 25, 2026
Merged

Move OIDC request handlers into @eventuras/fides-auth/server#17
losolio merged 1 commit into
mainfrom
extract-oidc-handlers

Conversation

@losolio

@losolio losolio commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Why

Completes the framework-agnostic server layer. After the foundation (CookieStore + session helpers, #16), the OIDC request handlers were the last orchestration still living in fides-auth-next. Their flow — rate limit, PKCE cookie lifecycle, code exchange, session persistence, safe redirect — is identical across frameworks; only cookie/header I/O is Next-specific. Moving them down lets a future React Router adapter reuse the same handlers.

What

@eventuras/fides-auth/server — three new handlers over the standard Request/Response:

  • handleOidcLogin, handleOidcCallback, handleHeartbeat
  • Each takes a CookieStore and an optional rateLimit callback. The proxy callback-path reconstruction, PKCE cookie cleanup, session persistence (via the existing persistSession), the CookieTooLargeError → 431 mapping, and the heartbeat response shape all move with them.
  • Orchestration tests move here and drive the handlers with an in-memory cookie store.

@eventuras/fides-auth-next:

  • handleOidcLogin / handleOidcCallback / handleHeartbeat become thin wrappers that supply nextCookieStore() and the global GET/POST rate limiters
  • Public config interfaces and behaviour are unchanged

Rate limiting stays in fides-auth-next (it reads the client IP from Next headers) and is injected into the core handlers as a callback.

Compatibility

Non-breaking. @eventuras/fides-auth gets a minor (new handler exports), @eventuras/fides-auth-next a patch. Changeset included.

Verification

  • @eventuras/fides-auth: 221 tests pass, builds.
  • @eventuras/fides-auth-next: 6 tests pass, builds.
  • store / react packages unaffected, build green.

Copilot AI review requested due to automatic review settings June 25, 2026 22:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR completes the framework-agnostic @eventuras/fides-auth/server layer by moving OIDC orchestration (login initiation, callback handling, and heartbeat refresh) out of fides-auth-next into core handlers that operate on standard Request/Response plus a CookieStore, leaving Next.js as thin adapters.

Changes:

  • Add core server handlers: handleOidcLogin, handleOidcCallback, and handleHeartbeat, with orchestration-level tests in @eventuras/fides-auth.
  • Refactor @eventuras/fides-auth-next handlers into wrappers that inject nextCookieStore() and global GET/POST rate limiters.
  • Add changeset for the new exports and package version bumps.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/fides-auth/src/server/oidc-login.ts New framework-agnostic login initiation handler that builds PKCE, stores cookies, and redirects.
packages/fides-auth/src/server/oidc-callback.ts New framework-agnostic callback handler for exchanging code, persisting session, cleaning cookies, and safe redirect.
packages/fides-auth/src/server/oidc-callback.test.ts New tests validating proxy callback-path reconstruction and basic guards.
packages/fides-auth/src/server/heartbeat-handler.ts New framework-agnostic heartbeat handler for refreshing sessions via CookieStore.
packages/fides-auth/src/server/heartbeat-handler.test.ts Updated tests to target the new core heartbeat handler behavior and dependencies.
packages/fides-auth/src/server/index.ts Exposes the new handlers and config types from the server entrypoint.
packages/fides-auth-next/src/oidc-login.ts Replaced Next-specific implementation with a thin wrapper over core handleOidcLogin.
packages/fides-auth-next/src/oidc-callback.ts Replaced Next-specific implementation with a thin wrapper over core handleOidcCallback (but currently drops a previously exported config field).
packages/fides-auth-next/src/oidc-callback.test.ts Removes the Next-side proxy-path regression test (now covered in core).
packages/fides-auth-next/src/heartbeat-handler.ts Replaced Next-specific implementation with a thin wrapper over core handleHeartbeat.
.changeset/server-oidc-handlers.md Changeset describing the move and version bumps.
Comments suppressed due to low confidence (1)

packages/fides-auth-next/src/oidc-callback.ts:17

  • OidcCallbackConfig previously allowed sessionDurationDays; removing it is a TypeScript-breaking change for consumers importing this type from @eventuras/fides-auth-next. Since session duration options are currently ignored by createAndPersistSession anyway, consider keeping the field for backwards compatibility (and documenting that it is reserved/ignored) rather than removing it in a patch release.
export interface OidcCallbackConfig {
  /** OAuth/OIDC configuration (issuer, clientId, clientSecret, redirect_uri, scope) */
  oauthConfig: OAuthConfig;

  /** Public application URL (used to reconstruct the callback URL and validate redirects) */

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +64 to +66
if (returnTo) {
await cookies.set('returnTo', returnTo, defaultOAuthCookieOptions);
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a51545f — when there's no valid returnTo, the handler now deletes any existing returnTo cookie, so an abandoned earlier login can't leak a stale path into the next one's post-login redirect. Covered by a new oidc-login test.

Comment on lines 56 to +64
export async function handleOidcCallback(
request: Request,
config: OidcCallbackConfig,
): Promise<Response> {
const {
oauthConfig,
applicationUrl,
rolesClaim = 'roles',
sessionDurationDays = 7,
defaultRedirectPath = '/',
validateReturnTo,
} = config;

// 1) Rate limit
if (!(await globalGETRateLimit())) {
logger.warn('Rate limit exceeded');
return new Response('Too many requests', { status: 429 });
}

try {
// 2) Reconstruct the public callback URL. Keep the request path: behind a
// TLS-terminating proxy applicationUrl is just the origin ("/"), and the
// token-exchange redirect_uri must match the callback path used at authorize.
const currentUrl = new URL(request.url);
const publicUrl = new URL(applicationUrl);
publicUrl.pathname = currentUrl.pathname;
publicUrl.search = currentUrl.search;

// 3) Read PKCE cookies
const storedState = await getAuthCookie('oauth_state');
const storedCodeVerifier = await getAuthCookie('oauth_code_verifier');

if (!storedState || !storedCodeVerifier) {
logger.warn('Missing state or code verifier');
return new Response('Please restart the login process.', { status: 400 });
}

// 4) Exchange code for tokens (generic OIDC logic in fides-auth)
const tokens = await exchangeAuthorizationCode(
oauthConfig,
publicUrl,
storedCodeVerifier,
storedState,
);

// 5) Build session from tokens and persist (split across session/session_at)
const session = buildSessionFromTokens(tokens, rolesClaim);
await createAndPersistSession(session, { sessionDurationDays });

// 7) Clean up PKCE & returnTo cookies
const returnTo = await getAuthCookie('returnTo');
await deleteAuthCookies(['oauth_state', 'oauth_code_verifier', 'returnTo']);

// 8) Build safe redirect URL (generic validation in fides-auth)
const redirectUrl = validateReturnUrl(
returnTo,
applicationUrl,
defaultRedirectPath,
validateReturnTo,
);
redirectUrl.searchParams.set('login', 'success');

logger.info({ redirectUrl: redirectUrl.toString() }, 'Login successful, redirecting');

return new Response(null, {
status: 302,
headers: { Location: redirectUrl.toString() },
});
} catch (error) {
// The session was too large for a cookie — surface this distinctly instead
// of hiding it in a generic 500, so it's diagnosable in production.
if (error instanceof CookieTooLargeError) {
logger.error({ error, cookieName: error.cookieName, size: error.size }, 'Session cookie too large to store');
return new Response('The session is too large to store. Please contact support.', {
status: 431,
});
}

logger.error({ error }, 'OIDC callback error');
return new Response('An unexpected error occurred. Please restart the login process.', {
status: 500,
});
}
return coreHandleOidcCallback(request, {
...config,
cookies: await nextCookieStore(),
rateLimit: globalGETRateLimit,
});

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — the refactor had actually dropped sessionDurationDays from the Next config, which would break callers passing it. Fixed in a51545f: it's restored on OidcCallbackConfig (documented as reserved/not-yet-applied) and the wrapper now maps fields explicitly so it isn't forwarded to the core handler.

Add handleOidcLogin, handleOidcCallback, and handleHeartbeat to
@eventuras/fides-auth/server. Each takes a CookieStore and an optional rate-limit
callback and works over the standard Request/Response, so the login, callback,
and heartbeat flows are framework-agnostic. The proxy callback-path handling,
PKCE cookie lifecycle, session persistence, and response shapes move with them.

fides-auth-next's handlers become thin wrappers that supply the Next cookie store
(nextCookieStore) and the global GET/POST rate limiters; their public config and
behaviour are unchanged. The orchestration tests move to core and drive the
handlers directly with an in-memory cookie store.
@losolio losolio force-pushed the extract-oidc-handlers branch from 926d09b to a51545f Compare June 25, 2026 22:19
@losolio losolio merged commit 50f6882 into main Jun 25, 2026
2 checks passed
@losolio losolio deleted the extract-oidc-handlers branch June 25, 2026 22:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants