Skip to content

[TESTING][SECURITY]: Core authentication manual test plan (JWT, Basic Auth, API tokens, email/password) #2390

@crivetimihai

Description

@crivetimihai

🔐 Testing: Core Authentication Manual Test Plan

Goal

Produce a comprehensive manual test plan for all local/internal authentication mechanisms including JWT tokens, Basic Auth, API tokens, and email/password authentication. This plan validates that authentication works correctly, securely rejects invalid credentials, and properly handles edge cases.

Why Now?

Core authentication is the foundation of the security model. Manual testing is needed to:

  1. Security Baseline: Validate that all authentication methods correctly identify and authenticate users before Release 1.0.0-RC1
  2. Credential Handling: Ensure invalid, expired, and malformed credentials are properly rejected with appropriate error messages
  3. Integration Validation: Verify authentication works consistently across all API endpoints and transports
  4. Regression Prevention: Establish baseline for automated regression testing

📖 User Stories

US-1: Security Engineer - JWT Token Validation

As a Security Engineer
I want comprehensive tests for JWT token validation
So that I can verify tokens are properly verified and invalid tokens are rejected

Acceptance Criteria:

Feature: JWT Token Validation

  Scenario: Valid JWT token grants access
    Given a JWT token signed with the correct secret
    And the token has not expired
    And the token contains valid claims (sub, exp, iat)
    When I make a request with the token in Authorization header
    Then the request should return status 200
    And the user context should be populated from token claims

  Scenario: Expired JWT token is rejected
    Given a JWT token that expired 1 hour ago
    When I make a request with the expired token
    Then the request should return status 401
    And the response should indicate "Token has expired"

  Scenario: JWT with invalid signature is rejected
    Given a JWT token signed with an incorrect secret
    When I make a request with the invalid token
    Then the request should return status 401
    And the response should indicate "Invalid token signature"

  Scenario: JWT with missing required claims is rejected
    Given a JWT token without the "sub" claim
    When I make a request with the token
    Then the request should return status 401
    And the response should indicate "Invalid token claims"

  Scenario: JWT extracted from cookie when header missing
    Given a valid JWT token stored in the "jwt_token" cookie
    And no Authorization header is present
    When I make a request with the cookie
    Then the request should return status 200
    And the user should be authenticated from the cookie token

  Scenario Outline: JWT audience/issuer validation
    Given REQUIRE_TOKEN_EXPIRATION is <exp_required>
    And a JWT token with <token_state>
    When I make a request with the token
    Then the request should return status <status>

    Examples:
      | exp_required | token_state      | status |
      | true         | no exp claim     | 401    |
      | false        | no exp claim     | 200    |
      | true         | valid exp claim  | 200    |

Technical Requirements:

  • JWT extracted from Authorization: Bearer <token> header
  • JWT extracted from jwt_token cookie as fallback
  • Signature verification using JWT_SECRET_KEY
  • Configurable expiration requirement (REQUIRE_TOKEN_EXPIRATION)
  • JTI claim support for revocation (REQUIRE_JTI)
US-2: Platform Admin - Basic Authentication

As a Platform Administrator
I want tests for Basic Auth username/password validation
So that I can verify admin access is properly secured

Acceptance Criteria:

Feature: Basic Authentication

  Scenario: Valid Basic Auth credentials grant access
    Given BASIC_AUTH_USER is "admin"
    And BASIC_AUTH_PASSWORD is "secure-password-123"
    When I make a request with Authorization: Basic <base64(admin:secure-password-123)>
    Then the request should return status 200
    And the user should be authenticated as admin

  Scenario: Invalid Basic Auth password is rejected
    Given BASIC_AUTH_USER is "admin"
    And BASIC_AUTH_PASSWORD is "secure-password-123"
    When I make a request with Authorization: Basic <base64(admin:wrong-password)>
    Then the request should return status 401
    And the response should indicate "Invalid credentials"

  Scenario: Invalid Basic Auth username is rejected
    Given BASIC_AUTH_USER is "admin"
    When I make a request with Authorization: Basic <base64(wronguser:any-password)>
    Then the request should return status 401

  Scenario: Malformed Basic Auth header is rejected
    When I make a request with Authorization: Basic not-valid-base64!!!
    Then the request should return status 401
    And the response should indicate "Invalid authorization header"

  Scenario: Basic Auth with empty password is rejected
    When I make a request with Authorization: Basic <base64(admin:)>
    Then the request should return status 401

  Scenario: Basic Auth header without credentials is rejected
    When I make a request with Authorization: Basic
    Then the request should return status 401

