2021-01-17 19:08:34+00:00

In multi-tenant Software-as-a-Service (SaaS) web platforms, ensuring that one client cannot access another client's data is a critical security requirement. If you rely on adding manual filters (e.g. WHERE organization_id = ?) to every database query, a developer's oversight can easily lead to data leak vulnerabilities. Google App Engine (GAE) provides built-in multi-tenant isolation via the Namespace Manager. Setting the namespace once scopes all subsequent operations automatically.

By implementing a custom WSGI middleware wrapper, we can intercept incoming requests, validate tenant contexts from authorization headers, and enforce namespace isolation.


1. Structuring Tenant Namespace Middleware

We write a Python WSGI middleware that intercepts requests, decodes authentication tokens, and sets the active namespace before executing views:

# namespace_middleware.py
from google.appengine.api import namespace_manager
import jwt

class TenantNamespaceMiddleware(object):
    def __init__(self, app, jwt_secret):
        self.app = app
        self.jwt_secret = jwt_secret

    def __call__(self, environ, start_response):
        # Extract JWT token from header
        auth_header = environ.get('HTTP_AUTHORIZATION', '')
        tenant_id = 'default'
        
        if auth_header.startswith('Bearer '):
            token = auth_header.split(' ')[1]
            try:
                # Decode and extract tenant namespace context
                payload = jwt.decode(token, self.jwt_secret, algorithms=['HS256'])
                tenant_id = payload.get('tenant_id', 'default')
            except jwt.ExpiredSignatureError:
                pass
            except jwt.InvalidTokenError:
                pass
                
        # Enforce App Engine database namespace scope
        namespace_name = f"tenant_{tenant_id}"
        namespace_manager.set_namespace(namespace_name)
        
        # Proceed with application request handling
        return self.app(environ, start_response)

2. Auto-Scoped Database Queries

Once the namespace is set by the middleware, queries run on the GAE environment are automatically partitioned. For example, executing a query on a CustomerInvoice entity will only search records within the current tenant's database partition, eliminating data leak vectors completely.