Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,10 @@ docs/static/api/next/postman/
!.claude/skills

# Dev-only CORS seed staged by `build.sh run` / `build.ps1 run` for the bootstrap one-shot (never committed or packaged)
backend/cmd/server/bootstrap/02-server-configurations.yaml
backend/cmd/server/bootstrap/03-dev-server-configurations.yaml

# Nx build cache (generated, never committed)
.nx/

# Development internals guide (not intended for commit)
THUNDERID_INTERNALS_GUIDE.md

16 changes: 16 additions & 0 deletions backend/cmd/server/bootstrap/02-server-configurations.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
resource_type: server_config
name: flow
value:
authFlow:
defaultHandle: default-flow
expirySeconds: 1800
registrationFlow:
expirySeconds: 3600
recoveryFlow:
expirySeconds: 1800
signOutFlow:
defaultHandle: default-flow
expirySeconds: 1800
userOnboardingFlow:
defaultHandle: default-flow
expirySeconds: 86400
3 changes: 0 additions & 3 deletions backend/cmd/server/config/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,6 @@
}
},
"flow": {
"default_auth_flow_handle": "default-flow",
"default_signout_flow_handle": "default-flow",
"user_onboarding_flow_handle": "default-flow",
"max_version_history": 10,
"auto_infer_registration": false,
"store": "composite"
Expand Down
11 changes: 9 additions & 2 deletions backend/cmd/server/servicemanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,11 +304,17 @@ func registerServices(mux *http.ServeMux, cacheManager cache.CacheManagerInterfa

emailClient := initEmailClient(ctx, logger)

// Create the flow server-config handler early so it can be registered before serverconfig is
// initialized. The handle-existence validator is injected in a second phase after flowMgtService
// is available.
flowConfigHandler := flowmgt.NewFlowConfigHandler()

// Initialize server-wide configuration after its handler dependencies.
serverConfigHandlers := map[serverconfig.ConfigName]serverconfig.ServerConfigHandlerInterface{
serverconfig.ConfigNameCORS: cors.OriginHandler{},
serverconfig.ConfigNameDefaultResourceServer: resource.NewDefaultResourceServerConfigHandler(resourceService),
serverconfig.ConfigNameSession: flowsession.ConfigHandler{},
serverconfig.ConfigNameFlow: flowConfigHandler,
}
serverConfigService, serverConfigExporter, err := serverconfig.Initialize(mux, cacheManager, serverConfigHandlers)
fatalOnError(ctx, logger, err, "Failed to initialize server config service")
Expand Down Expand Up @@ -367,7 +373,8 @@ func registerServices(mux *http.ServeMux, cacheManager cache.CacheManagerInterfa
)

flowMgtService, flowMgtExporter, err := flowmgt.Initialize(
mux, mcpServer, cacheManager, flowFactory, execRegistry, interceptorRegistry, graphBuilder)
mux, mcpServer, cacheManager, flowFactory, execRegistry, interceptorRegistry, graphBuilder,
serverConfigService, ouService, flowConfigHandler)
fatalOnError(ctx, logger, err, "Failed to initialize FlowMgtService")

// Two-phase initialization: inject the flow resolver into the OU service.
Expand Down Expand Up @@ -463,7 +470,7 @@ func registerServices(mux *http.ServeMux, cacheManager cache.CacheManagerInterfa
attestationProvider := initAttestationProvider(ctx, logger, runtimeCryptoSvc)
flowExecService, err := flowexec.Initialize(mux, flowMgtService, actorProvider,
execRegistry, interceptorRegistry, observabilitySvc, runtimeCryptoSvc, attestationProvider,
graphBuilder, runtimeStoreProvider, transactioner, flowConfig)
graphBuilder, runtimeStoreProvider, transactioner, serverConfigService, flowConfig)
fatalOnError(ctx, logger, err, "Failed to initialize flow execution service")

// Initialize OAuth services.
Expand Down
16 changes: 16 additions & 0 deletions backend/internal/flow/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,22 @@ type Config struct {
Session flowsession.Config
}

// FlowTypeConfig holds the server-level defaults for one flow type.
type FlowTypeConfig struct {
DefaultHandle string `json:"defaultHandle,omitempty"`
ExpirySeconds int64 `json:"expirySeconds,omitempty"`
}

// FlowSectionConfig is the value of the server-config "flow" section. It carries per-type default
// handles and context TTLs. A zero ExpirySeconds falls back to the built-in default for that type.
type FlowSectionConfig struct {
AuthFlow FlowTypeConfig `json:"authFlow"`
RegistrationFlow FlowTypeConfig `json:"registrationFlow"`
UserOnboardingFlow FlowTypeConfig `json:"userOnboardingFlow"`
RecoveryFlow FlowTypeConfig `json:"recoveryFlow"`
SignOutFlow FlowTypeConfig `json:"signOutFlow"`
}

// FromServerRuntime builds flow configuration from the global server runtime.
func FromServerRuntime() Config {
runtime := config.GetServerRuntime()
Expand Down
3 changes: 1 addition & 2 deletions backend/internal/flow/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (s *FlowConfigTestSuite) TearDownTest() {

func (s *FlowConfigTestSuite) TestFromServerRuntime() {
cfg := &config.Config{
Flow: engineconfig.FlowConfig{UserOnboardingFlowHandle: "onboarding-handle"},
Flow: engineconfig.FlowConfig{},
Server: engineconfig.ServerConfig{
HTTPOnly: true,
},
Expand All @@ -56,7 +56,6 @@ func (s *FlowConfigTestSuite) TestFromServerRuntime() {

result := FromServerRuntime()

s.Equal("onboarding-handle", result.Flow.UserOnboardingFlowHandle)
s.False(result.SecureCookies, "HTTPOnly deployment must not mark cookies Secure")
// Session config is sourced from the server-config section at the composition root, not here.
s.Zero(result.Session.IdleTimeoutSeconds)
Expand Down
1 change: 1 addition & 0 deletions backend/internal/flow/flowexec/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const (
defaultRegistrationFlowExpiry int64 = 3600 // 60 minutes in seconds
defaultUserOnboardingFlowExpiry int64 = 86400 // 24 hours in seconds
defaultRecoveryFlowExpiry int64 = 1800 // 30 minutes in seconds
defaultSignOutFlowExpiry int64 = 1800 // 30 minutes in seconds

fieldFlowSecret = "flowSecret"

Expand Down
165 changes: 165 additions & 0 deletions backend/internal/flow/flowexec/flowDefaultsProvider_mock_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion backend/internal/flow/flowexec/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ func Initialize(
graphBuilder graphbuilder.GraphBuilderInterface,
storeProvider providers.RuntimeStoreProvider,
transactioner providers.Transactioner,
serverConfigSvc serverConfigProvider,
cfg flowconfig.Config,
) (FlowExecServiceInterface, error) {
flowStore := newFlowStore(storeProvider)
Expand All @@ -52,7 +53,7 @@ func Initialize(
flowProvider, graphBuilder)
flowExecService := newFlowExecService(flowProvider, flowStore, flowEngine,
actorProvider, observabilitySvc, transactioner, cryptoSvc, attestationVerifier,
graphBuilder, cfg)
graphBuilder, serverConfigSvc, cfg)

@rajithacharith rajithacharith Jul 27, 2026

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.

Do we directly inject server config? or thought of having a proxy svc only to get default flows.
If we don't have any other data required from serverConfigs

FlowExec --> DefaultFlowProvider --> serverConfig 
                         |-> GetDefaultFlow(FlowType string) (flowId string)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Is this proxy pattern something we're following in other places when accessing server config svc? I have defined a serverConfigProvider interface to downscope it only to the required method, but this doesn't restrict it to flow configurations.

type serverConfigProvider interface {
	GetMergedConfig(ctx context.Context, name string) (any, *tidcommon.ServiceError)
}


// Mark the SSO cookie Secure unless the deployment is configured to serve over plain HTTP, and
// bound its lifetime to the session's configured absolute timeout (same fallback as the session
Expand Down
Loading
Loading