Technical Requirements:

  • Base64 decoding of credentials
  • Constant-time password comparison to prevent timing attacks
  • No information leakage in error messages (don't reveal if username exists)
US-3: Developer - API Token Authentication

As a Developer
I want tests for API token authentication
So that I can verify programmatic access works correctly

Acceptance Criteria:

Feature: API Token Authentication

  Scenario: Valid API token in Authorization: Bearer header grants access
    Given an active API token exists in the database
    When I make a request with Authorization: Bearer <token-value>
    Then the request should return status 200
    And the user context should match the token owner

  Scenario: Valid API token in Authorization Bearer header grants access
    Given an active API token exists in the database
    When I make a request with Authorization: Bearer <token-value>
    Then the request should return status 200

  Scenario: Revoked API token is rejected
    Given an API token that has been revoked
    When I make a request with the revoked token
    Then the request should return status 401
    And the response should indicate "Token revoked"

  Scenario: Expired API token is rejected
    Given an API token with expires_at in the past
    When I make a request with the expired token
    Then the request should return status 401
    And the response should indicate "Token expired"

  Scenario: Non-existent API token is rejected
    When I make a request with Authorization: Bearer non-existent-token-12345
    Then the request should return status 401
    And the response should indicate "Invalid token"

  Scenario: Inactive API token is rejected
    Given an API token with is_active=false
    When I make a request with the inactive token
    Then the request should return status 401

  Scenario: API token usage is logged
    Given a valid API token
    When I make a request with the token
    Then a TokenUsageLog entry should be created
    And the entry should include timestamp, endpoint, and IP address

Technical Requirements:

  • Token lookup in database by token value
  • Check is_active, expires_at, and revocation status
  • Log token usage for auditing
  • Support both Authorization: Bearer and Authorization Bearer headers
US-4: End User - Email/Password Authentication

As an End User
I want tests for email/password login flow
So that I can verify the login process works securely

Acceptance Criteria:

Feature: Email/Password Authentication

  Scenario: Valid email and password grants access
    Given a user exists with email "user@example.com"
    And the user's password is "ValidPass123!"
    When I POST to /auth/login with email and password
    Then the request should return status 200
    And the response should contain a JWT access token
    And the response should contain a refresh token

  Scenario: Invalid password is rejected
    Given a user exists with email "user@example.com"
    When I POST to /auth/login with email and wrong password
    Then the request should return status 401
    And the response should indicate "Invalid credentials"
    And an EmailAuthEvent should be logged with success=false

  Scenario: Non-existent email is rejected
    When I POST to /auth/login with email "nobody@example.com"
    Then the request should return status 401
    And the response should indicate "Invalid credentials"
    And the error should NOT reveal that the email doesn't exist

  Scenario: Inactive user cannot login
    Given a user exists with is_active=false
    When I POST to /auth/login with the user's credentials
    Then the request should return status 401
    And the response should indicate "Account disabled"

  Scenario: Password change required redirects appropriately
    Given a user with password_change_required=true
    When I POST to /auth/login with valid credentials
    Then the request should return status 200
    And the response should include password_change_required=true
    And limited access should be granted until password is changed

  Scenario: Successful login logs authentication event
    Given a valid user
    When I POST to /auth/login with valid credentials
    Then an EmailAuthEvent should be created
    And the event should include client_ip and user_agent
    And the event should have success=true

  Scenario: Failed login attempts are tracked
    Given a valid user
    When I POST to /auth/login with wrong password 5 times
    Then 5 EmailAuthEvent entries should be created with success=false
    And the security logger should detect potential brute force

Technical Requirements:

  • Argon2id password verification
  • JWT token generation on successful login
  • Authentication event logging
  • Password change enforcement
  • Brute force detection integration
US-5: Operations - Proxy Header Authentication

As an Operations Engineer
I want tests for proxy/header-based authentication
So that I can verify authentication works behind a reverse proxy

Acceptance Criteria:

Feature: Proxy Header Authentication

  Scenario: Trusted proxy header authenticates user
    Given TRUST_PROXY_AUTH is true
    And PROXY_USER_HEADER is "X-Authenticated-User"
    When I make a request with X-Authenticated-User: admin@example.com
    Then the request should be authenticated as admin@example.com

  Scenario: Proxy header ignored when trust disabled
    Given TRUST_PROXY_AUTH is false
    When I make a request with X-Authenticated-User: admin@example.com
    Then the request should return status 401
    And proxy headers should be ignored

  Scenario: Platform admin email grants admin access
    Given TRUST_PROXY_AUTH is true
    And PLATFORM_ADMIN_EMAIL is "superadmin@example.com"
    When I make a request with X-Authenticated-User: superadmin@example.com
    Then the user should have is_admin=true

  Scenario: Empty proxy header is rejected
    Given TRUST_PROXY_AUTH is true
    When I make a request with X-Authenticated-User: (empty)
    Then the request should return status 401

  Scenario: Malformed email in proxy header is rejected
    Given TRUST_PROXY_AUTH is true
    When I make a request with X-Authenticated-User: not-an-email
    Then the request should return status 401
    And the response should indicate "Invalid user identifier"

Technical Requirements:

  • TRUST_PROXY_AUTH configuration toggle
  • Configurable header name (PROXY_USER_HEADER)
  • Email validation for proxy header value
  • Platform admin email matching
US-6: Security Auditor - Authentication Failure Handling

As a Security Auditor
I want tests for authentication failure handling
So that I can verify errors don't leak sensitive information

Acceptance Criteria:

Feature: Authentication Failure Handling

  Scenario: No authentication provided returns 401
    Given AUTH_REQUIRED is true
    When I make a request without any authentication
    Then the request should return status 401
    And the response should indicate "Authentication required"

  Scenario: Error messages don't reveal internal details
    When I make a request with an invalid JWT token
    Then the response should NOT contain stack traces
    And the response should NOT contain internal error messages
    And the response should NOT contain file paths

  Scenario: Authentication errors are logged
    When authentication fails for any reason
    Then the failure should be logged with:
      | Field | Value |
      | level | WARNING or ERROR |
      | reason | failure type |
      | client_ip | request IP |
      | endpoint | request path |

  Scenario: Multiple auth methods tried in order
    Given a request with both Authorization header and Authorization: Bearer
    When the request is processed
    Then Bearer token should be tried first
    And API key should be tried if Bearer fails
    And the first successful auth should be used

  Scenario: Malformed Authorization header type is rejected
    When I make a request with Authorization: Digest abc123
    Then the request should return status 401
    And only Bearer and Basic types should be accepted

Technical Requirements:

  • Consistent error response format
  • No information disclosure in errors
  • Proper logging of all authentication attempts
  • Clear authentication method precedence

🏗 Test Architecture

Authentication Flow

┌─────────────────────────────────────────────────────────────────────────────┐
│                        AUTHENTICATION DECISION FLOW                          │
└─────────────────────────────────────────────────────────────────────────────┘

                              ┌──────────────────┐
                              │ Incoming Request │
                              └────────┬─────────┘
                                       │
                              ┌────────▼─────────┐
                              │ Has Authorization│
                              │ Header?          │
                              └────────┬─────────┘
                                       │
                    ┌──────────────────┼──────────────────┐
                    │ YES                                 │ NO
                    ▼                                     ▼
          ┌─────────────────┐                   ┌─────────────────┐
          │ Parse Header    │                   │ Has Authorization: Bearer   │
          │ Type            │                   │ Header?         │
          └────────┬────────┘                   └────────┬────────┘
                   │                                     │
        ┌──────────┼──────────┐               ┌──────────┼──────────┐
        │          │          │               │ YES                │ NO
        ▼          ▼          ▼               ▼                    ▼
   ┌────────┐ ┌────────┐ ┌────────┐    ┌───────────┐      ┌───────────────┐
   │ Bearer │ │ Basic  │ │ Other  │    │ API Token │      │ Has Cookie?   │
   │ Token  │ │ Auth   │ │ Type   │    │ Lookup    │      │ jwt_token  │
   └───┬────┘ └───┬────┘ └───┬────┘    └─────┬─────┘      └───────┬───────┘
       │          │          │               │                    │
       ▼          ▼          ▼               │          ┌─────────┼─────────┐
   ┌────────┐ ┌────────┐ ┌────────┐          │          │ YES             │ NO
   │ JWT    │ │ User/  │ │ Reject │          │          ▼                 ▼
   │ Verify │ │ Pass   │ │ 401    │          │    ┌───────────┐   ┌─────────────┐
   └───┬────┘ │ Check  │ └────────┘          │    │ JWT from  │   │ Proxy Auth? │
       │      └───┬────┘                     │    │ Cookie    │   └──────┬──────┘
       │          │                          │    └─────┬─────┘          │
       └──────────┴──────────────────────────┴──────────┴────────────────┘
                                       │
                              ┌────────▼─────────┐
                              │ Auth Success?    │
                              └────────┬─────────┘
                                       │
                    ┌──────────────────┼──────────────────┐
                    │ YES                                 │ NO
                    ▼                                     ▼
          ┌─────────────────┐                   ┌─────────────────┐
          │ Populate        │                   │ Return 401      │
          │ request.state   │                   │ Unauthorized    │
          │ .user           │                   │                 │
          └─────────────────┘                   └─────────────────┘

JWT Token Structure

┌─────────────────────────────────────────────────────────────────────────────┐
│                           JWT TOKEN CLAIMS                                   │
└─────────────────────────────────────────────────────────────────────────────┘

Header (Algorithm & Type)
{
  "alg": "HS256",
  "typ": "JWT"
}

Payload (Claims)
{
  "sub": "user@example.com",     # Subject (user email) - REQUIRED
  "exp": 1735689600,             # Expiration time - REQUIRED if REQUIRE_TOKEN_EXPIRATION
  "iat": 1735603200,             # Issued at time
  "jti": "unique-token-id",      # JWT ID - REQUIRED if REQUIRE_JTI
  "teams": ["team-alpha"],       # Team memberships (null = admin)
  "is_admin": false,             # Admin flag
  "permissions": ["tools.read"], # Optional permission restrictions
  "environment": "production"    # Environment claim for isolation
}

Signature
HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  JWT_SECRET_KEY
)

