Skip to content

Commit 2fb22f8

Browse files
committed
fix: Disable CA Cert validation on authless MCPs
Signed-off-by: Gabriel Costa <gabrielcg@proton.me>
1 parent 2e8542a commit 2fb22f8

5 files changed

Lines changed: 533 additions & 14 deletions

File tree

.secrets.baseline

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"files": "(?x)( package-lock\\.json$ |Cargo\\.lock$ |uv\\.lock$ |go\\.sum$ |mcpgateway/sri_hashes\\.json$ )|^.secrets.baseline$",
44
"lines": null
55
},
6-
"generated_at": "2026-06-05T12:31:44Z",
6+
"generated_at": "2026-06-05T14:10:37Z",
77
"plugins_used": [
88
{
99
"name": "AWSKeyDetector"
@@ -4336,7 +4336,7 @@
43364336
"hashed_secret": "c377074d6473f35a91001981355da793dc808ffd",
43374337
"is_secret": false,
43384338
"is_verified": false,
4339-
"line_number": 4323,
4339+
"line_number": 4397,
43404340
"type": "Hex High Entropy String",
43414341
"verified_result": null
43424342
}

mcpgateway/schemas.py

Lines changed: 122 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2857,19 +2857,52 @@ class GatewayCreate(BaseModelWithConfigDict):
28572857
auth_query_param_key: Optional[str] = Field(
28582858
None,
28592859
description="Query parameter name for authentication (e.g., 'api_key', 'tavilyApiKey')",
2860-
pattern=r"^[a-zA-Z_][a-zA-Z0-9_\-]*$",
28612860
)
28622861
auth_query_param_value: Optional[SecretStr] = Field(
28632862
None,
28642863
description="Query parameter value (API key). Stored encrypted.",
28652864
)
28662865

2866+
@field_validator("auth_query_param_key")
2867+
@classmethod
2868+
def validate_auth_query_param_key(cls, v: Optional[str]) -> Optional[str]:
2869+
"""Validate query param key format only if provided and non-empty.
2870+
2871+
Args:
2872+
v: Query parameter key to validate
2873+
2874+
Returns:
2875+
The validated query parameter key
2876+
2877+
Raises:
2878+
ValueError: If the key format is invalid
2879+
"""
2880+
if v is not None and v != "":
2881+
if not re.match(r"^[a-zA-Z_][a-zA-Z0-9_\-]*$", v):
2882+
raise ValueError("Query parameter key must start with a letter or underscore, " "followed by letters, numbers, underscores, or hyphens")
2883+
return v
2884+
28672885
# Adding `auth_value` as an alias for better access post-validation
28682886
auth_value: Optional[str] = Field(None, validate_default=True)
28692887

28702888
# One time auth - do not store the auth in gateway flag
28712889
one_time_auth: Optional[bool] = Field(default=False, description="The authentication should be used only once and not stored in the gateway")
28722890

2891+
@field_validator("auth_type", mode="before")
2892+
@classmethod
2893+
def normalize_auth_type(cls, v: Any) -> Optional[str]:
2894+
"""Normalize auth_type: convert string 'none' or 'None' to None.
2895+
2896+
Args:
2897+
v: The auth_type value (may be string "none" or "None")
2898+
2899+
Returns:
2900+
None if v is "none" or "None", otherwise returns v unchanged
2901+
"""
2902+
if isinstance(v, str) and v.lower() == "none":
2903+
return None
2904+
return v
2905+
28732906
tags: Optional[List[Union[str, Dict[str, str]]]] = Field(default_factory=list, description="Tags for categorizing the gateway")
28742907

28752908
# Team scoping fields for resource organization
@@ -3143,7 +3176,11 @@ def _process_auth_fields(info: ValidationInfo) -> Optional[str]:
31433176
# Validation is handled by model_validator
31443177
return None
31453178

3146-
raise ValueError("Invalid 'auth_type'. Must be one of: basic, bearer, oauth, authheaders, or query_param.")
3179+
# Handle no authentication (None or already normalized from "none")
3180+
if auth_type is None:
3181+
return None
3182+
3183+
raise ValueError("Invalid 'auth_type'. Must be one of: basic, bearer, oauth, authheaders, query_param, or none.")
31473184

31483185
@model_validator(mode="after")
31493186
def validate_query_param_auth(self) -> "GatewayCreate":
@@ -3207,6 +3244,21 @@ class GatewayUpdate(BaseModelWithConfigDict):
32073244
# Adding `auth_value` as an alias for better access post-validation
32083245
auth_value: Optional[str] = Field(None, validate_default=True)
32093246

3247+
@field_validator("auth_type", mode="before")
3248+
@classmethod
3249+
def normalize_auth_type(cls, v: Any) -> Optional[str]:
3250+
"""Normalize auth_type: convert string 'none' or 'None' to None.
3251+
3252+
Args:
3253+
v: The auth_type value (may be string "none" or "None")
3254+
3255+
Returns:
3256+
None if v is "none" or "None", otherwise returns v unchanged
3257+
"""
3258+
if isinstance(v, str) and v.lower() == "none":
3259+
return None
3260+
return v
3261+
32103262
# OAuth 2.0 configuration
32113263
oauth_config: Optional[Dict[str, Any]] = Field(
32123264
None, description="OAuth 2.0 configuration including grant_type, client_id, encrypted client_secret, URLs, scopes, audience (for Atlassian/Auth0), and resource (RFC 8707)"
@@ -3216,13 +3268,31 @@ class GatewayUpdate(BaseModelWithConfigDict):
32163268
auth_query_param_key: Optional[str] = Field(
32173269
None,
32183270
description="Query parameter name for authentication",
3219-
pattern=r"^[a-zA-Z_][a-zA-Z0-9_\-]*$",
32203271
)
32213272
auth_query_param_value: Optional[SecretStr] = Field(
32223273
None,
32233274
description="Query parameter value (API key)",
32243275
)
32253276

3277+
@field_validator("auth_query_param_key")
3278+
@classmethod
3279+
def validate_auth_query_param_key(cls, v: Optional[str]) -> Optional[str]:
3280+
"""Validate query param key format only if provided and non-empty.
3281+
3282+
Args:
3283+
v: Query parameter key to validate
3284+
3285+
Returns:
3286+
The validated query parameter key
3287+
3288+
Raises:
3289+
ValueError: If the key format is invalid
3290+
"""
3291+
if v is not None and v != "":
3292+
if not re.match(r"^[a-zA-Z_][a-zA-Z0-9_\-]*$", v):
3293+
raise ValueError("Query parameter key must start with a letter or underscore, " "followed by letters, numbers, underscores, or hyphens")
3294+
return v
3295+
32263296
# One time auth - do not store the auth in gateway flag
32273297
one_time_auth: Optional[bool] = Field(default=False, description="The authentication should be used only once and not stored in the gateway")
32283298

@@ -3460,7 +3530,11 @@ def _process_auth_fields(info: ValidationInfo) -> Optional[str]:
34603530
# Validation is handled by model_validator
34613531
return None
34623532

3463-
raise ValueError("Invalid 'auth_type'. Must be one of: basic, bearer, oauth, authheaders, or query_param.")
3533+
# Handle no authentication (None or already normalized from "none")
3534+
if auth_type is None:
3535+
return None
3536+
3537+
raise ValueError("Invalid 'auth_type'. Must be one of: basic, bearer, oauth, authheaders, query_param, or none.")
34643538

34653539
@model_validator(mode="after")
34663540
def validate_query_param_auth(self) -> "GatewayUpdate":
@@ -4661,6 +4735,22 @@ class A2AAgentCreate(BaseModel):
46614735

46624736
# Adding `auth_value` as an alias for better access post-validation
46634737
auth_value: Optional[str] = Field(None, validate_default=True)
4738+
4739+
@field_validator("auth_type", mode="before")
4740+
@classmethod
4741+
def normalize_auth_type(cls, v: Any) -> Optional[str]:
4742+
"""Normalize auth_type: convert string 'none' or 'None' to None.
4743+
4744+
Args:
4745+
v: The auth_type value (may be string "none" or "None")
4746+
4747+
Returns:
4748+
None if v is "none" or "None", otherwise returns v unchanged
4749+
"""
4750+
if isinstance(v, str) and v.lower() == "none":
4751+
return None
4752+
return v
4753+
46644754
tags: List[str] = Field(default_factory=list, description="Tags for categorizing the agent")
46654755

46664756
# Team scoping fields
@@ -4909,7 +4999,11 @@ def _process_auth_fields(info: ValidationInfo) -> Optional[str]:
49094999
# Validation is handled by model_validator
49105000
return None
49115001

4912-
raise ValueError("Invalid 'auth_type'. Must be one of: basic, bearer, oauth, authheaders, or query_param.")
5002+
# Handle no authentication (None or already normalized from "none")
5003+
if auth_type is None:
5004+
return None
5005+
5006+
raise ValueError("Invalid 'auth_type'. Must be one of: basic, bearer, oauth, authheaders, query_param, or none.")
49135007

49145008
@model_validator(mode="after")
49155009
def validate_query_param_auth(self) -> "A2AAgentCreate":
@@ -5002,6 +5096,24 @@ class A2AAgentUpdate(BaseModelWithConfigDict):
50025096
version: Optional[str] = Field(default=None, description="Agent version for UAID generation")
50035097
uaid_native_id_override: Optional[str] = Field(None, description="Override nativeId in UAID for cross-gateway routing (defaults to endpoint_url if not provided)")
50045098

5099+
@field_validator("auth_type")
5100+
@classmethod
5101+
def normalize_auth_type(cls, v: Any) -> Optional[str]:
5102+
"""Normalize auth_type by converting string 'none' or 'None' to Python None.
5103+
5104+
This allows the UI to send "none" as a string value which gets converted
5105+
to Python None for proper handling throughout the system.
5106+
5107+
Args:
5108+
v: The auth_type value (can be str or None)
5109+
5110+
Returns:
5111+
The normalized auth_type (None if input was "none"/"None", otherwise unchanged)
5112+
"""
5113+
if isinstance(v, str) and v.lower() == "none":
5114+
return None
5115+
return v
5116+
50055117
@field_validator("tags")
50065118
@classmethod
50075119
def validate_tags(cls, v: Optional[List[str]]) -> Optional[List[str]]:
@@ -5239,7 +5351,11 @@ def _process_auth_fields(info: ValidationInfo) -> Optional[str]:
52395351
# Validation is handled by model_validator
52405352
return None
52415353

5242-
raise ValueError("Invalid 'auth_type'. Must be one of: basic, bearer, oauth, authheaders, or query_param.")
5354+
# Handle no authentication (None or already normalized from "none")
5355+
if auth_type is None:
5356+
return None
5357+
5358+
raise ValueError("Invalid 'auth_type'. Must be one of: basic, bearer, oauth, authheaders, query_param, or none.")
52435359

52445360
@model_validator(mode="after")
52455361
def validate_query_param_auth(self) -> "A2AAgentUpdate":

mcpgateway/templates/admin.html

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3896,7 +3896,7 @@ <h3 class="text-lg font-bold dark:text-gray-200">
38963896
id="auth-type"
38973897
class="mt-1 px-3 py-2 block w-full rounded-md border border-gray-300 dark:border-gray-700 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:bg-gray-900 dark:placeholder-gray-300 dark:text-gray-300"
38983898
>
3899-
<option value="">None</option>
3899+
<option value="none">None</option>
39003900
<option value="basic">Basic</option>
39013901
<option value="bearer">Bearer Token</option>
39023902
<option value="authheaders">Custom Headers</option>
@@ -5444,7 +5444,7 @@ <h3 class="text-lg font-bold mb-4 dark:text-gray-200">
54445444
id="auth-type-gw"
54455445
class="mt-1 px-3 py-2 block w-full rounded-md border border-gray-300 dark:border-gray-700 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:bg-gray-900 dark:placeholder-gray-300 dark:text-gray-300"
54465446
>
5447-
<option value="">None</option>
5447+
<option value="none">None</option>
54485448
<option value="basic">Basic</option>
54495449
<option value="bearer">Bearer Token</option>
54505450
<option value="authheaders">Custom Headers</option>
@@ -6886,7 +6886,7 @@ <h3 class="text-lg font-medium text-gray-900 dark:text-gray-200 mb-4">
68866886
name="auth_type"
68876887
class="mt-1 px-3 py-2 block w-full rounded-md border border-gray-300 dark:border-gray-700 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:bg-gray-900 dark:text-gray-300"
68886888
>
6889-
<option value="">None</option>
6889+
<option value="none">None</option>
68906890
<option value="basic">Basic</option>
68916891
<option value="bearer">Bearer Token</option>
68926892
<option value="authheaders">Custom Headers</option>
@@ -8795,7 +8795,7 @@ <h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">
87958795
id="edit-auth-type"
87968796
class="mt-1 px-3 py-2 block w-full rounded-md border border-gray-300 dark:border-gray-600 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:bg-gray-900 dark:placeholder-gray-300 dark:text-gray-300"
87978797
>
8798-
<option value="">None</option>
8798+
<option value="none">None</option>
87998799
<option value="basic">Basic</option>
88008800
<option value="bearer">Bearer Token</option>
88018801
<option value="authheaders">Custom Headers</option>
@@ -9980,7 +9980,7 @@ <h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">
99809980
id="auth-type-gw-edit"
99819981
class="mt-1 px-3 py-2 block w-full rounded-md border border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:bg-gray-900 dark:placeholder-gray-300 dark:text-gray-300"
99829982
>
9983-
<option value="">None</option>
9983+
<option value="none">None</option>
99849984
<option value="basic">Basic</option>
99859985
<option value="bearer">Bearer Token</option>
99869986
<option value="authheaders">Custom Headers</option>
@@ -10487,7 +10487,7 @@ <h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">
1048710487
class="mt-1 px-3 py-2 block w-full rounded-md border border-gray-300 dark:border-gray-700 shadow-sm
1048810488
focus:border-indigo-500 focus:ring-indigo-500 dark:bg-gray-900 dark:text-gray-300"
1048910489
>
10490-
<option value="">None</option>
10490+
<option value="none">None</option>
1049110491
<option value="basic">Basic</option>
1049210492
<option value="bearer">Bearer Token</option>
1049310493
<option value="authheaders">Custom Headers</option>

0 commit comments

Comments
 (0)