Skip to Content

gRPC Authentication

Learn how to secure your gRPC services with API keys.

🎯 Overview

Django-CFG gRPC uses API Key authentication for secure service access:

MethodBest ForManagementLifespan
API KeysServices, CLI tools, webhooks, all use casesDjango AdminLong-lived (configurable)

Historical Note

Django-CFG previously supported JWT authentication for gRPC. As of v1.5.8, we’ve migrated to API Key authentication only for better security and manageability.

API Keys provide:

  • Centralized management through Django Admin
  • Easy revocation without changing secrets
  • Per-service key tracking
  • Usage statistics and monitoring
  • No token expiration complexity

🔑 API Key Authentication

Concept

API keys are long-lived tokens for machine-to-machine communication:

  • Create keys in Django Admin
  • Each key authenticates as a specific user
  • Can be revoked instantly
  • Usage is tracked automatically

Creating Keys

Via Django Admin:

  1. Go to /admin/gRPC API Keys
  2. Click “Add gRPC API Key”
  3. Fill in name, user, type, expiration
  4. Save and copy the generated key

Using Keys

With grpcurl:

grpcurl -H "x-api-key: YOUR_API_KEY" \ localhost:50051 api.users.UserService/GetUser

With Python client:

import grpc # Add API key to metadata metadata = [('x-api-key', 'YOUR_API_KEY')] with grpc.insecure_channel('localhost:50051') as channel: stub = UserServiceStub(channel) response = stub.GetUser(request, metadata=metadata)

In service:

class UserService(BaseService): def UpdateProfile(self, request, context): # Access authenticated user user = context.user # Set by API key auth # Also access the API key used api_key = context.api_key # Which key was used user.bio = request.bio user.save() return UserResponse(...)

Development Shortcut

For development, you can use Django’s SECRET_KEY:

# Use SECRET_KEY instead of creating API key grpcurl -H "x-api-key: django-insecure-dev-key..." \ localhost:50051 api.users.UserService/GetUser

Only use SECRET_KEY in development! Disable in production via accept_django_secret_key: False

🔧 Configuration

API Key Settings

# api/config.py from django_cfg import DjangoConfig, GRPCConfig, GRPCAuthConfig class MyConfig(DjangoConfig): grpc = GRPCConfig( enabled=True, enabled_apps=["apps.users"], # Authentication settings auth=GRPCAuthConfig( enabled=True, # Enable authentication require_auth=False, # Allow anonymous by default # API Key settings api_key_header="x-api-key", # Header name accept_django_secret_key=True, # Allow SECRET_KEY (dev only!) # Public methods (no auth needed) public_methods=[ "/grpc.health.v1.Health/Check", ], ), )

Authentication Modes

1. Disabled (Development):

auth=GRPCAuthConfig( enabled=False, # All methods public )

2. Optional Auth (Recommended):

auth=GRPCAuthConfig( enabled=True, require_auth=False, # Some methods can be public api_key_header="x-api-key", accept_django_secret_key=True, # Allow SECRET_KEY for dev )

3. Required Auth (Strict):

auth=GRPCAuthConfig( enabled=True, require_auth=True, # All methods require API key api_key_header="x-api-key", accept_django_secret_key=False, # Production: only DB keys public_methods=[ "/grpc.health.v1.Health/Check", # Except health check ], )

🛡️ Service Authorization

Public Methods

class UserService(BaseService): def GetUser(self, request, context): # No auth required - anyone can call user = User.objects.get(id=request.user_id) return UserResponse(...)

Protected Methods

class UserService(BaseService): def UpdateProfile(self, request, context): # Require authentication user = self.require_user(context) user.bio = request.bio user.save() return UserResponse(...)

Permission Checks

class UserService(BaseService): def DeleteUser(self, request, context): # Require staff permission self.require_staff(context) User.objects.get(id=request.user_id).delete() return Empty()

📊 Monitoring

View Logs

All authenticated requests are logged with:

  • Which user made the request
  • Which API key was used (if applicable)
  • Request duration and status

Django Admin:

/admin/ → gRPC Request Logs

Filter by:

  • User
  • API Key
  • Status (success/error)
  • Date range

Check API Key Usage

Django Admin:

/admin/ → gRPC API Keys

See for each key:

  • Total request count
  • Last used timestamp
  • Status (active/expired/revoked)

🔒 Best Practices

API Keys

DO:

  • Create separate keys for each service
  • Set expiration dates for better security
  • Revoke unused keys regularly
  • Monitor usage in Django Admin
  • Disable accept_django_secret_key in production
  • Use descriptive names (e.g., “Analytics Service”, “Mobile App Backend”)
  • Track usage statistics regularly

DON’T:

  • Share keys between services
  • Use SECRET_KEY in production
  • Hardcode keys in source code
  • Leave expired keys active
  • Create keys without expiration dates in production

📚 Common Patterns

Service-to-Service Communication

# Service A calls Service B # 1. Create API key for Service A in Django Admin # 2. Store key in Service A's environment variables # 3. Service A includes key in metadata # Service A (caller) import os metadata = [('x-api-key', os.getenv('SERVICE_B_API_KEY'))] response = service_b_stub.Method(request, metadata=metadata)

CLI Tools

# Create API key for CLI tool # 1. Go to Django Admin → gRPC API Keys # 2. Create key with type "cli" # 3. Copy key and store securely # Use in CLI tool grpcurl -H "x-api-key: YOUR_CLI_API_KEY" \ localhost:50051 api.users.UserService/GetUser

Webhook Handlers

# Webhook service calling gRPC # 1. Create API key with type "webhook" # 2. Configure webhook handler from django.views.decorators.csrf import csrf_exempt import grpc @csrf_exempt def webhook_handler(request): # Parse webhook data data = json.loads(request.body) # Call gRPC with API key metadata = [('x-api-key', settings.WEBHOOK_GRPC_API_KEY)] with grpc.insecure_channel('localhost:50051') as channel: stub = ServiceStub(channel) response = stub.Method(data, metadata=metadata) return JsonResponse({'status': 'ok'})

Development Testing

# Use Django SECRET_KEY for quick testing # (Only works if accept_django_secret_key=True) from django.conf import settings metadata = [('x-api-key', settings.SECRET_KEY)] response = stub.Method(request, metadata=metadata)

Next Steps: