Skip to content

Testing Authentication Flows

Learn how to test Agent Auth authentication flows in development, staging, and production environments with comprehensive testing strategies.

Thorough testing of authentication flows ensures your Agent Auth integration works reliably before production deployment. This guide covers testing strategies, tools, and best practices.

Purpose: Rapid iteration and debugging

Characteristics:

  • Local development server
  • Test accounts and credentials
  • Verbose logging enabled
  • Quick feedback loops

Setup:

development.env
SCALEKIT_ENV_URL=https://your-env.scalekit.dev
SCALEKIT_CLIENT_ID=dev_client_id
SCALEKIT_CLIENT_SECRET=dev_client_secret
DEBUG=true
LOG_LEVEL=debug

Purpose: Pre-production validation

Characteristics:

  • Production-like configuration
  • Realistic data volumes
  • Integration with staging third-party accounts
  • Performance testing

Setup:

staging.env
SCALEKIT_ENV_URL=https://your-env.scalekit.cloud
SCALEKIT_CLIENT_ID=staging_client_id
SCALEKIT_CLIENT_SECRET=staging_client_secret
DEBUG=false
LOG_LEVEL=info

Purpose: Live user traffic

Characteristics:

  • Real user data
  • Verified OAuth applications
  • Monitoring and alerts
  • Minimal logging

Setup:

production.env
SCALEKIT_ENV_URL=https://your-env.scalekit.cloud
SCALEKIT_CLIENT_ID=prod_client_id
SCALEKIT_CLIENT_SECRET=prod_client_secret
DEBUG=false
LOG_LEVEL=warn

Set up test accounts for each provider:

Google Workspace:

  1. Create test Google account
  2. Enable 2FA if testing MFA scenarios
  3. Use for Gmail, Calendar, Drive testing

Slack:

  1. Create free Slack workspace
  2. Install your Slack app
  3. Use for messaging and notification testing

Microsoft 365:

  1. Get Microsoft 365 developer account (free)
  2. Create test users
  3. Use for Outlook, Teams, OneDrive testing

Jira/Atlassian:

  1. Create free Atlassian Cloud account
  2. Set up test projects
  3. Generate API tokens for testing

Create different test users for scenarios:

