KeyHippo extends Postgres Row Level Security (RLS) to support API key authentication directly at the database level. By integrating API key management within Postgres, KeyHippo ensures a consistent, secure approach to managing access control for both session-based users and API key holders.

Row Level Security (RLS)

Row Level Security (RLS) allows Postgres to apply fine-grained access control directly at the data level. This enables the database to enforce rules about who can view or modify specific rows in a table based on the user’s identity.

KeyHippo builds on RLS by allowing API keys to be validated using the same policies that govern session-based access. This unifies access control across both authentication methods, ensuring that API key holders and logged-in users are subject to the same security checks.

Example:

An RLS policy restricting access to resources based on ownership might look like this:

CREATE POLICY resource_access
ON public.resources
USING (auth.uid() = resources.owner_id);

With KeyHippo, this can be extended to include API keys:

CREATE POLICY resource_access
ON public.resources
USING (
  auth.uid() = resources.owner_id
  OR auth.keyhippo_check(resources.owner_id)
);

This approach allows API key holders to access resources in the same way as authenticated users, without needing separate access control logic.

PostgREST Integration

KeyHippo works directly with PostgREST, which exposes your Postgres database as a RESTful API. By integrating with PostgREST, API keys can be used for authentication and authorization in HTTP requests, relying on RLS policies to control access to database resources.

This eliminates the need for custom API layers to manage authentication or access control, allowing Postgres to handle both with its built-in security features.

Stateless API Key Management

KeyHippo’s stateless design simplifies authentication by eliminating the need to maintain session state on the server. API keys are cryptographically signed and can be validated directly by Postgres without additional lookups or session tracking. This approach is particularly suited for applications that need to scale, as it reduces the overhead associated with managing user sessions.

API Key Lifecycle

  • Generation: API keys are generated using cryptographic methods and are associated with a specific user and purpose.
  • Validation: API keys are verified using their cryptographic signature, without requiring database lookups.
  • Revocation: API keys can be revoked at any time, immediately removing access for the key holder.

Key Derivation and Storage

API keys in KeyHippo are derived only once and are never stored in their original form. Instead, a cryptographic hash of the key is saved in the database, ensuring that the raw key cannot be recovered, even by someone with access to the database.

This design ensures that API keys cannot be reverse-engineered, and the original key is never available for re-derivation. Once a key is generated and returned to the user, it is never retrievable again in any form.

Key Derivation Process:

  1. Unique Key Generation: A key is created using cryptographic methods that incorporate user-specific data and timestamps.
  2. One-Time Derivation: The key is derived once during generation and never stored in raw form.
  3. Hash Storage: A cryptographic hash of the key is stored, which is used for future validation.
  4. Non-Retrievable: After the key is provided to the user, it cannot be accessed or re-derived by any party, ensuring maximum security.

Unified Access Control

By integrating with RLS, KeyHippo allows you to define access control policies that apply to both session-based users and API key holders. This unification of access control ensures consistency across different authentication methods and simplifies security management.

For example, if you want to ensure that both a user and their API key have the same access rights, a single RLS policy can handle both cases:

CREATE POLICY user_access
ON public.users
USING (auth.uid() = users.id OR auth.keyhippo_check(users.id));

This removes the need to create separate policies or logic for different authentication types.