Skip to content

Commit 36a8bb9

Browse files
Merge pull request #35 from keycardai/feat/extra-eks-vars
Feat/extra eks vars
2 parents cc18942 + 260085f commit 36a8bb9

File tree

8 files changed

+46
-19
lines changed

8 files changed

+46
-19
lines changed

docs/sdk/keycardai-mcp-server-auth-application_credentials.mdx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,13 @@ via initialization parameters or environment variables.
276276
The token is read fresh on each token exchange request, allowing for token rotation
277277
without requiring application restart.
278278

279+
**Environment Variables:**
280+
281+
When configured via `KEYCARD_APPLICATION_CREDENTIAL_TYPE="eks_workload_identity"`, the token file path is discovered from:
282+
1. `KEYCARD_EKS_WORKLOAD_IDENTITY_TOKEN_FILE` - Custom token file path (highest priority)
283+
2. `AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE` - AWS EKS default location
284+
3. `AWS_WEB_IDENTITY_TOKEN_FILE` - AWS fallback location
285+
279286

280287
**Methods:**
281288

packages/mcp-fastmcp/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,8 @@ export KEYCARD_CLIENT_SECRET="your_client_secret"
406406
```bash
407407
# For EKS Workload Identity
408408
export KEYCARD_APPLICATION_CREDENTIAL_TYPE="eks_workload_identity"
409-
export AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE="/var/run/secrets/eks.amazonaws.com/serviceaccount/token"
409+
# Optional: Custom token file path (defaults to AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE or AWS_WEB_IDENTITY_TOKEN_FILE)
410+
export KEYCARD_EKS_WORKLOAD_IDENTITY_TOKEN_FILE="/var/run/secrets/eks.amazonaws.com/serviceaccount/token"
410411

411412
# For Web Identity
412413
export KEYCARD_APPLICATION_CREDENTIAL_TYPE="web_identity"

packages/mcp-fastmcp/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ dependencies = [
1212
"httpx>=0.27.2",
1313
"keycardai-oauth>=0.5.0",
1414
"fastmcp==2.13.0",
15-
"keycardai-mcp>=0.14.0",
15+
"keycardai-mcp>=0.15.0",
1616
]
1717
keywords = ["fastmcp", "mcp", "model-context-protocol", "oauth", "token-exchange", "authentication", "keycard"]
1818
classifiers = [

packages/mcp-fastmcp/src/keycardai/mcp/integrations/fastmcp/provider.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,8 @@ def _discover_application_credential(self, application_credential: ApplicationCr
330330

331331
application_credential_type = os.getenv("KEYCARD_APPLICATION_CREDENTIAL_TYPE")
332332
if application_credential_type == "eks_workload_identity":
333-
return EKSWorkloadIdentity()
333+
custom_token_file_path = os.getenv("KEYCARD_EKS_WORKLOAD_IDENTITY_TOKEN_FILE")
334+
return EKSWorkloadIdentity(token_file_path=custom_token_file_path)
334335
elif application_credential_type == "web_identity":
335336
key_storage_dir = os.getenv("KEYCARD_WEB_IDENTITY_KEY_STORAGE_DIR")
336337
return WebIdentity(
@@ -343,8 +344,7 @@ def _discover_application_credential(self, application_credential: ApplicationCr
343344
)
344345

345346
# detect workload identity from environment variables
346-
workload_identity_token = os.getenv(EKSWorkloadIdentity.default_env_var_name)
347-
if workload_identity_token:
347+
if any(os.getenv(env_name) for env_name in EKSWorkloadIdentity.default_env_var_names):
348348
return EKSWorkloadIdentity()
349349

350350
return None

packages/mcp/README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,8 @@ export KEYCARD_WEB_IDENTITY_KEY_STORAGE_DIR="./mcp_keys" # Optional
260260

261261
# Option C: EKS Workload Identity
262262
export KEYCARD_APPLICATION_CREDENTIAL_TYPE="eks_workload_identity"
263-
export AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE="/var/run/secrets/token"
263+
# Optional: Custom token file path (defaults to AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE or AWS_WEB_IDENTITY_TOKEN_FILE)
264+
export KEYCARD_EKS_WORKLOAD_IDENTITY_TOKEN_FILE="/var/run/secrets/token"
264265
```
265266

266267
With environment variables configured, create the `AuthProvider` without explicit credentials:
@@ -293,7 +294,9 @@ When multiple configuration methods are present, the SDK follows this precedence
293294
| `KEYCARD_CLIENT_SECRET` | OAuth client secret | `ClientSecret` | None |
294295
| `KEYCARD_APPLICATION_CREDENTIAL_TYPE` | Explicit credential type selection | All | None |
295296
| `KEYCARD_WEB_IDENTITY_KEY_STORAGE_DIR` | Directory for private key storage | `WebIdentity` | `"./mcp_keys"` |
296-
| `AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE` | Path to EKS token file | `EKSWorkloadIdentity` | None |
297+
| `KEYCARD_EKS_WORKLOAD_IDENTITY_TOKEN_FILE` | Custom path to EKS token file | `EKSWorkloadIdentity` | None |
298+
| `AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE` | Path to EKS token file (AWS default) | `EKSWorkloadIdentity` | None |
299+
| `AWS_WEB_IDENTITY_TOKEN_FILE` | Path to EKS token file (AWS fallback) | `EKSWorkloadIdentity` | None |
297300

298301
##### Running Without Application Credentials
299302

packages/mcp/src/keycardai/mcp/server/auth/application_credentials.py

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -409,8 +409,13 @@ class EKSWorkloadIdentity:
409409
The token is read fresh on each token exchange request, allowing for token rotation
410410
without requiring application restart.
411411
412+
Environment Variable Discovery (when token_file_path is not provided):
413+
1. KEYCARD_EKS_WORKLOAD_IDENTITY_TOKEN_FILE - Custom token file path (highest priority)
414+
2. AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE - AWS EKS default location
415+
3. AWS_WEB_IDENTITY_TOKEN_FILE - AWS fallback location
416+
412417
Example:
413-
# Default configuration (uses AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE env var)
418+
# Default configuration (discovers from environment variables)
414419
provider = EKSWorkloadIdentity()
415420
416421
# Explicit token file path
@@ -423,39 +428,50 @@ class EKSWorkloadIdentity:
423428
env_var_name="MY_CUSTOM_TOKEN_FILE_ENV_VAR"
424429
)
425430
"""
426-
default_env_var_name = "AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE"
431+
default_env_var_names = ["AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE", "AWS_WEB_IDENTITY_TOKEN_FILE"]
427432

428433
def __init__(
429434
self,
430435
token_file_path: str | None = None,
431-
env_var_name: str = default_env_var_name,
436+
env_var_name: str | None = None,
432437
):
433438
"""Initialize EKS workload identity provider.
434439
435440
Args:
436441
token_file_path: Explicit path to the token file. If not provided,
437442
reads from the environment variable specified by env_var_name.
438443
env_var_name: Name of the environment variable containing the token file path.
439-
Defaults to AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE.
440444
441445
Raises:
442446
EKSWorkloadIdentityConfigurationError: If token file cannot be read or is empty.
443447
"""
444-
self.env_var_name = env_var_name
445-
446448
if token_file_path is not None:
447449
self.token_file_path = token_file_path
450+
self.env_var_name = env_var_name # Store the env_var_name even when token_file_path is provided
448451
else:
449-
self.token_file_path = os.environ.get(env_var_name)
452+
self.token_file_path, self.env_var_name = self._get_token_file_path(env_var_name)
450453
if not self.token_file_path:
451454
raise EKSWorkloadIdentityConfigurationError(
452455
token_file_path=None,
453456
env_var_name=env_var_name,
454-
error_details=f"Environment variable {env_var_name} is not set",
457+
error_details="Could not find token file path in environment variables",
455458
)
456459

457460
self._validate_token_file()
458461

462+
def _get_token_file_path(self, env_var_name: str | None) -> tuple[str, str]:
463+
"""Get the token file path from the environment variables.
464+
465+
Returns:
466+
Tuple containing the token file path and the environment variable name.
467+
"""
468+
env_names = self.default_env_var_names if env_var_name is None else [env_var_name, *self.default_env_var_names]
469+
return next((
470+
(os.environ.get(env_name), env_name)
471+
for env_name in env_names
472+
if os.environ.get(env_name)
473+
), (None, None))
474+
459475
def _validate_token_file(self) -> None:
460476
"""Validate that the token file exists and can be read.
461477

packages/mcp/src/keycardai/mcp/server/auth/provider.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,8 @@ def _discover_application_credential(self, application_credential: ApplicationCr
293293

294294
application_credential_type = os.getenv("KEYCARD_APPLICATION_CREDENTIAL_TYPE")
295295
if application_credential_type == "eks_workload_identity":
296-
return EKSWorkloadIdentity()
296+
custom_token_file_path = os.getenv("KEYCARD_EKS_WORKLOAD_IDENTITY_TOKEN_FILE")
297+
return EKSWorkloadIdentity(token_file_path=custom_token_file_path)
297298
elif application_credential_type == "web_identity":
298299
key_storage_dir = os.getenv("KEYCARD_WEB_IDENTITY_KEY_STORAGE_DIR")
299300
return WebIdentity(
@@ -306,8 +307,7 @@ def _discover_application_credential(self, application_credential: ApplicationCr
306307
)
307308

308309
# detect workload identity from environment variables
309-
workload_identity_token = os.getenv(EKSWorkloadIdentity.default_env_var_name)
310-
if workload_identity_token:
310+
if any(os.getenv(env_name) for env_name in EKSWorkloadIdentity.default_env_var_names):
311311
return EKSWorkloadIdentity()
312312

313313
return None

packages/mcp/tests/keycardai/mcp/server/auth/test_application_identity.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ async def test_initialization_fails_when_env_var_not_set(self):
330330
EKSWorkloadIdentity()
331331

332332
assert "Failed to initialize EKS workload identity" in str(exc_info.value)
333-
assert "AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE" in str(exc_info.value)
333+
assert "Could not find token file path in environment variables" in str(exc_info.value)
334334

335335
@pytest.mark.asyncio
336336
async def test_initialization_fails_when_token_file_empty(self):

0 commit comments

Comments
 (0)