graphql-http 2.0.2__tar.gz → 2.0.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {graphql_http-2.0.2 → graphql_http-2.0.3}/PKG-INFO +1 -1
- graphql_http-2.0.3/VERSION +1 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/examples/auth_server.py +9 -9
- {graphql_http-2.0.2 → graphql_http-2.0.3}/graphql_http/server.py +4 -8
- {graphql_http-2.0.2 → graphql_http-2.0.3}/graphql_http.egg-info/PKG-INFO +1 -1
- {graphql_http-2.0.2 → graphql_http-2.0.3}/tests/test_auth.py +13 -15
- graphql_http-2.0.2/VERSION +0 -1
- {graphql_http-2.0.2 → graphql_http-2.0.3}/.coverage +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/.github/workflows/python-package.yml +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/.gitignore +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/LICENSE +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/README.md +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/examples/README.md +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/examples/advanced_server.py +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/examples/basic_server.py +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/examples/graphql_api_server.py +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/graphql_http/__init__.py +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/graphql_http/error.py +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/graphql_http/graphiql/index.html +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/graphql_http/helpers.py +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/graphql_http.egg-info/SOURCES.txt +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/graphql_http.egg-info/dependency_links.txt +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/graphql_http.egg-info/requires.txt +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/graphql_http.egg-info/top_level.txt +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/pyproject.toml +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/setup.cfg +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/test_server.py +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/tests/__init__.py +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/tests/app.py +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/tests/app_federation.py +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/tests/conftest.py +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/tests/test_cors.py +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/tests/test_federation.py +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/tests/test_graphql_api.py +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/tests/test_server.py +0 -0
- {graphql_http-2.0.2 → graphql_http-2.0.3}/uv.lock +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.0.3
|
|
@@ -92,7 +92,7 @@ schema = GraphQLSchema(
|
|
|
92
92
|
resolve=get_public_info,
|
|
93
93
|
description="Public information available without authentication",
|
|
94
94
|
),
|
|
95
|
-
|
|
95
|
+
|
|
96
96
|
# Protected fields - require authentication
|
|
97
97
|
"me": GraphQLField(
|
|
98
98
|
UserType,
|
|
@@ -120,20 +120,20 @@ def create_server_with_auth():
|
|
|
120
120
|
jwks_uri = os.getenv("JWKS_URI", "https://your-auth0-domain.auth0.com/.well-known/jwks.json")
|
|
121
121
|
issuer = os.getenv("JWT_ISSUER", "https://your-auth0-domain.auth0.com/")
|
|
122
122
|
audience = os.getenv("JWT_AUDIENCE", "your-api-identifier")
|
|
123
|
-
|
|
123
|
+
|
|
124
124
|
return GraphQLHTTP(
|
|
125
125
|
schema=schema,
|
|
126
126
|
serve_graphiql=True,
|
|
127
127
|
allow_cors=True, # Enable CORS for web applications
|
|
128
128
|
health_path="/health", # Health check endpoint
|
|
129
|
-
|
|
129
|
+
|
|
130
130
|
# JWT Authentication configuration
|
|
131
131
|
auth_enabled=True,
|
|
132
132
|
auth_jwks_uri=jwks_uri,
|
|
133
133
|
auth_issuer=issuer,
|
|
134
134
|
auth_audience=audience,
|
|
135
|
-
|
|
136
|
-
|
|
135
|
+
auth_bypass_during_introspection=False,
|
|
136
|
+
|
|
137
137
|
graphiql_default_query="""
|
|
138
138
|
# Try these queries:
|
|
139
139
|
|
|
@@ -160,7 +160,7 @@ def create_server_with_auth():
|
|
|
160
160
|
# role
|
|
161
161
|
# }
|
|
162
162
|
# }
|
|
163
|
-
#
|
|
163
|
+
#
|
|
164
164
|
# {
|
|
165
165
|
# users {
|
|
166
166
|
# id
|
|
@@ -204,7 +204,7 @@ def main():
|
|
|
204
204
|
"""Run the GraphQL server."""
|
|
205
205
|
# Check if authentication should be enabled
|
|
206
206
|
enable_auth = os.getenv("ENABLE_AUTH", "false").lower() == "true"
|
|
207
|
-
|
|
207
|
+
|
|
208
208
|
if enable_auth:
|
|
209
209
|
print("Starting GraphQL server with JWT authentication...")
|
|
210
210
|
server = create_server_with_auth()
|
|
@@ -220,12 +220,12 @@ def main():
|
|
|
220
220
|
server = create_server_without_auth()
|
|
221
221
|
print("\nAuthentication disabled for development!")
|
|
222
222
|
print("Set ENABLE_AUTH=true to enable authentication")
|
|
223
|
-
|
|
223
|
+
|
|
224
224
|
print(f"\nEndpoints:")
|
|
225
225
|
print(f" GraphiQL: http://localhost:8000/graphql")
|
|
226
226
|
print(f" Health: http://localhost:8000/health")
|
|
227
227
|
print(f" API: http://localhost:8000/graphql")
|
|
228
|
-
|
|
228
|
+
|
|
229
229
|
# Run the server
|
|
230
230
|
server.run(host="0.0.0.0", port=8000)
|
|
231
231
|
|
|
@@ -94,7 +94,7 @@ class GraphQLHTTP:
|
|
|
94
94
|
auth_issuer: Optional[str] = None,
|
|
95
95
|
auth_audience: Optional[str] = None,
|
|
96
96
|
auth_enabled: bool = False,
|
|
97
|
-
|
|
97
|
+
auth_bypass_during_introspection: bool = True,
|
|
98
98
|
) -> None:
|
|
99
99
|
"""Initialize GraphQL HTTP server.
|
|
100
100
|
|
|
@@ -112,7 +112,7 @@ class GraphQLHTTP:
|
|
|
112
112
|
auth_issuer: Expected JWT issuer
|
|
113
113
|
auth_audience: Expected JWT audience
|
|
114
114
|
auth_enabled: Whether to enable JWT authentication
|
|
115
|
-
|
|
115
|
+
auth_bypass_during_introspection: Whether auth is required for introspection only queries
|
|
116
116
|
|
|
117
117
|
Raises:
|
|
118
118
|
ValueError: If invalid configuration is provided
|
|
@@ -142,7 +142,7 @@ class GraphQLHTTP:
|
|
|
142
142
|
self.auth_issuer = auth_issuer
|
|
143
143
|
self.auth_audience = auth_audience
|
|
144
144
|
self.auth_enabled = auth_enabled
|
|
145
|
-
self.
|
|
145
|
+
self.auth_bypass_during_introspection = auth_bypass_during_introspection
|
|
146
146
|
|
|
147
147
|
if auth_jwks_uri:
|
|
148
148
|
self.jwks_client = PyJWKClient(auth_jwks_uri)
|
|
@@ -325,8 +325,6 @@ class GraphQLHTTP:
|
|
|
325
325
|
Returns:
|
|
326
326
|
True if request is introspection-only
|
|
327
327
|
"""
|
|
328
|
-
if not self.auth_enabled or self.auth_enabled_for_introspection:
|
|
329
|
-
return False
|
|
330
328
|
|
|
331
329
|
# Simple heuristic: if it contains introspection fields AND no regular fields
|
|
332
330
|
query_data_lower = str(data).lower()
|
|
@@ -444,9 +442,7 @@ class GraphQLHTTP:
|
|
|
444
442
|
# Handle authentication
|
|
445
443
|
allow_only_introspection = False
|
|
446
444
|
if self.auth_enabled:
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
if not allow_only_introspection:
|
|
445
|
+
if not self.auth_bypass_during_introspection or not self._check_introspection_only(data):
|
|
450
446
|
auth_error = self._authenticate_request(request)
|
|
451
447
|
if auth_error:
|
|
452
448
|
return auth_error
|
|
@@ -163,11 +163,11 @@ class TestGraphQLHTTPAuthentication:
|
|
|
163
163
|
assert "Invalid token" in result["errors"][0]["message"]
|
|
164
164
|
|
|
165
165
|
def test_introspection_without_auth_when_disabled(self, schema):
|
|
166
|
-
"""Test introspection queries work without auth when
|
|
166
|
+
"""Test introspection queries work without auth when auth_introspection_bypass=False."""
|
|
167
167
|
server = GraphQLHTTP(
|
|
168
168
|
schema=schema,
|
|
169
169
|
auth_enabled=True,
|
|
170
|
-
|
|
170
|
+
auth_bypass_during_introspection=False,
|
|
171
171
|
auth_jwks_uri="https://example.com/.well-known/jwks.json",
|
|
172
172
|
auth_issuer="https://example.com/",
|
|
173
173
|
auth_audience="test-audience"
|
|
@@ -179,18 +179,18 @@ class TestGraphQLHTTPAuthentication:
|
|
|
179
179
|
"/graphql",
|
|
180
180
|
json={"query": "{ __schema { queryType { name } } }"}
|
|
181
181
|
)
|
|
182
|
-
assert response.status_code ==
|
|
183
|
-
|
|
182
|
+
assert response.status_code == 401
|
|
183
|
+
result = response.json()
|
|
184
|
+
assert "errors" in result
|
|
185
|
+
assert "Authorization header is missing" in result["errors"][0]["message"]
|
|
184
186
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
"""Test introspection queries require auth when auth_enabled_for_introspection=True."""
|
|
188
|
-
mock_jwks_client_class.return_value = MagicMock()
|
|
187
|
+
def test_introspection_with_auth_when_enabled(self, schema):
|
|
188
|
+
"""Test introspection queries require auth when auth_introspection_bypass=True."""
|
|
189
189
|
|
|
190
190
|
server = GraphQLHTTP(
|
|
191
191
|
schema=schema,
|
|
192
192
|
auth_enabled=True,
|
|
193
|
-
|
|
193
|
+
auth_bypass_during_introspection=True,
|
|
194
194
|
auth_jwks_uri="https://example.com/.well-known/jwks.json",
|
|
195
195
|
auth_issuer="https://example.com/",
|
|
196
196
|
auth_audience="test-audience"
|
|
@@ -202,17 +202,15 @@ class TestGraphQLHTTPAuthentication:
|
|
|
202
202
|
"/graphql",
|
|
203
203
|
json={"query": "{ __schema { queryType { name } } }"}
|
|
204
204
|
)
|
|
205
|
-
assert response.status_code ==
|
|
206
|
-
|
|
207
|
-
assert "errors" in result
|
|
208
|
-
assert "Authorization header is missing" in result["errors"][0]["message"]
|
|
205
|
+
assert response.status_code == 200
|
|
206
|
+
# Should get a valid introspection response, not an auth error
|
|
209
207
|
|
|
210
208
|
def test_regular_query_requires_auth(self, schema):
|
|
211
209
|
"""Test that regular queries always require auth when auth is enabled."""
|
|
212
210
|
server = GraphQLHTTP(
|
|
213
211
|
schema=schema,
|
|
214
212
|
auth_enabled=True,
|
|
215
|
-
|
|
213
|
+
auth_bypass_during_introspection=False, # Only introspection is exempt
|
|
216
214
|
auth_jwks_uri="https://example.com/.well-known/jwks.json",
|
|
217
215
|
auth_issuer="https://example.com/",
|
|
218
216
|
auth_audience="test-audience"
|
|
@@ -274,7 +272,7 @@ class TestGraphQLHTTPAuthentication:
|
|
|
274
272
|
server = GraphQLHTTP(
|
|
275
273
|
schema=schema,
|
|
276
274
|
auth_enabled=True,
|
|
277
|
-
|
|
275
|
+
auth_bypass_during_introspection=False,
|
|
278
276
|
auth_jwks_uri="https://example.com/.well-known/jwks.json",
|
|
279
277
|
auth_issuer="https://example.com/",
|
|
280
278
|
auth_audience="test-audience"
|
graphql_http-2.0.2/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
2.0.2
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|