Auth and owner rules
This continues the quickstart. A posts table already exists, with title and status. New tables are created with every method enabled, so confirm list, create, get, update, and delete are on. This recipe does not create the endpoint rows.
Until rules exist, an empty rule list allows the call. After the steps below, list returns only rows whose authorId is the logged-in user. Token lifetimes, the email cap, and the full rule catalog are on End-user auth and Access rules.
Enable project auth
Section titled “Enable project auth”-
Open the project at app.tefily.com.
-
Open Database → Schema Builder. The table list always shows
_users. Before auth is on, that row is a placeholder. -
On
_users, click the lock icon. The dialog title is Toggle Auth. The body is “Are you sure you want to enable auth?” Confirm Enable Auth.
Enabling auth creates _users with email, password, and emailVerified, plus an internal token collection (End-user auth). _users CRUD endpoints stay disabled. Do not call /api/p/:projectId/_users from the app. Register and login use the auth routes.
Optional admin call. Use a platform JWT or a PAT (kfx_…), not the client SDK.
POST /api/projects/YOUR_PROJECT_ID/auth/toggleAuthorization: Bearer kfx_…Content-Type: application/json
{ "enabled": true }Shapes: Schemas.
Add authorId
Section titled “Add authorId”On Database → Schema Builder, open the posts table and choose Add Field.
-
Field Name:
authorId -
Type: string. The picker describes it as “Text (UTF-8 strings).”
-
Required: on
-
Array: off
authorId stores the project user’s _id. It is not a reference field in this recipe.
Optional:
POST /api/projects/YOUR_PROJECT_ID/schemas/SCHEMA_ID/fieldsAuthorization: Bearer kfx_…Content-Type: application/json
{ "name": "authorId", "fieldType": "string", "isRequired": true }SCHEMA_ID is the posts table id from GET /api/projects/:projectId/schemas.
Owner rules
Section titled “Owner rules”Rules are per endpoint. A list rule does not apply to get. See Endpoints.
Open Database → Access Rules. The dropdown lists each method as METHOD · enabled (for example LIST · enabled). For each of list, get, create, update, and delete:
-
Add Rule, then type Require authentication (
requireAuth). No config. Save. -
Add Rule, then type Compare user field (
userFieldCompare):- User field:
_id(the dialog hint is “e.g. _id, role, department”) - Operator: equals (
eq) - Segment Record field (not Static value)
- Record field:
authorId
- User field:
Order is requireAuth, then userFieldCompare. First deny wins.
create is in that list because, on create and when there is no existing row, userFieldCompare reads the request body. A create whose authorId is missing or is someone else’s _id is denied. On get, update, and delete the check uses the stored row, so a caller cannot read or change another user’s post. On update, that check does not look at the new body: omit authorId from update payloads so a user cannot reassign the row. On list, the same rule becomes a filter. The client does not add its own authorId filter.
The same ruleConfig goes on every method:
{ "ruleType": "userFieldCompare", "ruleConfig": { "userField": "_id", "operator": "eq", "compareWith": "record", "recordField": "authorId" }}The requireAuth body is:
{ "ruleType": "requireAuth", "ruleConfig": {} }Optional, after GET /api/projects/:projectId/schemas/:schemaId/endpoints to learn endpointId:
POST /api/projects/YOUR_PROJECT_ID/endpoints/ENDPOINT_ID/rulesAuthorization: Bearer kfx_…Content-Type: application/json
{ "ruleType": "requireAuth", "ruleConfig": {}, "orderIndex": 0 }POST /api/projects/YOUR_PROJECT_ID/endpoints/ENDPOINT_ID/rulesAuthorization: Bearer kfx_…Content-Type: application/json
{ "ruleType": "userFieldCompare", "ruleConfig": { "userField": "_id", "operator": "eq", "compareWith": "record", "recordField": "authorId" }, "orderIndex": 1}Repeat for the five endpoint ids. The rule-type catalog is on Access rules.
Register, then the email, then login
Section titled “Register, then the email, then login”import { KataflaxClient } from '@kataflax/sdk';
const client = new KataflaxClient({ baseUrl: 'https://api.tefily.com', projectId: 'YOUR_PROJECT_ID',});
const registered = await client.auth.register({ email: 'jane@example.com', password: 'secret123',});// registered.user.emailVerified === false// registered.emailVerificationSent === true// client.token is still null
// After the console verification link:const session = await client.auth.login({ email: 'jane@example.com', password: 'secret123',});// login() stores the project JWT on the clientconst authorId = session.user['_id'] as string;login sets client.token. session.user is the _users document with password removed. Use _id, not the email, as authorId.
Create, then list only that user’s rows
Section titled “Create, then list only that user’s rows”const posts = client.collection('posts');
const mine = await posts.create({ title: 'Hello Tefily', status: 'draft', authorId,});
const page = await posts.list({ page: 1, pageSize: 20 });// page.items, page.total, page.page, page.pageSizeRegister a second user, verify that email, and log in on a second client. Create a post with that user’s _id. page.items on the first client contains mine and not the other user’s row. There is no .where('authorId', …) call. The list filter is the rule.
Update and delete stay on the owner path. Do not send authorId on update:
await posts.update(mine['_id'] as string, { title: 'Renamed' });await posts.delete(mine['_id'] as string);A call with no token, or with the other user’s token, is denied. requireAuth returns Authentication required. userFieldCompare returns User field "_id" does not match record field "authorId".
What not to put in this client
Section titled “What not to put in this client”This browser or app holds the project JWT only. A service token (kfxs_…) and a PAT (kfx_…) bypass these rules. See Backend service token and Security.