From c15b407e1cffea69dfd87b899458921789d460b3 Mon Sep 17 00:00:00 2001 From: Guillermo Pages Date: Thu, 2 Oct 2025 14:30:32 +0200 Subject: [PATCH] fix: remove hardcoded URLs and add configurable post-login redirect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fixed hardcoded finalize URL to build from rpCallbackUrl - Removed hardcoded /workspace redirect path - Added postLoginPath config option (default: '/') - Added allowContinueParam config option (default: true) - Continue parameter now flows through signed state to session to final redirect This fixes biblio-stats authentication flow and makes the package work for any RP. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/oidc/OIDCStandardRoutes.ts | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/oidc/OIDCStandardRoutes.ts b/src/oidc/OIDCStandardRoutes.ts index 0ffb342..b2ca1fa 100644 --- a/src/oidc/OIDCStandardRoutes.ts +++ b/src/oidc/OIDCStandardRoutes.ts @@ -65,6 +65,10 @@ interface OidcStandardConfig { // State signing secret (should be different from session secret) stateSigningSecret: string; + // Post-login redirect configuration + postLoginPath?: string; // Default path to redirect after login (default: '/') + allowContinueParam?: boolean; // Allow ?continue= param to override (default: true) + // Optional hook invoked after id_token verification succeeds onUserAuthenticated?: OnUserAuthenticatedHandler; } @@ -104,7 +108,9 @@ export function createOidcStandardRoutes(config: OidcStandardConfig): Router { rpCookieDomain, rpFrontendUrl, sessionCookieName, - stateSigningSecret + stateSigningSecret, + postLoginPath = '/', + allowContinueParam = true } = config; // Debug log the configuration @@ -140,12 +146,18 @@ export function createOidcStandardRoutes(config: OidcStandardConfig): Router { const jti = crypto.randomBytes(16).toString('base64url'); const nonce = crypto.randomBytes(16).toString('base64url'); + // Capture continue parameter for post-login redirect (if allowed) + const continueParam = allowContinueParam + ? (req.query.continue as string | undefined) + : undefined; + // Create signed state token with all necessary data const state = await new SignJWT({ jti, // Unique ID for replay prevention nonce, // For id_token validation client_id: swissoidClientId, - redirect_uri: rpCallbackUrl + redirect_uri: rpCallbackUrl, + continue: continueParam // Optional post-login redirect path }) .setProtectedHeader({ alg: 'HS256' }) .setIssuedAt() @@ -359,13 +371,14 @@ export function createOidcStandardRoutes(config: OidcStandardConfig): Router { // Step 6: Create opaque session (but don't set cookie yet) const sessionId = crypto.randomBytes(24).toString('base64url'); - // Store session data + // Store session data (including continue param for post-login redirect) await sessionService.createSession(sessionId, { sub: payload.sub, iat: payload.iat, exp: payload.exp, email: payload.email, - name: payload.name + name: payload.name, + continue: statePayload.continue // Carry over from signed state }); // Step 7: Create transit token and store in Redis @@ -389,7 +402,9 @@ export function createOidcStandardRoutes(config: OidcStandardConfig): Router { // Step 8: Redirect to finalize endpoint with transit token // This will set the cookie in first-party context - const finalizeUrl = `https://gateway.clockize.com/oidc/finalize?tx=${transitToken}`; + // Build finalize URL from rpCallbackUrl to support any RP + const callbackUrl = new URL(rpCallbackUrl); + const finalizeUrl = `${callbackUrl.protocol}//${callbackUrl.host}/oidc/finalize?tx=${transitToken}`; // Set security headers for the redirect res.setHeader('Cache-Control', 'no-store'); @@ -464,8 +479,11 @@ export function createOidcStandardRoutes(config: OidcStandardConfig): Router { res.cookie(sessionCookieName, sessionId, cookieOptions); - // Redirect to the app - const redirectUrl = `${rpFrontendUrl}/workspace`; + // Build redirect URL using continue parameter or default path + const continueValue = sessionData.continue || postLoginPath; + const redirectUrl = continueValue.startsWith('/') + ? `${rpFrontendUrl}${continueValue}` + : rpFrontendUrl; // Set security headers res.setHeader('Cache-Control', 'no-store');