Skip to content

feat(auth): add reusable OAuth2 base helper library#2858

Closed
LOVECAO1011 wants to merge 2144 commits intoIBM:mainfrom
LOVECAO1011:issue#1434
Closed

feat(auth): add reusable OAuth2 base helper library#2858
LOVECAO1011 wants to merge 2144 commits intoIBM:mainfrom
LOVECAO1011:issue#1434

Conversation

@LOVECAO1011
Copy link
Copy Markdown

@LOVECAO1011 LOVECAO1011 commented Feb 11, 2026

Introduce a canonical OAuth2/OIDC helper module for token validation, claims extraction, metadata discovery, token exchange/refresh, and scope operations so auth plugins can share consistent protocol logic. Add focused unit coverage for the new helper APIs and RFC-oriented behaviors.

🔗 Related Issue

Closes #


📝 Summary

  • mcpgateway/oauth2/base.py
  • validate_token() with JWKS JWT verification and introspection fallback
  • extract_claims() canonical claim mapping
  • discover_authorization_server_metadata() (RFC 8414)
  • discover_oidc_metadata() (OIDC discovery)
  • discover_protected_resource_metadata() (RFC 9728)
  • exchange_token() (RFC 8693)
  • refresh_token() (RFC 6749)
  • scope normalization/modification utilities
  • authorization URL builder with resource indicators (RFC 8707)
  • mcpgateway/oauth2/models.py typed request/response config models
  • mcpgateway/oauth2/exceptions.py unified OAuth2 error types
  • mcpgateway/oauth2/__init__.py public exports
  • tests/unit/mcpgateway/oauth2/test_base.py focused unit tests for helper APIs and error paths

🏷️ Type of Change

  • Bug fix
  • Feature / Enhancement
  • Documentation
  • Refactor
  • Chore (deps, CI, tooling)
  • Other (describe below)

🧪 Verification

Check Command Status
Lint suite make lint
Unit tests make test
Coverage ≥ 80% make coverage

✅ Checklist

  • Code formatted (make black isort pre-commit)
  • [x ] Tests added/updated for changes
  • Documentation updated (if applicable)
  • [x ] No secrets or credentials committed

📓 Notes (optional)

This PR delivers the OAuth2 base helper layer requested by #1434 and is designed to be consumed by plugin/auth flows in the #1422 epic.

  • It is additive and does not remove existing OAuth manager/DCR behavior.

araujof and others added 30 commits January 14, 2026 00:19
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
- Auto-wrap GlobalContext in PluginContext in invoke_hook_for_plugin()
  to prevent AttributeError exceptions on every invocation
- Change f-string to lazy logger formatting in debug statement

This eliminates unnecessary logger.error() calls that were triggered
by a type mismatch when GlobalContext was passed directly, resulting
in 2-5x performance improvement for plugin execution.

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
)

- Add mcpContextForge.service.annotations for LoadBalancer configuration
  (e.g., AWS NLB, GCP load balancer annotations)
- Add "none" as a valid CACHE_TYPE option in values.schema.json
  to allow disabling caching entirely

These changes were extracted from PR IBM#1798, which was superseded by
existing implementations for external PostgreSQL and secret injection.

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Shi Jin <jinzishuai@gmail.com>
* tests: add cProfile performance tests for plugins

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* tests: add comparison script for plugin framework profiles

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* fix: address linting issues in plugin profiling tests

- Add noqa E402 comments for intentional late imports after sys.path
- Fix type annotation: use Any instead of any (lowercase was incorrect)
- Remove unnecessary f-string prefixes from static strings

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

---------

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
…d pagination, and search on name or email (IBM#2067)

* unified team member view
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>

* Fix selection
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>

* Fix member search
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>

* Remove extra logging
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>

* Update search to non dom users
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>

* lint-web fixes
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>

* black fix

Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>

* team member cleanup
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>

* Segregate members and non members and fix pagination
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>

* Fix search
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>

* Linting fixes
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>

* Fix tests
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>

* Make list team members return paginated result by default
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>

* linting fixes
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>

* Fix cursor use in auth_service list users
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>

* Remove unused param
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>

* Add index on emai_user name
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>

* Fix non paginated response for list team members API
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>

* Update migration script
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>

* Fix failing doctest
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>

* Use next_cursor with alias
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>

* Remove print statements in alembic script
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>

* Fix close manage members
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>

* refactor: remove dead code _cursor_paginate_users method

Remove unused _cursor_paginate_users() method from EmailAuthService
that was made obsolete when list_users() was refactored to use
unified_paginate() for both cursor and page-based pagination.

Also remove:
- Tests for the dead code method
- Unused imports (base64, orjson, and_, Select)

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix: address pagination safety and role preservation issues

Critical fixes:
- Add loadedMembers tracking to prevent accidental removal of members
  from unloaded pages during infinite scroll pagination
- Only remove members who were visible in the form AND unchecked

High priority fixes:
- Preserve member roles when rendering search results (prevent owner demotion)
- Add loadedMembers hidden input in JS search to match template behavior
- Use fetchWithAuth instead of bare fetch for bearer token support

Low priority fixes:
- Fix owner_count to use count_team_owners() for accurate Last Owner badge
  across all pages, not just current page

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix: implement proper cursor pagination for EmailUser

EmailUser uses email as primary key (not id), so the generic
unified_paginate cursor using (created_at, id) doesn't work.

Changes:
- Add custom cursor pagination in list_users() using (created_at, email)
  as cursor fields for correct keyset pagination
- Add warning log in unified_paginate when model lacks 'id' field
  to help diagnose cursor pagination issues with other models
- Keep page-based pagination using unified_paginate (works correctly)

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix: add debounce and HTMX reinitialization for search

- Add debouncedServerSideUserSearch() wrapper with 300ms delay to
  prevent excessive API calls on each keystroke
- Call htmx.process() after innerHTML replacement when clearing search
  to reinitialize HTMX intersect triggers for infinite scroll
- Export debounced function for use in templates

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix: move imports to top level and add missing template

Move base64, orjson, and_ imports to module top level to satisfy pylint
C0415 (import-outside-toplevel).

Add team_members_selector.html template for the user selector with team
context, used when render=selector in admin users partial endpoint.

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix: team member cursor pagination, checkbox preservation, and tests

- Add created_at property alias to EmailTeamMember for cursor pagination
  compatibility with unified_paginate
- Fix ORDER BY in get_team_members to use (joined_at DESC, id DESC) for
  cursor-based pagination and (full_name, email) for page-based
- Preserve checkbox states during server-side user search to prevent
  in-progress add/remove selections from being lost
- Fix admin.html users list per_page from 10 to 20 for consistency
- Add cursor pagination tests for EmailUser list_users method testing
  the (created_at, email) keyset implementation

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix: cursor pagination, access control, and UI consistency issues

- Implement custom cursor pagination for team members using (joined_at, id)
  keyset instead of relying on unified_paginate's (created_at, id) which
  doesn't work for EmailTeamMember
- Implement custom cursor pagination for list_users_not_in_team using
  (created_at, email) keyset for EmailUser
- Fix backward compatibility check to only trigger when all pagination
  params (cursor, page, limit) are None
- Update legacy add-members route from non-existent /admin/teams/{id}/users/partial
  to /admin/users/partial?render=selector&team_id={id}
- Allow teams.manage_members permission for /users/search endpoint so team
  owners can search users to add to their teams
- Add email fallback for name display in team_users_selector.html and
  team_members_selector.html templates
- Remove unused created_at property alias from EmailTeamMember
- Add unit tests for team member cursor-based pagination

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix: make non-members list use same per_page as members section

Both members and non-members sections in the team management UI should
honor the same per_page setting from the view context for consistency.

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix: per_page consistency, legacy search URL, and limit cap

- Add data-per-page attributes to team member containers and read them
  in JS search reset to maintain consistent page sizes
- Fix legacy add-members search URL from non-existent
  /admin/teams/{id}/users/search to /admin/users/search
- Revert users list per_page from 20 back to 50 to match main branch
- Add le=settings.pagination_max_page_size constraint to teams router
  limit parameter and cap page_size in service for defense-in-depth

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* Linting

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix: prettier formatting in admin.js

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

---------

Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* feature/password-enforcement-1843

Signed-off-by: NAYANAR <nayana.r7813@gmail.com>

* pylint fix

Signed-off-by: NAYANAR <nayana.r7813@gmail.com>

* bandit fix

Signed-off-by: NAYANAR <nayana.r7813@gmail.com>

* added a small visible indicator on the page Password policy enabled password_policy_enabled  so you can immediately see the effective value the server used when rendering the page

Signed-off-by: NAYANAR <nayana.r7813@gmail.com>

* flake8 fix

Signed-off-by: NAYANAR <nayana.r7813@gmail.com>

* fix pytest

Signed-off-by: NAYANAR <nayana.r7813@gmail.com>

* fix flake8

Signed-off-by: NAYANAR <nayana.r7813@gmail.com>

* feat: add migration for password_changed_at column

Adds the missing Alembic migration to create the password_changed_at
column in the email_users table, required for password age tracking
and expiry enforcement.

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix: address password enforcement logic issues

Fixes 6 issues found during code review:

1. Default password enforcement bypass - moved needs_password_change
   inside the require_password_change_for_default_password check

2. password_change_required flag bypass - made the flag authoritative,
   no longer ignored based on password age (age is only for expiry)

3. Admin UI ignores policy toggle - added password_policy_enabled
   check to validate_password_strength function

4. Admin update missing timestamp - added password_changed_at update
   in admin user update paths (router and service layer)

5. Migration no backfill - added backfill logic to set
   password_changed_at = created_at for existing users

6. .env.example missing docs - added all new password enforcement
   configuration options

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

---------

Signed-off-by: NAYANAR <nayana.r7813@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* 2072 - MCP Registry "Add Server" button flow

- Backend now detects HTMX requests via HX-Request header and returns HTML fragments instead of JSON
- Replaced broken htmx-indicator pattern with hx-on::before-request and hx-on::after-request event handlers
- Implemented three button states: success (green), OAuth warning (yellow), error with retry (red)
- Fixed settings.root_path → settings.app_root_path bug
- Added hx-disabled-elt to prevent double-clicks
- Added comprehensive test coverage for OAuth registration and HTMX endpoints

Signed-off-by: Gabriel Costa <gabrielcg@proton.me>

* Flake8 fix

Signed-off-by: Gabriel Costa <gabrielcg@proton.me>

* fix: add proper HTML escaping and fix HTMX tests

- Add html.escape() for server_id, message, and error values in
  HTML fragments to prevent XSS vulnerabilities
- Fix HTMX tests to use __wrapped__ to bypass RBAC decorator
- Apply Black formatting and isort to test file

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix: address HTMX registration flow issues

- High: Use HX-Trigger-After-Swap header for success refresh instead of
  inline hx-on::after-request. Error buttons no longer get wiped by
  auto-refresh since they don't include the trigger header.
- Medium: Add oauth_required field to CatalogServerRegisterResponse schema
  and use it instead of brittle string matching on message content.
- Low: Add hx-on::response-error handlers to restore button state on
  transport/network failures.

Also adds event listener in template for catalogRegistrationSuccess trigger.

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix: address remaining HTMX registration issues

- Medium: OAuth registrations no longer trigger delayed refresh, so the
  yellow "OAuth Config Required" warning persists instead of being
  replaced by "Add Server" on refresh
- Low: Event listener uses window guard to prevent re-attachment when
  partial is refreshed multiple times
- Low: HTTP errors (401/403/500) now show orange "Request Failed - Click
  to Retry" button with visual feedback instead of silently resetting

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* Lint fix

Signed-off-by: Gabriel Costa <gabrielcg@proton.me>

* style: remove unnecessary else after return (pylint R1705)

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix: OAuth servers persist as registered after refresh

- Add requires_oauth_config field to CatalogServer schema
- Query all gateways (enabled and disabled) to track registration status
- Disabled gateways are marked as requires_oauth_config=True
- Template shows yellow "OAuth Config Required" state for disabled servers
- Add debounce to prevent multiple refreshes when registering rapidly

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix: only mark OAuth servers as requires_oauth_config

- Query auth_type from Gateway to distinguish OAuth from other disabled
  servers (only disabled + auth_type='oauth' = requires_oauth_config)
- Re-enable refresh for OAuth success since template now handles state
- Revert unrelated formatting change in migration file

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix: distinguish unconfigured OAuth servers from manually disabled ones

- Set auth_type="oauth" when registering OAuth servers (was None)
- Check oauth_config IS NULL to identify servers needing OAuth setup
- Manually disabled OAuth servers (with oauth_config populated) now
  correctly show "Already Registered" instead of "OAuth Config Required"

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* test: add unit tests for requires_oauth_config classification

Test cases for the get_catalog_servers method:
- Disabled OAuth server with oauth_config=None → requires_oauth_config=True
- Disabled OAuth server with oauth_config populated → requires_oauth_config=False
- Enabled OAuth server → requires_oauth_config=False

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix: use named logger for duplicate header warning

Change logging.warning() to logger.warning() for duplicate header key
warnings. This ensures proper log capture in pytest's caplog fixture,
fixing flaky test in CI.

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

---------

Signed-off-by: Gabriel Costa <gabrielcg@proton.me>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* 2077 - Reorder action buttons

Signed-off-by: Gabriel Costa <gabrielcg@proton.me>

* fix: Update search indices after moving Actions column to first position

Update JavaScript table filter functions to reflect the new column order
where Actions is at index 0 instead of being the last column. This fixes
search/filter functionality that was broken by the column reorder.

Affected functions:
- filterGatewaysTable: skip Actions(0) and S.No.(1)
- filterServerTable: skip Actions(0), Icon(1), S.No.(2)
- filterToolsTable: skip Actions(0), S.No.(1)
- filterResourcesTable: skip Actions(0)
- filterPromptsTable: skip Actions(0), S.No.(1)
- filterA2AAgentsTable: skip Actions(0), ID(1)
- simpleGatewaySearch: skip Actions(0), S.No.(1)

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

---------

Signed-off-by: Gabriel Costa <gabrielcg@proton.me>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* 2080 - Adjust Show inactive toggle

Fix the Show inactive toggle behaviour on the MCP Server Gateway and
Virtual Servers - Soon to be added to other tabs

Signed-off-by: Gabriel Costa <gabrielcg@proton.me>

* Web lint fix

Signed-off-by: Gabriel Costa <gabrielcg@proton.me>

* fix: restore HTMX-based toggle for servers/gateways with proper state preservation

- Revert from client-side to server-side filtering via HTMX to fix pagination
- Fix fallback ID lookup to include servers and gateways tables
- Preserve team_id and per_page URL params when toggling inactive filter
- Preserve and reapply search term after HTMX content swap
- Remove unused applyServerFilter/applyGatewayFilter functions

Addresses issues with sparse pages and search state loss when toggling
the "Show inactive" checkbox on servers and gateways tabs.

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

---------

Signed-off-by: Gabriel Costa <gabrielcg@proton.me>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
…M#2089)

* feat(teams): add server-side pagination and search for teams listing

Implement unified pagination support for teams management:

Service Layer:
- Update TeamManagementService.list_teams to use unified_paginate
- Support cursor, page, per_page, search_query, and visibility_filter
- Add get_all_team_ids method for admin lookups
- Add composite index on (name, id) for optimized search/pagination

API Router:
- Refactor list_teams endpoint with proper pagination flags
- Add CursorPaginatedTeamsResponse schema

Frontend (HTMX):
- Add teams_partial.html template for HTMX partial updates
- Add teams_selector_items.html for infinite-scroll dropdowns
- Implement server-side team search in admin.js

Testing:
- Update router tests to explicitly pass include_pagination=False
- Update service tests to match new unified pagination signature

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix(teams): address code review findings for pagination

Fixes identified in code review:

High Priority:
- Fix cursor pagination ordering: use created_at DESC for cursor mode,
  name ASC for page mode to match unified_paginate expectations
- Fix team action refresh: use /admin/teams/partial instead of legacy URL
- Remove inline filterTeams/filterByRelationship that override server-side versions

Medium Priority:
- Add get_teams_count() for accurate total in API responses
- URL encode q and relationship params in base_url construction
- URL encode q param in teams_selector_items.html template
- Fix admin controls: admins see manage/edit/delete for all teams (not "Request to Join")
- Add 500 limit to discover_public_teams for memory safety
- Fix non-admin pagination to respect page parameter
- Enable description search for consistency with in-memory filter

Low Priority:
- Fix error fallback links with consistent per_page values
- Fix loading indicator timing for HTMX (let HTMX handle indicator)
- Revert accidental per_page=20→10 changes in non-teams endpoints
- Add test for include_pagination=True cursor response path

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

---------

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Add "Authorize" and "Fetch Tools" buttons for OAuth-enabled gateways,
matching the main admin interface layout.

Use |tojson filter for gateway.name in onclick handler to prevent
potential XSS from gateway names containing quotes or backslashes.

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
…acking (IBM#2086)

Fix two critical bugs in core session and agent services:

1. Session Registry (session_registry.py)
   - Added null check for message content before passing to orjson.loads()
   - Prevents crash when _session_message contains None value
   - Logs warning instead of raising exception on edge case

2. Tool Execution (mcp_client_chat_service.py)
   - Fixed race condition where on_tool_end event arrives before on_tool_start
   - Implemented buffering for out-of-order tool events with TTL (30s) and cap (100)
   - Out-of-order events are buffered and reconciled when start event arrives
   - Tracks dropped tool ends (TTL-expired or buffer-full) with cap (200)
   - Later on_tool_start or on_tool_error clears from dropped set (prevents false orphans)
   - Emits aggregated tool_error at stream end for all orphan/dropped ends (de-duplicated)
   - on_tool_error clears buffered ends to avoid emitting both error and end
   - Uses UUID for aggregated error ID to avoid run_id collisions
   - Refactored output extraction into helper function

Tests added:
- test_respond_memory_backend_with_none_message_content: Tests None message handling
- test_chat_events_reconciles_out_of_order_tool_events: Tests event reconciliation
- test_chat_events_emits_error_for_orphan_tool_ends: Tests orphan error emission
- test_chat_events_tool_error_clears_buffered_end: Tests error/buffer consistency
- test_chat_events_buffer_full_drops_included_in_error: Tests buffer overflow tracking
- test_chat_events_ttl_expiry_included_in_error: Tests TTL expiry tracking
- test_chat_events_dropped_then_start_clears_from_dropped: Tests false orphan prevention
- test_chat_events_dropped_then_error_clears_from_dropped: Tests error clears dropped

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
…on (IBM#2097)

Add a new JWT_ISSUER_VERIFICATION configuration setting (default: true) to provide
granular control over JWT issuer claim validation, independent of audience
verification.

Previously, verify_jwt_token would unconditionally pass the issuer parameter
to PyJWT decode, enforcing presence and validity of the 'iss' claim even when
users intended to disable strict checks. This prevented accepting tokens with
varying or missing issuers, which is critical for:
- Dynamic Client Registration (DCR)
- Multi-tenant setups
- Custom authentication flows

Changes:
- Add jwt_issuer_verification setting (default: true) to mcpgateway/config.py
- Conditionally include issuer in decode_kwargs when verification is enabled
- Update all documentation (README, values.yaml, docs/) with the new setting
- Add .env.example entry for consistency
- Add unit test for issuer verification skip scenario
- Update all doctests with jwt_issuer_verification mock

Closes IBM#1792

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* feat(auth): decouple JWT issuer verification from audience verification

Add a new JWT_ISSUER_VERIFICATION configuration setting (default: true) to provide
granular control over JWT issuer claim validation, independent of audience
verification.

Previously, verify_jwt_token would unconditionally pass the issuer parameter
to PyJWT decode, enforcing presence and validity of the 'iss' claim even when
users intended to disable strict checks. This prevented accepting tokens with
varying or missing issuers, which is critical for:
- Dynamic Client Registration (DCR)
- Multi-tenant setups
- Custom authentication flows

Changes:
- Add jwt_issuer_verification setting (default: true) to mcpgateway/config.py
- Conditionally include issuer in decode_kwargs when verification is enabled
- Update all documentation (README, values.yaml, docs/) with the new setting
- Add .env.example entry for consistency
- Add unit test for issuer verification skip scenario
- Update all doctests with jwt_issuer_verification mock

Closes IBM#1792

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* Linting

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

---------

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* tests: minor fixes to plugins performance profiler

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* chore: fix yamlint issues

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* chore: fix trailing whitespace in performance test

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix: restore disabled status for plugins in performance config

Restore the original disabled mode for plugins that were unintentionally
enabled during YAML reformatting:
- RateLimiterPlugin
- PrivacyNoticeInjector
- WebhookNotification
- ContentModeration (also restore ibm_granite provider config)

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix: restore original context handling in performance profiler

Revert to passing GlobalContext directly instead of pre-creating a
shared PluginContext. The original behavior provides realistic
per-request profiling because:

- Each iteration gets a fresh PluginContext via auto-wrapping
- Stateful plugins (caches, circuit breakers) don't accumulate state
- Profile times reflect real-world per-request performance

Also removes the exception swallowing so failures properly surface
as ERROR in the summary table rather than computing averages from
partial iteration data.

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

---------

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* cache auth/crypto key material and derived objects

Signed-off-by: Shoumi <shoumimukherjee@gmail.com>

* lint fixes

Signed-off-by: Shoumi <shoumimukherjee@gmail.com>

* fixes for test

Signed-off-by: Shoumi <shoumimukherjee@gmail.com>

* fix pylint code duplication

Signed-off-by: Shoumi <shoumimukherjee@gmail.com>

* fix linting uses

Signed-off-by: Shoumi <shoumimukherjee@gmail.com>

* Lint

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

---------

Signed-off-by: Shoumi <shoumimukherjee@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* Initial commit for qr-code-server, created  using scaffold script

* Added config.yaml  based on requirements.

* feat: Implement configuration management for QR code generation - load and validade

* fix: Update dependencies in pyproject.toml for QR code server
added qrcode

* Refactored config.py
    -Removed extra comments
    -Added info when loaded
    -Removed wrong validation

* Added dev requirements to test and lint

* Add qr generation logic
 - implement create_qr
 - implemented simple happy path unit test

* "Fix lint issues in config.py"

* Improved qr_generator
    - Added base64 output
    - Added error handling
    - Added png, svg and ascii formats
    - Added tests to cover generator changes

* Create a resolve_output_path function
    - It will check if user input has filename on it
    - check if filename.ext has the correct ext
    - It creates the output folder if it does not exist

* Fixed resolve output path not creating correct path

* Added ImageAscii class to convert pillow image to ascii
     - save method added to allow use as qrcode image

* Refactored ImageAscii to be compatible to qrcode image factory

* Refactored file.utils to not raise error and added filename + extension as output

* Refactored generator
    - Uses qrcode image factory to generate images
    - Removed ascii logic from generator
    - improved output path handling
    - remove code used to run locally

* Added tests for generator, covering 100%
    - test for multiple formats PNG, SVG, ASCII
    - test for error handling
    - test create qr base64

* rename main folder from qr-code-serve to qr_code_server,
Added a server test to list tools

* Fixed generator test creating output path

* Fixed resolve output path to address filename without extension, it manage the following cases
    -  case 1: output_path is a folder
    - case 2: output_path has file extension
    - case 3: output_path has filename without extension
    - case 4: output_path does not have filename

* Removed qr code file generated by a test

* Added validator and decoder file with request validation model

* Improved image utils to handle batch qr code generation
    - Decoupled qr image generation from the create_qr_code
    - Improved Ascii save to handle BytesIO for batch zip generation

* Improved QRcode batch generator and added tests
    - Handle errors
    - Added tests to get 100% coverage
    - Test manually to check created files
    - changed ascii filename to have extension ascii

* Refactored Image utils
    - Created a image generator to handle batch
    - handled errors
    - changed ascii format to have ascii extension

* Added create_batch_qr_codes to server and test_server

* Added dependencies for QR code Decoder

* Fixed bugs in generator and improved test coverage
    -  Fixed ascii not encoding in base64
    -  Added validation to BatchQRgeneration
    -  Fixed lint

* Added a load image for decoder
    -  loads from file image
    - loads from base64
    - load multiple types

* Added max data lenght validation for Qr generator

* Added max image size error for decoder

* Added a function to decode string bytes size to bytes

* Added decoder validation for image format

* Removed hard coded supported formats
Added in progress  decoder

* Added request validations for validator
    - target_version
    - error correction

* Handled max concurrent requests using semaphore
    - use max request from config
    - queue max size 3*max request config

* Fixed folder and file handling for generator
    - Added tests for edge cases

* Improved convert to bytes function
    - Added tests for edge cases
    - raise value error when cannot decode

* Fixed double quotes inside double quotes in rstrip

* Refactored test_generator and linted
    - replaced TemporaryDirectory() with tmp_path fixture

* Added default values from config in generator

* Added QR validator Request  model

* Refactored and linted image utils

* Refactored and improved generator
    - Added config default to Generation Request model
    - fixed data validation
    - improved logs

* Refactored and cleaned generator errors
    - cleaned tmp_path
    - added base64 fail to generate test
    - cleaned unused imports

* Addeed functions to estimate bits and size
    - check smalles fitting version
    - added table size per version and error correction
    - function to estimate encoded bits for string and version

* Added validator tests and bits table size reference

* Added validate qr code to server
    - Fixed lint

* Improved load_image
    - Improved image size validation before b64 decoding
    - Added image size validation after decoding

* Added decoder qr code
    - Accept path to image or base64 image
    - preprocess image using cv2 gaussian blur and treshold
    - decode images with multiple codes

* Fixed bug where zipped png files were corrupted
    -  save image as Bytes buffer before adding to zip
    - Added test to check if file is image

* change project name script to qr-code-server

* Added qr code decoder in server
Also added health check endpoint

* Added semaphore server tests

* Fixed batch generator missing format input
    - Added generator test to server test
    - Added Batch generator to server test

* Renamed internal function decode_qr_code to qr_decode

* Added test decode qr  tool in server tests

* Added tests to call tools from server

* Fix small images not decoding
    - Created a preprocessing function
    - Improved logs
    - Added a resize small images when preprocessing
    - moved preprocessing to load image function in image utils

* Improved decoder log when cannot decode image or find file

* Added different types of qr images for decoding tests

* Improved decoder tests
    - Added multiple qr code decode test
    - Added multiple type tests

* Added test for multiple tools running concurrently

* Added more tests in server.py to reach > 90% coverage

* Improved test coverage for config.
    - Fixed config not loading default values

* Removed Cache generated qr code  config

* Linted and removed health endpoit as it is not on requirements

* Fixed wrong command in make format

* Linted and formatted the code

* Fixed image utils types and lint

* Fixed types and lint for decoder

* Added return type to qr_decode function

* Fixed types in validador

* Improved types in decoder

* Improved types and return tyoes in generator

* changed how to handle error correction in Qrgeneration config

* change BytesIO to Any in Ascii save image

* Normalized tools Output using Model

* Created Comprehensive Readme

* Normalized output for server errors using models

* Improved logs for server
    - Added Log when successfull
    - added log for success false

* Removed files from BatchQRresult

* Capped border at 100 to avoid long request and computer freezing

* Improved Readme with notes on Border and Colors

* Fixed lint and long lines

* Formatted and linted files for PR

* fix(qr-code-server): correct file extension handling in resolve_output_path

Fix edge cases in file path resolution:
- Handle filenames with non-matching extensions (e.g., file.jpg -> file.jpg.png)
- Handle filenames ending with dot (e.g., weird. -> weird.png)
- Only skip extension appending when existing extension matches target format

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix(qr-code-server): address security and correctness issues

Security fixes:
- Fix Containerfile build order (copy src/ before pip install)
- Add path traversal validation for naming_pattern to prevent zip-slip

Correctness fixes:
- Populate file_path, zip_file_path, output_directory in responses
- Use config values for batch defaults (size, output_directory, zip_output)
- Use config.decoding.preprocessing_enabled as default
- Fix wrong request names in logging (decode/validate used batch name)
- Convert NumPy arrays to lists for JSON serialization
- Fix README to show correct zip filename (qr.zip not qr_batch.zip)
- Fix test using wrong field name (version -> target_version)

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix(qr-code-server): improve validation and container compatibility

Validation fixes:
- Preserve whitespace in QR data (don't strip during validation)
- Clamp border to valid range [0, 100] (was only capping max)
- Add image_format validation against config.supported_image_formats
- Consistent data length validation in batch (use actual length)

Container fixes:
- Switch to opencv-python-headless (works on slim containers)

Test additions:
- Add tests for naming_pattern path traversal rejection
- Add tests for border validation clamping

Code cleanup:
- Remove unnecessary UTF-8 encoding declarations (ruff UP009)

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix(qr-code-server): ensure batch uses config for border and error_correction

Fixes:
- Add border and error_correction to BatchQRGenerationRequest model
- Pass border/error_correction to index_image_generator
- Add format validation to single QR generation (consistency with batch)
- Update server.py to expose border/error_correction for batch

Documentation:
- Update README examples to match actual output format
- Document that image_format is validation-only (auto-detection used)
- Add border/error_correction parameters to batch docs

Test fix:
- Use == instead of is for string comparison in format test

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

---------

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
…BM#2102)

* tests: add basic smoke tests for all plugins to check they initialize and run on all defined hooks

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* fix: yamlint issues

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* fix(tests): correct misleading class names in plugin smoke tests

Rename test classes from 'Disabled' to reflect that they test enabled
plugins in permissive mode, not disabled plugins:
- TestDisabledPluginsInstantiation -> TestPluginInstantiation
- TestDisabledPluginsHookInvocation -> TestPluginHookInvocation
- TestAllDisabledPluginsTogether -> TestAllPluginsTogether

Update associated docstrings for consistency.

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

---------

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: type mismatches and missing dependency checks in plugins

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

* chore: lint issues

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>

---------

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
…ot findings (IBM#2093)

Signed-off-by: Jonathan Springer <jps@s390x.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* feat: add MCP authentication controls and team membership validation

Implement configurable authentication and consistent authorization for MCP
endpoints:

- Add MCP_REQUIRE_AUTH config option to require Bearer tokens on /mcp endpoints
  (default: false allows public-only access for unauthenticated requests)
- Add team membership validation to MCP auth matching REST behavior with 60s
  cache TTL - users removed from teams lose access immediately
- Extend access checks to template resources ensuring visibility and team
  scoping applies consistently across all resource types

Configuration:
- MCP_REQUIRE_AUTH=false (default): unauthenticated requests get public-only access
- MCP_REQUIRE_AUTH=true: all /mcp requests require valid Bearer token

Updated: config.py, streamablehttp_transport.py, resource_service.py,
README.md, Helm charts, ADR-004, .env.example

Tests: 34 authorization tests, 60 MCP transport tests
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* feat: add visibility-based access control for A2A agents and resource templates

Implement consistent authorization controls across A2A agents and resource
templates, matching the existing tools/resources/prompts access patterns.

Changes:
- Add _apply_visibility_filter() and _check_agent_access() to A2AAgentService
  for listing, get, and invoke operations
- Add _apply_visibility_filter() to ResourceService for template listing
- Fix visibility parameter precedence bug where schema defaults overrode
  endpoint parameters
- Add Authorization header to nginx cache key to prevent cross-user data
  leakage
- Add MCP transport tests for team membership validation
- Fix flaky circuit breaker test timing

Access control rules:
- token_teams=None: Admin unrestricted access
- token_teams=[]: Public-only access (no owner access to private entities)
- token_teams=[...]: Team-scoped access with owner access to private entities

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

---------

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
- Add automatic JTI (JWT ID) generation to all tokens for revocation support
- Add REQUIRE_JTI configuration option to enforce JTI claims
- Log warning when tokens without JTI are accepted (for migration visibility)
- Add platform admin bootstrap authentication logging for audit trails
- Update DummySettings in doctests with require_jti attribute

Closes IBM#2127

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* feat: add REQUIRE_USER_IN_DB configuration option

- Add require_user_in_db setting to enforce database user existence
- When enabled, disables platform admin bootstrap mechanism
- Add rejection logging with security_event for audit trails
- Add startup warning for ephemeral storage without strict mode
- Fix: verify DB existence even when user is cached (prevents cache bypass)
- Add unit tests for all auth paths: fallback, batched, and cache-hit (5 new tests)
- Update README.md with REQUIRE_JTI and REQUIRE_USER_IN_DB docs
- Update Helm chart values.yaml and README.md
- Update config.py docstring with new environment variables

Closes IBM#2128

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* chore: add environment variable overrides to docker-compose

Add shell environment variable overrides for:
- AUTH_CACHE_ENABLED (default: true)
- REQUIRE_JTI (default: false)
- REQUIRE_USER_IN_DB (default: false)
- LOG_LEVEL (default: ERROR)

This allows flexibility in testing and production deployments.

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix: resolve pylint W1404 implicit string concatenation warning

Combine the two adjacent string literals into a single string
to avoid the implicit-str-concat pylint warning.

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

---------

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
…ation (IBM#2141) (IBM#2143)

- Add EMBED_ENVIRONMENT_IN_TOKENS config to include env claim in gateway JWTs
- Add VALIDATE_TOKEN_ENVIRONMENT config to reject tokens with mismatched env claim
- Add startup warnings for default JWT_ISSUER/JWT_AUDIENCE in non-dev environments
- Update documentation, schema files, and Helm chart values
- Fix pre-existing doctest failures in verify_credentials.py

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Signed-off-by: Keval Mahajan <mahajankeval23@gmail.com>
Signed-off-by: Keval Mahajan <mahajankeval23@gmail.com>
madhav165 and others added 9 commits February 11, 2026 11:14
* fix: disable preload on macOS to prevent worker SIGSEGV crashes

On macOS, gunicorn's preload feature causes SIGSEGV crashes when using
uvicorn workers with async libraries (SQLAlchemy, asyncio). This is due
to macOS's strict fork-safety requirements.

Changes:
- Disable preload_app by default on macOS in both config and shell script
- Users can override with GUNICORN_PRELOAD_APP=true if needed

Refs: benoitc/gunicorn#2761
Closes IBM#2837
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>

* lint

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

---------

Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
…M#2835)

* add test-verbose target for sequential test execution with real-time output

Signed-off-by: Shoumi <shoumimukherjee@gmail.com>

* additional enhancements

Signed-off-by: Shoumi <shoumimukherjee@gmail.com>

* minor fix

Signed-off-by: Shoumi <shoumimukherjee@gmail.com>

* fix: use --instafail and --maxfail=0 in test-verbose target

Run all tests to completion showing failures inline in real-time,
rather than stopping at the first failure.

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

---------

Signed-off-by: Shoumi <shoumimukherjee@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
…#2842)

Replace arbitrary wait_for_timeout calls with proper Playwright
condition-based waits (wait_for_visible, wait_for_selector, expect,
wait_for_load_state, wait_for_count_change). Fix broken login response
handling that used non-existent wait_for_response API (was silently
swallowed by broad except Exception). Narrow exception handling from
Exception to PlaywrightTimeoutError to surface real errors.

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: remove deprecated forwarding methods and fix SSE data parsing

Remove deprecated forward_request, _forward_request_to_gateway, and
_forward_request_to_all methods from gateway_service.py. Replace
forwarding fallbacks in main.py with proper JSON-RPC error responses.

Fix SSE parser in llm_proxy_service.py and translate.py to handle
data: lines with or without a space after the colon, per the SSE spec.

Closes IBM#1595

Signed-off-by: Keval Mahajan <mahajankeval23@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix: clean up redundant except clause and align SSE parsing in tests

Simplify except (ValueError, Exception) to except Exception since
ValueError is a subclass of Exception. Update SSE data: line parsing
in integration and e2e tests to match the spec-correct pattern used
in production code.

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

---------

Signed-off-by: Keval Mahajan <mahajankeval23@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* Update testing

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* Update testing

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* Update testing

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

---------

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
…cy (IBM#2846)

* fix baseUrl

Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>

* fix x-data base-url

Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>

* add new tests

Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>

* fix: complete pagination adjustment and fix broken mock targets

- Recompute total_pages, has_next, has_prev, and page in
  _adjust_pagination_for_conversion_failures to avoid stale UI state
- Fix test_auth_cache_import_error: patch the already-imported function
  reference instead of sys.modules (module-level import was already
  resolved)
- Fix test_redis_available_cache_hit: patch get_redis_client in the
  consuming module, not the source module
- Rename misleading test to test_offset_paginate_page_not_clamped_when_no_items
- Add tests for derived pagination field recomputation

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix: do not clamp page after conversion and use root_path for tool-ops

- Remove page clamping from _adjust_pagination_for_conversion_failures
  since data was already fetched for the original page; clamping would
  make metadata claim a different page than what is displayed
- Use request.scope root_path for tool-ops partial endpoint base_url
  instead of settings.app_root_path, consistent with all other partials

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

---------

Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
Introduce a canonical OAuth2/OIDC helper module for token validation, claims extraction, metadata discovery, token exchange/refresh, and scope operations so auth plugins can share consistent protocol logic. Add focused unit coverage for the new helper APIs and RFC-oriented behaviors.

Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: hany1 <hany1@tcd.ie>
@crivetimihai
Copy link
Copy Markdown
Member

Thanks @LOVECAO1011. A reusable OAuth2/OIDC helper module could reduce duplication across our OAuth integrations.

Key concerns:

  1. Overlap: How does this relate to the existing implementation in mcpgateway/auth.py and mcpgateway/services/oauth_manager.py? Does it replace, extend, or run alongside them?
  2. Dependencies: Does this introduce new external dependencies?
  3. Testing: Are there unit tests? Token validation and claims extraction are security-critical.
  4. Integration: Which existing code paths will use this, and are migration PRs planned?

@LOVECAO1011
Copy link
Copy Markdown
Author

Thanks for the detailed review and questions.

  1. Overlap
    This PR is additive and introduces a reusable OAuth2/OIDC helper layer. It does not replace mcpgateway/auth.py or mcpgateway/services/oauth_manager.py yet. Existing paths remain unchanged for safety; migration is planned incrementally.

  2. Dependencies
    No new third-party dependencies were introduced. The helper reuses existing project dependencies (pyjwt, httpx) and existing internal HTTP client patterns.

  3. Testing
    Yes — focused unit tests were added at:
    tests/unit/mcpgateway/oauth2/test_base.py
    Coverage includes token validation (JWKS + introspection), claims extraction, metadata discovery, token exchange/refresh, scope handling, and error paths.
    I also ran related regression suites:

  • tests/unit/mcpgateway/services/test_oauth_manager.py
  • tests/unit/mcpgateway/services/test_dcr_service.py
  • tests/unit/mcpgateway/utils/test_verify_credentials.py
  1. Integration / Migration plan
    Planned follow-up integration PRs:
  • Wire oauth_manager flows to use the new helper APIs.
  • Incrementally route token validation/claims paths to the helper.
  • Remove duplicated protocol logic once migration is complete.

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.

Good work — this is a clean, well-structured OAuth2/OIDC helper library with proper RFC references (8414, 9728, 8693, 6749, 8707).

Strengths:

  • Dataclass models with slots=True for efficiency
  • Proper exception hierarchy (OAuth2BaseError → specific errors)
  • Issuer mismatch validation in discovery methods
  • Comprehensive test coverage with mock HTTP clients

Minor observations:

  • _validate_jwt_with_jwks creates a new PyJWKClient on every call — consider caching the client per jwks_uri to avoid repeated JWKS fetches
  • httpx import at line 212 (inside _validate_token_with_introspection) — should be at module level for consistency with the rest of the file
  • The library is currently standalone with no integration into existing auth flows — that's fine for Phase 1, just noting it for follow-up

Clean code, good tests, proper error handling. Approved.

@crivetimihai crivetimihai added enhancement New feature or request SHOULD P2: Important but not vital; high-value items that are not crucial for the immediate release labels Feb 21, 2026
yiannis2804 added a commit to yiannis2804/mcp-context-forge that referenced this pull request Feb 23, 2026
Implements Issue IBM#1437 - Create IAM pre-tool plugin

Features:
- Token caching with configurable TTL (60s safety buffer)
- Bearer token injection via http_pre_request hook
- Plugin framework integration with proper configuration
- Ready for OAuth2 integration (pending PR IBM#2858)

Components:
- Plugin implementation with token cache and injection logic
- Configuration models for server credentials
- Comprehensive unit tests (6 tests, all passing)
- Documentation with usage examples and architecture diagrams

Phase 1 deliverable: Foundation ready for OAuth2 client credentials
flow once PR IBM#2858 (OAuth2 base library) merges.

Related:
- Issue IBM#1437 (this implementation)
- Issue IBM#1422 (EPIC: Agent and tool authentication)
- Issue IBM#1434 (OAuth2 base library - PR IBM#2858)
- Issue IBM#1438 (Future enhancements)

Signed-off-by: Ioannis Ioannou <yiannis2804@example.com>
Signed-off-by: yiannis2804 <yiannis2804@gmail.com>
@crivetimihai
Copy link
Copy Markdown
Member

Reopened as #3198. CI/CD will re-run on the new PR. You are still credited as the author.

crivetimihai pushed a commit that referenced this pull request Feb 24, 2026
Implements Issue #1437 - Create IAM pre-tool plugin

Features:
- Token caching with configurable TTL (60s safety buffer)
- Bearer token injection via http_pre_request hook
- Plugin framework integration with proper configuration
- Ready for OAuth2 integration (pending PR #2858)

Components:
- Plugin implementation with token cache and injection logic
- Configuration models for server credentials
- Comprehensive unit tests (6 tests, all passing)
- Documentation with usage examples and architecture diagrams

Phase 1 deliverable: Foundation ready for OAuth2 client credentials
flow once PR #2858 (OAuth2 base library) merges.

Related:
- Issue #1437 (this implementation)
- Issue #1422 (EPIC: Agent and tool authentication)
- Issue #1434 (OAuth2 base library - PR #2858)
- Issue #1438 (Future enhancements)

Signed-off-by: Ioannis Ioannou <yiannis2804@example.com>
Signed-off-by: yiannis2804 <yiannis2804@gmail.com>
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request SHOULD P2: Important but not vital; high-value items that are not crucial for the immediate release

Projects

None yet

Development

Successfully merging this pull request may close these issues.