Warning
This project is currently in active development and may change drastically in the near future while we work towards solidifying a first release.
STAC Auth Proxy is a proxy API that mediates between the client and your internally accessible STAC API to provide flexible authentication, authorization, and content-filtering mechanisms.
- π Authentication: Selectively apply OIDC auth to some or all endpoints & methods
- ποΈ Content Filtering: Apply CQL2 filters to client requests, filtering API content based on user context
- π OpenAPI Augmentation: Update OpenAPI with security requirements, keeping auto-generated docs/UIs accurate (e.g. Swagger UI)
Note
Currently, the project can only be installed by downloading the repository. It will eventually be available on Docker (#5) and PyPi (#30).
For local development, we use uv
to manage project dependencies and environment.
uv sync
Otherwise, the application can be installed as a standard Python module:
pip install -e .
The simplest way to run the project is by calling the module directly:
python -m stac_auth_proxy
Alternatively, the application's factory can be passed to Uvicorn:
uvicorn --factory stac_auth_proxy:create_app
The application is configurable via environment variables.
- Core
UPSTREAM_URL
, STAC API URL- Type: HTTP(S) URL
- Required: Yes
- Example:
https://your-stac-api.com/stac
WAIT_FOR_UPSTREAM
, wait for upstream API to become available before starting proxy- Type: boolean
- Required: No, defaults to
true
- Example:
false
,1
,True
HEALTHZ_PREFIX
, path prefix for health check endpoints- Type: string
- Required: No, defaults to
/healthz
- Example:
''
(disabled)
- Authentication
OIDC_DISCOVERY_URL
, OpenID Connect discovery document URL- Type: HTTP(S) URL
- Required: Yes
- Example:
https://auth.example.com/.well-known/openid-configuration
OIDC_DISCOVERY_INTERNAL_URL
, internal network OpenID Connect discovery document URL- Type: HTTP(S) URL
- Required: No, defaults to the value of
OIDC_DISCOVERY_URL
- Example:
http://auth/.well-known/openid-configuration
DEFAULT_PUBLIC
, default access policy for endpoints- Type: boolean
- Required: No, defaults to
false
- Example:
false
,1
,True
PRIVATE_ENDPOINTS
, endpoints explicitly marked as requiring authentication, used whenDEFAULT_PUBLIC == True
- Type: JSON object mapping regex patterns to HTTP methods OR tuples of an HTTP method and string representing required scopes
- Required: No, defaults to the following:
{ "^/collections$": ["POST"], "^/collections/([^/]+)$": ["PUT", "PATCH", "DELETE"], "^/collections/([^/]+)/items$": ["POST"], "^/collections/([^/]+)/items/([^/]+)$": ["PUT", "PATCH", "DELETE"], "^/collections/([^/]+)/bulk_items$": ["POST"] }
PUBLIC_ENDPOINTS
, endpoints explicitly marked as not requiring authentication, used whenDEFAULT_PUBLIC == False
- Type: JSON object mapping regex patterns to HTTP methods
- Required: No, defaults to the following:
{ r"^/api.html$": ["GET"], r"^/api$": ["GET"], r"^/docs/oauth2-redirect": ["GET"], r"^/healthz": ["GET"], }
OPENAPI_SPEC_ENDPOINT
, path of OpenAPI specification, used for augmenting spec response with auth configuration- Type: string or null
- Required: No, defaults to
null
(disabled) - Example:
/api
- Filtering
ITEMS_FILTER
, cql2 expression generator for item-level filtering- Type: JSON object with class configuration
- Required: No, defaults to
null
(disabled) - Components:
cls
: Python import pathargs
: List of positional argumentskwargs
: Dictionary of keyword arguments
- Example:
{ "cls": "my_package.filters.OrganizationFilter", "args": ["org1"], "kwargs": { "field_name": "properties.organization" } }
While the project is designed to work out-of-the-box as an application, it might not address every projects needs. When the need for customization arises, the codebase can instead be treated as a library of components that can be used to augment any ASGI-compliant webserver (e.g. Django, Falcon, FastAPI, Litestar, Responder, Sanic, Starlette). Review app.py
to get a sense of how we make use of the various components to construct a FastAPI application.
The majority of the proxy's functionality occurs within a chain of middlewares. Each request passes through this chain, wherein each middleware performs a specific task:
-
EnforceAuthMiddleware
- Handles authentication and authorization
- Configurable public/private endpoints
- OIDC integration
- Places auth token payload in request state
-
BuildCql2FilterMiddleware
- Builds CQL2 filters based on request context/state
- Places CQL2 expression in request state
-
ApplyCql2FilterMiddleware
- Retrieves CQL2 expression from request state
- Augments request with CQL2 filter:
- Modifies query strings for
GET
requests - Modifies JSON bodies for
POST
/PUT
/PATCH
requests
- Modifies query strings for
-
OpenApiMiddleware
- Modifies OpenAPI specification based on endpoint configuration, adding security requirements
- Only active if
openapi_spec_endpoint
is configured
-
AddProcessTimeHeaderMiddleware
- Adds processing time headers
- Useful for monitoring/debugging
The system supports generating CQL2 filters based on request context to provide row-level content filtering. These CQL2 filters are then set on outgoing requests prior to the upstream API.
Important
The upstream STAC API must support the STAC API Filter Extension, including the Features Filter conformance class on to the Features resource (/collections/{cid}/items
) #37.
Tip
Integration with external authorization systems (e.g. Open Policy Agent) can be achieved by specifying an ITEMS_FILTER
that points to a class/function that, once initialized, returns a cql2.Expr
object when called with the request context.
If enabled, filters are intended to be applied to the following endpoints:
GET /search
- Supported: β
- Action: Read Item
- Applied Filter:
ITEMS_FILTER
- Strategy: Append query params with generated CQL2 query.
POST /search
- Supported: β
- Action: Read Item
- Applied Filter:
ITEMS_FILTER
- Strategy: Append body with generated CQL2 query.
GET /collections/{collection_id}
- Supported: β (#23)
- Action: Read Collection
- Applied Filter:
COLLECTIONS_FILTER
- Strategy: Append query params with generated CQL2 query.
GET /collections/{collection_id}/items
- Supported: β
- Action: Read Item
- Applied Filter:
ITEMS_FILTER
- Strategy: Append query params with generated CQL2 query.
GET /collections/{collection_id}/items/{item_id}
- Supported: β (#25)
- Action: Read Item
- Applied Filter:
ITEMS_FILTER
- Strategy: Validate response against CQL2 query.
POST /collections/
- Supported: β (#22)
- Action: Create Collection
- Applied Filter:
COLLECTIONS_FILTER
- Strategy: Validate body with generated CQL2 query.
PUT /collections/{collection_id}}
- Supported: β (#22)
- Action: Update Collection
- Applied Filter:
COLLECTIONS_FILTER
- Strategy: Fetch Collection and validate CQL2 query; merge Item with body and validate with generated CQL2 query.
DELETE /collections/{collection_id}
- Supported: β (#22)
- Action: Delete Collection
- Applied Filter:
COLLECTIONS_FILTER
- Strategy: Fetch Collectiion and validate with CQL2 query.
POST /collections/{collection_id}/items
- Supported: β (#21)
- Action: Create Item
- Applied Filter:
ITEMS_FILTER
- Strategy: Validate body with generated CQL2 query.
PUT /collections/{collection_id}/items/{item_id}
- Supported: β (#21)
- Action: Update Item
- Applied Filter:
ITEMS_FILTER
- Strategy: Fetch Item and validate CQL2 query; merge Item with body and validate with generated CQL2 query.
DELETE /collections/{collection_id}/items/{item_id}
- Supported: β (#21)
- Action: Delete Item
- Applied Filter:
ITEMS_FILTER
- Strategy: Fetch Item and validate with CQL2 query.
POST /collections/{collection_id}/bulk_items
- Supported: β (#21)
- Action: Create Items
- Applied Filter:
ITEMS_FILTER
- Strategy: Validate items in body with generated CQL2 query.
sequenceDiagram
Client->>Proxy: GET /collections
Note over Proxy: EnforceAuth checks credentials
Note over Proxy: BuildCql2Filter creates filter
Note over Proxy: ApplyCql2Filter applies filter to request
Proxy->>STAC API: GET /collection?filter=(collection=landsat)
STAC API->>Client: Response