Authentication Method Precedence

┌─────────────────────────────────────────────────────────────────────────────┐
│                    AUTHENTICATION METHOD PRECEDENCE                          │
└─────────────────────────────────────────────────────────────────────────────┘

Priority │ Method              │ Header/Source              │ Notes
─────────┼─────────────────────┼────────────────────────────┼─────────────────
   1     │ Bearer Token (JWT)  │ Authorization: Bearer xxx  │ Primary method
   2     │ Basic Auth          │ Authorization: Basic xxx   │ Admin access
   3     │ API Token           │ Authorization: Bearer xxx             │ Programmatic
   4     │ API Token           │ Authorization: Bearer xxx  │ Fallback lookup
   5     │ Cookie Token        │ Cookie: jwt_token=xxx   │ Web UI sessions
   6     │ Proxy Header        │ X-Authenticated-User: xxx      │ If TRUST_PROXY_AUTH

📋 Test Environment Setup

Prerequisites

0. Start Docker Compose Stack

# Start the stack
docker compose up -d

# Verify health
curl http://localhost:8080/health

1. Configuration

# Core authentication settings
export JWT_SECRET_KEY="test-secret-key-minimum-32-characters-long"
export BASIC_AUTH_USER="admin"
export BASIC_AUTH_PASSWORD="AdminPass123!"
export AUTH_REQUIRED=true
export REQUIRE_TOKEN_EXPIRATION=true
export REQUIRE_JTI=false

# Proxy authentication (disabled by default)
export TRUST_PROXY_AUTH=false
export PROXY_USER_HEADER="X-Authenticated-User"
export PLATFORM_ADMIN_EMAIL="superadmin@example.com"

2. Test Users

Create the following test users in the database:

Email Password is_admin is_active password_change_required
active@example.com ActivePass123! false true false
admin@example.com AdminPass123! true true false
inactive@example.com InactivePass123! false false false
pwchange@example.com TempPass123! false true true
# Create test users via API (requires admin token)
curl -X POST "http://localhost:8080/auth/email/admin/users" \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "active@example.com",
    "password": "ActivePass123!",
    "is_admin": false
  }'

3. Test Tokens

Generate tokens for testing:

# Valid JWT token (expires in 1 hour)
export VALID_JWT=$(python -m mcpgateway.utils.create_jwt_token \
  --username active@example.com \
  --exp 60 \
  --secret "$JWT_SECRET_KEY")

# Expired JWT token
export EXPIRED_JWT=$(python -m mcpgateway.utils.create_jwt_token \
  --username active@example.com \
  --exp -60 \
  --secret "$JWT_SECRET_KEY")

# JWT with wrong signature
export INVALID_SIG_JWT=$(python -m mcpgateway.utils.create_jwt_token \
  --username active@example.com \
  --exp 60 \
  --secret "wrong-secret-key-for-testing")