# Test user configurations
TEST_USERS = {
"basic_user": {
"identifier": "test_user_001",
"providers": ["gmail"],
"scenario": "Single provider, basic authentication"
},
"power_user": {
"identifier": "test_user_002",
"providers": ["gmail", "slack", "jira", "calendar"],
"scenario": "Multiple providers, full feature access"
},
"expired_user": {
"identifier": "test_user_003",
"providers": ["gmail"],
"scenario": "Expired tokens, test refresh logic",
"setup": "Manually expire tokens"
},
"revoked_user": {
"identifier": "test_user_004",
"providers": ["slack"],
"scenario": "User revoked access, test re-auth flow"
}
}
import unittest
from unittest.mock import Mock, patch
class TestConnectedAccountCreation(unittest.TestCase):
def setUp(self):
self.actions = Mock()
self.user_id = "test_user_123"
self.provider = "gmail"
def test_create_connected_account_success(self):
"""Test successful connected account creation"""
# Mock response
mock_response = Mock()
mock_response.connected_account = Mock(
id="account_123",
status="PENDING",
connection_name="gmail"
)
self.actions.get_or_create_connected_account.return_value = mock_response
# Execute
response = self.actions.get_or_create_connected_account(
connection_name=self.provider,
identifier=self.user_id
)
# Assert
self.assertEqual(response.connected_account.status, "PENDING")
self.assertEqual(response.connected_account.connection_name, "gmail")
def test_generate_authorization_link(self):
"""Test authorization link generation"""
mock_response = Mock()
mock_response.link = "https://accounts.google.com/oauth/authorize?..."
self.actions.get_authorization_link.return_value = mock_response
response = self.actions.get_authorization_link(
connection_name=self.provider,
identifier=self.user_id
)
self.assertIn("https://", response.link)
self.actions.get_authorization_link.assert_called_once()
if __name__ == '__main__':
unittest.main()
def test_token_refresh_scenarios(self):
"""Test various token refresh scenarios"""
test_cases = [
{
"name": "successful_refresh",
"initial_status": "EXPIRED",
"expected_status": "ACTIVE",
"should_succeed": True
},
{
"name": "refresh_token_invalid",
"initial_status": "EXPIRED",
"expected_status": "EXPIRED",
"should_succeed": False
},
{
"name": "already_active",
"initial_status": "ACTIVE",
"expected_status": "ACTIVE",
"should_succeed": True
}
]
for case in test_cases:
with self.subTest(case=case["name"]):
# Setup mock
mock_account = Mock()
mock_account.status = case["expected_status"]
if case["should_succeed"]:
self.actions.refresh_connected_account.return_value = mock_account
else:
self.actions.refresh_connected_account.side_effect = Exception("Refresh failed")
# Execute
try:
result = self.actions.refresh_connected_account(
identifier="test_user",
connection_name="gmail"
)
success = True
except Exception:
success = False
# Assert
self.assertEqual(success, case["should_succeed"])
import time
def test_complete_oauth_flow_integration():
"""
Integration test for complete OAuth authentication flow.
Requires manual intervention for OAuth consent.
"""
user_id = "integration_test_user"
provider = "gmail"
# Step 1: Create connected account
print("Step 1: Creating connected account...")
response = actions.get_or_create_connected_account(
connection_name=provider,
identifier=user_id
)
account = response.connected_account
assert account.status == "PENDING", f"Expected PENDING, got {account.status}"
print(f"✓ Connected account created: {account.id}")
# Step 2: Generate authorization link
print("\nStep 2: Generating authorization link...")
link_response = actions.get_authorization_link(
connection_name=provider,
identifier=user_id
)
print(f"✓ Authorization link: {link_response.link}")
print("\n⚠ MANUAL STEP: Open this link in a browser and complete OAuth")
print(" Press Enter after completing OAuth flow...")
input()
# Step 3: Verify account is now active
print("\nStep 3: Verifying account status...")
time.sleep(2) # Brief delay for processing
account = actions.get_connected_account(
identifier=user_id,
connection_name=provider
)
assert account.status == "ACTIVE", f"Expected ACTIVE, got {account.status}"
print(f"✓ Account is ACTIVE")
print(f" Granted scopes: {account.scopes}")
# Step 4: Test tool execution
print("\nStep 4: Testing tool execution...")
result = actions.execute_tool(
identifier=user_id,
tool_name="gmail_get_profile",
tool_input={}
)
assert result is not None, "Tool execution failed"
print(f"✓ Tool executed successfully")
print("\n✓✓✓ Integration test completed successfully")
# Run with: pytest test_auth_integration.py -s (to see output)
def test_error_scenarios():
"""Test various error scenarios"""
user_id = "error_test_user"
# Test 1: Invalid provider
print("Test 1: Invalid provider...")
try:
actions.get_or_create_connected_account(
connection_name="invalid_provider",
identifier=user_id
)
assert False, "Should have raised error"
except Exception as e:
print(f"✓ Caught expected error: {type(e).__name__}")
# Test 2: Execute tool without authentication
print("\nTest 2: Tool execution without auth...")
try:
actions.execute_tool(
identifier="nonexistent_user",
tool_name="gmail_send_email",
tool_input={"to": "test@example.com"}
)
assert False, "Should have raised error"
except Exception as e:
print(f"✓ Caught expected error: {type(e).__name__}")
# Test 3: Missing required scopes
print("\nTest 3: Missing required scopes...")
# This test requires setup with insufficient scopes
print("⚠ Skipped: Requires special setup")
print("\n✓✓✓ Error scenario tests completed")
.github/workflows/test-auth.yml
name: Test Authentication Flows
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install pytest pytest-cov
- name: Run unit tests
env:
SCALEKIT_CLIENT_ID: ${{ secrets.TEST_CLIENT_ID }}
SCALEKIT_CLIENT_SECRET: ${{ secrets.TEST_CLIENT_SECRET }}
SCALEKIT_ENV_URL: ${{ secrets.TEST_ENV_URL }}
run: |
pytest tests/test_auth.py -v --cov=src/auth
- name: Run integration tests (non-OAuth)
env:
SCALEKIT_CLIENT_ID: ${{ secrets.TEST_CLIENT_ID }}
SCALEKIT_CLIENT_SECRET: ${{ secrets.TEST_CLIENT_SECRET }}
SCALEKIT_ENV_URL: ${{ secrets.TEST_ENV_URL }}
run: |
pytest tests/test_auth_integration.py -v -k "not oauth"
from unittest.mock import patch, Mock
def test_oauth_flow_with_mocks():
"""Test OAuth flow with mocked responses (no actual OAuth)"""
with patch('scalekit.actions.get_or_create_connected_account') as mock_create, \
patch('scalekit.actions.get_authorization_link') as mock_link, \
patch('scalekit.actions.get_connected_account') as mock_get:
# Mock connected account creation
mock_account = Mock()
mock_account.id = "account_123"
mock_account.status = "PENDING"
mock_response = Mock()
mock_response.connected_account = mock_account
mock_create.return_value = mock_response
# Mock authorization link
mock_link_response = Mock()
mock_link_response.link = "https://mock-oauth-url.com"
mock_link.return_value = mock_link_response
# Mock successful authentication (simulate user completing OAuth)
mock_account.status = "ACTIVE"
mock_account.scopes = ["gmail.readonly", "gmail.send"]
mock_get.return_value = mock_account
# Test the flow
# 1. Create account
response = mock_create(connection_name="gmail", identifier="user_123")
assert response.connected_account.status == "PENDING"
# 2. Get auth link
link = mock_link(connection_name="gmail", identifier="user_123")
assert "https://" in link.link
# 3. Simulate user completing OAuth (status changes to ACTIVE)
account = mock_get(identifier="user_123", connection_name="gmail")
assert account.status == "ACTIVE"
assert len(account.scopes) > 0
print("✓ OAuth flow test with mocks completed")
import time
def test_token_refresh_performance():
"""Measure token refresh latency"""
user_id = "perf_test_user"
provider = "gmail"
# Setup: Create account with expired token
# (This requires manually setting up an expired account)
iterations = 10
refresh_times = []
for i in range(iterations):
start_time = time.time()
try:
actions.refresh_connected_account(
identifier=user_id,
connection_name=provider
)
elapsed = time.time() - start_time
refresh_times.append(elapsed)
print(f"Iteration {i+1}: {elapsed:.3f}s")
except Exception as e:
print(f"Iteration {i+1} failed: {e}")
if refresh_times:
avg_time = sum(refresh_times) / len(refresh_times)
min_time = min(refresh_times)
max_time = max(refresh_times)
print(f"\nToken Refresh Performance:")
print(f" Average: {avg_time:.3f}s")
print(f" Min: {min_time:.3f}s")
print(f" Max: {max_time:.3f}s")
# Assert reasonable performance (adjust threshold as needed)
assert avg_time < 2.0, f"Average refresh time too slow: {avg_time:.3f}s"
  1. Unit tests - Test individual authentication functions
  2. Integration tests - Test complete OAuth flows
  3. Error handling - Test all error scenarios
  4. Token refresh - Test automatic and manual refresh
  5. Multi-provider - Test multiple simultaneous connections
  6. Performance - Measure and optimize latency
  7. Security - Verify token encryption and secure storage

Do:

  • Use separate test accounts for each provider
  • Test both success and failure scenarios
  • Mock external OAuth calls in unit tests
  • Test token refresh before expiration
  • Verify error messages are helpful
  • Test with realistic data volumes

Don’t:

  • Use production accounts for testing
  • Hardcode test credentials in source code
  • Skip error scenario testing
  • Assume OAuth always succeeds
  • Neglect performance testing
  • Test only happy path scenarios
def test_security_scenarios():
"""Test security-related authentication scenarios"""
# Test 1: Verify tokens are not exposed in logs
print("Test 1: Token exposure check...")
with patch('logging.Logger.debug') as mock_log:
account = actions.get_connected_account(
identifier="test_user",
connection_name="gmail"
)
# Verify no access tokens in log calls
for call in mock_log.call_args_list:
log_message = str(call)
assert "access_token" not in log_message.lower()
assert "refresh_token" not in log_message.lower()
print("✓ No tokens in logs")
# Test 2: Verify HTTPS for OAuth redirects
print("\nTest 2: HTTPS verification...")
link_response = actions.get_authorization_link(
connection_name="gmail",
identifier="test_user"
)
assert link_response.link.startswith("https://")
print("✓ OAuth uses HTTPS")
# Test 3: State parameter validation
print("\nTest 3: State parameter present...")
assert "state=" in link_response.link
print("✓ State parameter included")
print("\n✓✓✓ Security tests completed")