# Api Keys Akshatsinha0

A Bijection api keys component for Bijection.

- Package: @bijectionhq/api-keys-akshatsinha0
- Category: Authentication
- Status: Reference
- License: Apache-2.0
- Bijection source (repository access required): https://github.com/bijectionhq/api-keys-akshatsinha0/tree/21ff297298cf673be5324a403c5b4514e74dfcd1
- Upstream: https://github.com/akshatsinha0/convex-api-keys/tree/8088f6c205e3a77aa33f625528ac16e96d46e0db
- Documentation: https://docs.bijection.com/components/using
- Directory: https://bijection.com/components/api-keys-akshatsinha0

## Get the source

Bijection repository access is required. Request access at https://bijection.com/contact.

```sh
git clone https://github.com/bijectionhq/api-keys-akshatsinha0.git
cd api-keys-akshatsinha0
git checkout 21ff297298cf673be5324a403c5b4514e74dfcd1
```

## Before using this component

Read the package README at the recorded revision for setup, dependencies, and API examples. Upstream documentation describes the original package and may differ from the Bijection adaptation. This is a source candidate for evaluation. Catalog inclusion does not establish npm availability or runtime qualification. Check compatibility with your SDK and deployment before adoption. External providers run their own compute.

## Upstream package documentation

Recorded 2026-10-23. This describes @00akshatsinha00/convex-api-keys; check the Bijection adaptation separately.

A zero-dependency Convex component for API key management that handles generation, SHA-256 hashing, verification, rate limiting, RBAC, and audit logging entirely within Convex's transactional guarantees. Provides cryptographically secure keys with sliding window rate limits, usage credits with automatic refills, role-based permissions, and real-time analytics without external services or separate databases.

### Use cases

• **SaaS API tiering** - Issue API keys to customers with different rate limits and usage quotas (free, pro, enterprise plans) while tracking consumption in real-time
• **Microservice gateway authorization** - Secure internal services with granular RBAC permissions, allowing keys to access only authorized endpoints like billing:read or users:write
• **Compliance-driven key rotation** - Automatically rotate API keys every 90 days for PCI/SOC2 requirements with configurable grace periods to prevent service disruption
• **Webhook endpoint protection** - Rate limit and authenticate incoming webhooks from external partners while tracking usage patterns and preventing abuse
• **Multi-tenant API management** - Isolate API keys by namespace and owner, enabling white-label solutions or departmental separation within the same Convex deployment

### How it works

The component stores API keys as SHA-256 hashes in Convex tables and provides both direct component access and a class-based SDK. You register it in `convex.config.ts` using `app.use(apiKeys)`, then call functions like `apiKeys.create()` to generate base62-encoded keys and `apiKeys.verify()` for authentication.

Key verification runs atomically within a single Convex mutation, checking rate limits, decrementing usage credits, logging the attempt, and returning validation results. The sliding window rate limiter and RBAC system use helper functions like `hasPermission()` to check authorization. Usage analytics automatically roll up into hourly and daily buckets for dashboard queries.

All configuration happens through the ApiKeys constructor, including key entropy (`keyBytes`), default prefixes, namespaces, and log retention. The component supports key rotation with `rotate()` functions that maintain grace periods, and provides cursor-based pagination for listing keys with `getKeysByOwner()`.

### Upstream README

# Convex API Keys

A native, zero-dependency Convex component for API key management. Handles key generation, SHA-256 hashing, verification, rate limiting, RBAC, usage credits, and audit logging -- all within Convex's transactional guarantees.

Found a bug? Feature request? [File it here](https://github.com/akshatsinha0/convex-api-keys/issues).

## Why This Component?

Most API key solutions require external services, webhook plumbing, and separate databases. This component runs entirely inside Convex, giving you:

- **Transactional verification** -- key lookup, rate limit check, credit decrement, and audit log write all happen in a single atomic mutation
- **Reactive dashboards** -- use Convex queries to build real-time key usage dashboards that update automatically
- **Zero infrastructure** -- no Redis for rate limiting, no separate analytics pipeline, no webhook endpoints to maintain
- **Component isolation** -- all data lives in the component's private tables; your app tables stay clean

## Features

- Cryptographically secure key generation (configurable entropy, base62 encoded)
- SHA-256 hashed storage (plaintext keys never persist)
- Sliding window rate limiting with per-key and per-owner shared overrides
- Usage credits with automatic refill (hourly/daily/weekly/monthly)
- Role-based access control (RBAC) with permissions and roles
- Key rotation with configurable grace periods
- Full audit logging with action type filtering
- Cursor-based pagination for key listings
- Real-time reactive queries for dashboards
- Analytics rollups (hourly + daily) with time-bucketed queries
- Top keys by usage ranking
- Multi-tenant support via namespaces and owner isolation
- Configurable key bytes, log retention, and rollup intervals

## Installation

```sh
npm install @00akshatsinha00/convex-api-keys
```

Register the component in your `convex/convex.config.ts`:

```ts
import { defineApp } from "convex/server";
import apiKeys from "@00akshatsinha00/convex-api-keys/convex.config";

const app = defineApp();
app.use(apiKeys, { name: "apiKeys" });
export default app;
```

## Quick Start

### Direct component access

```ts
import { mutation, query } from "./_generated/server";
import { components } from "./_generated/api";
import { v } from "convex/values";

export const createKey = mutation({
  args: { name: v.string() },
  handler: async (ctx, args) => {
    const userId = (await ctx.auth.getUserIdentity())!.subject;
    return await ctx.runMutation(components.apiKeys.lib.create, {
      ownerId: userId,
      name: args.name,
      namespace: "default",
    });
  },
});

export const verifyKey = mutation({
  args: { key: v.string() },
  handler: async (ctx, args) => {
    return await ctx.runMutation(components.apiKeys.lib.verify, {
      key: args.key,
    });
  },
});
```

### Client SDK (class-based)

```ts
import { ApiKeys } from "@00akshatsinha00/convex-api-keys";
import { components } from "./_generated/api";

const apiKeys = new ApiKeys(components.apiKeys, {
  defaultNamespace: "production",
  defaultPrefix: "sk_live_",
  keyBytes: 32,              // configurable key entropy (default: 32)
  logRetentionDays: 90,      // auto-applied to purgeVerificationLogs
});

export const createKey = mutation({
  args: { name: v.string() },
  handler: async (ctx, args) => {
    const userId = (await ctx.auth.getUserIdentity())!.subject;
    return await apiKeys.create(ctx, { ownerId: userId, name: args.name });
  },
});

export const verifyKey = mutation({
  args: { key: v.string() },
  handler: async (ctx, args) => {
    const result = await apiKeys.verify(ctx, { key: args.key });
    // Static class methods for permission checking
    if (result.valid && ApiKeys.hasPermission(result, "write:data")) {
      // authorized
    }
    return result;
  },
});
```

---

## Real-World Scenarios

### Scenario 1: SaaS API with Tiered Plans

You run a SaaS product and want to issue API keys to customers on different pricing tiers. Free users get 100 requests/day, Pro users get 10,000, and Enterprise gets unlimited.

```ts
// convex/apiKeyManager.ts
import { ApiKeys } from "@00akshatsinha00/convex-api-keys";
import { components } from "./_generated/api";
import { mutation, query } from "./_generated/server";
import { v } from "convex/values";

const apiKeys = new ApiKeys(components.apiKeys, {
  defaultNamespace: "api-v1",
  defaultPrefix: "sk_live_",
});

// Issue a key when a customer signs up or upgrades
export const issueKeyForPlan = mutation({
  args: {
    plan: v.union(v.literal("free"), v.literal("pro"), v.literal("enterprise")),
  },
  handler: async (ctx, args) => {
    const userId = (await ctx.auth.getUserIdentity())!.subject;

    const planConfig = {
      free:       { remaining: 100,   refill: { amount: 100,   interval: "daily" as const } },
      pro:        { remaining: 10000, refill: { amount: 10000, interval: "daily" as const } },
      enterprise: {},  // no usage cap
    };

    const config = planConfig[args.plan];

    return await apiKeys.create(ctx, {
      ownerId: userId,
      name: `${args.plan}-key`,
      meta: { plan: args.plan },
      ...config,
      ratelimit: { limit: 60, duration: 60000 },  // 60 req/min burst protection for all tiers
    });
  },
});

// Verify on every API request
export const handleApiRequest = mutation({
  args: { key: v.string(), endpoint: v.string() },
  handler: async (ctx, args) => {
    const result = await apiKeys.verify(ctx, {
      key: args.key,
      tags: { endpoint: args.endpoint },
    });

    if (!result.valid) {
      return { error: result.code, message: result.message };
    }

    return {
      authorized: true,
      remaining: result.remaining,  // undefined for Enterprise (unlimited)
      plan: result.meta?.plan,
    };
  },
});

// Dashboard query: show customer their usage
export const myKeyUsage = query({
  args: { keyId: v.string() },
  handler: async (ctx, args) => {
    return await apiKeys.getUsageStats(ctx, { keyId: args.keyId });
    // Returns: { total, valid, rateLimited, usageExceeded, expired, revoked, disabled, notFound }
  },
});
```

### Scenario 2: Multi-Service RBAC (Microservice Gateway)

You have several internal services (billing, users, analytics) behind a gateway. Each API key should only access authorized services.

```ts
// convex/gateway.ts
import { ApiKeys, hasPermission, hasAllPermissions } from "@00akshatsinha00/convex-api-keys";
import { components } from "./_generated/api";
import { mutation } from "./_generated/server";
import { v } from "convex/values";

const apiKeys = new ApiKeys(components.apiKeys, {
  defaultNamespace: "gateway",
  defaultPrefix: "gw_",
});

// Admin: set up permissions and roles once
export const bootstrapRbac = mutation({
  handler: async (ctx) => {
    // Create granular permissions
    await apiKeys.createPermission(ctx, { name: "billing:read",  description: "View invoices" });
    await apiKeys.createPermission(ctx, { name: "billing:write", description: "Create charges" });
    await apiKeys.createPermission(ctx, { name: "users:read",    description: "List users" });
    await apiKeys.createPermission(ctx, { name: "users:write",   description: "Manage users" });
    await apiKeys.createPermission(ctx, { name: "analytics:read", description: "View metrics" });

    // Bundle permissions into roles
    await apiKeys.createRole(ctx, {
      name: "billing-admin",
      permissions: ["billing:read", "billing:write"],
    });
    await apiKeys.createRole(ctx, {
      name: "readonly",
      permissions: ["billing:read", "users:read", "analytics:read"],
    });
    await apiKeys.createRole(ctx, {
      name: "superadmin",
      permissions: ["billing:read", "billing:write", "users:read", "users:write", "analytics:read"],
    });
  },
});

// Issue a key with specific roles
export const issueServiceKey = mutation({
  args: { serviceName: v.string(), roles: v.array(v.string()) },
  handler: async (ctx, args) => {
    const result = await apiKeys.create(ctx, {
      ownerId: args.serviceName,
      name: `${args.serviceName}-key`,
    });

    // Assign roles to the newly created key
    await apiKeys.assignRoles(ctx, {
      keyId: result.keyId,
      roles: args.roles,
    });

    return result;
  },
});

// Gateway: verify and check permissions per endpoint
export const routeRequest = mutation({
  args: { key: v.string(), service: v.string(), action: v.string() },
  handler: async (ctx, args) => {
    const result = await apiKeys.verify(ctx, { key: args.key });

    if (!result.valid) {
      return { status: 401, error: result.code };
    }

    const requiredPermission = `${args.service}:${args.action}`;
    if (!hasPermission(result, requiredPermission)) {
      return { status: 403, error: "INSUFFICIENT_PERMISSIONS" };
    }

    return { status: 200, keyId: result.keyId, permissions: result.permissions };
  },
});
```

### Scenario 3: Key Rotation for PCI/SOC2 Compliance

Your security policy requires rotating API keys every 90 days. You want zero-downtime rotation with a grace period so clients can migrate.

```ts
// convex/compliance.ts
import { ApiKeys, isKeyExpiringSoon, calculateExpiration } from "@00akshatsinha00/convex-api-keys";
import { components } from "./_generated/api";
import { mutation, query } from "./_generated/server";
import { v } from "convex/values";

const apiKeys = new ApiKeys(components.apiKeys, {
  defaultNamespace: "production",
  defaultPrefix: "sk_live_",
});

// Create key with 90-day expiry
export const createComplianceKey = mutation({
  args: { name: v.string() },
  handler: async (ctx, args) => {
    const userId = (await ctx.auth.getUserIdentity())!.subject;
    return await apiKeys.create(ctx, {
      ownerId: userId,
      name: args.name,
      expires: calculateExpiration(90),  // 90 days from now
    });
  },
});

// Rotate a key with 48-hour grace period -- both old and new key work during grace
export const rotateKey = mutation({
  args: { keyId: v.string() },
  handler: async (ctx, args) => {
    const userId = (await ctx.auth.getUserIdentity())!.subject;

    const key = await apiKeys.getKey(ctx, { keyId: args.keyId });
    if (!key || key.ownerId !== userId) {
      throw new Error("Unauthorized");
    }

    // Old key stays valid for 48 hours; new key is returned immediately
    return await apiKeys.rotate(ctx, {
      keyId: args.keyId,
      gracePeriodMs: 48 * 60 * 60 * 1000,  // 48 hours
    });
  },
});

// Dashboard: show keys that need rotation soon
export const keysNeedingRotation = query({
  args: {},
  handler: async (ctx) => {
    const userId = (await ctx.auth.getUserIdentity())!.subject;
    const keys = await apiKeys.getKeysByOwner(ctx, { ownerId: userId });

    return keys.filter((k) => isKeyExpiringSoon(k.expires, 14));  // expiring within 14 days
  },
});
```

### Scenario 4: Webhook Endpoint Protection

You expose a webhook URL that external services call. You want to rate limit incoming webhooks and track which integration sent each request.

```ts
// convex/webhooks.ts
import { ApiKeys } from "@00akshatsinha00/convex-api-keys";
import { components } from "./_generated/api";
import { httpAction, mutation } from "./_generated/server";
import { v } from "convex/values";

const apiKeys = new ApiKeys(components.apiKeys, {
  defaultNamespace: "webhooks",
  defaultPrefix: "whk_",
});

// Issue a webhook key for each integration partner
export const createWebhookKey = mutation({
  args: {
    partnerName: v.string(),
    maxPerMinute: v.number(),
  },
  handler: async (ctx, args) => {
    return await apiKeys.create(ctx, {
      ownerId: args.partnerName,
      name: `${args.partnerName}-webhook`,
      meta: { type: "webhook", partner: args.partnerName },
      ratelimit: { limit: args.maxPerMinute, duration: 60000 },
    });
  },
});

// HTTP action: receive webhook, verify key from header
export const receiveWebhook = httpAction(async (ctx, request) => {
  const authHeader = request.headers.get("

[README truncated for prompt length]