# Admin JWT token
export ADMIN_JWT=$(python -m mcpgateway.utils.create_jwt_token \
  --username admin@example.com \
  --is-admin \
  --exp 60 \
  --secret "$JWT_SECRET_KEY")

# Base64 encoded Basic Auth
export BASIC_AUTH_VALID=$(echo -n "admin:AdminPass123!" | base64)
export BASIC_AUTH_INVALID=$(echo -n "admin:wrongpassword" | base64)

4. API Tokens

Create API tokens in the database for testing:

# Create a valid API token
curl -X POST "http://localhost:8080/tokens" \
  -H "Authorization: Bearer $ADMIN_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "test-api-token",
    "expires_in_days": 30
  }'
# Save the returned token value as TEST_API_TOKEN

🧪 Manual Test Cases

Section 1: JWT Token Authentication

Case Token State Expected HTTP Status
JWT-01 Valid, not expired Authenticated 200
JWT-02 Expired Rejected 401
JWT-03 Invalid signature Rejected 401
JWT-04 Missing sub claim Rejected 401
JWT-05 Missing exp (required) Rejected 401
JWT-06 Missing exp (not required) Authenticated 200
JWT-07 Revoked (JTI in blacklist) Rejected 401
JWT-08 Malformed (not valid JWT) Rejected 401
JWT-09 Empty token Rejected 401
JWT-10 From cookie (valid) Authenticated 200
JWT-01: Valid JWT token authenticates

Preconditions:

  • Valid JWT token generated with correct secret
  • Token not expired

Steps:

curl -X GET "http://localhost:8080/tools" \
  -H "Authorization: Bearer $VALID_JWT" \
  -w "\nHTTP Status: %{http_code}\n"

Expected Result:

  • HTTP 200 OK
  • Response contains list of accessible tools
  • User context populated from token claims
JWT-02: Expired JWT token rejected

Preconditions:

  • JWT token with exp claim in the past

Steps:

curl -X GET "http://localhost:8080/tools" \
  -H "Authorization: Bearer $EXPIRED_JWT" \
  -w "\nHTTP Status: %{http_code}\n"

Expected Result:

  • HTTP 401 Unauthorized
  • Response: {"detail": "Token has expired"} or similar
  • No user context populated
JWT-03: Invalid signature rejected

Preconditions:

  • JWT token signed with incorrect secret

Steps:

curl -X GET "http://localhost:8080/tools" \
  -H "Authorization: Bearer $INVALID_SIG_JWT" \
  -w "\nHTTP Status: %{http_code}\n"

Expected Result:

  • HTTP 401 Unauthorized
  • Response indicates invalid token
  • No information about the secret leaked
JWT-10: JWT from cookie authenticates

Preconditions:

  • Valid JWT token
  • No Authorization header

