Genace

generation API tenant authorization: protect your application operation mapping

A server key identifies a Genace account, not every tenant in your own product. If several local customers share that key, Genace’s account ownership check cannot decide which customer may see each job. Your application must authorize its own operation mapping before forwarding status, streaming progress, cancellation, or asset delivery requests.

Resolve the local resource under the authenticated tenant

Use an application operation ID in browser-facing routes. Derive tenant identity from your trusted session and query the operation using both fields. Do not load by operation ID and later trust a caller-supplied tenant value. A random-looking identifier makes guessing harder but is not an authorization rule.

SELECT operation_id, remote_job_id, render_state, delivery_state
FROM app_media_operations
WHERE operation_id = $1
  AND tenant_id = $2;

Bind both values as parameters. The second value comes from your authenticated server context. Decide your own not-found versus forbidden response policy and apply it consistently without leaking whether another tenant’s operation exists.

Forward only the stored remote identifier

Once authorized, use the row’s saved remote job ID for the upstream request. Do not let a separate body field override that ID. A normal accepted response provides job_id; a known submission-failure envelope can provide error.job_id; subsequent job snapshots use id.

Validate that a retrieved snapshot matches the expected remote identifier before storing it. Keep the server key in the outbound bearer header and return a limited application representation to the browser. Neither request headers nor the whole environment belong in serialized response data.

Cover every action, not only GET

The SSE relay must repeat the same ownership check before opening a stream. A cancellation route needs both ownership and permission to cancel, which may differ from permission to view. Download and asset-serving routes need authorization too; a protected status page does not help if its copied object is published through an unrestricted predictable path.

For long-lived streams, define how local session expiry or membership removal affects access. Your application may choose a bounded stream lifetime and reauthorization on reconnect. Do not claim that an upstream stream automatically understands your tenant membership changes.

Keep auditing useful and minimal

Record the local actor, operation ID, action category, authorization outcome, and safe remote job reference when available. Avoid logging the full prompt or signed URL just to explain an access denial. Access logs should support incident review without becoming a second asset distribution channel.

If a job’s remote account changes through configuration mistakes, stop and investigate. Do not work around a NOT_OWNER response by trying unrelated account keys or exposing an arbitrary upstream query endpoint.

Verification checklist

Create two test tenants and exercise status, SSE, cancellation, and asset reads across their boundaries. Test a valid operation ID paired with an altered remote ID and confirm the stored mapping wins. Remove membership during an observation cycle and verify your chosen reauthorization policy.

Finally, confirm rejected requests never reach the upstream client. These checks validate the application boundary without creating paid renders.

See server-side keys, PostgreSQL mappings, and browser proxy CSRF.

Continue with security

Where these facts come from

  • Editorial keyword hypothesis; search volume not measured
  • codebase: src/ai/api/auth.ts
  • codebase: src/app/api/v1/jobs/[id]/route.ts
  • codebase: src/app/api/v1/jobs/[id]/stream/route.ts
  • codebase: src/app/api/v1/jobs/[id]/cancel/route.ts