Prompt and Applicable Scenarios
A multi-tenant B2B platform exposes server-to-server APIs. Each customer may create several keys for different integrations. The system needs separate live and test environments, scoped permissions, per-key and per-tenant quotas, optional expiration, zero-downtime rotation, and rapid revocation after compromise.
Design the issuance, storage, verification, authorization, rotation, revocation, and audit paths. Also explain where API keys are the wrong credential. The client is a trusted server capable of protecting a secret; browser and mobile applications are outside this credential model.
The design must preserve five invariants:
- The complete secret is shown only once and never stored or logged in plaintext.
- A valid key identifies a machine principal, but does not automatically authorize every action.
- A key belongs to exactly one tenant and environment.
- Revocation takes effect within a defined, testable propagation target.
- Rotation can overlap old and new keys without hiding which credential made a request.
What the Interviewer Evaluates
The first signal is whether the candidate separates authentication from authorization. Verifying a secret establishes which API-key principal sent the request. The service must still enforce scopes, endpoint policy, resource ownership, and tenant isolation.
The second is secret handling. A strong answer generates an opaque high-entropy secret with a cryptographically secure random generator, displays it once, stores only a verifier, redacts it everywhere, and keeps any server-side pepper in a dedicated secret manager.
The third is lifecycle design. Keys need names, environments, status, expiry, scopes, attribution, rotation, and revocation. Treating a key as one permanent database string leaves operators with unsafe sharing and disruptive replacement.
The fourth is operational reasoning. Caches, rate limits, logs, incident response, and availability all affect the security boundary. “Immediate revocation” is not true if an edge cache accepts a revoked key for ten minutes.
Finally, the candidate should reject API keys for untrusted clients and sufficiently sensitive operations. A static bearer secret copied into frontend code is recoverable by users and attackers. High-value or user-delegated workflows may require short-lived workload credentials, OAuth, mutual TLS, request signing, or step-up controls.
Clarifying Questions Before Answering
- Who holds the credential? A backend service can protect a secret; a browser, mobile app, desktop binary, or public repository cannot guarantee secrecy.
- What does a key represent? Define whether it represents a tenant integration, an internal workload, or a human. This design uses a tenant-owned machine principal, not an end-user session.
- How sensitive are the operations? Read-only analytics and money movement do not deserve identical controls.
- What are the revocation and availability targets? Choose a measurable propagation target and decide how authentication behaves if the primary key store is unavailable.
- How are environments isolated? Test and live keys need distinct prefixes, data boundaries, permissions, and quotas.
- How granular are permissions? Establish coarse scopes plus resource-level authorization; avoid an unbounded custom policy language unless the product requires it.
- How many keys may a tenant create? A limit prevents key sprawl and stops customers from bypassing per-key quotas by minting unlimited credentials.
- What audit and compliance rules apply? Retention, creator attribution, last-used data, approvals, and emergency access may be regulated.
30-Second Answer Framework
“I would issue a key with a searchable public ID and an opaque random secret, such as aklive7F3KQ2.m8…Vw. The complete value is returned once. The database stores the public ID, tenant, environment, scopes, status, expiry, and a keyed verifier, never the plaintext secret.
On each TLS request, the gateway extracts the key from a header, finds the row by public ID, recomputes the verifier, compares it in constant time, and checks status and expiry. It then creates a machine-principal context. Endpoint scopes and tenant ownership are checked separately. Rate limits apply to both the key and tenant, and audit logs contain only the public ID.
For rotation, create a second key, deploy it, observe usage of both IDs, then revoke the old one. A leak triggers immediate revocation with no grace period, log review, and replacement. Revocation invalidates caches within the declared target. I would use stronger or short-lived credentials for public clients, user delegation, and high-value actions.”
Step-by-Step Deep Dive
Step 1: Model identity and key records
Make each key a distinct machine principal. Do not share one tenant-wide secret across every integration. A practical record is:
ApiKey(
key_id, tenant_id, environment, name, verifier,
verifier_version, scopes, status, expires_at,
created_at, created_by, last_used_at
)keyid is public and searchable. name helps an operator distinguish “billing export” from “warehouse sync.” status supports at least active and revoked states; expiry is evaluated independently. createdby and approximate lastusedat improve attribution. Do not update lastusedat synchronously on every request: that creates a write hot spot. Aggregate or sample it asynchronously when minute-level precision is sufficient.
Scopes describe coarse capabilities such as invoices:read. They do not replace resource authorization. After accepting the key, a request for invoice 123 still needs a query constrained by the authenticated tenant_id. Never accept a caller-supplied tenant as the authority.
Step 2: Generate once, reveal once, store a verifier
Generate a 32-byte random secret with a cryptographically secure random generator. This is a concrete design choice, not a universal protocol requirement. Encode it in a transport-safe alphabet and combine it with a recognizable prefix and public ID:
ak_live_7F3KQ2.m8...opaque-secret...VwThe prefix lets support tools identify the credential type and environment without exposing the secret. Return the complete key only in the successful creation response. The UI must state that it cannot be recovered; losing it means creating a replacement.
Store HMAC-SHA-256(serverpepper, completesecret) as the verifier. The pepper stays in a secret manager, separate from the database. A plain SHA-256 digest is viable for a sufficiently random token; the keyed verifier adds defense in depth if only the database is exposed. Version the verifier so the service can migrate algorithms or peppers. A pepper migration must support bounded dual verification or a deliberate key-reissue plan; silently invalidating every customer key is unacceptable.
Creation is an authenticated management action. Enforce tenant role, key-count limits, allowed scopes, environment, expiry policy, and any required approval before generating the secret. Store the record and return the secret through a response that is never cached. Redact authorization headers and response bodies at the application, proxy, tracing, error-reporting, and support-tool layers.
Step 3: Authenticate a request without broadening authority
Require TLS and accept the key in an authorization or dedicated header, never in the URL query string. URLs commonly reach histories, analytics, proxy logs, and referrer data. Split and validate the format, use the public ID for an indexed lookup, and reject malformed input before expensive work.
For a candidate row, recompute the verifier and use a constant-time comparison. Then check environment, active status, and expiry. Return the same generic external error for unknown, malformed, expired, and revoked keys so the endpoint does not become a key-enumeration oracle. Internally, record a safe reason code without the secret.
Successful verification creates a context containing keyid, tenantid, environment, and scopes. The route policy checks required scopes; the data layer constrains access to that tenant and resource. Administrative or high-value endpoints can reject API-key principals entirely or require an additional control.
Step 4: Bound abuse with layered quotas and monitoring
Rate limiting does not prove identity, but it limits the damage of a stolen or buggy credential. Apply a burst and sustained limit per key, plus an aggregate tenant limit. The tenant layer prevents a customer from multiplying capacity by creating many keys. Expensive endpoints may need separate cost-weighted budgets and concurrency limits.
Log the public key ID, tenant, route, decision, latency, source network metadata allowed by policy, and request correlation ID. Never log the complete key, verifier, or reusable authorization header. Alert on unusual geography or network changes, sudden failure spikes, scope-denial spikes, dormant keys becoming active, and traffic after a rotation notice. These are investigation signals, not automatic proof of compromise.
Restrict key-management endpoints more heavily than ordinary data endpoints. They deserve strong human authentication, CSRF protection for cookie-based consoles, explicit authorization, audit events, creation limits, and possibly reauthentication or approval.
Step 5: Reconcile caching with rapid revocation
An indexed verification lookup is simple and gives the database an authoritative decision, but very high request volume may justify a cache. Cache only the verifier and minimal authorization metadata under the public ID, encrypt transport, and keep entries bounded. Never cache the presented secret.
Revocation writes the authoritative status first and publishes invalidations to gateways. A short TTL is the fallback if an invalidation is lost. The product must state a measurable target—for this scenario, revoked keys stop authenticating at every gateway within five seconds—and test it under packet loss and node restart. A ten-minute TTL cannot support that promise.
Choose failure behavior by risk. For sensitive writes, failure to obtain sufficiently fresh key state should fail closed. For selected low-risk reads, a briefly stale previously valid cache entry may be an explicit availability tradeoff, but it violates strict immediate revocation and must not be smuggled in as a default.
Step 6: Rotate and revoke as different workflows
Routine rotation needs overlap:
- Create a new key with no more privilege than the old one.
- Deliver it through the customer's secret-management path.
- Deploy and canary the new key.
- Observe requests by public key ID until the old key is quiet.
- Revoke the old key and verify that no traffic still depends on it.
Do not mutate the old secret in place; two separate IDs preserve attribution and rollback during migration. Expiration can enforce a maximum lifetime, but forced expiry without adoption telemetry creates avoidable outages.
A suspected leak follows a different order: revoke first with no grace period, invalidate caches, identify affected tenant, scopes, routes, and time window, review audit evidence, issue a least-privilege replacement, and remediate the leak source. Deleting the row immediately can erase useful incident evidence; retain non-secret metadata according to policy.
Step 7: Know when API keys are insufficient
API keys are bearer credentials: possession is enough to use them. They do not prove the caller still runs on an expected workload, provide end-user consent, or prevent replay by themselves. Never embed a secret key in browser JavaScript, a mobile binary, sample code, a container image, or a repository.
For cloud workloads, prefer short-lived workload identity when available. For user-delegated access, use an authorization protocol with narrow consent and expiring tokens. For especially sensitive service-to-service calls, consider mutual TLS or request signing so a copied database value alone is insufficient. The correct choice follows the threat model; adding every mechanism to every API only creates operational complexity.
Step 8: Test security, lifecycle, and failure modes
Build an adversarial matrix covering:
- malformed prefixes, unknown IDs, wrong secrets, and constant-time verifier handling;
- expired, revoked, test-in-live, and insufficient-scope keys;
- cross-tenant object access after otherwise valid authentication;
- secret redaction in proxy, application, trace, error, and audit output;
- rotation overlap, old-key usage telemetry, expiry, and emergency revocation;
- stale cache entries, dropped invalidations, gateway restart, and key-store outage;
- per-key and per-tenant quota enforcement, including many keys from one tenant;
- concurrent creation, revocation during an in-flight request, and duplicate management calls.
Also scan repositories and deployment configuration for recognizable key prefixes. A detector is a recovery aid, not permission to place secrets in source control. Verify incident drills with a test key: measure time from revocation confirmation to rejection at every gateway and confirm that logs retain attribution without retaining the secret.
Strong Sample Answer
“I would treat every API key as a named machine principal owned by one tenant and one environment. The issued value contains a public lookup ID and an opaque random secret. I return it once over TLS, store a keyed verifier plus lifecycle metadata, and redact the complete value from every logging and tracing layer.
The gateway looks up the public ID, recomputes the verifier, compares it in constant time, and checks environment, status, and expiry. Acceptance produces a context with the tenant and scopes; each route still checks its scope, and each data query still enforces tenant ownership. Per-key limits contain one integration, while tenant limits stop quota multiplication.
If verification metadata is cached, revocation updates the source of truth and pushes invalidations, with a short TTL as fallback. I would define and test a five-second revocation target. Routine rotation creates a second key, canaries it, observes both public IDs, and then revokes the old one. A suspected leak skips the grace period: revoke, investigate the key's scopes and activity window, issue a narrower replacement, and fix the exposure source.
I would not use this static secret in browsers or mobile applications, as an end-user identity, or as the sole control for high-value actions. Those cases need delegated, short-lived, workload-bound, or stronger credentials.”
Common Mistakes
- Storing the plaintext key so it can be shown again. Recovery convenience turns a database read into credential disclosure. Show once and support replacement.
- Using only a global hash without lifecycle metadata. Verification alone cannot answer tenant, environment, scope, expiry, attribution, or revocation questions.
- Putting keys in query strings. URLs are routinely copied and recorded. Use a header over TLS.
- Treating scopes as tenant authorization.
invoices:readdoes not prove that invoice123belongs to the authenticated tenant. - Giving every integration one shared tenant key. A leak then has a larger blast radius and attribution becomes ambiguous.
- Rate-limiting only per key. A tenant can create or rotate multiple keys and multiply its allowed traffic.
- Claiming immediate revocation while caching for minutes. State a propagation target, invalidate actively, and test cache failure.
- Rotating by overwriting the secret. This removes overlap, attribution, canarying, and a clean rollback path.
- Using a static secret in a public client. Obfuscation cannot create a confidential storage boundary.
- Logging the credential to debug authentication. Log a public ID and safe reason code; never log the reusable secret.
Follow-Up Questions and Answers
Why use a public ID plus secret instead of hashing the whole key and scanning every row?
The public ID gives an indexed lookup, a safe support identifier, and useful attribution. The secret remains the proof. Scanning every verifier is slow and encourages dangerous logging or secondary indexes. The public ID is intentionally non-secret, so knowing it must not help derive the secret.
Is a fast hash safe for an API-key verifier?
It can be safe when the secret has enough cryptographic randomness, because unlike human passwords it is not drawn from a guessable dictionary. A keyed HMAC adds protection when the database is exposed without the separate pepper. Password hashes remain the right tool for human-chosen passwords; the threat models differ.
How do you rotate the server-side pepper?
Store a verifier version with every row. During a bounded migration, the verifier can select the old or new pepper, and a successfully authenticated old-version key can be re-verifed under the new version if the presented secret is available in memory. Another option is a scheduled customer key reissue. Never remove the old pepper before all dependent verifiers have migrated or expired.
Should lastusedat be exact?
Usually not. Updating one row on every request adds write load and contention. Send a sampled or aggregated usage event and update periodically. Security audit events may remain append-only at the request layer, while the management UI labels lastusedat as approximate.
How do you handle a revocation racing with an in-flight request?
Define the boundary explicitly. Authentication can guarantee that requests starting after propagation are rejected. A sensitive operation may recheck authorization before commit or bind the authenticated key status into a transaction-aware policy. Revocation cannot retroactively erase an operation that already committed, so incident response must inspect that window.
Should keys expire automatically?
Expiration limits indefinite exposure, but it is not a substitute for rotation or revocation. The platform should notify owners, expose old-key usage, allow safe overlap, and reject after the deadline. The right maximum lifetime depends on risk and whether better short-lived credentials are available.
Would IP allowlisting solve key theft?
It is an optional additional restriction for customers with stable egress addresses. It does not replace secret verification, and it can create outages or false confidence when networks change or proxies are shared. Treat it as one signal or policy layer, not the root of identity.
What should the create-key response contain?
Return the complete key once, its public ID or prefix, name, environment, scopes, expiry, and creation metadata. Mark the response non-cacheable and never return the verifier or pepper. Later list APIs return only the public identifier and metadata.