Steps:

curl -X GET "http://localhost:8080/tools" \
  -H "Cookie: jwt_token=$VALID_JWT" \
  -w "\nHTTP Status: %{http_code}\n"

Expected Result:

  • HTTP 200 OK
  • User authenticated from cookie token

Section 2: Basic Authentication

Case Credentials Expected HTTP Status
BASIC-01 Valid username/password Authenticated 200
BASIC-02 Invalid password Rejected 401
BASIC-03 Invalid username Rejected 401
BASIC-04 Malformed base64 Rejected 401
BASIC-05 Empty password Rejected 401
BASIC-06 Empty username Rejected 401
BASIC-07 Missing colon separator Rejected 401
BASIC-01: Valid Basic Auth credentials

Preconditions:

  • BASIC_AUTH_USER and BASIC_AUTH_PASSWORD configured

Steps:

curl -X GET "http://localhost:8080/health" \
  -H "Authorization: Basic $BASIC_AUTH_VALID" \
  -w "\nHTTP Status: %{http_code}\n"

Expected Result:

  • HTTP 200 OK
  • User authenticated as admin
BASIC-02: Invalid password rejected

Preconditions:

  • BASIC_AUTH_USER configured

Steps:

curl -X GET "http://localhost:8080/health" \
  -H "Authorization: Basic $BASIC_AUTH_INVALID" \
  -w "\nHTTP Status: %{http_code}\n"

Expected Result:

  • HTTP 401 Unauthorized
  • Response does not reveal that password was wrong (vs username)

Section 3: API Token Authentication

Case Token State Expected HTTP Status
API-01 Valid, active Authenticated 200
API-02 Revoked Rejected 401
API-03 Expired Rejected 401
API-04 Non-existent Rejected 401
API-05 Inactive (is_active=false) Rejected 401
API-06 Via Authorization: Bearer header Authenticated 200
API-07 Via Authorization Bearer Authenticated 200
API-01: Valid API token authenticates

Preconditions:

  • Active API token exists in database

Steps:

curl -X GET "http://localhost:8080/tools" \
  -H "Authorization: Bearer $TEST_API_TOKEN" \
  -w "\nHTTP Status: %{http_code}\n"

Expected Result:

  • HTTP 200 OK
  • User context matches token owner
  • TokenUsageLog entry created

Section 4: Email/Password Authentication

Case Credentials Expected HTTP Status
EMAIL-01 Valid email/password JWT returned 200
EMAIL-02 Invalid password Rejected 401
EMAIL-03 Non-existent email Rejected 401
EMAIL-04 Inactive user Rejected 401
EMAIL-05 Password change required Limited access 200
EMAIL-01: Valid login returns JWT

Preconditions:

Steps:

curl -X POST "http://localhost:8080/auth/login" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "active@example.com",
    "password": "ActivePass123!"
  }' \
  -w "\nHTTP Status: %{http_code}\n"

Expected Result:

  • HTTP 200 OK
  • Response contains jwt_token (JWT)
  • Response may contain refresh_token
  • EmailAuthEvent logged with success=true
EMAIL-03: Non-existent email rejected without revealing

Preconditions:

  • Email does not exist in database

Steps:

curl -X POST "http://localhost:8080/auth/login" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "nobody@example.com",
    "password": "AnyPassword123!"
  }' \
  -w "\nHTTP Status: %{http_code}\n"

Expected Result:

  • HTTP 401 Unauthorized
  • Response: {"detail": "Invalid credentials"}
  • Error message does NOT reveal that email doesn't exist
  • Same response as invalid password (timing-safe)

Section 5: Proxy Header Authentication

Case Configuration Header Expected HTTP Status
PROXY-01 TRUST=true Valid email Authenticated 200
PROXY-02 TRUST=false Valid email Ignored, 401 401
PROXY-03 TRUST=true Platform admin Admin access 200
PROXY-04 TRUST=true Empty header Rejected 401
PROXY-05 TRUST=true Invalid email Rejected 401
PROXY-01: Trusted proxy header authenticates

