Odile Gate

Let an AI-built app read only the signed-in user's data

Enforce row ownership on the server using the signed-in user's verified identity. Gate can also check that a request's path or required query filter names the user in that person's signed login token.

The problem

You build an app in a weekend. Its screen shows only the current user's orders, but its database API key can read every row. Changing an id in the request exposes another person's orders. Hiding a button or filtering the response in browser code does not stop someone calling the API directly.

The fix without Gate

Verify the login token on your backend and enforce row-level rules in the database or API. Derive the user from that verified token; do not trust a user id supplied by the browser. Test that Alice cannot read Bob's rows even with a valid login. Keep service/admin keys on the server. Your API must also verify that an order belongs to the user named in the path.

The fix with Gate

Start with a Gate project pointing at your API. Keep its admin key on your backend as $ADMIN and its id as $PROJECT. Configure user_tokens using your login provider's real HTTPS jwks_url and exact issuer; the example values below must be replaced. Set observe_only to false to enforce refusals, including if this project previously ran an observe-only pilot. These settings are available on Free and Pro despite the endpoint's name:

curl -X PUT "https://gate.odilelabs.com/api/projects/$PROJECT/enterprise" \
  -H "Authorization: Bearer $ADMIN" -H 'content-type: application/json' \
  -d '{"observe_only":false,"user_tokens":{"jwks_url":"https://login.example.com/.well-known/jwks.json","issuer":"https://login.example.com/","claim":"sub"}}'

claim defaults to sub. Optional audience requires a matching token audience; set it when your provider issues tokens for multiple APIs. Gate verifies RS256, ES256 or EdDSA signatures, expiry and issuer. It refuses unsigned tokens. Register an app with only the user-scoped read rule:

curl -X POST "https://gate.odilelabs.com/api/projects/$PROJECT/apps" \
  -H "Authorization: Bearer $ADMIN" -H 'content-type: application/json' \
  -d '{"name":"signed-in-app","routes":["GET /users/{user}/orders/:id"],"per_minute":60}'

Use its returned key as $APP_KEY. For this example, $USER_TOKEN is a real login JWT whose configured user claim is alice; use your user's actual claim value in the path. Send that token separately from the app key:

curl "https://gate.odilelabs.com/g/$PROJECT/users/alice/orders/42" \
  -H "Authorization: Bearer $APP_KEY" -H "x-odile-user-token: $USER_TOKEN"

A valid Alice token permits the Alice path; changing it to /users/bob/orders/42 gets 403. A missing, expired or forged token gets 401. Gate neither stores this login token nor forwards it upstream. Keep privileged app credentials on your backend even when the user signs in through a browser.

A query rule such as GET /orders?user_id=eq.{user} requires that exact filter for the verified user; an omitted, changed or repeated filter fails. Query pins cannot scope POST inserts, so put {user} in the path for writes and have the API derive ownership from it. Avoid broader rules that also allow unscoped reads.

Gate checks the method, path and required query values, not the ownership of returned rows or fields in a body. It cannot tell who owns order 42 from GET /orders/42 alone. Keep your API's ownership checks and row-level security, including checking that order 42 belongs to Alice, and prevent the app from bypassing Gate.

Free for 3 apps and 10,000 calls a month.

Other guides

Give each agent its own key · See every agent call · Stop wrong-customer changes · Approve one exact action · Avoid duplicate writes.