9409ddf9bc
## Summary Projects can create, inspect, expire, and revoke multiple API keys for each environment. Plaintext values are shown only at creation; stored credentials are hashed and the API keys page displays only an obfuscated suffix afterward. Self-hosted installations support full-access additional keys by default. Authorization extensions can provide additional access presets and optional task selection. Additional keys can also mint scoped public access tokens through the Trigger.dev API without receiving the environment signing key. ## Feature notes - Only admin+ can create API keys (Developer can make in Development branch). - JWT self-signing will be a server call when used with new `_ak_` keys. - JWTs with long expiry can keep working even with api key deleted (gets priveleges from api key, signed with root key) - Unfiltered session listings intentionally preserve the existing broad task-read behavior. Filtered listings enforce task-level scopes for every requested task. - Buffered runs without a task identifier are not safely authorizable, so cancel/replay requests fail closed rather than resolving an unscoped run. - Batch and waitpoint endpoints intentionally return server-minted, narrowly scoped public tokens to all callers. These tokens have bounded lifetimes and may remain valid until expiry after API-key revocation. ## Deployment notes Deploy the management UI and public-token endpoint with new key creation disabled. Enable creation for selected organizations after the authentication path and released SDK have been verified, then expand availability gradually. Revoking an API key prevents new bearer requests and new token minting. Public tokens already minted by that key remain valid until their own expiration because they are signed by the environment signing key. ## TODO - [x] Add "Created by" to the key table - [x] Document that streamed batch ingestion is non-atomic and may partially accept items before a validation or authorization error. ## Follow-ups - [x] Add an organization-level feature flag for the API key management UI and creation action. - [x] Document rollout ordering: enable additional-key lookup before enabling issuance. - [x] Add a system-wide gate that can stop new key issuance without disabling authentication for existing keys. - [x] Replace the generic SDK compatibility warning with the first published compatible version. Old SDK will mint an unusable token if given an `_ak_` key. - [x] Add public documentation covering creation, storage, expiration, revocation, SDK compatibility, and public-token lifetime behavior. - [x] Add observability for key creation, revocation, policy preparation failures, and public-token mint failures. - [ ] Exercise create, copy-once display, authenticate, mint, expire, and revoke flows end to end before broad enablement.
50 lines
1.5 KiB
TypeScript
50 lines
1.5 KiB
TypeScript
import { getMeter } from "@internal/tracing";
|
|
import { singleton } from "~/utils/singleton";
|
|
|
|
export type ApiKeyOperation = "create" | "prepare_policy" | "revoke";
|
|
export type ApiKeyOperationResult = "success" | "rejected" | "error";
|
|
export type ApiKeyOperationReason =
|
|
| "none"
|
|
| "database_error"
|
|
| "not_found_or_revoked"
|
|
| "policy_rejected"
|
|
| "policy_error";
|
|
|
|
export type PublicTokenMintResult = "success" | "rejected" | "error";
|
|
export type PublicTokenMintReason =
|
|
| "none"
|
|
| "invalid_body"
|
|
| "scope_not_allowed"
|
|
| "invalid_expiration"
|
|
| "expiration_not_future"
|
|
| "expiration_too_long"
|
|
| "signing_failed";
|
|
|
|
const telemetry = singleton("apiKeyTelemetry", () => {
|
|
const meter = getMeter("api-key");
|
|
|
|
return {
|
|
operations: meter.createCounter("api_key.operations", {
|
|
description: "Additional environment API key management operations",
|
|
}),
|
|
publicTokenMintAttempts: meter.createCounter("public_token.mint_attempts", {
|
|
description: "Public access token mint attempts using environment API keys",
|
|
}),
|
|
};
|
|
});
|
|
|
|
export const apiKeyTelemetry = {
|
|
recordOperation(
|
|
operation: ApiKeyOperation,
|
|
result: ApiKeyOperationResult,
|
|
reason: ApiKeyOperationReason = "none"
|
|
) {
|
|
telemetry.operations.add(1, { operation, result, reason });
|
|
},
|
|
recordPublicTokenMint(result: PublicTokenMintResult, reason: PublicTokenMintReason = "none") {
|
|
telemetry.publicTokenMintAttempts.add(1, { result, reason });
|
|
},
|
|
};
|
|
|
|
export type ApiKeyTelemetry = typeof apiKeyTelemetry;
|