# Security Guide

This guide covers the security features and best practices for using the GitHub OAuth Helper safely.

## Overview

The GitHub OAuth Helper is designed with security as a primary concern. This guide explains the built-in security features and provides recommendations for secure usage.

## Built-in Security Features

### 1. No Hard-Coded Secrets

The library never hard-codes OAuth secrets in source code:

```python
# ✅ SECURE: Uses environment variables
oauth = GitHubOAuth()  # Reads from GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET

# ❌ INSECURE: Hard-coded secrets
oauth = GitHubOAuth(
    client_id="ghp_hardcoded_secret",  # Visible in source code
    client_secret="secret_123"         # Security risk
)
```

**Why this matters:**
- Prevents accidental exposure in version control
- Reduces risk of secrets in compiled binaries
- Enables secure deployment practices

### 2. Environment Variable Management

Automatic environment variable handling with validation:

```python
import os
from gh_oauth_helper import GitHubOAuth, GitHubOAuthError

# The library validates that required variables are set
try:
    oauth = GitHubOAuth()
except GitHubOAuthError as e:
    print(f"Missing required environment variables: {e}")
```

**Required variables:**
- `GITHUB_CLIENT_ID`: Your OAuth app client ID
- `GITHUB_CLIENT_SECRET`: Your OAuth app client secret

**Optional variables:**
- `GITHUB_REDIRECT_URI`: Callback URL (defaults to localhost)

### 3. CSRF Protection with State Parameters

Automatic generation and verification of state parameters:

```python
# State parameter automatically generated
auth_url, state = oauth.generate_authorization_url()

# Save state for verification (in session, database, etc.)
session['oauth_state'] = state

# Later, verify the state when processing the callback
try:
    token_data = oauth.exchange_code_for_token(
        code=request.args.get('code'),
        state=session.get('oauth_state')  # Verify against saved state
    )
except GitHubOAuthError as e:
    print(f"CSRF verification failed: {e}")
```

**State parameter features:**
- Cryptographically secure random generation
- Prevents cross-site request forgery attacks
- Automatic verification during token exchange

### 4. Transport Security

Automatic HTTPS enforcement based on context:

#### Standard Mode (Default)
```python
oauth = GitHubOAuth()  # Standard mode

# Localhost development - HTTP allowed
auth_url, state = oauth.generate_authorization_url()
# Uses redirect_uri = "http://localhost:8080/callback"

# Production HTTPS - automatically secure
oauth = GitHubOAuth(redirect_uri="https://myapp.com/oauth/callback")
```

#### Secure Mode
```python
oauth = GitHubOAuth(secure_mode=True)  # Strict HTTPS enforcement

# This will raise an error in secure mode
oauth = GitHubOAuth(
    redirect_uri="http://example.com/callback",  # Non-localhost HTTP
    secure_mode=True
)
# GitHubOAuthError: Secure mode requires HTTPS redirect URI
```

**Transport security rules:**
- **Localhost**: HTTP allowed in both modes (development)
- **Non-localhost**: HTTP allowed in standard mode (with warning), HTTPS required in secure mode
- **HTTPS**: Always secure in both modes

### 5. Token Safety

Secure token handling throughout the library:

```python
# Tokens are never logged or exposed in error messages
try:
    user_info = oauth.test_api_access(access_token)
except GitHubOAuthError as e:
    # Error message doesn't contain the token
    print(f"Token validation failed: {e}")

# Tokens are not stored longer than necessary
token_data = oauth.exchange_code_for_token(code, state)
# Token is returned immediately, not cached
```

**Token safety features:**
- No logging of sensitive data
- No caching of tokens in memory
- Secure error handling without token exposure
- Proper cleanup of sensitive variables

## CLI Security Features

### 1. Secure Parameter Handling

```bash
# ✅ SECURE: Environment variables (not visible in process list)
export GITHUB_CLIENT_SECRET="ghp_secret"
gh-oauth-helper auth

# ❌ INSECURE: Command-line parameters (visible in `ps` output)
gh-oauth-helper auth --client-secret ghp_secret
```

### 2. Secure Mode Flag

```bash
# Enable strict HTTPS enforcement
gh-oauth-helper --secure auth --redirect-uri https://myapp.com/callback

# This will fail in secure mode:
gh-oauth-helper --secure auth --redirect-uri http://staging.com/callback
# Error: Secure mode requires HTTPS redirect URI for non-localhost addresses
```

### 3. Colored Security Warnings

The CLI provides visual security warnings:

```bash
# Warning for HTTP redirect URIs
⚠️ Warning: Using HTTP redirect URI for non-localhost address. Consider using HTTPS in production.

# Error for security violations
❌ OAuth Error: Secure mode requires HTTPS redirect URI for non-localhost addresses
```