Preconditions:

  • TRUST_PROXY_AUTH=true
  • PROXY_USER_HEADER=X-Authenticated-User

Steps:

curl -X GET "http://localhost:8080/tools" \
  -H "X-Authenticated-User: admin@example.com" \
  -w "\nHTTP Status: %{http_code}\n"

Expected Result:

Section 6: Authentication Edge Cases

Case Scenario Expected HTTP Status
EDGE-01 No auth, AUTH_REQUIRED=true Rejected 401
EDGE-02 No auth, AUTH_REQUIRED=false Anonymous access 200
EDGE-03 Multiple auth headers First valid wins 200
EDGE-04 Unsupported auth type Rejected 401
EDGE-05 Null/undefined token Rejected 401

🧾 Complete Test Matrix

JWT Token Validation

# Claim State Secret Expired Expected
1 Valid sub, valid exp Correct No ALLOW
2 Valid sub, valid exp Correct Yes DENY
3 Valid sub, valid exp Wrong No DENY
4 Missing sub Correct No DENY
5 Valid sub, no exp Correct N/A ALLOW (if not required)
6 Valid sub, no exp Correct N/A DENY (if required)
7 Valid, JTI revoked Correct No DENY
8 Malformed JWT N/A N/A DENY

Authentication Method Priority

# Bearer Basic Authorization: Bearer Cookie Proxy Expected Auth
1 Valid - - - - Bearer JWT
2 Invalid Valid - - - Basic Auth
3 - - Valid - - API Token
4 - - - Valid - Cookie JWT
5 - - - - Valid Proxy Header
6 - - - - - 401 (if required)

Error Response Validation

# Scenario Response Must NOT Contain
1 Invalid JWT Stack trace, file paths
2 Wrong password "password incorrect" (vs "user not found")
3 Invalid signature Actual secret or key info
4 Expired token Internal timestamps
5 Any auth failure Database error details

✅ Success Criteria

  • All JWT validation scenarios tested (valid, expired, invalid signature, missing claims)
  • Basic Auth tested with valid/invalid credentials
  • API token authentication tested across all states
  • Email/password login flow validated
  • Proxy header authentication tested when enabled/disabled
  • Error messages verified to not leak sensitive information
  • Authentication events properly logged
  • Cookie-based JWT authentication working
  • All test cases documented with curl examples
  • Edge cases covered (no auth, multiple auth headers, malformed input)

🏁 Definition of Done

  • Manual test plan reviewed by Security team
  • All test users and tokens created in test environment
  • All test cases executed with documented results
  • JWT validation covers all claim scenarios
  • Basic Auth timing-safe password comparison verified
  • API token lifecycle (active, revoked, expired) tested
  • Email/password flow including password change tested
  • Proxy auth tested in both enabled and disabled modes
  • Error responses validated for information disclosure
  • Authentication logging verified (success and failure)
  • Test results documented and any failures triaged
  • Plan linked to automated regression suite (#TBD)

🔗 References

  • mcpgateway/auth.py - Core authentication logic
  • mcpgateway/routers/auth.py - Auth endpoints
  • mcpgateway/services/email_auth_service.py - Email/password service
  • mcpgateway/config.py - Authentication configuration
  • mcpgateway/middleware/rbac.py - RBAC middleware

📝 Notes

🔹 Timing Attacks: Password comparison must use constant-time comparison to prevent timing attacks that could reveal valid usernames.

🔹 Error Messages: Authentication errors should be generic ("Invalid credentials") rather than specific ("Password incorrect" vs "User not found") to prevent user enumeration.

🔹 Token Precedence: When multiple authentication methods are present, Bearer token takes precedence over API key, which takes precedence over cookies.

🔹 Credential Caching: JWT verification may be cached for performance. Tests should account for cache TTL when testing token revocation.

Metadata

Metadata

Assignees

Labels

MUSTP1: Non-negotiable, critical requirements without which the product is non-functional or unsafechoreLinting, formatting, dependency hygiene, or project maintenance choresmanual-testingManual testing / test planning issuesreadyValidated, ready-to-work-on itemssecurityImproves securitytestingTesting (unit, e2e, manual, automated, etc)

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions