Loading…
Loading…
Explains how the platform authenticates API requests using tenant-specific API keys in a multi-tenant environment, detailing the lookup and usage tracking
The TenantApiKeyRepository is a runtime component responsible for authenticating inbound API requests in a multi-tenant deployment. Its primary function is to identify the tenant associated with a given API key, enabling the system to apply the correct security context and permissions for the remainder of the request lifecycle.
This component solves a critical bootstrapping problem during authentication. When an API request first arrives at the gateway, the system has not yet identified the tenant. Database connections at this stage operate under a reserved, non-specific tenant context. The TenantApiKeyRepository uses a specialized, secure database function to look up the API key across all tenants, bypassing the standard Row-Level Security (RLS) that would otherwise prevent such access.
It is called by the tenant resolution logic (e.g., SaasTenantResolver) early in the request processing pipeline.
Inputs:
Outputs:
tenant_id) and status of the tenant who owns the key.last_used_at timestamp.NOTE
This authentication mechanism is active only in the multi-tenant saas deployment mode. It is not used in byoc (Bring Your Own Cloud) deployments.
The authentication process involves two main operations: looking up the key to identify the tenant and then updating the key's usage timestamp.
sequenceDiagram
participant C as API Consumer
participant G as API Gateway
participant R as TenantApiKeyRepository
participant DB as Database
C->>G: Request with API Key
G->>G: Calculate SHA-256 hash of key
G->>R: findByKeyHash(keyHash)
R->>DB: SELECT ... FROM s2r_lookup_tenant_api_key(?)
note right of R: Uses a SECURITY DEFINER function<br/>to bypass RLS for the initial lookup.
DB-->>R: Returns tenant_id, status
R-->>G: ApiKeyLookup(tenantId, status)
alt Key is active and found
G->>G: Bind tenant context using tenantId
G->>R: touchLastUsed(keyHash)
R->>DB: UPDATE s2r_tenant_api_key SET last_used_at=now() ...
note right of R: Best-effort update.<br/>Failures are logged and ignored.
DB-->>R: (ack)
G->>G: Process request under tenant context
else Key is not active or not found
G-->>C: 401 Unauthorized
endThe findByKeyHash method is the entry point for authentication.
findByKeyHash with this hash.s2r_lookup_tenant_api_key database function.
SELECT tenant_id, status FROM s2r_lookup_tenant_api_key(?)
SECURITY DEFINER privileges, allowing it to run with the permissions of its owner. This is essential for bypassing the tenant-based Row-Level Security that is active on the underlying s2r_tenant_api_key table. Without this, the lookup would be confined to a default tenant and would fail to find keys belonging to other tenants.tenant_id and status. The function is designed to prevent information leakage by only returning these two fields.ApiKeyLookup object containing the results. If no key matches the hash, it returns an empty result.After a successful lookup, the touchLastUsed method is called to record the activity.
tenant_id returned by the lookup to the current connection's context.touchLastUsed with the same key hash.UPDATE statement to set the last_used_at column to the current time.
UPDATE s2r_tenant_api_key SET last_used_at = now() WHERE key_hash = ? AND ...
UPDATE is constrained by RLS. The WHERE clause includes REQUIRE_BOUND_TENANT_SQL, which ensures the update can only modify the key belonging to the tenant that was just bound to the connection. This prevents accidental writes outside the established tenant context.IMPORTANT
The timestamp update is a "best-effort" operation. If the UPDATE fails for any reason, the exception is caught, logged at a DEBUG level, and suppressed. This ensures that a failure in this non-critical tracking step does not cause an otherwise valid, authenticated API request to fail.
The component exposes a Java API for use by the runtime.
TenantApiKeyRepository ClassOptional<ApiKeyLookup> findByKeyHash(String keyHash)Looks up an API key by its SHA-256 hex digest using a security-hardened database function.
keyHash (String): The lowercase, SHA-256 hex digest of the API key. Must not be null or blank.Optional<ApiKeyLookup> containing the key's tenantId and status on an exact match. Returns Optional.empty() if no key with the specified hash is found.void touchLastUsed(String keyHash)Performs a best-effort update of the last_used_at timestamp for the key corresponding to the given hash. This method requires that the key's owning tenant context has already been bound to the session by the caller.
keyHash (String): The SHA-256 hex digest of the key that was just used for authentication.void. Failures are logged and swallowed.ApiKeyLookup RecordA data record representing the result of a successful key lookup.
| Field | Type | Description |
|---|---|---|
tenantId | UUID | The unique identifier of the tenant owner. |
status | String | The status of the API key (e.g., "active"). |
boolean isActive()A convenience method on the ApiKeyLookup record.
true if the key's status is exactly equal to "active", false otherwise.If findByKeyHash returns an empty Optional, it means no key matching the provided keyHash exists in the s2r_tenant_api_key table. This is the expected behavior for an invalid or revoked API key and will result in an authentication failure (e.g., a 401 Unauthorized response to the API consumer).
last_used_at Timestamp Not UpdatingIf an API key's last_used_at field is not being updated upon use, it may indicate a failure within the touchLastUsed method. As these failures are intentionaly suppressed to avoid failing the entire request, they will not be visible to the API consumer. Check the service logs at the DEBUG level for messages matching:
TenantApiKeyRepository: best-effort last_used_at touch failed: ...
This log entry will contain the underlying exception message, which can help diagnose the cause of the update failure (e.g., database connectivity issues, permissions problems).