## Best Practices

### 1. Environment Variable Security

#### Development Environment
```bash
# Use .env files (add to .gitignore)
echo "GITHUB_CLIENT_ID=your_id" >> .env
echo "GITHUB_CLIENT_SECRET=your_secret" >> .env
echo ".env" >> .gitignore

# Load environment variables
export $(cat .env | xargs)
```

#### Production Environment
```bash
# Use your platform's secret management
# Examples:

# Docker
docker run -e GITHUB_CLIENT_ID="$CLIENT_ID" myapp

# Kubernetes
kubectl create secret generic github-oauth \
  --from-literal=client-id="$CLIENT_ID" \
  --from-literal=client-secret="$CLIENT_SECRET"

# AWS Systems Manager Parameter Store
aws ssm put-parameter \
  --name "/myapp/github/client-id" \
  --value "$CLIENT_ID" \
  --type "SecureString"
```

### 2. HTTPS Configuration

#### Production Setup
```python
# Always use HTTPS in production
oauth = GitHubOAuth(
    redirect_uri="https://myapp.com/oauth/callback",
    secure_mode=True  # Enforce HTTPS
)
```

#### SSL Certificate Validation
```python
import ssl
import requests

# Ensure SSL certificates are validated
session = requests.Session()
session.verify = True  # Default, but explicit is better

# Don't disable SSL verification in production
# session.verify = False  # ❌ NEVER DO THIS
```

### 3. State Parameter Management

#### Web Application Example
```python
from flask import Flask, session, request, redirect
from gh_oauth_helper import GitHubOAuth, GitHubOAuthError

app = Flask(__name__)
app.secret_key = 'your-secret-key'  # Use a proper secret

@app.route('/login')
def login():
    oauth = GitHubOAuth()
    auth_url, state = oauth.generate_authorization_url()
    
    # Store state in session
    session['oauth_state'] = state
    
    return redirect(auth_url)

@app.route('/oauth/callback')
def oauth_callback():
    code = request.args.get('code')
    state = request.args.get('state')
    stored_state = session.pop('oauth_state', None)
    
    # Verify state parameter
    if not stored_state or state != stored_state:
        return "CSRF verification failed", 400
    
    try:
        oauth = GitHubOAuth()
        token_data = oauth.exchange_code_for_token(code, state)
        # Handle successful authentication
        return f"Authenticated successfully!"
    except GitHubOAuthError as e:
        return f"Authentication failed: {e}", 400
```

### 4. Token Storage and Management

#### Secure Token Storage
```python
import os
import keyring  # Optional: for OS keychain integration

def store_token_securely(username: str, token: str) -> None:
    """Store token using OS keychain (recommended)."""
    try:
        keyring.set_password("github-oauth", username, token)
    except Exception:
        # Fallback to environment variable
        os.environ[f"GITHUB_TOKEN_{username.upper()}"] = token

def get_stored_token(username: str) -> str:
    """Retrieve token from secure storage."""
    try:
        return keyring.get_password("github-oauth", username)
    except Exception:
        return os.environ.get(f"GITHUB_TOKEN_{username.upper()}")
```

#### Token Rotation
```python
import time
from datetime import datetime, timedelta

class TokenManager:
    def __init__(self):
        self.tokens = {}  # In production, use secure database
    
    def store_token(self, user_id: str, token_data: dict) -> None:
        """Store token with metadata."""
        self.tokens[user_id] = {
            'access_token': token_data['access_token'],
            'created_at': datetime.now(),
            'expires_in': token_data.get('expires_in', 3600),
            'scope': token_data.get('scope', '')
        }
    
    def is_token_expired(self, user_id: str) -> bool:
        """Check if token needs refresh."""
        if user_id not in self.tokens:
            return True
        
        token_info = self.tokens[user_id]
        expires_at = token_info['created_at'] + timedelta(
            seconds=token_info['expires_in']
        )
        
        # Refresh 5 minutes before expiry
        return datetime.now() >= (expires_at - timedelta(minutes=5))
    
    def revoke_token(self, user_id: str) -> None:
        """Revoke and remove token."""
        if user_id in self.tokens:
            oauth = GitHubOAuth()
            oauth.revoke_token(self.tokens[user_id]['access_token'])
            del self.tokens[user_id]
```

### 5. Error Handling and Logging

#### Secure Error Handling

**Note**: Use the `--verbose` flag for detailed debugging information while maintaining security (sensitive data is never exposed).

