> **Building with AI coding agents?** If you're using an AI coding agent, install the official Scalekit plugin. It gives your agent full awareness of the Scalekit API — reducing hallucinations and enabling faster, more accurate code generation.
>
> - **Claude Code**: `/plugin marketplace add scalekit-inc/claude-code-authstack` then `/plugin install <auth-type>@scalekit-auth-stack`
> - **GitHub Copilot CLI**: `copilot plugin marketplace add scalekit-inc/github-copilot-authstack` then `copilot plugin install <auth-type>@scalekit-auth-stack`
> - **Codex**: run the bash installer, restart, then open Plugin Directory and enable `<auth-type>`
> - **Skills CLI** (Windsurf, Cline, 40+ agents): `npx skills add scalekit-inc/skills --list` then `--skill <skill-name>`
>
> `<auth-type>` / `<skill-name>`: `agent-auth`, `full-stack-auth`, `mcp-auth`, `modular-sso`, `modular-scim` — [Full setup guide](https://docs.scalekit.com/dev-kit/build-with-ai/)

---

# Testing Authentication Flows

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

## Testing environments

### Development environment

**Purpose:** Rapid iteration and debugging

**Characteristics:**
- Local development server
- Test accounts and credentials
- Verbose logging enabled
- Quick feedback loops

**Setup:**
```python
# 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
```

### Staging environment

**Purpose:** Pre-production validation

**Characteristics:**
- Production-like configuration
- Realistic data volumes
- Integration with staging third-party accounts
- Performance testing

**Setup:**
```python
# 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
```

### Production environment

**Purpose:** Live user traffic

**Characteristics:**
- Real user data
- Verified OAuth applications
- Monitoring and alerts
- Minimal logging

**Setup:**
```python
# 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
```

## Test account setup

### Creating test providers

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

### Test user patterns

Create different test users for scenarios:

```python
# 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"
    }
}
```

## Unit testing authentication

### Test connected account creation

```python
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()
```

```javascript
const { describe, it, expect, jest, beforeEach } = require('@jest/globals');

describe('Connected Account Creation', () => {
  let mockActions;
  const userId = 'test_user_123';
  const provider = 'gmail';

  beforeEach(() => {
    mockActions = {
      getOrCreateConnectedAccount: jest.fn(),
      getAuthorizationLink: jest.fn()
    };
  });

  it('should create connected account successfully', async () => {
    // Mock response
    const mockResponse = {
      connectedAccount: {
        id: 'account_123',
        status: 'PENDING',
        connectionName: 'gmail'
      }
    };

    mockActions.getOrCreateConnectedAccount.mockResolvedValue(mockResponse);

    // Execute
    const response = await mockActions.getOrCreateConnectedAccount({
      connectionName: provider,
      identifier: userId
    });

    // Assert
    expect(response.connectedAccount.status).toBe('PENDING');
    expect(response.connectedAccount.connectionName).toBe('gmail');
  });

  it('should generate authorization link', async () => {
    const mockResponse = {
      link: 'https://accounts.google.com/oauth/authorize?...'
    };

    mockActions.getAuthorizationLink.mockResolvedValue(mockResponse);

    const response = await mockActions.getAuthorizationLink({
      connectionName: provider,
      identifier: userId
    });

    expect(response.link).toContain('https://');
    expect(mockActions.getAuthorizationLink).toHaveBeenCalledTimes(1);
  });
});
```

```go
package auth_test

import (
    "testing"
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/mock"
)

type MockActions struct {
    mock.Mock
}

func (m *MockActions) GetOrCreateConnectedAccount(connectionName, identifier string) (*ConnectedAccountResponse, error) {
    args := m.Called(connectionName, identifier)
    return args.Get(0).(*ConnectedAccountResponse), args.Error(1)
}

func TestCreateConnectedAccount(t *testing.T) {
    // Arrange
    mockActions := new(MockActions)
    userId := "test_user_123"
    provider := "gmail"

    expectedResponse := &ConnectedAccountResponse{
        ConnectedAccount: ConnectedAccount{
            ID:             "account_123",
            Status:         "PENDING",
            ConnectionName: "gmail",
        },
    }

    mockActions.On("GetOrCreateConnectedAccount", provider, userId).
        Return(expectedResponse, nil)

    // Act
    response, err := mockActions.GetOrCreateConnectedAccount(provider, userId)

    // Assert
    assert.NoError(t, err)
    assert.Equal(t, "PENDING", response.ConnectedAccount.Status)
    assert.Equal(t, "gmail", response.ConnectedAccount.ConnectionName)
    mockActions.AssertExpectations(t)
}
```

```java
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

class ConnectedAccountCreationTest {
    @Mock
    private Actions mockActions;

    private String userId;
    private String provider;

    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
        userId = "test_user_123";
        provider = "gmail";
    }

    @Test
    void testCreateConnectedAccountSuccess() {
        // Arrange
        ConnectedAccount account = new ConnectedAccount();
        account.setId("account_123");
        account.setStatus("PENDING");
        account.setConnectionName("gmail");

        ConnectedAccountResponse mockResponse = new ConnectedAccountResponse();
        mockResponse.setConnectedAccount(account);

        when(mockActions.getOrCreateConnectedAccount(provider, userId))
            .thenReturn(mockResponse);

        // Act
        ConnectedAccountResponse response = mockActions
            .getOrCreateConnectedAccount(provider, userId);

        // Assert
        assertEquals("PENDING", response.getConnectedAccount().getStatus());
        assertEquals("gmail", response.getConnectedAccount().getConnectionName());
        verify(mockActions, times(1)).getOrCreateConnectedAccount(provider, userId);
    }
}
```

### Test token refresh logic

```python
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"])
```

## Integration testing

### Test complete authentication flow

```python
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)
```

### Test error scenarios

```python
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")
```

## Automated testing

### Test authentication in CI/CD

```yaml
# .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"
```

### Mock OAuth flows

```python
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")
```

## Performance testing

### Test token refresh performance

```python
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"
```

## Best practices

### Test checklist

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
### Testing dos and don'ts

✅ **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

### Security testing

```python
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")
```

## Next steps

- [Authentication Troubleshooting](/agent-auth/authentication/troubleshooting) - Debug authentication issues
- [Multi-Provider Authentication](/agent-auth/authentication/multi-provider) - Test multiple providers

---

## More Scalekit documentation

| Resource | What it contains | When to use it |
|----------|-----------------|----------------|
| [/llms.txt](/llms.txt) | Structured index with routing hints per product area | Start here — find which documentation set covers your topic before loading full content |
| [/llms-full.txt](/llms-full.txt) | Complete documentation for all Scalekit products in one file | Use when you need exhaustive context across multiple products or when the topic spans several areas |
| [sitemap-0.xml](https://docs.scalekit.com/sitemap-0.xml) | Full URL list of every documentation page | Use to discover specific page URLs you can fetch for targeted, page-level answers |
