API Key Authentication in Ellar¶
This guide demonstrates how to implement API Key authentication in Ellar using both header-based and query parameter-based approaches.
Overview¶
API Key authentication is a simple yet effective way to secure your APIs. Ellar provides built-in support for three types of API Key authentication:
- Header-based API Key
- Query Parameter-based API Key
- Cookie-based API Key
Implementation Methods¶
1. Using Authentication Handler¶
# auth/api_key_scheme.py
from ellar.auth import UserIdentity
from ellar.auth.handlers import (
HeaderAPIKeyAuthenticationHandler,
QueryAPIKeyAuthenticationHandler,
CookieAPIKeyAuthenticationHandler
)
from ellar.common import IHostContext
from ellar.di import injectable
@injectable
class HeaderAPIKeyAuth(HeaderAPIKeyAuthenticationHandler):
api_key = "your-secret-api-key" # In production, use secure storage
api_key_name = "X-API-Key"
async def authentication_handler(
self,
context: IHostContext,
api_key: str,
) -> UserIdentity | None:
if api_key == self.api_key:
return UserIdentity(auth_type="api_key", api_key=api_key)
return None
@injectable
class QueryAPIKeyAuth(QueryAPIKeyAuthenticationHandler):
api_key = "your-secret-api-key"
api_key_name = "api_key"
async def authentication_handler(
self,
context: IHostContext,
api_key: str,
) -> UserIdentity | None:
if api_key == self.api_key:
return UserIdentity(auth_type="api_key", api_key=api_key)
return None
Register the authentication handlers:
# server.py
from ellar.app import AppFactory, use_authentication_schemes
application = AppFactory.create_from_app_module(
lazyLoad('project_name.root_module:ApplicationModule'),
config_module="project_name.config:DevelopmentConfig"
)
use_authentication_schemes([HeaderAPIKeyAuth, QueryAPIKeyAuth])
2. Using Guard Strategy¶
# auth/guards.py
from ellar.auth import UserIdentity
from ellar.auth.guards import (
GuardAPIKeyHeader,
GuardAPIKeyQuery
)
from ellar.di import injectable
@injectable
class HeaderAPIKeyGuard(GuardAPIKeyHeader):
api_key = "your-secret-api-key"
api_key_name = "X-API-Key"
async def authentication_handler(
self,
context: IExecutionContext,
api_key: str,
) -> UserIdentity | None:
if api_key == self.api_key:
return UserIdentity(auth_type="api_key", api_key=api_key)
self.raise_exception()
@injectable
class QueryAPIKeyGuard(GuardAPIKeyQuery):
api_key = "your-secret-api-key"
api_key_name = "api_key"
async def authentication_handler(
self,
context: IExecutionContext,
api_key: str,
) -> UserIdentity | None:
if api_key == self.api_key:
return UserIdentity(auth_type="api_key", api_key=api_key)
self.raise_exception()
Controller Implementation¶
from ellar.common import Controller, get
from ellar.auth import AuthenticationRequired
@Controller('/api')
@AuthenticationRequired(['HeaderAPIKeyAuth', 'QueryAPIKeyAuth'])
class APIController:
@get('/data')
async def get_data(self):
return {"message": "Protected data"}
Testing the Implementation¶
# Using Header-based API Key
curl http://localhost:8000/api/data \
-H "X-API-Key: your-secret-api-key"
# Using Query Parameter-based API Key
curl "http://localhost:8000/api/data?api_key=your-secret-api-key"
Security Best Practices¶
-
API Key Storage:
- Never hardcode API keys in source code
- Use environment variables or secure key management systems
- Rotate API keys periodically
-
Transport Security:
- Always use HTTPS in production
- Consider implementing rate limiting
- Log and monitor API key usage
-
Key Management:
- Implement API key rotation
- Maintain an audit trail of API key usage
- Implement key revocation mechanisms
Advanced Implementation¶
API Key with Scopes¶
from typing import List
from ellar.auth import UserIdentity
from ellar.auth.handlers import HeaderAPIKeyAuthenticationHandler
@injectable
class ScopedAPIKeyAuth(HeaderAPIKeyAuthenticationHandler):
api_keys = {
"key1": ["read"],
"key2": ["read", "write"],
}
api_key_name = "X-API-Key"
async def authentication_handler(
self,
context: IHostContext,
api_key: str,
) -> UserIdentity | None:
if api_key in self.api_keys:
return UserIdentity(
auth_type="api_key",
api_key=api_key,
scopes=self.api_keys[api_key]
)
return None
Manual OpenAPI Integration¶
Ellar automatically generates OpenAPI documentation when you use and class in ellar.auth.handlers
and ellar.auth.guards
. But if you want to manually add it, you can do so by using the OpenAPIDocumentBuilder
class.
from ellar.openapi import ApiTags, OpenAPIDocumentBuilder
@Controller
@ApiTags(name='API', security=[{"ApiKeyAuth": []}])
class APIController:
pass
# In your OpenAPI configuration
document_builder = OpenAPIDocumentBuilder()
document_builder.add_security_scheme(
"ApiKeyAuth",
{
"type": "apiKey",
"in": "header",
"name": "X-API-Key"
}
)
Custom Error Handling¶
from ellar.common import IExecutionContext
from ellar.common.responses import JSONResponse
from ellar.core.exceptions import as_exception_handler
@as_exception_handler(401)
def invalid_api_key_exception_handler(ctx: IExecutionContext, exc: Exception) -> JSONResponse:
return JSONResponse(
status_code=401,
content={
"message": "Invalid API key",
"error": "unauthorized"
}
)
Complete Examples¶
For a complete working example of API Key authentication, visit the Ellar examples repository.