Skip to content

Commit 6022ff8

Browse files
akbogclaude
andcommitted
Add per-HTTP-attempt timeout for non-JWT authentication
Mirror the existing JWTClient / JWTClientTimeout pattern for general (non-JWT) authentication so callers can bound per-HTTP-attempt time for auth requests independently of the query timeout. Before this change, gosnowflake routed JWT auth through a dedicated JWTClient (default 10s) but every other authenticator (warehouse password, native OAuth, MFA, SSO, PAT, external browser) shared the main Client with the query path (default ClientTimeout = 900s). That meant downstream callers like dbt-fusion could not tighten the per-attempt auth timeout without also limiting how long queries can run. Changes: - New AuthClient *http.Client field on snowflakeRestful, instantiated from sc.cfg.AuthClientTimeout in connection.go. - getClientFor() now returns AuthClient for any non-JWT authenticator, falling back to Client when AuthClient is nil (for tests/callers that build snowflakeRestful directly without going through the standard connection path). - New Config.AuthClientTimeout field with defaultAuthClientTimeout = 60s (matches snowflake-connector-python's per-call requests timeout for auth requests; see auth/by_plugin.py). - DSN serialization and parsing for the authClientTimeout key, plus default-fill logic in FillMissingConfigParameters. JWT auth path is unchanged. Query path is unchanged. Existing fixtures in dsn_test.go are extended with the new defaulted field. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 337c068 commit 6022ff8

4 files changed

Lines changed: 126 additions & 5 deletions

File tree

connection.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -916,6 +916,15 @@ func buildSnowflakeConn(ctx context.Context, config Config) (*snowflakeConn, err
916916
Timeout: sc.cfg.JWTClientTimeout,
917917
Transport: st,
918918
},
919+
AuthClient: &http.Client{
920+
// Per-HTTP-attempt timeout for non-JWT authentication (warehouse
921+
// password, native OAuth, MFA, SSO, PAT, external browser, etc.).
922+
// Allows callers to bound auth roundtrips without also bounding
923+
// query roundtrips, mirroring `snowflake-connector-python`'s
924+
// `requests` per-call timeout for auth requests.
925+
Timeout: sc.cfg.AuthClientTimeout,
926+
Transport: st,
927+
},
919928
TokenAccessor: tokenAccessor,
920929
LoginTimeout: sc.cfg.LoginTimeout,
921930
RequestTimeout: sc.cfg.RequestTimeout,

dsn.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
const (
2121
defaultClientTimeout = 900 * time.Second // Timeout for network round trip + read out http response
2222
defaultJWTClientTimeout = 10 * time.Second // Timeout for network round trip + read out http response but used for JWT auth
23+
defaultAuthClientTimeout = 60 * time.Second // Timeout for network round trip + read out http response used for non-JWT authentication requests
2324
defaultLoginTimeout = 300 * time.Second // Timeout for retry for login EXCLUDING clientTimeout
2425
defaultRequestTimeout = 0 * time.Second // Timeout for retry for request EXCLUDING clientTimeout
2526
defaultJWTTimeout = 60 * time.Second
@@ -103,6 +104,8 @@ type Config struct {
103104
// Deprecated: timeouts may be reorganized in a future release.
104105
JWTClientTimeout time.Duration // Timeout for network round trip + read out http response used when JWT token auth is taking place
105106
// Deprecated: timeouts may be reorganized in a future release.
107+
AuthClientTimeout time.Duration // Timeout for network round trip + read out http response used for non-JWT authentication requests
108+
// Deprecated: timeouts may be reorganized in a future release.
106109
ExternalBrowserTimeout time.Duration // Timeout for external browser login
107110
// Deprecated: timeouts may be reorganized in a future release.
108111
CloudStorageTimeout time.Duration // Timeout for a single call to a cloud storage provider
@@ -303,6 +306,9 @@ func DSN(cfg *Config) (dsn string, err error) {
303306
if cfg.JWTClientTimeout != defaultJWTClientTimeout {
304307
params.Add("jwtClientTimeout", strconv.FormatInt(int64(cfg.JWTClientTimeout/time.Second), 10))
305308
}
309+
if cfg.AuthClientTimeout != defaultAuthClientTimeout {
310+
params.Add("authClientTimeout", strconv.FormatInt(int64(cfg.AuthClientTimeout/time.Second), 10))
311+
}
306312
if cfg.LoginTimeout != defaultLoginTimeout {
307313
params.Add("loginTimeout", strconv.FormatInt(int64(cfg.LoginTimeout/time.Second), 10))
308314
}
@@ -651,6 +657,9 @@ func fillMissingConfigParameters(cfg *Config) error {
651657
if cfg.JWTClientTimeout == 0 {
652658
cfg.JWTClientTimeout = defaultJWTClientTimeout
653659
}
660+
if cfg.AuthClientTimeout == 0 {
661+
cfg.AuthClientTimeout = defaultAuthClientTimeout
662+
}
654663
if cfg.ExternalBrowserTimeout == 0 {
655664
cfg.ExternalBrowserTimeout = defaultExternalBrowserTimeout
656665
}
@@ -918,6 +927,11 @@ func parseDSNParams(cfg *Config, params string) (err error) {
918927
if err != nil {
919928
return
920929
}
930+
case "authClientTimeout":
931+
cfg.AuthClientTimeout, err = parseTimeout(value)
932+
if err != nil {
933+
return
934+
}
921935
case "loginTimeout":
922936
cfg.LoginTimeout, err = parseTimeout(value)
923937
if err != nil {

0 commit comments

Comments
 (0)