```python
import logging
from gh_oauth_helper import GitHubOAuthError

# Configure logging to exclude sensitive data
class SensitiveDataFilter(logging.Filter):
    def filter(self, record):
        # Remove tokens from log messages
        if hasattr(record, 'msg'):
            record.msg = self.sanitize_message(record.msg)
        return True
    
    def sanitize_message(self, message):
        import re
        # Remove GitHub tokens (ghp_, gho_, etc.)
        return re.sub(r'gh[a-z]_[A-Za-z0-9_]{36}', '[REDACTED]', str(message))

# Set up logging
logger = logging.getLogger(__name__)
logger.addFilter(SensitiveDataFilter())

def handle_oauth_error(e: GitHubOAuthError) -> dict:
    """Handle OAuth errors securely."""
    # Log the error without exposing sensitive details
    logger.error(f"OAuth operation failed: {type(e).__name__}")
    
    # Return generic error to user
    return {
        'error': 'Authentication failed',
        'message': 'Please try again or contact support',
        'code': 'OAUTH_ERROR'
    }
```

### 6. Input Validation

#### Validate OAuth Parameters
```python
import re
from gh_oauth_helper import GitHubOAuthError

def validate_oauth_code(code: str) -> str:
    """Validate GitHub authorization code format."""
    if not code:
        raise ValueError("Authorization code is required")
    
    # GitHub codes are typically 20 characters, alphanumeric
    if not re.match(r'^[a-zA-Z0-9]{20}$', code):
        raise ValueError("Invalid authorization code format")
    
    return code

def validate_state_parameter(state: str) -> str:
    """Validate state parameter."""
    if not state:
        raise ValueError("State parameter is required")
    
    # State should be at least 8 characters
    if len(state) < 8:
        raise ValueError("State parameter too short")
    
    # Should contain only safe characters
    if not re.match(r'^[a-zA-Z0-9_-]+$', state):
        raise ValueError("State parameter contains invalid characters")
    
    return state
```

## Security Checklist

### Development
- [ ] Store OAuth secrets in environment variables
- [ ] Add `.env` files to `.gitignore`
- [ ] Use HTTPS for redirect URIs in staging/production
- [ ] Implement proper state parameter verification
- [ ] Validate all user inputs
- [ ] Use secure logging practices

### Production
- [ ] Use secure secret management (AWS Secrets Manager, Azure Key Vault, etc.)
- [ ] Enable secure mode for OAuth helper
- [ ] Use HTTPS everywhere
- [ ] Implement token rotation
- [ ] Set up proper monitoring and alerting
- [ ] Regular security audits
- [ ] Implement rate limiting
- [ ] Use secure session management

### CLI Usage
- [ ] Use environment variables instead of command-line arguments
- [ ] Enable secure mode with `--secure` flag
- [ ] Use HTTPS redirect URIs in production
- [ ] Implement proper secret rotation procedures

## Common Security Mistakes to Avoid

### 1. Hard-coding Secrets
```python
# ❌ DON'T
oauth = GitHubOAuth(
    client_id="ghp_hardcoded",
    client_secret="secret123"
)

# ✅ DO
oauth = GitHubOAuth()  # Uses environment variables
```

### 2. Ignoring State Verification
```python
# ❌ DON'T
token_data = oauth.exchange_code_for_token(code)  # No state verification

# ✅ DO
token_data = oauth.exchange_code_for_token(code, state)  # Verify state

# 💡 Alternative: Use the CLI's paste-the-URL method for easier secure flows
# See OAUTH_FLOW_GUIDE.md for details
```

### 3. Using HTTP in Production
```python
# ❌ DON'T
oauth = GitHubOAuth(redirect_uri="http://myapp.com/callback")

# ✅ DO
oauth = GitHubOAuth(
    redirect_uri="https://myapp.com/callback",
    secure_mode=True
)
```

### 4. Logging Sensitive Data
```python
# ❌ DON'T
logger.info(f"Received token: {access_token}")

# ✅ DO
logger.info(f"Token received: {access_token[:10]}...")
```

### 5. Not Revoking Tokens
```python
# ✅ DO: Always revoke tokens when done
def logout_user(access_token):
    oauth = GitHubOAuth()
    oauth.revoke_token(access_token)
    # Clear from storage
```

## Reporting Security Issues

If you discover a security vulnerability:

1. **DO NOT** create a public GitHub issue
2. Email the maintainers directly at security@example.com
3. Include:
   - Description of the vulnerability
   - Steps to reproduce
   - Potential impact
   - Suggested fix (if available)

We follow responsible disclosure practices and will acknowledge security reports within 48 hours.

## Security Updates

- Monitor the [GitHub repository](https://github.com/jondmarien/gh-oauth-helper) for security updates
- Subscribe to release notifications
- Keep the library updated to the latest version
- Review the [SECURITY.md](../SECURITY.md) file for the latest security policies

