Skip to content

Commit 0ab4563

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

5 files changed

Lines changed: 346 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-05T08:00:42Z",
6+
"generated_at": "2026-06-05T10:34:47Z",
77
"plugins_used": [
88
{
99
"name": "AWSKeyDetector"
@@ -4328,7 +4328,7 @@
43284328
"hashed_secret": "c377074d6473f35a91001981355da793dc808ffd",
43294329
"is_secret": false,
43304330
"is_verified": false,
4331-
"line_number": 4317,
4331+
"line_number": 4391,
43324332
"type": "Hex High Entropy String",
43334333
"verified_result": null
43344334
}

mcpgateway/schemas.py

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

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

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

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

28732906
# Team scoping fields for resource organization
@@ -3141,7 +3174,11 @@ def _process_auth_fields(info: ValidationInfo) -> Optional[str]:
31413174
# Validation is handled by model_validator
31423175
return None
31433176

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

31463183
@model_validator(mode="after")
31473184
def validate_query_param_auth(self) -> "GatewayCreate":
@@ -3205,20 +3242,53 @@ class GatewayUpdate(BaseModelWithConfigDict):
32053242
# Adding `auth_value` as an alias for better access post-validation
32063243
auth_value: Optional[str] = Field(None, validate_default=True)
32073244

3245+
@field_validator("auth_type", mode="before")
3246+
@classmethod
3247+
def normalize_auth_type(cls, v: Any) -> Optional[str]:
3248+
"""Normalize auth_type: convert string 'none' or 'None' to None.
3249+
3250+
Args:
3251+
v: The auth_type value (may be string "none" or "None")
3252+
3253+
Returns:
3254+
None if v is "none" or "None", otherwise returns v unchanged
3255+
"""
3256+
if isinstance(v, str) and v.lower() == "none":
3257+
return None
3258+
return v
3259+
32083260
# OAuth 2.0 configuration
32093261
oauth_config: Optional[Dict[str, Any]] = Field(None, description="OAuth 2.0 configuration including grant_type, client_id, encrypted client_secret, URLs, and scopes")
32103262

32113263
# Query Parameter Authentication (INSECURE)
32123264
auth_query_param_key: Optional[str] = Field(
32133265
None,
32143266
description="Query parameter name for authentication",
3215-
pattern=r"^[a-zA-Z_][a-zA-Z0-9_\-]*$",
32163267
)
32173268
auth_query_param_value: Optional[SecretStr] = Field(
32183269
None,
32193270
description="Query parameter value (API key)",
32203271
)
32213272

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

@@ -3456,7 +3526,11 @@ def _process_auth_fields(info: ValidationInfo) -> Optional[str]:
34563526
# Validation is handled by model_validator
34573527
return None
34583528

3459-
raise ValueError("Invalid 'auth_type'. Must be one of: basic, bearer, oauth, authheaders, or query_param.")
3529+
# Handle no authentication (None or already normalized from "none")
3530+
if auth_type is None:
3531+
return None
3532+
3533+
raise ValueError("Invalid 'auth_type'. Must be one of: basic, bearer, oauth, authheaders, query_param, or none.")
34603534

34613535
@model_validator(mode="after")
34623536
def validate_query_param_auth(self) -> "GatewayUpdate":
@@ -4653,6 +4727,22 @@ class A2AAgentCreate(BaseModel):
46534727

46544728
# Adding `auth_value` as an alias for better access post-validation
46554729
auth_value: Optional[str] = Field(None, validate_default=True)
4730+
4731+
@field_validator("auth_type", mode="before")
4732+
@classmethod
4733+
def normalize_auth_type(cls, v: Any) -> Optional[str]:
4734+
"""Normalize auth_type: convert string 'none' or 'None' to None.
4735+
4736+
Args:
4737+
v: The auth_type value (may be string "none" or "None")
4738+
4739+
Returns:
4740+
None if v is "none" or "None", otherwise returns v unchanged
4741+
"""
4742+
if isinstance(v, str) and v.lower() == "none":
4743+
return None
4744+
return v
4745+
46564746
tags: List[str] = Field(default_factory=list, description="Tags for categorizing the agent")
46574747

46584748
# Team scoping fields
@@ -4901,7 +4991,11 @@ def _process_auth_fields(info: ValidationInfo) -> Optional[str]:
49014991
# Validation is handled by model_validator
49024992
return None
49034993

4904-
raise ValueError("Invalid 'auth_type'. Must be one of: basic, bearer, oauth, authheaders, or query_param.")
4994+
# Handle no authentication (None or already normalized from "none")
4995+
if auth_type is None:
4996+
return None
4997+
4998+
raise ValueError("Invalid 'auth_type'. Must be one of: basic, bearer, oauth, authheaders, query_param, or none.")
49054999

49065000
@model_validator(mode="after")
49075001
def validate_query_param_auth(self) -> "A2AAgentCreate":
@@ -4992,6 +5086,24 @@ class A2AAgentUpdate(BaseModelWithConfigDict):
49925086
version: Optional[str] = Field(default=None, description="Agent version for UAID generation")
49935087
uaid_native_id_override: Optional[str] = Field(None, description="Override nativeId in UAID for cross-gateway routing (defaults to endpoint_url if not provided)")
49945088

5089+
@field_validator("auth_type")
5090+
@classmethod
5091+
def normalize_auth_type(cls, v: Any) -> Optional[str]:
5092+
"""Normalize auth_type by converting string 'none' or 'None' to Python None.
5093+
5094+
This allows the UI to send "none" as a string value which gets converted
5095+
to Python None for proper handling throughout the system.
5096+
5097+
Args:
5098+
v: The auth_type value (can be str or None)
5099+
5100+
Returns:
5101+
The normalized auth_type (None if input was "none"/"None", otherwise unchanged)
5102+
"""
5103+
if isinstance(v, str) and v.lower() == "none":
5104+
return None
5105+
return v
5106+
49955107
@field_validator("tags")
49965108
@classmethod
49975109
def validate_tags(cls, v: Optional[List[str]]) -> Optional[List[str]]:
@@ -5229,7 +5341,11 @@ def _process_auth_fields(info: ValidationInfo) -> Optional[str]:
52295341
# Validation is handled by model_validator
52305342
return None
52315343

5232-
raise ValueError("Invalid 'auth_type'. Must be one of: basic, bearer, oauth, authheaders, or query_param.")
5344+
# Handle no authentication (None or already normalized from "none")
5345+
if auth_type is None:
5346+
return None
5347+
5348+
raise ValueError("Invalid 'auth_type'. Must be one of: basic, bearer, oauth, authheaders, query_param, or none.")
52335349

52345350
@model_validator(mode="after")
52355351
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>
@@ -6867,7 +6867,7 @@ <h3 class="text-lg font-medium text-gray-900 dark:text-gray-200 mb-4">
68676867
name="auth_type"
68686868
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"
68696869
>
6870-
<option value="">None</option>
6870+
<option value="none">None</option>
68716871
<option value="basic">Basic</option>
68726872
<option value="bearer">Bearer Token</option>
68736873
<option value="authheaders">Custom Headers</option>
@@ -8757,7 +8757,7 @@ <h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">
87578757
id="edit-auth-type"
87588758
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"
87598759
>
8760-
<option value="">None</option>
8760+
<option value="none">None</option>
87618761
<option value="basic">Basic</option>
87628762
<option value="bearer">Bearer Token</option>
87638763
<option value="authheaders">Custom Headers</option>
@@ -9942,7 +9942,7 @@ <h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">
99429942
id="auth-type-gw-edit"
99439943
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"
99449944
>
9945-
<option value="">None</option>
9945+
<option value="none">None</option>
99469946
<option value="basic">Basic</option>
99479947
<option value="bearer">Bearer Token</option>
99489948
<option value="authheaders">Custom Headers</option>
@@ -10449,7 +10449,7 @@ <h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">
1044910449
class="mt-1 px-3 py-2 block w-full rounded-md border border-gray-300 dark:border-gray-700 shadow-sm
1045010450
focus:border-indigo-500 focus:ring-indigo-500 dark:bg-gray-900 dark:text-gray-300"
1045110451
>
10452-
<option value="">None</option>
10452+
<option value="none">None</option>
1045310453
<option value="basic">Basic</option>
1045410454
<option value="bearer">Bearer Token</option>
1045510455
<option value="authheaders">Custom Headers</option>

0 commit comments

Comments
 (0)