feat(go): Add authentication and authorization support to Go Feature Server (HTTP & gRPC)
- Dominant language
- Python
- Stars
- 7.3k
- Forks
- 1.4k
- Avg merge
- 1d 21h
- Merged PRs (30d)
- 15
Description
**Is your feature request related to a problem? Please describe.**
The Go Feature Server (`go/internal/feast/server/`) has no authentication or authorization support. Any caller can reach `/get-online-features` without credentials, making it unsuitable for use in environments that require access control.
The Python Feature Server enforces access control at three layers — online serving, offline serving, and registry — covering 8 distinct actions across all Feast resource types. The Go server enforces none of them.
**How the Go server is reached in practice**
The Python SDK supports a `remote` online store mode ([`sdk/python/feast/infra/online_stores/remote.py#L644`](https://github.com/feast-dev/feast/blob/f08b4e823abe2b64de5f91cec205c8367d61a44a/sdk/python/feast/infra/online_stores/remote.py#L644)), where the client calls a Feature Server over HTTP instead of connecting to the store directly:
```yaml
# feature_store.yaml — SDK goes through the Feature Server
online_store:
type: remote
path: http://feast-server:8080
```
`RemoteOnlineStore` sends requests to `/get-online-features` regardless of whether the server on the other end is Python or Go — the HTTP API surface is identical. This means an operator can legitimately point `path` at the Go Feature Server for its lower latency characteristics, and **all auth enforcement silently disappears**. There is no warning, no fallback, and no fail-secure behavior.
By contrast, when the Python Feature Server is the target, auth is enforced via `inject_user_details` on every endpoint ([`sdk/python/feast/feature_server.py#L368`](https://github.com/feast-dev/feast/blob/f08b4e823abe2b64de5f91cec205c8367d61a44a/sdk/python/feast/feature_server.py#L368)).
**Current state of the Go server** — only metrics and panic-recovery middleware are chained; no auth hook exists:
- HTTP: [`go/internal/feast/server/http_server.go#L386-L398`](https://github.com/feast-dev/feast/blob/f08b4e823abe2b64de5f91cec205c8367d61a44a/go/internal/feast/server/http_server.go#L386-L398)
- gRPC: [`go/internal/feast/server/grpc_server.go`](https://github.com/feast-dev/feast/blob/f08b4e823abe2b64de5f91cec205c8367d61a44a/go/internal/feast/server/grpc_server.go) (no interceptors)
**Python permission model overview**
The Python servers protect 8 action types ([`sdk/python/feast/permissions/action.py#L4-L20`](https://github.com/feast-dev/feast/blob/f08b4e823abe2b64de5f91cec205c8367d61a44a/sdk/python/feast/permissions/action.py#L4-L20)):
| Action | Description |
|---|---|
| `READ_ONLINE` | Read from the online store |
| `READ_OFFLINE` | Read from the offline store |
| `WRITE_ONLINE` | Write to the online store (materialization push) |
| `WRITE_OFFLINE` | Write to the offline store |
| `CREATE` / `DESCRIBE` / `UPDATE` / `DELETE` | Registry CRUD operations |
These are enforced in three separate servers:
| Server | Transport | Actions enforced |
|---|---|---|
| `feature_server.py` | HTTP (FastAPI) | `READ_ONLINE` per FeatureView/FeatureService; `WRITE_ONLINE`, `WRITE_OFFLINE` for push endpoints ([L183-L484](https://github.com/feast-dev/feast/blob/f08b4e823abe2b64de5f91cec205c8367d61a44a/sdk/python/feast/feature_server.py#L183-L484)) |
| `offline_server.py` | Arrow Flight | `READ_OFFLINE` per FeatureView/DataSource; `WRITE_OFFLINE` ([L302-L540](https://github.com/feast-dev/feast/blob/f08b4e823abe2b64de5f91cec205c8367d61a44a/sdk/python/feast/offline_server.py#L302-L540)) |
| `registry_server.py` | gRPC | `CREATE` / `DESCRIBE` / `UPDATE` / `DELETE` on all resource types |
A critical security property: if no `Permission` objects are defined in the registry, access is **denied for all resources** (fail-secure default) — [`sdk/python/feast/permissions/enforcer.py#L42-L48`](https://github.com/feast-dev/feast/blob/f08b4e823abe2b64de5f91cec205c8367d61a44a/sdk/python/feast/permissions/enforcer.py#L42-L48). The Go server has no equivalent behavior and is permissive by default.
---
**Describe the solution you'd like**
Add authentication and authorization support to the Go Feature Server (both HTTP and gRPC), consistent with what the Python Feature Server already provides via `sdk/python/feast/permissions/`.
**Authentication** — implement the same three auth types as Python ([`sdk/python/feast/permissions/auth/auth_type.py`](https://github.com/feast-dev/feast/blob/f08b4e823abe2b64de5f91cec205c8367d61a44a/sdk/python/feast/permissions/auth/auth_type.py)):
- `auth.type: none` — no authentication (preserve current behavior explicitly)
- `auth.type: oidc` — JWT Bearer token validation via JWKS endpoint (RS256). A single implementation covers all OIDC-compliant providers (Keycloak, Azure AD, Okta, etc.) via the standard discovery URL
- `auth.type: kubernetes` — Kubernetes ServiceAccount token validation
**Authorization** — enforce `Permission` objects loaded from the registry at request time, applying role/group/namespace-based policies per FeatureView and FeatureService — consistent with [`sdk/python/feast/permissions/enforcer.py`](https://github.com/feast-dev/feast/blob/f08b4e823abe2b64de5f91cec205c8367d61a44a/sdk/python/feast/permissions/enforcer.py), including the fail-secure default (deny all when no permissions are defined).
**Implementation points:**
- HTTP: add an `authMiddleware(next http.Handler) http.Handler` that validates the `Authorization: Bearer ` header, mirroring [`sdk/python/feast/permissions/server/rest.py#L16-L46`](https://github.com/feast-dev/feast/blob/f08b4e823abe2b64de5f91cec205c8367d61a44a/sdk/python/feast/permissions/server/rest.py#L16-L46)
- gRPC: add a `UnaryServerInterceptor` for token extraction and validation
- Auth type selected via `auth_config` in `feature_store.yaml`, already partially parsed in `go/internal/feast/registry/repoconfig.go`
**Permission caching**
The Python `SecurityManager.permissions` property fetches `Permission` objects from the registry on every call without caching ([`sdk/python/feast/permissions/security_manager.py#L51-L56`](https://github.com/feast-dev/feast/blob/f08b4e823abe2b64de5f91cec205c8367d61a44a/sdk/python/feast/permissions/security_manager.py#L51-L56)):
```python
@property
def permissions(self) -> list[Permission]:
return self._registry.list_permissions(project=self._project)
# allow_cache is not passed — registry is hit on every permission check
```
The Go registry implementation has no caching mechanism for `Permission` objects at all. In high-throughput online serving, a registry read on every request will become a bottleneck. The Go implementation must include a TTL-based in-memory cache for `Permission` objects, refreshed in the background — consistent with the existing `cached_registry_proto` / `cache_ttl_seconds` pattern already used for other registry objects ([`sdk/python/feast/infra/registry/registry.py#L195-L257`](https://github.com/feast-dev/feast/blob/f08b4e823abe2b64de5f91cec205c8367d61a44a/sdk/python/feast/infra/registry/registry.py#L195-L257)).
**Acceptance criteria:**
- [ ] `auth.type: none` leaves behavior unchanged
- [ ] `auth.type: oidc` validates JWT via JWKS endpoint; same `feature_store.yaml` config as the Python server works
- [ ] `auth.type: kubernetes` validates ServiceAccount tokens
- [ ] `READ_ONLINE` is enforced per FeatureView and FeatureService on `/get-online-features`
- [ ] When no `Permission` objects are defined in the registry, all requests are denied (fail-secure)
- [ ] `Permission` objects are cached in-memory with a configurable TTL; the registry is not hit on every request
---
**Describe alternatives you've considered**
A sidecar proxy (e.g., Envoy, oauth2-proxy) can handle JWT validation at the network layer, but it cannot enforce Feast-native fine-grained authorization. Rules like "role `reader` may only call `READ_ONLINE` on Feature Views matching `driver_.*`" require reading `Permission` objects from the Feast registry at request time — something only the Feature Server itself can do.
| Requirement | Sidecar | In-process |
|---|---|---|
| JWT / OIDC authentication | ✅ | ✅ |
| Endpoint-level access control | ✅ | ✅ |
| Feature View / Service level authorization | ❌ | ✅ |
| Fail-secure default (deny when no permissions defined) | ❌ | ✅ |
---
**Additional context**
Key reference points in the Python implementation:
- Auth type definition: [`sdk/python/feast/permissions/auth/auth_type.py`](https://github.com/feast-dev/feast/blob/f08b4e823abe2b64de5f91cec205c8367d61a44a/sdk/python/feast/permissions/auth/auth_type.py)
- HTTP auth middleware: [`sdk/python/feast/permissions/server/rest.py#L16-L46`](https://github.com/feast-dev/feast/blob/f08b4e823abe2b64de5f91cec205c8367d61a44a/sdk/python/feast/permissions/server/rest.py#L16-L46)
- OIDC/JWT parser (RS256 + JWKS): [`sdk/python/feast/permissions/auth/oidc_token_parser.py`](https://github.com/feast-dev/feast/blob/f08b4e823abe2b64de5f91cec205c8367d61a44a/sdk/python/feast/permissions/auth/oidc_token_parser.py)
- Permission enforcer with fail-secure default: [`sdk/python/feast/permissions/enforcer.py`](https://github.com/feast-dev/feast/blob/f08b4e823abe2b64de5f91cec205c8367d61a44a/sdk/python/feast/permissions/enforcer.py)
- Auth config model: [`sdk/python/feast/permissions/auth_model.py`](https://github.com/feast-dev/feast/blob/f08b4e823abe2b64de5f91cec205c8367d61a44a/sdk/python/feast/permissions/auth_model.py)
Contributor guide
Assessment
This issue has not been assessed yet.