Skip to content

Enforce Token Expiration Policy via REQUIRE_TOKEN_EXPIRATION Setting#2898

Merged
crivetimihai merged 1 commit intomainfrom
issue_2836__token_with_no_expdt
Feb 15, 2026
Merged

Enforce Token Expiration Policy via REQUIRE_TOKEN_EXPIRATION Setting#2898
crivetimihai merged 1 commit intomainfrom
issue_2836__token_with_no_expdt

Conversation

@rakdutta
Copy link
Copy Markdown
Collaborator

@rakdutta rakdutta commented Feb 13, 2026

closes issue #2836

This PR introduces support for enforcing a server policy that requires API tokens to have an expiration date. The enforcement is implemented at the service layer, reflected in the Admin UI, and covered with unit tests to ensure consistent behavior.


Key Changes

Backend Enforcement

  • Added validation to block token creation when expiration is required but not provided.
  • Validation checks REQUIRE_TOKEN_EXPIRATION before allowing token creation.
  • Clear error message is returned when policy is violated.

Files Updated

  • token_catalog_service.py

Admin UI Enhancements

  • Added visual indicator (*) when expiration is required.
  • Made the "Expires In (Days)" field conditionally mandatory.
  • Added helper text explaining expiration policy.

Files Updated

  • admin.html

Configuration Exposure

  • Exposed require_token_expiration setting through admin API.
  • Allows UI to dynamically adjust behavior based on server configuration.

Files Updated

  • admin.py

Behavior Scenarios

  • REQUIRE_TOKEN_EXPIRATION = false

    • Tokens can be created without expiration.
  • REQUIRE_TOKEN_EXPIRATION = true

    • Token creation fails if expiration is missing.
    • Clear validation error is returned.

Testing

  • Updated existing tests to include expiration where required.

  • Added new test cases covering:

    • Policy enabled/disabled scenarios
    • Tokens with and without expiration
    • Team and scoped token validation
    • Edge cases

Impact

  • Prevents creation of tokens without expiration when policy requires it.
  • Improves UI clarity for admins.
  • Ensures consistent enforcement across backend and frontend.

@rakdutta rakdutta changed the title Issue 2836 token with no expdt Enforce Token Expiration Policy via REQUIRE_TOKEN_EXPIRATION Setting Feb 13, 2026
Copy link
Copy Markdown
Member

@crivetimihai crivetimihai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this PR, @rakdutta. The enforcement logic is correct and test coverage is thorough (15 new test cases covering enabled/disabled x various expiry scenarios).

Negative expires_in_days is not guarded
A value like expires_in_days=-1 is truthy, so it passes the if expires_in_days: gate and produces expires_at in the past. The new check sees expires_at is not None and allows it. The UI's min="1" is the only defense; the API endpoint is unprotected. Consider adding a server-side if expires_in_days is not None and expires_in_days < 1: raise ValueError(...) guard.

HTML diff is ~95% formatter noise
Consider splitting the Prettier/formatting pass into a separate commit to make the functional changes (~20 lines) reviewable independently.

Good: Enforcement is correctly centralized in TokenCatalogService.create_token. Both API endpoints delegate to it, and session tokens (which use create_jwt_token() directly) are unaffected since they always include expiration. No migration needed — this is a runtime config setting.

@rakdutta
Copy link
Copy Markdown
Collaborator Author

rakdutta commented Feb 13, 2026

@crivetimihai , the current implementation already validates negative expires_in_days. I tested this from both the API endpoint and the Admin UI, and the user correctly receives a validation error.

expires_in_days must be greater than or equal to 1.
If 0 or a negative value is provided, the backend validation rejects the request with the appropriate error message.

Validation Implementation

mcpgateway/schemas.py (Line 6230)

expires_in_days: Optional[int] = Field(default=None, ge=1, description="Expiry in days (must be >= 1 if specified)")

The ge=1 constraint in the Pydantic Field ensures that:

  • Values of 0 or negative numbers are rejected at the API level.
  • Validation occurs before any business logic is executed.
  • Proper validation error messages are returned (as demonstrated below).

API Example

curl -X 'POST' \
  'http://localhost:4444/tokens' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer $mytoken' \
  -H 'Content-Type: application/json' \
  -d '{
  "name": "test_neg",
  "expires_in_days": 0,
  "is_active": true
}'

Error Response

{
  "detail": [
    {
      "type": "greater_than_equal",
      "loc": [
        "body",
        "expires_in_days"
      ],
      "msg": "Input should be greater than or equal to 1",
      "input": 0,
      "ctx": {
        "ge": 1
      }
    }
  ]
}

UI Behavior

If Expires in Days is set to 0 or -1, the same validation error is displayed in the UI.

UI Reference

