[TT-16017] Fix issue with strip auth data GQL APIs#7831
Conversation
|
This pull request addresses a bug where authorization headers were not being propagated to upstream services for GraphQL APIs, particularly for subscriptions over WebSockets, even when Files Changed Analysis
Architecture & Impact Assessment
Request Flow VisualizationsequenceDiagram
participant Client
participant GW as "Tyk Gateway (GraphQL Engine)"
participant Upstream Service
Client->>GW: GQL Request with Auth Header
activate GW
GW->>GW: Check apiDefinition.StripAuthData
alt StripAuthData is false
GW->>GW: activeAuthType() determines which auth header to use
GW->>GW: propagateAuthHeaders() copies header to upstream request
GW->>Upstream Service: Forward Request with Auth Header
else StripAuthData is true
GW->>Upstream Service: Forward Request without Auth Header
end
activate Upstream Service
Upstream Service-->>GW: Response
deactivate Upstream Service
GW-->>Client: Response
deactivate GW
Scope Discovery & Context Expansion
Metadata
Powered by Visor from Probelabs Last updated: 2026-06-09T09:44:45.766Z | Triggered by: pr_updated | Commit: 2b2f8b8 💡 TIP: You can chat with Visor using |
Architecture Issues (1)
✅ Performance Check PassedNo performance issues found – changes LGTM. \n\nArchitecture Issues (1)
No performance issues found – changes LGTM. \n\n✅ Quality Check PassedNo quality issues found – changes LGTM. Powered by Visor from Probelabs Last updated: 2026-06-09T09:44:37.712Z | Triggered by: pr_updated | Commit: 2b2f8b8 💡 TIP: You can chat with Visor using |
|
API Changes --- prev.txt 2026-03-11 17:03:02.111835683 +0000
+++ current.txt 2026-03-11 17:02:52.013803777 +0000
@@ -2374,7 +2374,7 @@
ProtectedResourceMetadata *ProtectedResourceMetadata `bson:"protectedResourceMetadata,omitempty" json:"protectedResourceMetadata,omitempty"`
// CertificateAuth represents certificate-based authentication configuration.
- CertificateAuth *CertificateAuth `bson:"certificateAuth,omitempty" json:"certificateAuth,omitempty"`
+ CertificateAuth CertificateAuth `bson:"certificateAuth,omitempty" json:"certificateAuth,omitempty"`
}
Authentication contains configuration about the authentication methods and
security policies applied to requests.
@@ -6343,6 +6343,7 @@
AllowRemoteConfig bool `bson:"allow_remote_config" json:"allow_remote_config"`
// Set to true to enable the /config and /env endpoints for configuration inspection.
+ // These endpoints require X-Tyk-Authorization header with the secret value.
// Default: false
EnableConfigInspection bool `json:"enable_config_inspection"`
@@ -6826,9 +6827,6 @@
// JWKS holds the configuration for Tyk JWKS functionalities
JWKS JWKSConfig `json:"jwks"`
-
- // AllowUnsafePolicyIds allows unsafe policy identifiers
- AllowUnsafePolicyIds bool `json:"allow_unsafe_policy_ids"`
}
Config is the configuration object used by Tyk to set up various parameters.
@@ -7013,7 +7011,6 @@
//
// Note:
// If you set `proxy_default_timeout` to a value greater than 120 seconds, you must also increase [http_server_options.write_timeout](#http-server-options-write-timeout) to a value greater than `proxy_default_timeout`. The `write_timeout` setting defaults to 120 seconds and controls how long Tyk waits to write the response back to the client. If not adjusted, the client connection will be closed before the upstream response is received.
- // This timeout does not apply to MCP (Model Context Protocol) SSE streams — the write deadline is cleared for MCP connections to allow long-lived streaming, similar to WebSocket connections.
WriteTimeout int `json:"write_timeout"`
// Set to true to enable SSL connections
@@ -7453,9 +7450,9 @@
// Specify public keys used for Certificate Pinning on global level.
PinnedPublicKeys map[string]string `json:"pinned_public_keys"`
- // AllowUnsafeDynamicMTLSToken is provided for backward compatibility with clients that are authorized using just the
- // token for APIs secured with legacy Dynamic mTLS. If set to false (default), the client certificate must be presented
- // and the mTLS handshake will be enforced. This is the recommended setting.
+ // AllowUnsafeDynamicMTLSToken controls whether certificate presence is required for
+ // dynamic mTLS authentication. If set to false (default), requests with a token but
+ // no certificate will be rejected for APIs using dynamic mTLS.
AllowUnsafeDynamicMTLSToken bool `json:"allow_unsafe_dynamic_mtls_token"`
Certificates CertificatesConfig `json:"certificates"`
@@ -9520,13 +9517,6 @@
NewPythonDispatcher wraps all the actions needed for this CP.
func NormalisePath(a *analytics.AnalyticsRecord, globalConfig *config.Config)
-func NormalizeMCPEndpoints(session *user.SessionState)
- NormalizeMCPEndpoints calls synthesizeMCPEndpoints on every AccessDefinition
- in the session's AccessRights map. Call this after ApplyPolicies returns and
- before the session is used for rate limiting.
-
- Synthesized entries are in-memory only — never persisted to Redis.
-
func ParseRSAPublicKey(data []byte) (interface{}, error)
func ProtoMap(inputMap map[string][]string) map[string]string
ProtoMap is a helper function for maps with string slice values.
@@ -9913,11 +9903,6 @@
RecordAccessLog is used for Success/Error handler logging. It emits a log
entry with populated access log fields.
-func (t *BaseMiddleware) RecordMetrics(r *http.Request, statusCode int, latency analytics.Latency, response *http.Response)
- RecordMetrics builds a RequestContext from the current request state and
- records all configured OTEL metric instruments. Pass the upstream response
- when available (success path); nil is safe (error path).
-
func (t *BaseMiddleware) SetName(name string)
func (t *BaseMiddleware) SetOrgExpiry(orgid string, expiry int64)
@@ -10520,7 +10505,6 @@
StorageConnectionHandler *storage.ConnectionHandler
BundleChecksumVerifier bundleChecksumVerifyFunction
-
// Has unexported fields.
}
@@ -10581,10 +10565,6 @@
func (gw *Gateway) InitHostCheckManager(ctx context.Context, store storage.Handler)
-func (gw *Gateway) InitOpenTelemetryInstruments()
- InitOpenTelemetryInstruments initializes the OpenTelemetry tracer provider
- and metric instruments from the current gateway configuration.
-
func (gw *Gateway) LoadAPI(specs ...*APISpec) (out []*APISpec)
func (gw *Gateway) LoadDefinitionsFromRPCBackup() ([]*APISpec, error)
@@ -11052,29 +11032,6 @@
Timestamp time.Time
}
-type JSONRPCAccessControlMiddleware struct {
- *BaseMiddleware
-}
- JSONRPCAccessControlMiddleware enforces method-level access control for
- JSON-RPC APIs using json_rpc_methods_access_rights from the session's access
- definition.
-
- Scoped to MCP APIs only — registered in the chain only when spec.IsMCP() is
- true (Step 8.2). If method-level access control is later needed for other
- JSON-RPC protocols (A2A, etc.), the chain guard in api_loader.go can be
- widened to if spec.JsonRpcVersion == apidef.JsonRPC20.
-
-func (m *JSONRPCAccessControlMiddleware) EnabledForSpec() bool
- EnabledForSpec returns true when the API is an MCP Proxy. Defence-in-depth:
- the if spec.IsMCP() guard in api_loader.go is the primary guard; this is the
- secondary safety net consistent with the standard Tyk middleware pattern.
-
-func (m *JSONRPCAccessControlMiddleware) Name() string
- Name returns the middleware name.
-
-func (m *JSONRPCAccessControlMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int)
- ProcessRequest enforces JSON-RPC method allow/block rules.
-
type JSONRPCError struct {
Code int `json:"code"`
Message string `json:"message"`
@@ -11296,36 +11253,6 @@
Init initializes the LogMessageEventHandler instance with the given
configuration.
-type LoggingSSEHook struct {
- // Has unexported fields.
-}
- LoggingSSEHook logs each SSE event flowing through the SSETap. It always
- allows events to pass through unmodified.
-
-func NewLoggingSSEHook(logger *logrus.Entry) *LoggingSSEHook
-
-func (h *LoggingSSEHook) FilterEvent(event *SSEEvent) (bool, *SSEEvent)
-
-type MCPAccessControlMiddleware struct {
- *BaseMiddleware
-}
- MCPAccessControlMiddleware enforces primitive-level access control for MCP
- APIs using mcp_access_rights from the session's access definition.
-
- MCP-specific: activates only for MCP APIs. Method-level access control is
- handled by JSONRPCAccessControlMiddleware.
-
-func (m *MCPAccessControlMiddleware) EnabledForSpec() bool
- EnabledForSpec returns true when the API is an MCP Proxy using JSON-RPC 2.0.
-
-func (m *MCPAccessControlMiddleware) Name() string
- Name returns the middleware name.
-
-func (m *MCPAccessControlMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int)
- ProcessRequest enforces MCP primitive allow/block rules. Skips when
- state.PrimitiveType is empty (non-primitive methods such as initialize,
- ping, tools/list).
-
type MCPVEMContinuationMiddleware struct {
*BaseMiddleware
}
@@ -11413,7 +11340,7 @@
// Has unexported fields.
}
-func (m *MultiTargetProxy) CopyResponse(dst io.Writer, src io.Reader, flushInterval time.Duration) error
+func (m *MultiTargetProxy) CopyResponse(dst io.Writer, src io.Reader, flushInterval time.Duration)
func (m *MultiTargetProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) ProxyResponse
@@ -12279,7 +12206,7 @@
type ReturningHttpHandler interface {
ServeHTTP(http.ResponseWriter, *http.Request) ProxyResponse
ServeHTTPForCache(http.ResponseWriter, *http.Request) ProxyResponse
- CopyResponse(io.Writer, io.Reader, time.Duration) error
+ CopyResponse(io.Writer, io.Reader, time.Duration)
}
type RevProxyTransform struct {
@@ -12326,7 +12253,7 @@
func (p *ReverseProxy) CheckHeaderInRemoveList(hdr string, spec *APISpec, req *http.Request) bool
-func (p *ReverseProxy) CopyResponse(dst io.Writer, src io.Reader, flushInterval time.Duration) error
+func (p *ReverseProxy) CopyResponse(dst io.Writer, src io.Reader, flushInterval time.Duration)
func (p *ReverseProxy) HandleResponse(rw http.ResponseWriter, res *http.Response, ses *user.SessionState) error
@@ -12347,65 +12274,6 @@
func (r *RoundRobin) WithLen(len int) int
-type SSEEvent struct {
- ID string // Last event ID.
- Event string // Event type (default: "message").
- Data []string // Data lines (joined with \n when serialized).
- Retry int // Reconnection time in milliseconds (0 = not set).
-}
- SSEEvent represents a single Server-Sent Event
- as defined by the W3C EventSource specification
- (https://html.spec.whatwg.org/multipage/server-sent-events.html).
-
-type SSEHook interface {
- // FilterEvent inspects an SSE event and decides whether it should be
- // forwarded to the downstream client.
- //
- // Return values:
- // - allowed: if false the event is silently dropped.
- // - modifiedEvent: if non-nil it replaces the original event in the
- // output stream. Ignored when allowed is false.
- FilterEvent(event *SSEEvent) (allowed bool, modifiedEvent *SSEEvent)
-}
- SSEHook allows middleware to intercept individual SSE events as they flow
- through the gateway. Hooks are invoked by SSETap for every parsed event.
-
-type SSETap struct {
- // Has unexported fields.
-}
- SSETap wraps an upstream http.Response.Body and intercepts individual SSE
- events, running them through a chain of SSEHook implementations before
- forwarding them to the downstream consumer (typically CopyResponse).
-
- When no hooks are registered the tap operates in pass-through mode with
- minimal overhead: raw bytes are forwarded without parsing.
-
- SSETap implements io.ReadCloser and is safe for concurrent use. The mutex
- is released during blocking upstream reads so that Close can be called from
- another goroutine (e.g. a deadline timer) without deadlocking.
-
-func NewSSETap(reader io.ReadCloser, hooks ...SSEHook) *SSETap
- NewSSETap creates a new SSETap wrapping reader. If no hooks are provided the
- tap operates in a fast pass-through mode.
-
-func (t *SSETap) Close() error
- Close releases all internal buffers and closes the underlying reader.
- It is safe to call from a different goroutine while Read is blocked;
- the closed flag unblocks Read after the underlying reader is closed.
-
-func (t *SSETap) Read(p []byte) (int, error)
- Read satisfies io.Reader. It never returns (0, nil) which would violate the
- io.Reader contract. It loops internally until output data is available,
- the upstream reaches EOF, or an error occurs.
-
- The mutex is released while waiting on the upstream reader so that Close can
- be called concurrently (e.g. from a deadline timer) without deadlocking.
-
-func (t *SSETap) UnderlyingCloser() io.Closer
- UnderlyingCloser returns the upstream reader's io.Closer. This allows
- callers (such as deadline timers) to close the underlying connection
- directly without going through SSETap's mutex.
-
type SafeHealthCheck struct {
// Has unexported fields.
}
@@ -13191,13 +13059,6 @@
the version definition and ensures that required fields have appropriate
values.
-func RestoreUnicodeEscapesInError(err error) error
- RestoreUnicodeEscapesInError takes an error and applies the
- RestoreUnicodeEscapesInRegexp transformation to its message. For example,
- it converts RE2-compatible escapes like `\x{0041}` back to `\u0041`. It
- returns a new error with the transformed message. If the input error is nil,
- it returns nil.
-
func SetAsDefault(versionName string) option.Option[apidef.VersionDefinition]
SetAsDefault creates an option that marks a specific version as the default.
This sets the Default field in the VersionDefinition to the specified
@@ -13214,35 +13075,6 @@
TYPES
-type DataBytesModifier struct {
- // Has unexported fields.
-}
-
-func NewDataBytesModifier(data []byte) *DataBytesModifier
-
-func (d *DataBytesModifier) Data(data []byte)
-
-func (d *DataBytesModifier) Reset()
-
-func (d *DataBytesModifier) RestoreUnicodeEscapesFromRE2()
- RestoreUnicodeEscapesFromRE2 translates RE2-compatible hexadecimal escape
- sequences (`\x{XXXX}`) back to their original ECMA-262 compliant Unicode
- escape sequence representation (`\uXXXX`). This function is typically used
- when exporting an API definition or any other data structure where regex
- patterns were previously sanitized for internal use with Go's RE2 engine.
- It ensures that external consumers of the data receive the regex patterns in
- their original, more widely supported format.
-
-func (d *DataBytesModifier) Result() []byte
-
-func (d *DataBytesModifier) TransformUnicodeEscapesToRE2()
- TransformUnicodeEscapesToRE2 transforms ECMA-262 compliant Unicode escape
- sequences (`\uXXXX`) into a format that is compatible with Go's RE2 regex
- engine (`\x{XXXX}`). This is necessary because RE2 does not support the `\u`
- escape sequence but does support hexadecimal escapes, which can represent
- any Unicode code point. The function returns a new byte array with the
- transformed pattern.
-
type VersionParameter int
VersionParameter represents the type of parameter used in API version
configuration. It defines the possible parameters that can be used when
@@ -13433,8 +13265,6 @@
func Domain(msg string) Error
-func Domainf(format string, a ...any) Error
-
func Infra(msg string) Error
func New(msg string, opts ...Option) Error
@@ -13446,8 +13276,6 @@
func (e Error) Error() string
-func (e Error) Is(err error) bool
-
func (e Error) TypeOf(typ Type) bool
type Option func(*Error)
@@ -13458,45 +13286,6 @@
// Has unexported fields.
}
-# Package: ./pkg/identifier
-
-package identifier // import "github.com/TykTechnologies/tyk/pkg/identifier"
-
-
-VARIABLES
-
-var (
- ErrInvalidCustomPolicyId = errpack.Domain("Invalid Policy ID: Allowed characters: a-z, A-Z, 0-9, ., _, -, ~")
-)
-
-TYPES
-
-type CustomPolicyId string
- CustomPolicyId (user-defined-identifier)
-
-func (c CustomPolicyId) String() string
-
-func (c CustomPolicyId) Validate() error
-
-# Package: ./pkg/validator
-
-package validator // import "github.com/TykTechnologies/tyk/pkg/validator"
-
-
-TYPES
-
-type Option func(*validatorCfg)
-
-func WithAllowUnsafePolicyIds(disabled bool) Option
-
-type ValidateFn func(val reflect.Value) error
-
-type Validator interface {
- Validate(v any) error
-}
-
-func New(opts ...Option) Validator
-
# Package: ./regexp
package regexp // import "github.com/TykTechnologies/tyk/regexp"
@@ -15330,14 +15119,6 @@
CONSTANTS
const (
- PrimitiveTypeTool = "tool"
- PrimitiveTypeResource = "resource"
- PrimitiveTypePrompt = "prompt"
-)
- Primitive type identifiers for MCP access control. Use these constants
- wherever a primitive type string is required.
-
-const (
HashPlainText HashType = ""
HashBCrypt = "bcrypt"
HashSha256 = "sha256" |
|
🎯 Recommended Merge TargetsBased on JIRA ticket TT-16017: Authorization headers are not passed to the upstream service for GraphQL subscriptions over WebSockets Fix Version: Tyk 5.8.15Required:
Fix Version: Tyk 5.13.1Required:
Fix Version: Tyk 5.14.0
Required:
📋 Workflow
|
🚨 Jira Linter FailedCommit: The Jira linter failed to validate your PR. Please check the error details below: 🔍 Click to view error detailsNext Steps
This comment will be automatically deleted once the linter passes. |
|
|
/release to release-5.8 |
|
/release to release-5.13 |
|
✅ Cherry-pick successful. A PR was created: #8298 |
|
✅ Cherry-pick successful. A PR was created: #8299 |
…L APIs (#7831) (#8299) [TT-16017] Fix issue with strip auth data GQL APIs (#7831) <!-- Provide a general summary of your changes in the Title above --> ## Description <!-- Describe your changes in detail --> ## Related Issue <!-- This project only accepts pull requests related to open issues. --> <!-- If suggesting a new feature or change, please discuss it in an issue first. --> <!-- If fixing a bug, there should be an issue describing it with steps to reproduce. --> <!-- OSS: Please link to the issue here. Tyk: please create/link the JIRA ticket. --> ## Motivation and Context <!-- Why is this change required? What problem does it solve? --> ## How This Has Been Tested <!-- Please describe in detail how you tested your changes --> <!-- Include details of your testing environment, and the tests --> <!-- you ran to see how your change affects other areas of the code, etc. --> <!-- This information is helpful for reviewers and QA. --> ## Screenshots (if appropriate) ## Types of changes <!-- What types of changes does your code introduce? Put an `x` in all the boxes that apply: --> - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Refactoring or add test (improvements in base code or adds test coverage to functionality) ## Checklist <!-- Go over all the following points, and put an `x` in all the boxes that apply --> <!-- If there are no documentation updates required, mark the item as checked. --> <!-- Raise up any additional concerns not covered by the checklist. --> - [ ] I ensured that the documentation is up to date - [ ] I explained why this PR updates go.mod in detail with reasoning why it's required - [ ] I would like a code coverage CI quality gate exception and have explained why <!---TykTechnologies/jira-linter starts here--> ### Ticket Details <details> <summary> <a href="https://tyktech.atlassian.net/browse/TT-16017" title="TT-16017" target="_blank">TT-16017</a> </summary> | | | |---------|----| | Status | In Dev | | Summary | Authorization headers are not passed to the upstream service for GraphQL subscriptions over WebSockets | Generated at: 2026-06-02 08:43:48 </details> <!---TykTechnologies/jira-linter ends here--> --------- Co-authored-by: Long Le <[email protected]> Co-authored-by: Radosław Krawczyk <[email protected]> Co-authored-by: Maciej Miś <[email protected]> [TT-16017]: https://tyktech.atlassian.net/browse/TT-16017?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ Co-authored-by: Long Le <[email protected]> Co-authored-by: Long Le <[email protected]> Co-authored-by: Radosław Krawczyk <[email protected]> Co-authored-by: Maciej Miś <[email protected]>
… APIs (#7831) (#8298) [TT-16017] Fix issue with strip auth data GQL APIs (#7831) <!-- Provide a general summary of your changes in the Title above --> ## Description <!-- Describe your changes in detail --> ## Related Issue <!-- This project only accepts pull requests related to open issues. --> <!-- If suggesting a new feature or change, please discuss it in an issue first. --> <!-- If fixing a bug, there should be an issue describing it with steps to reproduce. --> <!-- OSS: Please link to the issue here. Tyk: please create/link the JIRA ticket. --> ## Motivation and Context <!-- Why is this change required? What problem does it solve? --> ## How This Has Been Tested <!-- Please describe in detail how you tested your changes --> <!-- Include details of your testing environment, and the tests --> <!-- you ran to see how your change affects other areas of the code, etc. --> <!-- This information is helpful for reviewers and QA. --> ## Screenshots (if appropriate) ## Types of changes <!-- What types of changes does your code introduce? Put an `x` in all the boxes that apply: --> - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Refactoring or add test (improvements in base code or adds test coverage to functionality) ## Checklist <!-- Go over all the following points, and put an `x` in all the boxes that apply --> <!-- If there are no documentation updates required, mark the item as checked. --> <!-- Raise up any additional concerns not covered by the checklist. --> - [ ] I ensured that the documentation is up to date - [ ] I explained why this PR updates go.mod in detail with reasoning why it's required - [ ] I would like a code coverage CI quality gate exception and have explained why <!---TykTechnologies/jira-linter starts here--> ### Ticket Details <details> <summary> <a href="https://tyktech.atlassian.net/browse/TT-16017" title="TT-16017" target="_blank">TT-16017</a> </summary> | | | |---------|----| | Status | In Dev | | Summary | Authorization headers are not passed to the upstream service for GraphQL subscriptions over WebSockets | Generated at: 2026-06-02 08:43:48 </details> <!---TykTechnologies/jira-linter ends here--> --------- Co-authored-by: Long Le <[email protected]> Co-authored-by: Radosław Krawczyk <[email protected]> Co-authored-by: Maciej Miś <[email protected]> [TT-16017]: https://tyktech.atlassian.net/browse/TT-16017?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ Co-authored-by: Long Le <[email protected]> Co-authored-by: Long Le <[email protected]> Co-authored-by: Radosław Krawczyk <[email protected]> Co-authored-by: Maciej Miś <[email protected]>





Description
Related Issue
Motivation and Context
How This Has Been Tested
Screenshots (if appropriate)
Types of changes
Checklist
Ticket Details
TT-16017
Generated at: 2026-06-02 08:43:48