Category: Uncategorized

  • Python Selenium Web Driver Complete Tutorial

    Welcome to the ultimate Python Selenium WebDriver complete tutorial! Whether you’re a beginner eager to dip your toes into browser automation or a seasoned tester looking to sharpen your skills, this guide will walk you through everything you need to know—from setting up the environment to crafting robust, maintainable test scripts. By the end, you’ll be able to automate real‑world web applications with confidence, speed, and precision.

    Why Choose Selenium with Python?

    Selenium remains the industry‑standard tool for web automation, and Python’s clean syntax makes it an ideal partner. Here are a few compelling reasons to combine them:

    • Cross‑browser support: Test on Chrome, Firefox, Edge, Safari, and more.
    • Open source & community‑driven: Free to use with a massive ecosystem of plugins and tutorials.
    • Easy integration: Works seamlessly with CI/CD pipelines, pytest, and reporting tools.
    • Readable code: Python’s simplicity reduces maintenance overhead.

    1. Setting Up Your Environment

    Install Python and Pip

    First, ensure you have Python 3.8+ installed. Verify the installation:

    python --version
    pip --version

    Install Selenium and WebDriver Manager

    Use pip to install the Selenium library and a handy WebDriver manager that automatically handles driver binaries:

    pip install selenium webdriver-manager

    Choose Your Browser

    While Chrome is the most popular, you might need Firefox (GeckoDriver) or Edge (msedgedriver). The webdriver-manager package abstracts the download process, so you don’t have to manage binaries manually.

    2. Creating Your First Selenium Script

    Basic Script Structure

    Below is a minimal example that launches Chrome, navigates to python.org, and prints the page title.

    from selenium import webdriver
    from webdriver_manager.chrome import ChromeDriverManager
    
    # Initialize Chrome driver
    driver = webdriver.Chrome(ChromeDriverManager().install())
    
    try:
        driver.get("https://www.python.org")
        print("Page title:", driver.title)
    finally:
        driver.quit()

    Understanding Key Concepts

    • WebDriver: The core object that controls the browser.
    • Locator strategies: Find elements by id, name, xpath, css selector, etc.
    • Explicit waits: Synchronize script actions with dynamic page content.

    3. Mastering Element Locators

    Effective element location is the backbone of reliable automation. Below are the most common strategies with code snippets.

    1. By ID
      element = driver.find_element(By.ID, "search-input")
    2. By Name
      element = driver.find_element(By.NAME, "q")
    3. By CSS Selector
      element = driver.find_element(By.CSS_SELECTOR, ".nav > a[href='/downloads/']")
    4. By XPath
      element = driver.find_element(By.XPATH, "//button[text()='Submit']")

    4. Implementing Waits for Stable Tests

    Implicit vs. Explicit Waits

    Implicit waits apply globally, while explicit waits target specific conditions. Explicit waits are generally preferred for their precision.

    Example: Using WebDriverWait

    from selenium.webdriver.common.by import By
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    
    wait = WebDriverWait(driver, 10)  # 10‑second timeout
    search_box = wait.until(EC.visibility_of_element_located((By.NAME, "q")))
    search_box.send_keys("Selenium Python")
    search_box.submit()

    5. Structuring Tests with PyTest

    Integrating Selenium with pytest brings powerful fixtures, parallel execution, and beautiful reporting.

    Sample pytest Fixture

    import pytest
    from selenium import webdriver
    from webdriver_manager.chrome import ChromeDriverManager
    
    @pytest.fixture(scope="function")
    def driver():
        driver = webdriver.Chrome(ChromeDriverManager().install())
        driver.maximize_window()
        yield driver
        driver.quit()

    Writing a Test Case

    def test_python_homepage_title(driver):
        driver.get("https://www.python.org")
        assert "Python" in driver.title

    6. Advanced Topics

    Handling Alerts, Frames, and Windows

    • Alerts: driver.switch_to.alert.accept()
    • Frames: driver.switch_to.frame("frameName")
    • Multiple windows: Switch using driver.window_handles and driver.switch_to.window(handle)

    Taking Screenshots for Debugging

    driver.save_screenshot("error_state.png")

    Running Tests in Headless Mode

    Headless browsers are perfect for CI pipelines where a UI isn’t available.

    from selenium.webdriver.chrome.options import Options
    
    options = Options()
    options.add_argument("--headless")
    driver = webdriver.Chrome(ChromeDriverManager().install(), options=options)

    7. Best Practices for Maintainable Selenium Code

    • Page Object Model (POM): Encapsulate page interactions in dedicated classes.
    • Use explicit waits: Avoid flaky tests caused by timing issues.
    • Keep locators centralized: One place to update when UI changes.
    • Leverage logging: Use Python’s logging module for traceable output.
    • Run on a grid: Parallelize across browsers with Selenium Grid or cloud services like BrowserStack.

    8. Deploying Selenium Tests to CI/CD

    Integrate your test suite with popular CI tools (GitHub Actions, GitLab CI, Jenkins). A minimal GitHub Actions workflow might look like this:

    name: Selenium Tests
    
    on: [push, pull_request]
    
    jobs:
      test:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v3
          - name: Set up Python
            uses: actions/setup-python@v4
            with:
              python-version: "3.10"
          - name: Install dependencies
            run: |
              pip install selenium webdriver-manager pytest
          - name: Run tests
            run: |
              pytest -v tests/

    Conclusion

    Congratulations! You’ve just completed a comprehensive Python Selenium WebDriver tutorial that covered everything from installation to advanced automation techniques. By applying the best practices outlined above—such as using explicit waits, embracing the Page Object Model, and integrating with CI/CD—you’ll create fast, reliable, and maintainable test suites that scale with your projects. Keep experimenting, stay updated with Selenium’s latest releases, and watch your automation skills soar.

  • Python Playwright Web Automation Guide

    Looking to supercharge your web testing or data‑scraping projects with a modern, reliable tool? Python Playwright offers a fast, reliable, and cross‑browser automation framework that rivals Selenium and Puppeteer. In this comprehensive guide, we’ll walk through everything you need to know to get started, from installation and basic concepts to advanced techniques like handling iframes, intercepting network requests, and running tests in CI/CD pipelines. Whether you’re a seasoned QA engineer or a Python hobbyist, this guide will equip you with the knowledge to harness Playwright’s full potential.

    Why Choose Playwright for Python?

    Playwright was created by the same team that built Microsoft Edge’s automation engine, and it’s designed to address many pain points developers face with older tools. Here are the top reasons to consider Playwright for your next automation project:

    • Cross‑browser support: One API works with Chromium, Firefox, and WebKit (Safari) out of the box.
    • Auto‑waiting: Playwright intelligently waits for elements to be ready, reducing flaky tests.
    • Powerful selectors: Use CSS, XPath, text, and even role‑based selectors for accessibility testing.
    • Network control: Intercept, modify, or mock network requests and responses.
    • Headless and headed modes: Run browsers in the background or with a UI for debugging.
    • Built‑in test runner: Playwright Test provides parallel execution, fixtures, and powerful reporting.

    Getting Started: Installation and First Script

    Step 1: Install Playwright and Its Browsers

    Playwright can be installed via pip. The playwright package includes a helper command to download the required browsers.

    pip install playwright
    python -m playwright install
    

    The install command pulls the latest stable versions of Chromium, Firefox, and WebKit, ensuring consistency across environments.

    Step 2: Write a Simple Script

    Below is a minimal script that launches Chromium, navigates to example.com, takes a screenshot, and closes the browser.

    from playwright.sync_api import sync_playwright
    
    def run():
        with sync_playwright() as p:
            browser = p.chromium.launch(headless=True)
            page = browser.new_page()
            page.goto("https://example.com")
            page.screenshot(path="example.png")
            browser.close()
    
    if __name__ == "__main__":
        run()
    

    Save this as example.py and run python example.py. You’ll find example.png in the same directory, confirming that Playwright successfully rendered the page.

    Core Concepts You Need to Master

    1. Browser, Context, and Page

    Playwright’s architecture separates three key objects:

    • Browser: Represents the actual browser executable (Chromium, Firefox, WebKit). Launch it once per test suite for efficiency.
    • BrowserContext: Analogous to an incognito window. Each context isolates cookies, storage, and cache, allowing parallel tests without interference.
    • Page: A single tab or window inside a context. All interactions—clicks, navigation, typing—happen on a page.

    Using contexts wisely reduces memory consumption and speeds up test execution.

    2. Selectors and Locator API

    Playwright’s locator API provides a fluent, auto‑waiting interface. Instead of manually waiting for an element, you can chain actions directly:

    page.locator("button:has-text('Submit')").click()
    

    Playwright also supports role‑based selectors for accessibility testing:

    page.get_by_role("button", name="Login").click()
    

    3. Auto‑waiting and Assertions

    Playwright automatically waits for the following before performing an action:

    • Element to be attached to the DOM.
    • Element to be visible and stable (no animation).
    • Network idle (optional).

    Combine this with built‑in assertions for robust checks:

    await expect(page.locator("h1")).to_have_text("Welcome")
    

    Advanced Automation Techniques

    Handling Iframes and Shadow DOM

    Many modern sites embed content inside iframes or use Shadow DOM for component encapsulation. Playwright makes these interactions straightforward.

    # Switch to an iframe by its name or selector
    frame = page.frame(name="payment-frame")
    frame.locator("input[name='cardNumber']").fill("4111 1111 1111 1111")
    
    # Interact with Shadow DOM
    shadow_root = page.locator("my-component").evaluate("el => el.shadowRoot")
    shadow_root.locator("button").click()
    

    Network Interception and Mocking

    Testing error handling or API contracts often requires mocking network responses. Playwright can intercept requests and provide custom responses.

    def handle_route(route, request):
        if "api/users" in request.url:
            route.fulfill(
                status=200,
                content_type="application/json",
                body='[{"id":1,"name":"Alice"}]'
            )
        else:
            route.continue_()
    
    page.route("**/*", handle_route)
    page.goto("https://myapp.test")
    

    This snippet forces the /api/users endpoint to return a static JSON payload, allowing you to test UI behavior without a live backend.

    Parallel Execution with Playwright Test

    Playwright includes a powerful test runner that supports parallelism, retries, and fixtures. Install the test package and create a test file:

    pip install pytest-playwright
    
    # tests/test_login.py
    import pytest
    from playwright.sync_api import Page
    
    @pytest.fixture(scope="session")
    def browser_context(browser):
        return browser.new_context()
    
    def test_successful_login(browser_context: Page):
        page = browser_context.new_page()
        page.goto("https://example.com/login")
        page.get_by_label("Username").fill("testuser")
        page.get_by_label("Password").fill("secret")
        page.get_by_role("button", name="Log in").click()
        expect(page).to_have_url("https://example.com/dashboard")
    

    Run the suite with pytest -n auto to automatically distribute tests across available CPU cores.

    Best Practices for Reliable Playwright Scripts

    • Prefer locator over query_selector: Locators provide built‑in waiting and retry logic.
    • Use explicit timeouts sparingly: Rely on auto‑waiting; only set a custom timeout when a specific condition is known to be slow.
    • Isolate tests with separate contexts: This prevents state leakage between tests and mirrors real user sessions.
    • Capture screenshots and videos on failure: Configure Playwright Test to automatically record artifacts for debugging.
    • Keep selectors resilient: Use data‑testids or role‑based selectors instead of brittle CSS paths.
    • Version‑pin browsers: In CI, lock browser versions to avoid unexpected changes.

    Running Playwright in CI/CD Pipelines

    Integrating Playwright into GitHub Actions, GitLab CI, or Azure Pipelines is straightforward. Below is a minimal GitHub Actions workflow that installs dependencies, runs Playwright tests, and uploads a test report.

    name: Playwright Tests
    
    on: [push, pull_request]
    
    jobs:
      test:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - name: Set up Python
            uses: actions/setup-python@v5
            with:
              python-version: "3.11"
          - name: Install dependencies
            run: |
              python -m pip install --upgrade pip
              pip install -r requirements.txt
              pip install pytest-playwright
              python -m playwright install --with-deps
          - name: Run tests
            run: pytest -n auto --html=report.html
          - name: Upload report
            uses: actions/upload-artifact@v4
            with:
              name: test-report
              path: report.html
    

    This workflow ensures that every commit is validated against your Playwright suite, catching regressions early.

    Common Pitfalls and How to Avoid Them

    Flaky Tests Due to Timing Issues

    Even though Playwright auto‑waits, some dynamic applications load content via websockets or long‑polling. In such cases, add explicit waits for specific network events:

    with page.expect_response("**/notifications"):
        page.click("button#refresh")
    

    Running Out of Memory in Large Test Suites

    Launching a new browser for each test can quickly exhaust resources. Reuse the browser instance and create a fresh context per test instead of a new browser.

    Cross‑Browser Inconsistencies

    Features like CSS grid or certain JavaScript APIs may behave differently across Chromium, Firefox, and WebKit. Use Playwright’s test.describe.parallel to run the same test across all browsers and flag inconsistencies early.

    Conclusion

    Python Playwright has rapidly become the go‑to solution for modern web automation, offering a clean API, reliable auto‑waiting, and powerful cross‑browser capabilities. By mastering the fundamentals—browser contexts, locators, and the built‑in test runner—and applying best practices for selectors, network handling, and CI integration, you’ll be able to build fast, maintainable, and scalable automation suites. Whether

  • Python Server-Sent Events Sse Web App

    Server‑Sent Events (SSE) give Python developers a lightweight, standards‑based way to push real‑time updates from the server directly to the browser. Unlike WebSockets, SSE works over plain HTTP, automatically handles reconnections, and integrates seamlessly with modern front‑end frameworks. In this guide we’ll explore the fundamentals of Python Server‑Sent Events, walk through a complete SSE web app built with Flask, and share best‑practice tips for scaling, security, and SEO optimization.

    What Are Server‑Sent Events?

    Server‑Sent Events, defined by the HTML5 specification, enable a unidirectional, persistent connection where the server streams text‑based events to the client. Each event is formatted as a simple key‑value pair, making it easy to parse and debug.

    • Unidirectional: Data flows only from server to client.
    • Built‑in reconnection: Browsers automatically retry lost connections.
    • Simple MIME type: Uses text/event-stream with minimal overhead.
    • SEO friendly: Since the initial page load is a normal HTTP response, search engines can index the content without extra tricks.

    Why Choose SSE Over WebSockets?

    Both SSE and WebSockets provide real‑time capabilities, but each shines in different scenarios. Here’s a quick comparison:

    Feature SSE WebSocket
    Direction Server → client only Full duplex
    Protocol HTTP/1.1 (or HTTP/2) Custom (ws/wss)
    Complexity Low – simple text stream Higher – binary framing
    Browser support All modern browsers All modern browsers (with fallback)
    SEO impact Neutral – normal page load Potentially negative – requires SSR

    For applications that primarily need to broadcast updates—such as live dashboards, notifications, or stock tickers—SSE is often the more efficient choice.

    Setting Up a Python SSE Server

    Choosing a Framework

    Python offers several web frameworks that support SSE out of the box or with minimal extensions:

    • Flask: Lightweight, easy to prototype.
    • FastAPI: Async‑first, ideal for high‑throughput streams.
    • Django: Can use django-sse or Channels for integration.

    In this tutorial we’ll use Flask because its simplicity highlights the core SSE concepts without extra boilerplate.

    Flask SSE Example

    Below is a complete, production‑ready Flask app that streams random numbers to the client every second.

    from flask import Flask, Response, stream_with_context
    import time
    import random
    import json
    
    app = Flask(__name__)
    
    def event_stream():
        """Yield Server‑Sent Events in the proper format."""
        while True:
            # Simulate a data source, e.g., sensor reading or DB query
            data = {
                "timestamp": int(time.time()),
                "value": random.randint(0, 100)
            }
            # SSE format: 'event: \n' optional, then 'data: \n\n'
            yield f"data: {json.dumps(data)}\n\n"
            time.sleep(1)
    
    @app.route('/stream')
    def stream():
        # Set the correct MIME type for SSE
        return Response(
            stream_with_context(event_stream()),
            mimetype='text/event-stream',
            headers={'Cache-Control': 'no-cache'}
        )
    
    @app.route('/')
    def index():
        # Simple HTML page that connects to the SSE endpoint
        return '''
        
        
        
            Python SSE Demo
            
            
        
        
            

    Real‑time Random Numbers

    
    




    '''

    if __name__ == '__main__':
    app.run(debug=True, threaded=True)

    Key Points in the Code

    • Generator function: event_stream() yields a properly formatted SSE string.
    • Content‑type: text/event-stream tells the browser to treat the response as an event source.
    • Cache control: no-cache prevents intermediate proxies from buffering the stream.
    • Threaded server: Enables concurrent handling of multiple SSE connections in Flask’s built‑in server (use gunicorn or uvicorn for production).

    Client‑Side Integration

    On the front end, the native EventSource API handles most of the heavy lifting. Here’s a quick breakdown of the most useful properties and events:

    • onopen – Fires when the connection is established.
    • onmessage – Receives each data payload.
    • onerror – Triggers on network failures; the browser automatically retries.
    • addEventListener('customEvent', …) – Allows you to listen for named events if you send event: customEvent from the server.

    For React, Vue, or Svelte projects you can wrap EventSource in a custom hook or composable to keep the UI reactive.

    Scaling SSE in Production

    Use a Dedicated ASGI Server

    Flask’s built‑in server is fine for development, but a production environment should run behind an ASGI server like uvicorn or hypercorn. These servers handle async I/O efficiently, allowing thousands of concurrent connections.

    Reverse Proxy Configuration

    When deploying behind Nginx or Apache, ensure the proxy forwards the text/event-stream MIME type unchanged and disables buffering:

    # Example Nginx snippet
    location /stream {
        proxy_pass http://localhost:8000/stream;
        proxy_set_header Host $host;
        proxy_http_version 1.1;
        proxy_set_header Connection '';
        proxy_buffering off;          # Important for SSE
        proxy_cache off;
    }
    

    Load Balancing & Sticky Sessions

    Because SSE connections are long‑lived, load balancers should use sticky sessions (or IP hash) to keep a client attached to the same backend instance. Otherwise, a client might lose its stream during a balancer‑initiated re‑routing.

    Performance Tips

    • Batch events: If you have high‑frequency data, bundle multiple updates into a single SSE payload to reduce overhead.
    • Compress wisely: GZIP can be beneficial for large JSON payloads, but be aware that some browsers already compress the stream.
    • Limit payload size: Keep each event under a few kilobytes to avoid latency spikes.

    Security Considerations

    • CORS: Set Access-Control-Allow-Origin only for trusted domains.
    • Authentication: Use session cookies or JWTs before establishing the SSE connection; the server should validate the token on each request.
    • Rate limiting: Prevent a single client from opening excessive connections that could exhaust server resources.
    • Content sanitization: Since SSE sends plain text, ensure any user‑generated data is escaped to avoid injection attacks in the client’s JavaScript.

    SEO Benefits of an SSE Web App

    Search engine crawlers typically do not execute JavaScript, which can hide dynamic content from indexing. With SSE, the initial HTML page is fully rendered on the server, giving crawlers immediate access to static SEO metadata (title, meta description, structured data). Real‑time updates appear only after the page loads, which does not affect the core SEO signals.

    To maximize SEO:

    • Include relevant keywords—Python Server‑Sent Events, SSE web app, real‑time Python dashboard—in the <title> and <meta name="description"> tags.
    • Use semantic HTML (e.g., <section>, <article>
  • Python Https Local Development Setup

    Setting up a secure HTTPS environment for local Python development can feel like a daunting task, especially when you’re juggling frameworks, certificates, and browser warnings. Yet, mastering this workflow is essential for building modern web applications that handle sensitive data, comply with security best practices, and behave exactly as they will in production. In this guide, we’ll walk through a step‑by‑step, SEO‑friendly tutorial on how to configure a fully functional HTTPS server on your local machine using Python. Whether you’re working with Flask, Django, or a plain http.server module, you’ll learn how to generate self‑signed certificates, trust them locally, and automate the whole process for a smooth development experience.

    Why Use HTTPS in Local Development?

    Before diving into the technical steps, it’s worth understanding the real benefits of running your Python app over HTTPS during development:

    • Accurate testing: Browser APIs like Service Workers, WebSockets, and Geolocation enforce HTTPS, so a local HTTPS setup mirrors production constraints.
    • Security awareness: Working with TLS early helps developers spot insecure patterns (e.g., mixed content) before they reach staging.
    • OAuth & third‑party integrations: Many authentication providers (Google, GitHub, etc.) require a secure redirect URI, even for localhost.
    • Compliance readiness: GDPR, PCI‑DSS, and other regulations expect encryption; testing locally reduces surprises later.

    Generating a Self‑Signed Certificate

    The cornerstone of any HTTPS setup is a TLS certificate. For local development, a self‑signed certificate is sufficient and can be created in seconds using openssl:

    # Create a private key
    openssl genrsa -out localdev.key 2048
    
    # Generate a self‑signed certificate (valid for 365 days)
    openssl req -new -x509 -key localdev.key -out localdev.crt -days 365 \
      -subj "/C=US/ST=California/L=San Francisco/O=MyCompany/OU=Dev/CN=localhost"
    

    Store localdev.key and localdev.crt in a safe, version‑controlled directory such as certs/. Remember that browsers will flag self‑signed certificates as untrusted, so we’ll add them to the system’s trust store later.

    Trusting the Certificate on Your Machine

    To eliminate security warnings, you need to tell your OS and browser to trust the newly created certificate:

    macOS

    • Open Keychain Access.
    • Drag localdev.crt into the “System” keychain.
    • Double‑click the certificate, expand “Trust”, and set “When using this certificate” to Always Trust.

    Windows

    • Run mmc.exe and add the “Certificates” snap‑in for the “Computer account”.
    • Import localdev.crt into Trusted Root Certification Authorities.

    Linux (Ubuntu/Debian)

    sudo cp localdev.crt /usr/local/share/ca-certificates/
    sudo update-ca-certificates
    

    After adding the certificate to the trust store, restart your browser to apply the changes.

    Running a Simple HTTPS Server with Python

    If you just need a quick test server, Python’s built‑in http.server module can be wrapped with SSL in a single line:

    python -m http.server 8443 \
      --bind 127.0.0.1 \
      --directory /path/to/your/project \
      --certfile certs/localdev.crt \
      --keyfile certs/localdev.key
    

    Visit https://localhost:8443 in your browser. You should see your static files served over HTTPS without any mixed‑content warnings.

    Integrating HTTPS with Flask

    Flask developers often rely on the development server’s app.run() method. To enable HTTPS, pass the certificate paths directly:

    from flask import Flask
    
    app = Flask(__name__)
    
    @app.route('/')
    def hello():
        return "Hello, secure Flask!"
    
    if __name__ == '__main__':
        app.run(
            host='127.0.0.1',
            port=5000,
            ssl_context=('certs/localdev.crt', 'certs/localdev.key')
        )
    

    Running python app.py now serves the application at https://localhost:5000. For a more production‑like environment, consider using Gunicorn with gevent or uvicorn for ASGI frameworks.

    Setting Up HTTPS in Django

    Django’s development server also supports SSL with a simple command‑line flag. First, ensure your settings.py reflects a secure environment:

    # settings.py
    SECURE_SSL_REDIRECT = True          # Redirect HTTP → HTTPS
    SESSION_COOKIE_SECURE = True
    CSRF_COOKIE_SECURE = True
    

    Then launch the server with the certificate options:

    python manage.py runserver_plus \
      --cert-file certs/localdev.crt \
      --key-file certs/localdev.key \
      127.0.0.1:8000
    

    The runserver_plus command comes from the django-extensions package, which you can install via pip install django-extensions. After starting, open https://localhost:8000 to see your Django site running securely.

    Automating Certificate Renewal with a Script

    Self‑signed certificates expire after a set period (commonly 365 days). To avoid manual regeneration, add a small Bash script to your project’s scripts/ folder and schedule it with cron or Task Scheduler:

    #!/usr/bin/env bash
    # renew_cert.sh – Regenerate self‑signed certs if they are older than 350 days
    
    CERT_PATH="certs/localdev.crt"
    KEY_PATH="certs/localdev.key"
    MAX_AGE=$((350*24*60*60))   # 350 days in seconds
    
    if [ ! -f "$CERT_PATH" ] || [ $(( $(date +%s) - $(stat -c %Y "$CERT_PATH") )) -gt $MAX_AGE ]; then
        echo "Generating new self‑signed certificate..."
        openssl req -new -x509 -nodes -days 365 \
            -keyout "$KEY_PATH" -out "$CERT_PATH" \
            -subj "/C=US/ST=California/L=San Francisco/O=MyCompany/OU=Dev/CN=localhost"
        echo "Certificate renewed. Remember to re‑trust it if your OS requires it."
    else
        echo "Certificate is still valid."
    fi
    

    Make the script executable (chmod +x scripts/renew_cert.sh) and add a cron entry:

    0 0 * * 0 /path/to/project/scripts/renew_cert.sh >> /var/log/renew_cert.log 2>&1
    

    This runs the renewal check every Sunday at midnight, keeping your local HTTPS environment up‑to‑date automatically.

    Testing HTTPS with Automated Tools

    Once your local server is running over TLS, you can verify its security posture with tools you’d normally use on production sites:

    • curl: curl -k https://localhost:5000 (the -k flag ignores trust warnings for quick checks).
    • OpenSSL s_client: openssl s_client -connect localhost:5000 -servername localhost to inspect the certificate chain.
    • Browser DevTools: Open the “Security” tab to confirm “Secure Connection” and view TLS version details.
    • Automated test suites: Use requests with verify=False for unit tests, then switch to a trusted CA for integration tests.

    Common Pitfalls and How to Fix Them

    Even with a clear roadmap, developers often encounter a few recurring issues:

    1. Browser Still Shows “Not Secure”

    Make sure the certificate’s CN or Subject Alternative Name (SAN) exactly matches localhost. Modern browsers ignore the CN alone, so add a SAN using the -addext flag (available in OpenSSL 1.1.1+):

    openssl req -new -x509 -nodes -days 365 \
      -keyout localdev.key -out localdev.crt \
      -subj "/C=US/ST=CA/L=SF/O=MyCompany/OU=Dev/CN=localhost" \
      -addext "subjectAltName = DNS:localhost"
    

    2. “Permission denied” When Binding to Port 443

    Ports below 1024 require elevated privileges. Instead of running the entire Python process as root, use a reverse proxy (like nginx or Caddy) that terminates TLS on port 443

  • Python Web Security Best Practices Guide

    When you build a web application with Python, you’re not just writing code—you’re creating a gateway that millions of users could trust with their data. Yet, even the most elegant Python code can crumble under the weight of common security pitfalls. This guide walks you through the essential Python web security best practices, from secure configuration to defensive coding, so you can protect your users and keep your app resilient against the ever‑evolving threat landscape.

    Why Python Web Security Matters

    Python powers some of the world’s most popular web frameworks, including Django, Flask, and FastAPI. While these frameworks provide many built‑in safeguards, developers often overlook critical steps that can expose vulnerabilities such as injection attacks, cross‑site scripting (XSS), and data leakage. Implementing robust security measures not only safeguards user data but also improves SEO rankings, boosts user confidence, and helps you comply with regulations like GDPR and CCPA.

    Secure Development Foundations

    1. Keep Dependencies Up‑to‑Date

    • Use pip list --outdated regularly to identify vulnerable packages.
    • Leverage tools like Safety or Bandit to scan for known CVEs.
    • Pin exact versions in requirements.txt or pyproject.toml to avoid accidental upgrades.

    2. Adopt a Secure Coding Standard

    Follow the OWASP Top Ten as a baseline. Incorporate static analysis tools (e.g., flake8 with security plugins) into your CI/CD pipeline to catch insecure patterns early.

    3. Use Virtual Environments

    Isolate each project with venv or conda to prevent dependency conflicts and limit the attack surface of your global Python installation.

    Framework‑Specific Hardening

    Django Security Checklist

    1. Enable HTTPS – Set SECURE_SSL_REDIRECT = True and configure SECURE_PROXY_SSL_HEADER when behind a load balancer.
    2. Use Strong Secret Keys – Generate a 50‑character random key and keep it out of source control (e.g., via environment variables).
    3. Set Content Security Policy (CSP) – Add django-csp middleware to restrict inline scripts and untrusted sources.
    4. Limit Clickjacking – Include X-Frame-Options: SAMEORIGIN via SECURE_BROWSER_XSS_FILTER and X_CONTENT_OPTIONS settings.
    5. Database Security – Use parameterized queries with Django ORM; never concatenate raw SQL strings.
    6. Session Management – Set SESSION_COOKIE_SECURE = True and SESSION_COOKIE_HTTPONLY = True to protect session cookies.

    Flask Security Checklist

    1. Enable HTTPS – Use Flask-Talisman to enforce HTTPS and set security headers automatically.
    2. Secure Secret Key – Load app.secret_key from a vault or environment variable; never hard‑code it.
    3. Validate Input – Employ WTForms or marshmallow for robust request validation.
    4. Protect Against CSRF – Activate Flask-WTF CSRF protection for all state‑changing routes.
    5. Limit File Uploads – Verify MIME types, enforce size limits, and store uploads outside the web root.

    Common Attack Vectors & How to Defend Them

    SQL Injection

    Never interpolate user input directly into SQL strings. Use parameterized queries or the ORM’s built‑in query methods.

    # Bad practice – vulnerable to injection
    cursor.execute(f"SELECT * FROM users WHERE email = '{email}'")
    
    # Safe practice – parameterized query
    cursor.execute("SELECT * FROM users WHERE email = %s", (email,))
    

    Cross‑Site Scripting (XSS)

    Always escape output in templates. Django’s template engine auto‑escapes by default; in Flask, use {{ variable|e }} or the MarkupSafe library.

    Cross‑Site Request Forgery (CSRF)

    CSRF tokens tie a user’s session to a unique secret that must be submitted with every POST request. Both Django ({% csrf_token %}) and Flask‑WTF provide built‑in token generation.

    Insecure Deserialization

    Never deserialize untrusted data with pickle. Prefer safe formats like JSON or msgpack and validate schema before processing.

    Directory Traversal

    When handling file paths supplied by users, use os.path.abspath and compare against a whitelist directory.

    import os
    
    def safe_join(base, *paths):
        final_path = os.path.abspath(os.path.join(base, *paths))
        if not final_path.startswith(os.path.abspath(base)):
            raise ValueError("Attempted directory traversal")
        return final_path
    

    Authentication & Authorization Best Practices

    • Prefer Password Hashing Libraries – Use argon2-cffi or bcrypt instead of legacy MD5/SHA1.
    • Enforce Multi‑Factor Authentication (MFA) – Integrate with TOTP apps (e.g., Google Authenticator) via django-otp or pyotp.
    • Implement Least Privilege – Assign minimal permissions to each role; avoid “admin” for regular users.
    • Use Secure Token Formats – Adopt JWT with short expiration and sign with RS256 (asymmetric keys) for better key rotation.
    • Rate‑Limit Login Attempts – Apply django-axes or Flask‑Limiter to mitigate brute‑force attacks.

    Secure Configuration Management

    Environment Variables Over Hard‑Coding

    Store secrets (API keys, DB passwords) in environment variables or secret managers (AWS Secrets Manager, HashiCorp Vault). Access them in Python with os.getenv() and never commit them to version control.

    Security‑Focused Settings

    Separate development, testing, and production settings. In production, disable debug mode (DEBUG=False) and set ALLOWED_HOSTS explicitly.

    Logging & Monitoring

    • Use structlog or logging with JSON output for easy ingestion into SIEM tools.
    • Mask sensitive data in logs (e.g., replace passwords with ***).
    • Implement alerting for suspicious activities such as repeated failed logins or unusual API calls.

    Testing & Continuous Integration

    Automated Security Scans

    Integrate tools like Bandit, Snyk, and OWASP Dependency‑Check into your CI pipeline (GitHub Actions, GitLab CI, Jenkins). Fail the build on high‑severity findings.

    Penetration Testing

    Periodically run manual or automated penetration tests using tools like OWASP ZAP or Burp Suite. Focus on authentication flows, file upload endpoints, and API surfaces.

    Coverage of Security Tests

    Write unit tests for validation logic, authentication, and permission checks. Use pytest fixtures to simulate attack scenarios.

    Secure Deployment Practices

    • Run Behind a Reverse Proxy – Use Nginx or Traefik to terminate TLS, hide server details, and enforce rate limits.
    • Container Hardening – If using Docker, run containers as non‑root users, set read‑only filesystem where possible, and scan images with Clair or Trivy.
    • Use WSGI Servers Securely – Deploy with gunicorn or uvicorn behind a proxy; configure worker timeouts and limit request sizes.
    • Enable HTTP Security Headers – Add Strict-Transport-Security, X-Content-Type-Options, Referrer-Policy, and Feature-Policy headers.

    Performance‑Friendly Security

    Security should never cripple user experience. Employ caching (Redis, Memcached) for session data, and use asynchronous request handling (e.g., FastAPI with asyncio) to keep latency low while still validating every request.

  • Python Multi-Factor Authentication Mfa Setup

    In today’s security‑first landscape, relying on a single password is no longer enough to protect your Python applications. Multi‑factor authentication (MFA) adds a critical second layer, dramatically reducing the risk of unauthorized access. Whether you’re building a web service with Flask, a command‑line tool, or an internal API, implementing MFA in Python can be straightforward and highly effective. This guide walks you through the concepts, popular libraries, and step‑by‑step code you need to set up robust MFA, ensuring your users and data stay safe.

    Why Multi‑Factor Authentication Matters for Python Projects

    Search engines and security auditors alike reward applications that adopt strong authentication practices. MFA not only:

    • Blocks credential stuffing by requiring a time‑based one‑time password (TOTP) or push notification.
    • Meets compliance standards such as GDPR, HIPAA, and PCI DSS.
    • Boosts user trust by demonstrating a commitment to data protection.

    For Python developers, integrating MFA means adding a few well‑maintained packages and a little extra logic—without sacrificing performance or developer productivity.

    Choosing the Right MFA Method

    Python supports several MFA strategies. The most common are:

    1. TOTP (Time‑Based One‑Time Password) – generated by apps like Google Authenticator or Authy.
    2. SMS or Email OTP – a code sent to the user’s phone or inbox.
    3. Push‑Based Authentication – a notification sent to a mobile app for approval.
    4. Hardware Tokens – YubiKey or similar devices using U2F/WebAuthn.

    For most web and API projects, TOTP offers the best balance of security, user experience, and ease of implementation. The following sections focus on a TOTP‑based MFA setup using the popular pyotp and Flask libraries.

    Setting Up the Development Environment

    Install Required Packages

    pip install Flask pyotp qrcode[pil] pillow

    These packages provide:

    • Flask – the web framework for handling routes and sessions.
    • pyotp – generation and verification of TOTP codes.
    • qrcode – creation of QR codes that users scan with their authenticator app.
    • Pillow – image handling required by qrcode.

    Project Structure

    .
    ├── app.py
    ├── templates/
    │   ├── login.html
    │   ├── mfa_setup.html
    │   └── mfa_verify.html
    └── static/
        └── style.css
    

    Implementing MFA in a Flask Application

    1. Create the Flask App and User Model

    from flask import Flask, render_template, request, redirect, session, url_for, flash
    import pyotp, qrcode, io, base64
    
    app = Flask(__name__)
    app.secret_key = 'replace‑with‑a‑strong‑secret'
    
    # Simple in‑memory user store for demo purposes
    USERS = {
        "alice": {"password": "s3cr3t", "mfa_secret": None},
        "bob":   {"password": "p@ssw0rd", "mfa_secret": None}
    }
    

    2. Login Route (Password Only)

    @app.route('/login', methods=['GET', 'POST'])
    def login():
        if request.method == 'POST':
            username = request.form['username']
            password = request.form['password']
            user = USERS.get(username)
    
            if user and user['password'] == password:
                session['username'] = username
                # If MFA not set up, redirect to setup; otherwise go to verify
                if not user['mfa_secret']:
                    return redirect(url_for('mfa_setup'))
                return redirect(url_for('mfa_verify'))
            flash('Invalid credentials')
        return render_template('login.html')
    

    3. MFA Setup – Generate Secret and QR Code

    @app.route('/mfa/setup')
    def mfa_setup():
        if 'username' not in session:
            return redirect(url_for('login'))
    
        username = session['username']
        user = USERS[username]
    
        # Generate a new base32 secret if one does not exist
        if not user['mfa_secret']:
            user['mfa_secret'] = pyotp.random_base32()
    
        totp = pyotp.TOTP(user['mfa_secret'])
        provisioning_uri = totp.provisioning_uri(name=username, issuer_name="MyPythonApp")
    
        # Create QR code image in memory
        img = qrcode.make(provisioning_uri)
        buf = io.BytesIO()
        img.save(buf, format='PNG')
        qr_b64 = base64.b64encode(buf.getvalue()).decode('utf-8')
    
        return render_template('mfa_setup.html', qr_code=qr_b64, secret=user['mfa_secret'])
    

    4. Verify the TOTP Code

    @app.route('/mfa/verify', methods=['GET', 'POST'])
    def mfa_verify():
        if 'username' not in session:
            return redirect(url_for('login'))
    
        username = session['username']
        user = USERS[username]
    
        if request.method == 'POST':
            token = request.form['token']
            totp = pyotp.TOTP(user['mfa_secret'])
            if totp.verify(token):
                session['mfa_authenticated'] = True
                return redirect(url_for('protected'))
            flash('Invalid MFA code')
        return render_template('mfa_verify.html')
    

    5. Protected Route Example

    @app.route('/protected')
    def protected():
        if not session.get('mfa_authenticated'):
            return redirect(url_for('login'))
        return f"Welcome, {session['username']}! You have passed MFA."
    

    6. Logout Route

    @app.route('/logout')
    def logout():
        session.clear()
        return redirect(url_for('login'))
    

    Best Practices for a Production‑Ready MFA Implementation

    • Store secrets securely – move the in‑memory USERS dict to a database and encrypt the mfa_secret column with a key management service.
    • Rate‑limit verification attempts – protect against brute‑force attacks by limiting the number of TOTP submissions per minute.
    • Backup codes – generate one‑time use backup codes for users who lose their authenticator device.
    • HTTPS everywhere – ensure all MFA traffic is encrypted to prevent man‑in‑the‑middle interception.
    • Grace period for new devices – consider a short, logged “trusted device” window after successful MFA.

    Testing Your MFA Flow

    Before deploying, run the application locally and follow these steps:

    1. Visit /login and sign in with a test user.
    2. You’ll be redirected to /mfa/setup. Scan the displayed QR code with Google Authenticator, Authy, or any TOTP app.
    3. Enter the 6‑digit code generated by the app on the /mfa/verify page.
    4. Upon successful verification, you should reach the protected route.

    Automated tests can use pyotp.TOTP(secret).now() to simulate valid tokens and assert correct redirects.

    Extending MFA Beyond TOTP

    If your project requires a richer authentication experience, Python’s ecosystem offers additional options:

    • Twilio Verify API – send SMS or voice OTPs with a single HTTP call.
    • Auth0 or Okta SDKs – outsource the entire authentication flow, including MFA, to a managed identity provider.
    • WebAuthn (FIDO2) – use the webauthn Python package to integrate hardware keys and biometric factors.

    Each option has trade‑offs in cost, complexity, and user experience, so choose the one that aligns with your security policy and user base.

    SEO Tips Embedded in This Article

    To help this guide rank for “Python multi‑factor authentication MFA setup”, we’ve naturally incorporated high‑value keywords such as Python MFA, multi‑factor authentication, TOTP, Flask MFA example, and pyotp tutorial. Using clear headings, bullet points, and code blocks improves readability for both users and search engines. Remember to add meta descriptions, alt text for QR code images, and internal links to related Python security articles for maximum SEO impact.

    Conclusion

    Implementing multi‑factor authentication in Python doesn’t have to be a daunting task. By leveraging lightweight libraries like pyotp and following the structured steps outlined above, you can protect your applications against credential‑theft

  • Python Password Reset Via Email Tutorial

    Imagine a user forgetting their password and being stuck at the login screen—frustrating, right? A well‑implemented password reset via email not only saves users from that hassle but also builds trust in your application’s security. In this tutorial you’ll learn how to create a robust, SEO‑friendly password reset flow using Python, covering everything from token generation to sending secure emails, with clear code examples and best‑practice tips.

    Why a Password Reset via Email Is Essential

    Offering a reliable password reset mechanism is a cornerstone of modern web applications. It improves user experience, reduces support tickets, and demonstrates that you care about security. Search engines also favor sites that provide clear, helpful documentation, so a well‑structured tutorial can boost your visibility.

    Core Components of a Password Reset System

    1. User Model

    Your user database must store at least an email address and a hashed password. Optionally, you can keep a reset_token and its expiry timestamp.

    2. Secure Token Generation

    The token is a one‑time, cryptographically‑secure string that proves the user’s identity. It should be:

    • Randomly generated (e.g., using secrets.token_urlsafe())
    • Long enough (minimum 32 characters) to resist brute‑force attacks
    • Time‑limited (commonly 1 hour)

    3. Email Service

    Sending the reset link reliably is critical. You can use:

    • SMTP servers (Gmail, SendGrid, Mailgun)
    • Third‑party APIs for higher deliverability

    4. Reset Form

    The final step lets the user enter a new password. It must:

    • Validate password strength
    • Confirm the password matches the confirmation field
    • Invalidate the token after successful reset

    Step‑by‑Step Tutorial Using Flask

    Flask is lightweight, making it perfect for demonstrating the full flow. Below is a complete walkthrough, from project setup to a working password‑reset endpoint.

    1. Set up the environment
      python -m venv venv
      source venv/bin/activate  # Windows: venv\Scripts\activate
      pip install Flask Flask-Mail itsdangerous Werkzeug
    2. Create the Flask app and configuration
      from flask import Flask
      from flask_mail import Mail, Message
      
      app = Flask(__name__)
      app.config.update(
          SECRET_KEY='your‑super‑secret‑key',
          MAIL_SERVER='smtp.gmail.com',
          MAIL_PORT=587,
          MAIL_USE_TLS=True,
          MAIL_USERNAME='your.email@example.com',
          MAIL_PASSWORD='your‑email‑password',
      )
      
      mail = Mail(app)
    3. Define a simple user model (in‑memory for demo)
      users = {
          'alice@example.com': {
              'password_hash': 'hashed‑password',
              'reset_token': None,
              'reset_expiry': None,
          }
      }
    4. Generate a secure token
      from itsdangerous import URLSafeTimedSerializer
      
      def generate_reset_token(email):
          serializer = URLSafeTimedSerializer(app.config['SECRET_KEY'])
          return serializer.dumps(email, salt='password-reset-salt')
    5. Verify the token
      def verify_reset_token(token, max_age=3600):
          serializer = URLSafeTimedSerializer(app.config['SECRET_KEY'])
          try:
              email = serializer.loads(token, salt='password‑reset‑salt', max_age=max_age)
          except Exception:
              return None
          return email
    6. Send the reset email
      def send_reset_email(to_email, token):
          reset_url = f'http://localhost:5000/reset_password/{token}'
          msg = Message('Password Reset Request',
                        sender='no-reply@example.com',
                        recipients=[to_email])
          msg.body = f'''Hi,
      
      We received a request to reset your password. Click the link below
      to choose a new password (valid for 1 hour):
      
      {reset_url}
      
      If you didn’t ask for this, simply ignore this email.
      
      Thanks,
      Your App Team'''
          mail.send(msg)
    7. Create the request route
      from flask import request, redirect, url_for, flash, render_template_string
      
      @app.route('/forgot_password', methods=['GET', 'POST'])
      def forgot_password():
          if request.method == 'POST':
              email = request.form['email']
              if email in users:
                  token = generate_reset_token(email)
                  send_reset_email(email, token)
                  flash('A reset link has been sent to your email.', 'info')
              else:
                  flash('Email not found.', 'danger')
              return redirect(url_for('forgot_password'))
          return render_template_string('''
              <h3>Forgot Password</h3>
              <form method="post">
                  <input name="email" type="email" placeholder="Enter your email" required>
                  <button type="submit">Send Reset Link</button>
              </form>
          ''')
    8. Build the reset form
      @app.route('/reset_password/<token>', methods=['GET', 'POST'])
      def reset_password(token):
          email = verify_reset_token(token)
          if not email:
              flash('The reset link is invalid or has expired.', 'danger')
              return redirect(url_for('forgot_password'))
      
          if request.method == 'POST':
              pwd = request.form['password']
              confirm = request.form['confirm']
              if pwd != confirm:
                  flash('Passwords do not match.', 'danger')
                  return redirect(request.url)
              # Here you would hash the password with Werkzeug or bcrypt
              users[email]['password_hash'] = pwd  # Simplified for demo
              flash('Your password has been updated!', 'success')
              return redirect(url_for('login'))
      
          return render_template_string('''
              <h3>Reset Password for {{email}}</h3>
              <form method="post">
                  <input name="password" type="password" placeholder="New password" required>
                  <input name="confirm" type="password" placeholder="Confirm password" required>
                  <button type="submit">Update Password</button>
              </form>
          ''', email=email)
    9. Run the app
      if __name__ == '__main__':
          app.run(debug=True)

    That’s it! You now have a functional password‑reset flow that sends a secure, time‑limited link to the user’s inbox.

    Implementing the Same Flow in Django

    If you prefer Django, the framework already includes many of these pieces. Here’s a quick checklist:

    • Use django.core.signing.TimestampSigner for token creation.
    • Configure EMAIL_BACKEND and related settings in settings.py.
    • Leverage Django’s built‑in PasswordResetView and PasswordResetConfirmView classes.
    • Customize the email template with {% url 'password_reset_confirm' uidb64 token %}.

    Common Pitfalls and Security Tips

    Never Store Plain‑Text Tokens

    Even though tokens are short‑lived, storing them hashed (e.g., with SHA‑256) adds an extra layer of protection.

    Enforce Rate Limiting

    Limit the number of reset requests per email address (e.g., 5 per hour) to thwart brute‑force or enumeration attacks.

    Use HTTPS Everywhere

    Always serve the reset link over HTTPS. A man‑in‑the‑middle could otherwise intercept the token.

    Validate Password Strength

    Implement checks for minimum length, mixed case, numbers, and special characters. Libraries like zxcvbn can help assess entropy.

    Invalidate Tokens After Use

    Mark the token as used or delete it from the database immediately after a successful password change.

    Testing Your Reset Flow

    Before deploying, run through these tests:

    1. Submit a valid email and verify the reset link arrives.
    2. Attempt to use an expired token (modify the system clock or set a short max_age).
    3. Enter mismatched passwords and confirm the error message appears.
    4. Try resetting with an unregistered email
  • Python Github Oauth Authentication Flow

    When developers want to let users log in to their Python web applications with a GitHub account, the GitHub OAuth authentication flow is the most reliable and secure choice. Not only does it off‑load password management to GitHub, but it also grants your app access to the user’s public profile and repositories (with permission). In this guide you’ll learn every step of the Python GitHub OAuth authentication flow, from creating a GitHub OAuth app to handling access tokens safely in Flask or Django. By the end, you’ll be able to copy‑paste a ready‑to‑run code snippet, understand the underlying OAuth2 protocol, and avoid the most common pitfalls that trip up beginners.

    Why Use GitHub OAuth with Python?

    • Security first: Users never share passwords with your app.
    • Developer‑friendly: GitHub’s API is well‑documented and widely used in CI/CD pipelines.
    • Scalable: OAuth tokens can be refreshed or revoked without touching your database.
    • SEO boost: Articles that target “Python GitHub OAuth authentication flow” rank well because developers frequently search for this exact phrase.

    OAuth2 Basics You Should Know

    OAuth2 is an authorization framework that separates the authentication step (who you are) from the resource‑access step (what you can do). The typical flow includes four key stages:

    1. Authorization Request: Your app redirects the user to GitHub’s https://github.com/login/oauth/authorize endpoint.
    2. User Consent: The user logs in (if needed) and authorizes your requested scopes.
    3. Authorization Code: GitHub redirects back to your redirect_uri with a temporary code query parameter.
    4. Access Token Exchange: Your server exchanges the code for an access_token via a POST request to https://github.com/login/oauth/access_token.

    Once you have the access_token, you can call GitHub’s API on behalf of the user.

    Step 1 – Create a GitHub OAuth App

    Before any Python code runs, you need a registered OAuth application on GitHub:

    • Log in to GitHub and go to Settings → Developer settings → OAuth Apps.
    • Click “New OAuth App”.
    • Fill in:
      • Application name – e.g., “MyPythonApp”.
      • Homepage URL – your site’s root (e.g., https://example.com).
      • Authorization callback URL – the endpoint that will receive the code. For Flask, https://example.com/callback works well.
    • After creation, note the Client ID and Client Secret. You’ll reference these in your Python code.

    Step 2 – Choose a Python Web Framework

    Both Flask and Django have excellent support for OAuth. This tutorial focuses on Flask for brevity, but the concepts translate directly to Django or FastAPI.

    Step 3 – Install Required Packages

    pip install Flask requests python-dotenv

    python-dotenv keeps your CLIENT_ID and CLIENT_SECRET out of source control.

    Step 4 – Store Secrets Securely

    Create a .env file in your project root:

    # .env
    GITHUB_CLIENT_ID=your_client_id_here
    GITHUB_CLIENT_SECRET=your_client_secret_here
    SECRET_KEY=super_secret_flask_key
    

    Never commit this file to a public repository.

    Step 5 – Implement the OAuth Flow in Flask

    5.1 Basic Flask App Skeleton

    from flask import Flask, redirect, request, session, url_for, render_template_string
    import os, requests
    from dotenv import load_dotenv
    
    load_dotenv()
    app = Flask(__name__)
    app.secret_key = os.getenv('SECRET_KEY')
    

    5.2 Build the Authorization URL

    @app.route('/')
    def index():
        return render_template_string('''
            <h2>Login with GitHub</h2>
            <a href="{{ url_for('login') }}">Sign in</a>
        ''')
    

    5.3 Redirect to GitHub

    @app.route('/login')
    def login():
        client_id = os.getenv('GITHUB_CLIENT_ID')
        redirect_uri = url_for('callback', _external=True)
        scope = 'read:user repo'  # Adjust scopes as needed
        auth_url = (
            f"https://github.com/login/oauth/authorize?"
            f"client_id={client_id}&redirect_uri={redirect_uri}&scope={scope}"
        )
        return redirect(auth_url)
    

    5.4 Handle the Callback and Exchange Code for Token

    @app.route('/callback')
    def callback():
        code = request.args.get('code')
        if not code:
            return 'Error: No code provided', 400
    
        token_url = 'https://github.com/login/oauth/access_token'
        headers = {'Accept': 'application/json'}
        payload = {
            'client_id': os.getenv('GITHUB_CLIENT_ID'),
            'client_secret': os.getenv('GITHUB_CLIENT_SECRET'),
            'code': code,
            'redirect_uri': url_for('callback', _external=True)
        }
    
        token_response = requests.post(token_url, headers=headers, data=payload)
        token_json = token_response.json()
        access_token = token_json.get('access_token')
    
        if not access_token:
            return f"Error retrieving access token: {token_json}", 400
    
        # Store token securely in the session (or a DB for production)
        session['github_token'] = access_token
        return redirect(url_for('profile'))
    

    5.5 Fetch the User Profile Using the Token

    @app.route('/profile')
    def profile():
        token = session.get('github_token')
        if not token:
            return redirect(url_for('index'))
    
        user_api = 'https://api.github.com/user'
        headers = {'Authorization': f'token {token}'}
        resp = requests.get(user_api, headers=headers)
        if resp.status_code != 200:
            return f'Failed to fetch profile: {resp.text}', 400
    
        user_data = resp.json()
        return render_template_string('''
            <h2>GitHub Profile</h2>
            <img src="{{ avatar_url }}" width="100">
            <p><strong>Name:</strong> {{ name }}</p>
            <p><strong>Username:</strong> {{ login }}</p>
            <p><strong>Public Repos:</strong> {{ public_repos }}</p>
            <a href="{{ url_for('logout') }}">Logout</a>
        ''', **user_data)
    

    5.6 Logout Endpoint

    @app.route('/logout')
    def logout():
        session.clear()
        return redirect(url_for('index'))
    

    Step 6 – Run the Application

    if __name__ == '__main__':
        app.run(debug=True)
    

    Visit http://127.0.0.1:5000/, click “Sign in”, and you’ll be redirected through the full Python GitHub OAuth authentication flow. After granting permission, you’ll see your GitHub avatar and basic profile data.

    Best Practices for Token Management

    • Never expose the token in URLs or client‑side JavaScript. Keep it on the server or in an HttpOnly cookie.
    • Use short‑lived tokens when possible. GitHub’s tokens don’t expire by default, but you can revoke them via the user’s settings or the API.
    • Store tokens encrypted at rest. If you persist them in a database, encrypt with a key management service (KMS).
    • Validate scopes. Request only the permissions you need; excess scopes raise security concerns and reduce conversion rates.

    Common Pitfalls and How to Debug Them

    Redirect URI Mismatch

    If GitHub returns error=redirect_uri_mismatch, double‑check that the Authorization callback URL in your GitHub app exactly matches the redirect_uri you send in the /login route. Include the trailing slash if you used one.

    State Parameter Omission

    While the simple example above omits the state parameter for brevity, production apps should generate a random state token, store it in the session, and verify it on callback to prevent CSRF attacks.

  • Python Google Oauth2 Login Integration

    Looking to let users sign in to your Python web app with their Google accounts? Integrating Google OAuth2 not only streamlines the login experience but also boosts security and trust. In this guide, we’ll walk you through every step—from creating Google credentials to implementing a fully‑functional OAuth2 flow using popular Python frameworks such as Flask and Django. By the end, you’ll have a ready‑to‑deploy solution that improves user acquisition, reduces password fatigue, and aligns with modern best practices for authentication.

    Why Choose Google OAuth2 for Your Python Application?

    • Universal reach: Billions of users already have Google accounts.
    • Secure token handling: OAuth2 uses short‑lived access tokens and refresh tokens, minimizing credential exposure.
    • Reduced friction: Users can log in with a single click, increasing conversion rates.
    • Compliance: Google’s OAuth2 implementation adheres to industry standards like OpenID Connect.

    Prerequisites Before You Start

    1. Python 3.8+ installed on your development machine.
    2. A virtual environment (venv, pipenv, or poetry) to isolate dependencies.
    3. Basic familiarity with a Python web framework (Flask or Django).
    4. A Google Cloud project with the OAuth consent screen configured.

    Step 1: Create Google OAuth2 Credentials

    1.1 Set Up a Google Cloud Project

    • Visit Google Cloud Console and create a new project.
    • Navigate to APIs & Services > OAuth consent screen. Choose “External” for public apps.
    • Fill out the required fields (App name, Support email, Authorized domains) and save.

    1.2 Generate Client ID and Secret

    • Go to APIs & Services > Credentials and click “Create Credentials > OAuth client ID”.
    • Select “Web application” as the application type.
    • Enter a name (e.g., “MyPythonApp”).
    • Under Authorized redirect URIs, add the callback URL your app will use, e.g. http://localhost:5000/auth/callback for Flask or http://localhost:8000/accounts/google/login/callback/ for Django.
    • Click “Create” and note the generated Client ID and Client Secret. Store them securely (e.g., in environment variables).

    Step 2: Install Required Python Packages

    Both Flask and Django have dedicated extensions that simplify OAuth2 integration. Choose the one that matches your stack:

    # For Flask
    pip install Flask-OAuthlib==0.9.6
    # For Django
    pip install social-auth-app-django==5.4.0
    # Common dependency
    pip install python-dotenv
    

    We’ll demonstrate implementations for both frameworks, so you can pick the one that fits your project.

    Step 3: Implementing Google OAuth2 in Flask

    3.1 Project Structure

    my_flask_app/
    │
    ├─ app.py
    ├─ .env
    └─ templates/
       └─ index.html
    

    3.2 Configure Environment Variables

    # .env
    GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
    GOOGLE_CLIENT_SECRET=your-client-secret
    SECRET_KEY=your-flask-secret-key
    

    3.3 Flask Application Code

    import os
    from flask import Flask, redirect, url_for, session, request, render_template
    from flask_oauthlib.client import OAuth
    from dotenv import load_dotenv
    
    load_dotenv()  # Load .env variables
    
    app = Flask(__name__)
    app.secret_key = os.getenv('SECRET_KEY')
    
    # OAuth configuration
    oauth = OAuth(app)
    google = oauth.remote_app(
        'google',
        consumer_key=os.getenv('GOOGLE_CLIENT_ID'),
        consumer_secret=os.getenv('GOOGLE_CLIENT_SECRET'),
        request_token_params={
            'scope': 'openid email profile'
        },
        base_url='https://www.googleapis.com/oauth2/v1/',
        request_token_url=None,
        access_token_method='POST',
        access_token_url='https://oauth2.googleapis.com/token',
        authorize_url='https://accounts.google.com/o/oauth2/auth',
    )
    
    @app.route('/')
    def index():
        user = session.get('google_user')
        return render_template('index.html', user=user)
    
    @app.route('/login')
    def login():
        return google.authorize(
            callback=url_for('authorized', _external=True)
        )
    
    @app.route('/auth/callback')
    def authorized():
        resp = google.authorized_response()
        if resp is None or resp.get('access_token') is None:
            return 'Access denied: reason={} error={}'.format(
                request.args['error'],
                request.args.get('error_description')
            )
        session['google_token'] = (resp['access_token'], '')
        user_info = google.get('userinfo')
        session['google_user'] = user_info.data
        return redirect(url_for('index'))
    
    @google.tokengetter
    def get_google_oauth_token():
        return session.get('google_token')
    
    if __name__ == '__main__':
        app.run(debug=True)
    

    3.4 Simple HTML Template

    <!-- templates/index.html -->
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Google OAuth2 with Flask</title>
    </head>
    <body>
        {% if user %}
            <h2>Welcome, {{ user['name'] }}!</h2>
            <p>Email: {{ user['email'] }}</p>
            <img src="{{ user['picture'] }}" alt="Profile picture">
        {% else %}
            <a href="{{ url_for('login') }}">Sign in with Google</a>
        {% endif %}
    </body>
    </html>
    

    Step 4: Implementing Google OAuth2 in Django

    4.1 Install and Configure Social‑Auth

    First, add the required apps to INSTALLED_APPS and configure authentication backends.

    # settings.py
    INSTALLED_APPS = [
        # … other apps …
        'social_django',
    ]
    
    AUTHENTICATION_BACKENDS = (
        'social_core.backends.google.GoogleOAuth2',
        'django.contrib.auth.backends.ModelBackend',
    )
    
    # Google OAuth2 keys (use environment variables for production)
    SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = os.getenv('GOOGLE_CLIENT_ID')
    SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = os.getenv('GOOGLE_CLIENT_SECRET')
    
    # Redirect URL after login
    LOGIN_REDIRECT_URL = '/'
    LOGOUT_REDIRECT_URL = '/'
    
    # Optional: request additional profile fields
    SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE = [
        'email',
        'profile',
    ]
    

    4.2 URL Configuration

    # urls.py
    from django.urls import path, include
    from django.contrib import admin
    from django.views.generic import TemplateView
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('auth/', include('social_django.urls', namespace='social')),
        path('', TemplateView.as_view(template_name='home.html'), name='home'),
    ]
    

    4.3 Template for Login/Logout

    <!-- templates/home.html -->
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Django Google OAuth2</title>
    </head>
    <body>
        {% if user.is_authenticated %}
            <h2>Hello, {{ user.get_full_name|default:user.username }}!</h2>
            <p>Email: {{ user.email }}</p>
            <a href="{% url 'logout' %}">Logout</a>
        {% else %}
            <a href="{% url 'social:begin' 'google-oauth2' %}">Sign in with Google</a>
        {% endif %}
    </body>
    </html>
    

    Step 5: Handling Tokens and User Data Securely

    Regardless of framework, follow these best practices to keep authentication data safe:

    • Never hard‑code credentials. Use environment variables or secret managers.
    • Validate ID tokens. Use Google’s public keys to verify the token signature and claims.
    • Store minimal user data. Typically, you only need the Google sub (unique user ID), email, and name.
    • Implement token refresh. Access tokens expire after an hour; use the refresh token to obtain a new one without re‑prompting the user.

    Step 6: Common Pitfalls and How to Debug Them

    6.1 Mismatched Redirect URI

    If you receive redirect_uri_mismatch, double‑check that the URI registered in Google Cloud exactly matches the one your app sends (including http:// vs https:// and trailing slashes).

    6.2 Invalid Client Secret

    Make sure the client secret is copied correctly and that you’re not using the “Web client” secret for a mobile

  • Python Web App Rate Limiting With Redis

    When you launch a Python‑powered web application, traffic can surge faster than you expect. Without proper controls, a sudden spike—whether from a legitimate marketing campaign or a malicious bot—can overwhelm your servers, degrade user experience, and even bring your service down. Rate limiting is the defensive technique that throttles requests to a safe level, protecting resources while keeping legitimate users happy. In this guide we’ll explore how to implement robust, distributed rate limiting using Redis as the backing store, with practical examples for both Flask and Django applications.

    Why Rate Limiting Is Essential for Modern Python Web Apps

    Rate limiting isn’t just a nice‑to‑have feature; it’s a cornerstone of API reliability and security. Here are the top reasons every Python developer should enforce request limits:

    • Prevent abuse: Stop credential stuffing, brute‑force login attempts, and scraping bots.
    • Maintain performance: Guard your database and third‑party services from overload.
    • Ensure fairness: Give all users an equal share of bandwidth, especially in public APIs.
    • Comply with SLAs: Meet contractual request‑per‑second (RPS) guarantees.
    • Enable graceful degradation: When traffic spikes, throttling keeps core functionality alive.

    Choosing Redis for Rate Limiting

    Redis shines as a rate‑limiting datastore for several reasons:

    • In‑memory speed: Sub‑millisecond latency ensures that the limiter itself never becomes a bottleneck.
    • Atomic operations: Commands like INCR, EXPIRE, and Lua scripts run atomically, guaranteeing accurate counters even under heavy concurrency.
    • Distributed consistency: Multiple web workers can share the same Redis instance, making the limit global across all instances.
    • Rich data structures: Sorted sets, hashes, and strings give you flexibility to implement different algorithms (fixed‑window, sliding‑window, token‑bucket, etc.).
    • Built‑in TTL: Expiration times let you automatically reset counters without extra cleanup code.

    Core Rate‑Limiting Algorithms

    Fixed‑Window Counter

    The simplest approach: count requests in a fixed time bucket (e.g., 1 minute). If the count exceeds the threshold, reject the request.

    key = f"rate:{client_id}:{current_minute}"
    count = redis.incr(key)
    if count == 1:
        redis.expire(key, 60)  # set TTL to 60 seconds
    if count > limit:
        reject()
    

    Sliding‑Window Log

    Stores timestamps of each request in a sorted set. The algorithm removes entries older than the window, then checks the remaining count.

    key = f"sw:{client_id}"
    now = time.time()
    redis.zadd(key, {now: now})
    redis.zremrangebyscore(key, 0, now - window_seconds)
    if redis.zcard(key) > limit:
        reject()
    

    Token Bucket

    Provides a smooth request flow by refilling tokens at a steady rate. When a request arrives, a token is consumed; if none are left, the request is throttled.

    key = f"tb:{client_id}"
    tokens = redis.get(key) or max_tokens
    tokens = min(max_tokens, tokens + refill_rate * elapsed)
    if tokens < 1:
        reject()
    else:
        redis.set(key, tokens - 1, ex=window_seconds)
    

    Implementing Rate Limiting in Flask

    Flask’s lightweight nature makes it perfect for adding a custom decorator. Below is a concise, production‑ready example that uses the fixed‑window algorithm.

    from functools import wraps
    from flask import Flask, request, jsonify
    import redis, time
    
    app = Flask(__name__)
    r = redis.Redis(host='localhost', port=6379, db=0)
    
    def rate_limit(limit: int, period: int):
        def decorator(f):
            @wraps(f)
            def wrapped(*args, **kwargs):
                client_ip = request.remote_addr
                key = f"rl:{client_ip}:{int(time.time()) // period}"
                current = r.incr(key)
                if current == 1:
                    r.expire(key, period)
                if current > limit:
                    return jsonify({
                        'error': 'Too Many Requests',
                        'retry_after': period
                    }), 429
                return f(*args, **kwargs)
            return wrapped
        return decorator
    
    @app.route('/api/data')
    @rate_limit(limit=100, period=60)  # 100 requests per minute per IP
    def get_data():
        return jsonify({'message': 'Success'})
    
    if __name__ == '__main__':
        app.run()
    

    Key points to note:

    • The decorator extracts the client IP and builds a Redis key that rolls over every period seconds.
    • Using INCR and EXPIRE together guarantees atomicity.
    • Returning HTTP 429 aligns with the standard “Too Many Requests” response.

    Implementing Rate Limiting in Django

    Django developers often prefer middleware because it automatically wraps every view. The following middleware implements a sliding‑window log using Redis sorted sets.

    # myproject/middleware.py
    import time
    from django.http import JsonResponse
    import redis
    
    r = redis.Redis(host='localhost', port=6379, db=0)
    
    class RedisRateLimitMiddleware:
        def __init__(self, get_response):
            self.get_response = get_response
            self.limit = 200          # requests
            self.window = 60          # seconds
    
        def __call__(self, request):
            client_id = request.META.get('REMOTE_ADDR')
            key = f"sw:{client_id}"
            now = time.time()
    
            # Add current request timestamp
            r.zadd(key, {now: now})
            # Remove timestamps older than the window
            r.zremrangebyscore(key, 0, now - self.window)
    
            # Count remaining timestamps
            if r.zcard(key) > self.limit:
                return JsonResponse(
                    {'error': 'Rate limit exceeded', 'retry_after': self.window},
                    status=429
                )
    
            # Optional: set TTL to auto‑expire the sorted set
            r.expire(key, self.window * 2)
    
            response = self.get_response(request)
            return response
    

    To activate the middleware, add it to settings.py:

    MIDDLEWARE = [
        # …
        'myproject.middleware.RedisRateLimitMiddleware',
        # …
    ]
    

    Advanced Tips for Production‑Ready Rate Limiting

    Use Lua Scripts for True Atomicity

    While INCR + EXPIRE is safe for most cases, a single Lua script can guarantee that the increment and TTL update happen in one atomic step, eliminating race conditions under extreme concurrency.

    RATE_LIMIT_SCRIPT = """
    local key = KEYS[1]
    local limit = tonumber(ARGV[1])
    local period = tonumber(ARGV[2])
    
    local current = redis.call('INCR', key)
    if current == 1 then
        redis.call('EXPIRE', key, period)
    end
    if current > limit then
        return 0
    else
        return 1
    end
    """
    
    def allow_request(client_id, limit, period):
        key = f"rl:{client_id}"
        return r.eval(RATE_LIMIT_SCRIPT, 1, key, limit, period) == 1
    

    Distributed Rate Limiting Across Multiple Instances

    When you run several Flask or Django workers behind a load balancer, all of them must point to the same Redis cluster. Consider using Redis Sentinel or Redis Cluster for high availability, and configure your client library with automatic failover.

    Monitoring and Alerting

    • Track key metrics such as rate_limit:blocked_requests and rate_limit:allowed_requests using Redis INCRBY in your limiter logic.
    • Export these counters to Prometheus or Grafana for real‑time dashboards.
    • Set alerts when blocked request percentages exceed a predefined threshold, indicating possible abuse.

    Fine‑Grained Limits

    Combine multiple dimensions—IP address, API key, user ID, and endpoint path—to create nuanced policies. For example:

    • Public endpoints: 60 req/min per IP.
    • Authenticated endpoints: 500 req/min per API key.
    • Admin routes: 20 req/min per user ID.

    Implement this by constructing composite Redis keys, e.g., rl:{api_key}:{endpoint}.

    Graceful Degradation Strategies

    Instead of outright rejecting excess traffic, you can:

    • Return a 429 with a Retry-After header, encouraging clients to back off.
    • Serve