image ```

@rakdutta rakdutta force-pushed the issue_2836__token_with_no_expdt branch from 2490f9d to e065423 Compare February 13, 2026 11:49
@rakdutta rakdutta marked this pull request as ready for review February 13, 2026 12:02
@crivetimihai crivetimihai self-assigned this Feb 14, 2026
@crivetimihai crivetimihai added this to the Release 1.0.0-RC1 milestone Feb 14, 2026
@crivetimihai crivetimihai force-pushed the issue_2836__token_with_no_expdt branch from e065423 to 9b1af4e Compare February 14, 2026 13:05
@crivetimihai
Copy link
Copy Markdown
Member

Rebased onto current main, squashed 9 commits into 1 clean commit, and cleaned up the diff:

Changes kept:

  • Service-layer enforcement of REQUIRE_TOKEN_EXPIRATION in TokenCatalogService.create_token()
  • Admin UI template: conditional required indicator, HTML5 validation, and helper text
  • require_token_expiration flag passed to admin template context
  • 6 new test cases covering policy enabled/disabled, team tokens, and edge cases
  • Updated existing tests to provide expires_in_days where needed

Cleanup applied:

  • Removed ~8900 lines of whitespace noise in admin.html (editor reformatting)
  • Trimmed redundant test permutations (13 → 6 distinct scenarios)
  • Simplified error message (removed config instructions from user-facing error)
  • Auto-formatted with autoflake, isort, black

All tests pass (token catalog service, tokens router, admin).

crivetimihai
crivetimihai previously approved these changes Feb 14, 2026
Copy link
Copy Markdown
Member

@crivetimihai crivetimihai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Rebased, cleaned up, and verified — all tests pass.

…tting

Enforce the existing REQUIRE_TOKEN_EXPIRATION config setting at
token creation time. Previously the setting only validated incoming
tokens but allowed creation of tokens without expiration.

- Add validation in TokenCatalogService.create_token() to reject
  tokens without expiration when REQUIRE_TOKEN_EXPIRATION=true
- Pass require_token_expiration flag to admin UI template context
- Add conditional required field indicator and helper text in the
  token creation form
- Update existing tests to provide expires_in_days where needed
- Add new test cases covering policy enabled/disabled scenarios,
  team tokens, and edge cases (zero expiry days)

Closes #2836

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
@crivetimihai
Copy link
Copy Markdown
Member

Rebased onto main and applied minor cleanups:

  • Merged implicit string concatenation into a single string in the ValueError message (token_catalog_service.py)
  • Replaced dead bare expressions in tests with actual assertions (assert mock_db.execute.called, assert mock_api_token.description == original_desc)
  • Removed spurious # Standard isort section markers from inline imports in test functions

All 122 token catalog service tests pass. Coverage on mcpgateway/services/token_catalog_service.py is 100% (324/324 statements).

@crivetimihai crivetimihai merged commit cffa514 into main Feb 15, 2026
54 checks passed
@crivetimihai crivetimihai deleted the issue_2836__token_with_no_expdt branch February 15, 2026 11:42
@crivetimihai crivetimihai restored the issue_2836__token_with_no_expdt branch February 15, 2026 12:21
suciu-daniel pushed a commit that referenced this pull request Feb 16, 2026
…tting (#2898)

Enforce the existing REQUIRE_TOKEN_EXPIRATION config setting at
token creation time. Previously the setting only validated incoming
tokens but allowed creation of tokens without expiration.

- Add validation in TokenCatalogService.create_token() to reject
  tokens without expiration when REQUIRE_TOKEN_EXPIRATION=true
- Pass require_token_expiration flag to admin UI template context
- Add conditional required field indicator and helper text in the
  token creation form
- Update existing tests to provide expires_in_days where needed
- Add new test cases covering policy enabled/disabled scenarios,
  team tokens, and edge cases (zero expiry days)

Closes #2836

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
vishu-bh pushed a commit that referenced this pull request Feb 18, 2026
…tting (#2898)

Enforce the existing REQUIRE_TOKEN_EXPIRATION config setting at
token creation time. Previously the setting only validated incoming
tokens but allowed creation of tokens without expiration.

- Add validation in TokenCatalogService.create_token() to reject
  tokens without expiration when REQUIRE_TOKEN_EXPIRATION=true
- Pass require_token_expiration flag to admin UI template context
- Add conditional required field indicator and helper text in the
  token creation form
- Update existing tests to provide expires_in_days where needed
- Add new test cases covering policy enabled/disabled scenarios,
  team tokens, and edge cases (zero expiry days)

Closes #2836

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Signed-off-by: Vishu Bhatnagar <vishu.bhatnagar@ibm.com>
kcostell06 pushed a commit to kcostell06/mcp-context-forge that referenced this pull request Feb 24, 2026
…tting (IBM#2898)

Enforce the existing REQUIRE_TOKEN_EXPIRATION config setting at
token creation time. Previously the setting only validated incoming
tokens but allowed creation of tokens without expiration.

- Add validation in TokenCatalogService.create_token() to reject
  tokens without expiration when REQUIRE_TOKEN_EXPIRATION=true
- Pass require_token_expiration flag to admin UI template context
- Add conditional required field indicator and helper text in the
  token creation form
- Update existing tests to provide expires_in_days where needed
- Add new test cases covering policy enabled/disabled scenarios,
  team tokens, and edge cases (zero expiry days)

Closes IBM#2836

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants