From 798e75f6ee45d471feafcbacf748d78bee5be4d3 Mon Sep 17 00:00:00 2001 From: Rein Krul Date: Thu, 18 Jun 2026 15:10:04 +0200 Subject: [PATCH] Use longer OAuth flow timeout for interactive frontend logins (#4345) The state-to-OAuthSession mapping for the authorization-code callback flows used a hard 1-minute TTL. That window has to cover the entire interactive round-trip (redirect to the external IdP, user login/MFA/ consent, redirect back to the callback), which is too tight for a human login and made the callback fail with "invalid or expired state". Add oauthFrontendFlowTimeout (5 min) and apply it via WithTTL on the two interactive auth-code flows (OpenID4VCI auth-code and OpenID4VP user login). Back-end (service-to-service) flows keep the 1-minute oAuthFlowTimeout. Both flows share the same store; the per-entry TTL distinguishes them, so the callback reader is unaffected. Assisted by AI --- auth/api/iam/openid4vci.go | 3 ++- auth/api/iam/user.go | 9 ++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/auth/api/iam/openid4vci.go b/auth/api/iam/openid4vci.go index ec4b187370..b471041e3b 100644 --- a/auth/api/iam/openid4vci.go +++ b/auth/api/iam/openid4vci.go @@ -35,6 +35,7 @@ import ( "github.com/nuts-foundation/nuts-node/core" "github.com/nuts-foundation/nuts-node/crypto" nutsHttp "github.com/nuts-foundation/nuts-node/http" + "github.com/nuts-foundation/nuts-node/storage" "github.com/nuts-foundation/nuts-node/vdr/resolver" ) @@ -119,7 +120,7 @@ func (r Wrapper) RequestOpenid4VCICredentialIssuance(ctx context.Context, reques IssuerCredentialConfigurationID: credentialConfigID, IssuerCredentialIssuer: credentialIssuerMetadata.CredentialIssuer, CredentialRequestParams: credentialRequestParams, - }) + }, storage.WithTTL(oauthFrontendFlowTimeout)) if err != nil { return nil, fmt.Errorf("failed to store session: %w", err) } diff --git a/auth/api/iam/user.go b/auth/api/iam/user.go index 76f3881b3d..92bb46dc69 100644 --- a/auth/api/iam/user.go +++ b/auth/api/iam/user.go @@ -40,7 +40,14 @@ import ( const ( // oAuthFlowTimeout is the timeout for the oauth flow. // The maximum time between the initial authorize request and the final token request. + // Used for back-end (service-to-service) flows, which complete quickly without user interaction. + // This is too short for interactive flows; use oauthFrontendFlowTimeout for those. oAuthFlowTimeout = time.Minute + // oauthFrontendFlowTimeout is the timeout for interactive OAuth authorization-code flows that + // involve a user logging in at an external Authorization Server (e.g. OpenID4VCI auth-code, + // OpenID4VP user login). The TTL has to cover the entire interactive round-trip: redirect to the + // external IdP, the user authenticating (password, MFA, consent) and the redirect back to the callback. + oauthFrontendFlowTimeout = 5 * time.Minute // userRedirectTimeout is the timeout for the user redirect session. // This is the maximum time between the creation of the redirect for the user and the actual GET request to the user/wallet page. userRedirectTimeout = time.Second * 5 @@ -118,7 +125,7 @@ func (r Wrapper) handleUserLanding(echoCtx echo.Context) error { TokenEndpoint: metadata.TokenEndpoint, } // store user session in session store under sessionID and clientState - err = r.oauthClientStateStore().Put(oauthSession.ClientState, oauthSession) + err = r.oauthClientStateStore().Put(oauthSession.ClientState, oauthSession, storage.WithTTL(oauthFrontendFlowTimeout)) if err != nil { return err }