@harperfast/template-react-ts-studio 1.12.1 → 1.12.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (20) hide show
  1. package/.agents/skills/harper-best-practices/AGENTS.md +672 -400
  2. package/.agents/skills/harper-best-practices/rules/automatic-apis.md +131 -55
  3. package/.agents/skills/harper-best-practices/rules/checking-authentication.md +155 -61
  4. package/.agents/skills/harper-best-practices/rules/deploying-to-harper-fabric.md +78 -48
  5. package/.agents/skills/harper-best-practices/rules/programmatic-table-requests.md +111 -52
  6. package/.agents/skills/harper-best-practices/rules/querying-rest-apis.md +38 -70
  7. package/.agents/skills/harper-best-practices/rules/schema-design-tooling.md +81 -69
  8. package/.agents/skills/harper-best-practices/rules/v5-upgrade.md +108 -60
  9. package/.agents/skills/harper-best-practices/rules.manifest.yaml +212 -0
  10. package/agent/skills/harper-best-practices/AGENTS.md +672 -400
  11. package/agent/skills/harper-best-practices/rules/automatic-apis.md +131 -55
  12. package/agent/skills/harper-best-practices/rules/checking-authentication.md +155 -61
  13. package/agent/skills/harper-best-practices/rules/deploying-to-harper-fabric.md +78 -48
  14. package/agent/skills/harper-best-practices/rules/programmatic-table-requests.md +111 -52
  15. package/agent/skills/harper-best-practices/rules/querying-rest-apis.md +38 -70
  16. package/agent/skills/harper-best-practices/rules/schema-design-tooling.md +81 -69
  17. package/agent/skills/harper-best-practices/rules/v5-upgrade.md +108 -60
  18. package/agent/skills/harper-best-practices/rules.manifest.yaml +212 -0
  19. package/package.json +1 -1
  20. package/skills-lock.json +1 -1
@@ -4,23 +4,29 @@ description: How to use Harper's automatically generated REST and WebSocket APIs
4
4
  metadata:
5
5
  mode: generate
6
6
  sources:
7
- - reference/v5/rest/overview.md
7
+ - reference/v5/rest/overview.md#How the REST Interface Works
8
+ - reference/v5/rest/overview.md#Configuration
9
+ - reference/v5/rest/overview.md#Tables and Their Automatic Endpoints
10
+ - reference/v5/rest/overview.md#URL Structure
11
+ - reference/v5/rest/overview.md#HTTP Methods
12
+ - reference/v5/rest/overview.md#Content Types
13
+ - reference/v5/rest/overview.md#OpenAPI
8
14
  - reference/v5/rest/websockets.md
9
- sourceCommit: 3749d0c54be457a2a65d9a63c738a5dc88989ecd
10
- inputHash: 6c7013b1307d9f7b
15
+ sourceCommit: 677ad213d67822e109c83619e181ca23a59823db
16
+ inputHash: a9356e92dd3dc106
11
17
  ---
12
18
 
13
19
  # Automatic APIs
14
20
 
15
- Instructions for the agent to follow when enabling and using Harper's automatically generated REST and WebSocket APIs.
21
+ Instructions for the agent to follow when using Harper's automatically generated REST and WebSocket APIs for exported tables and resources.
16
22
 
17
23
  ## When to Use
18
24
 
19
- Apply this rule when adding REST or WebSocket API access to Harper tables or custom resources. Use it when configuring `config.yaml` to expose endpoints, mapping HTTP methods to resource operations, or implementing real-time WebSocket connections on a resource class.
25
+ Apply this rule when enabling HTTP REST endpoints or WebSocket subscriptions for Harper tables without writing custom handler code. Use it whenever a schema type needs to be served over HTTP, when configuring real-time subscriptions, or when setting up conditional caching behavior for REST responses.
20
26
 
21
27
  ## How It Works
22
28
 
23
- 1. **Enable the REST plugin**: Add `rest: true` to your application's `config.yaml`. This activates the HTTP REST interface on the application server port (default `9926`) and enables WebSocket support automatically.
29
+ 1. **Enable REST in `config.yaml`**: Add `rest: true` to the application configuration file. This registers REST endpoints and, by default, WebSocket subscriptions for all exported resources.
24
30
 
25
31
  ```yaml
26
32
  rest: true
@@ -34,26 +40,47 @@ Apply this rule when adding REST or WebSocket API access to Harper tables or cus
34
40
  webSocket: false # disables automatic WebSocket support (enabled by default)
35
41
  ```
36
42
 
37
- 2. **Export your resource in the schema**: Tables are not exposed by default. Use the `@export` directive in your schema definition to expose a table as a REST endpoint. The exported name defines the base URL path.
43
+ 2. **Export the table in the schema**: Add `@export` to the type definition. Without `@export`, Harper registers no REST route and callers receive `404`. Without `rest: true`, even an exported table does not respond to HTTP requests. Both are required.
38
44
 
39
- 3. **Use the correct URL structure**: Target resources using these path conventions:
45
+ ```graphql
46
+ type Product @table @export {
47
+ id: Long @primaryKey
48
+ name: String
49
+ price: Float
50
+ }
51
+ ```
52
+
53
+ Reference the schema file in `config.yaml`:
54
+
55
+ ```yaml
56
+ graphqlSchema:
57
+ files: schema.graphql
58
+ rest: true
59
+ ```
40
60
 
41
- | Path | Description |
42
- | -------------------------------------------- | ----------------------------------------------------------- |
43
- | `/my-resource` | Returns resource metadata |
44
- | `/my-resource/` | Collection — all records; append query parameters to search |
45
- | `/my-resource/record-id` | Specific record by primary key |
46
- | `/my-resource/record-id/` | Collection of records with the given id prefix |
47
- | `/my-resource/record-id/with/multiple/parts` | Record id with multiple path segments |
61
+ 3. **Use the automatically registered endpoints**: Harper serves the following endpoints on the application HTTP server port (default `9926`) with no route definitions or handler code required.
48
62
 
49
- 4. **Map operations to HTTP methods**: Each HTTP method maps to a resource method:
50
- - **GET** — Retrieve a record or search. Calls `get()`. Responses include an `ETag` header; send `If-None-Match` on subsequent requests to receive `304 Not Modified` when unchanged.
51
- - **PUT** — Create or replace a record (upsert). Calls `put(record)`. The stored record exactly matches the submitted body; omitted properties are removed.
52
- - **POST** — Create a record without specifying a primary key. Calls `post(data)`. The assigned key is returned in the `Location` response header.
53
- - **PATCH** — Partially update a record, merging only provided top-level properties. Calls the resource's patch handler. Merge is **shallow** — nested objects are replaced entirely, not deep-merged.
54
- - **DELETE** — Delete a record by id or all records matching a query.
63
+ | Endpoint | Description |
64
+ | ---------------------------- | --------------------------------------------------------------------------- |
65
+ | `GET /Product` | Returns resource description (table name, database, attributes) |
66
+ | `GET /Product/` | Returns the record collection; append query parameters to filter |
67
+ | `GET /Product/{id}` | Returns a single record by primary key; `404` if not found |
68
+ | `GET /Product/{id}.property` | Returns a single declared property of one record |
69
+ | `POST /Product/` | Creates a record; responds `201`; primary key returned in `Location` header |
70
+ | `PUT /Product/{id}` | Creates or replaces the record at `{id}` (upsert) |
71
+ | `PATCH /Product/{id}` | Merges body into existing record (shallow, top-level only) |
72
+ | `DELETE /Product/{id}` | Deletes the record at `{id}` |
73
+ | `DELETE /Product/?query` | Deletes every record matching the query |
55
74
 
56
- 5. **Connect via WebSocket**: A WebSocket connection to a resource URL subscribes to that resource and streams change events. See [real-time-apps.md](real-time-apps.md) for full real-time patterns.
75
+ 4. **Handle `POST` primary key and `Location`**: On a successful `POST`, the new record's primary key is returned in the `Location` response header — the value the body supplied if it carried the primary-key property, otherwise a Harper-assigned key. The header carries the bare key value, not a URL.
76
+
77
+ 5. **Understand `PUT` write behavior**: `PUT` replaces the stored record exactly. Three exceptions always apply: a `@createdTime` attribute keeps the original value, an `@updatedTime` attribute is re-stamped with the time of the write, and the primary key is forced to match the `{id}` in the URL.
78
+
79
+ 6. **Use conditional requests for caching**: GET responses include an `ETag` header encoding the record's version/last-modification time. Send `If-None-Match` on subsequent requests with the cached `ETag` value. If the record has not changed, Harper returns `304 Not Modified` with no body.
80
+
81
+ 7. **Select content type with `Accept`**: Use the `Accept` header to request a specific response format. The suffixes `.json`, `.cbor`, `.msgpack`, and `.csv` are reserved as content-type selectors on property paths and take precedence over property names. See [querying-rest-apis.md](querying-rest-apis.md) for query syntax details.
82
+
83
+ 8. **Connect via WebSocket**: WebSocket support is enabled automatically when `rest` is enabled. Connecting to a resource URL subscribes to changes for that resource. See [real-time-apps.md](real-time-apps.md) for real-time patterns.
57
84
 
58
85
  ```javascript
59
86
  let ws = new WebSocket('wss://server/my-resource/341');
@@ -62,29 +89,84 @@ Apply this rule when adding REST or WebSocket API access to Harper tables or cus
62
89
  };
63
90
  ```
64
91
 
65
- 6. **Implement a custom `connect()` handler** on a resource class to control WebSocket behavior. The method receives `incomingMessages` and must return an async iterable producing messages to send to the client.
66
-
67
- 7. **Retrieve the OpenAPI spec**: Harper auto-generates an OpenAPI specification for all exported resources, available at:
92
+ 9. **Implement a custom `connect()` handler** when default subscription behavior is insufficient. The method must return an async iterable that produces messages to send to the client.
68
93
 
69
- ```
70
- GET /openapi
94
+ ```javascript
95
+ export class Echo extends Resource {
96
+ async *connect(incomingMessages) {
97
+ for await (let message of incomingMessages) {
98
+ yield message; // echo each message back
99
+ }
100
+ }
101
+ }
71
102
  ```
72
103
 
73
104
  ## Examples
74
105
 
75
- **Simple echo WebSocket server**:
106
+ ### Full schema and config setup
76
107
 
77
- ```javascript
78
- export class Echo extends Resource {
79
- async *connect(incomingMessages) {
80
- for await (let message of incomingMessages) {
81
- yield message; // echo each message back
82
- }
83
- }
108
+ ```graphql
109
+ # schema.graphql
110
+ type Product @table @export {
111
+ id: Long @primaryKey
112
+ name: String
113
+ price: Float
84
114
  }
85
115
  ```
86
116
 
87
- **Custom `connect()` using the default iterable with `send()` and `close` event**:
117
+ ```yaml
118
+ # config.yaml
119
+ graphqlSchema:
120
+ files: schema.graphql
121
+ rest: true
122
+ ```
123
+
124
+ ### Conditional GET with ETag caching
125
+
126
+ ```
127
+ GET /Product/123
128
+ # Response includes:
129
+ # ETag: "abc123"
130
+
131
+ GET /Product/123
132
+ If-None-Match: "abc123"
133
+ # Response: 304 Not Modified (no body transferred)
134
+ ```
135
+
136
+ ### POST and read the Location header
137
+
138
+ ```
139
+ POST /Product/
140
+ Content-Type: application/json
141
+
142
+ { "name": "Widget", "price": 9.99 }
143
+
144
+ # Response:
145
+ # 201 Created
146
+ # Location: 7f3a9c
147
+ ```
148
+
149
+ ### PATCH (shallow merge only)
150
+
151
+ ```
152
+ PATCH /Product/123
153
+ Content-Type: application/json
154
+
155
+ { "price": 12.99 }
156
+ ```
157
+
158
+ Only `price` is updated; other top-level properties are preserved. Nested objects in the body replace the stored sub-object wholesale — deep merge does not occur.
159
+
160
+ ### Request MessagePack response
161
+
162
+ ```
163
+ GET /Product/123
164
+ Accept: application/msgpack
165
+ ```
166
+
167
+ Alternatively, use the `.msgpack` suffix on the URL path where supported.
168
+
169
+ ### WebSocket with custom outgoing messages
88
170
 
89
171
  ```javascript
90
172
  export class Example extends Resource {
@@ -96,7 +178,7 @@ export class Example extends Resource {
96
178
  }, 1000);
97
179
 
98
180
  incomingMessages.on('data', (message) => {
99
- outgoingMessages.send(message); // echo incoming messages
181
+ outgoingMessages.send(message);
100
182
  });
101
183
 
102
184
  outgoingMessages.on('close', () => {
@@ -108,26 +190,20 @@ export class Example extends Resource {
108
190
  }
109
191
  ```
110
192
 
111
- **Common REST operations**:
112
-
113
- ```
114
- GET /MyTable/123
115
- GET /MyTable/?name=Harper
116
- PUT /MyTable/123
117
- PATCH /MyTable/123
118
- DELETE /MyTable/?status=archived
119
- ```
193
+ ### Disable WebSocket while keeping REST
120
194
 
121
- ```json
122
- { "name": "some data" }
195
+ ```yaml
196
+ rest:
197
+ webSocket: false
123
198
  ```
124
199
 
125
200
  ## Notes
126
201
 
127
- - `rest: true` is the minimal config to enable both REST and WebSocket support. Set `webSocket: false` under the `rest` key to disable WebSocket only.
128
- - The `@export` directive in the schema is required for any table to appear as a REST endpoint — tables are not exported by default.
129
- - PATCH merges are shallow (top-level only). Nested objects in the request body replace the entire existing sub-object. Dot-path keys (e.g., `"settings.theme"`) are stored as literal keys, not interpreted as paths.
130
- - For MQTT over WebSocket, set the sub-protocol header `Sec-WebSocket-Protocol: mqtt`.
131
- - In distributed environments, non-retained messages are delivered in arrival order; retained messages (PUT/updated records) keep only the latest timestamp as the winning record.
132
- - For full query syntax on GET and DELETE, see [querying-rest-apis.md](querying-rest-apis.md).
133
- - For building real-time features with WebSocket subscriptions, see [real-time-apps.md](real-time-apps.md).
202
+ - The trailing slash is significant: `/Product` addresses the resource itself; `/Product/` addresses its record collection. `POST /Product` (no trailing slash) returns `404`.
203
+ - `HEAD` is served as `GET` with the body omitted. `QUERY` is accepted on the collection path and reads its search from the request body.
204
+ - A `POST` to an existing primary key fails with `409` — it does not overwrite.
205
+ - A component directory with **no configuration file** gets REST enabled by Harper's built-in default. As soon as a `config.yaml` exists it is used verbatim — add `rest: true` explicitly or REST is off.
206
+ - Do not apply `@export` to a schema type and also export a same-named JavaScript subclass of that table — this produces conflicting endpoints.
207
+ - Server-Sent Events subscriptions are served on the same paths, negotiated via `Accept: text/event-stream`. They are not affected by the `webSocket` option.
208
+ - Every non-hidden exported resource is included in the generated OpenAPI document at `GET /openapi`. Mark a type `@hidden` or set `static hidden = true` on a programmatic Resource to omit it.
209
+ - MQTT over WebSockets requires the sub-protocol header `Sec-WebSocket-Protocol: mqtt`.
@@ -8,22 +8,31 @@ metadata:
8
8
  reference/v5/resources/resource-api.md#`getCurrentUser(): User |
9
9
  undefined`
10
10
  - reference/v5/resources/resource-api.md#Session and Login from a Resource
11
- - reference/v5/security/jwt-authentication.md
12
- sourceCommit: b7fbddadd42eb4487190b650a9abc4bcfeef5819
13
- inputHash: fdd9ec3b11011490
11
+ - reference/v5/security/jwt-authentication.md#Create Authentication Tokens
12
+ - reference/v5/security/jwt-authentication.md#Using the Operation Token
13
+ - reference/v5/security/jwt-authentication.md#Refreshing the Operation Token
14
+ - reference/v5/security/jwt-authentication.md#Scoped Tokens (Inline Role)
15
+ - >-
16
+ reference/v5/security/jwt-authentication.md#Issuing Tokens from a Custom
17
+ Resource
18
+ - reference/v5/security/jwt-authentication.md#Token Expiry Configuration
19
+ - reference/v5/security/jwt-authentication.md#When to Use JWT Auth
20
+ - reference/v5/security/jwt-authentication.md#Security Notes
21
+ sourceCommit: 677ad213d67822e109c83619e181ca23a59823db
22
+ inputHash: 084363f039abfe73
14
23
  ---
15
24
 
16
25
  # Checking Authentication
17
26
 
18
- Instructions for the agent to follow when handling user authentication and session management inside Harper Resources.
27
+ Instructions for the agent to handle user authentication, sessions, and JWT token issuance in Harper Resources.
19
28
 
20
29
  ## When to Use
21
30
 
22
- Apply this rule when implementing authentication checks, login/logout flows, or token issuance inside a custom Resource. Use it any time a Resource needs to identify the current user, establish a session, or issue JWTs to clients. See [custom-resources.md](custom-resources.md) for the general Resource authoring pattern.
31
+ Apply this rule when implementing login/logout flows, protecting Resource endpoints by checking the current user, issuing or refreshing JWT tokens, or configuring token expiry in Harper. Use it whenever a custom Resource needs to authenticate callers or mint credentials for downstream consumers. See [custom-resources.md](custom-resources.md) for the broader Resource authoring context.
23
32
 
24
33
  ## How It Works
25
34
 
26
- 1. **Check the current user** with `getCurrentUser()`. Call it inside any Resource method to retrieve the authenticated user or `undefined` if no user is authenticated. Guard protected endpoints by returning a `401` when the result is `undefined`.
35
+ 1. **Check the current authenticated user**: Call `getCurrentUser()` inside any Resource method. It returns the user object (with `username`, `role`, and `role.permission`) or `undefined` if unauthenticated. Guard endpoints by returning a 401 when no user is present.
27
36
 
28
37
  ```javascript
29
38
  async get(target) {
@@ -33,20 +42,9 @@ Apply this rule when implementing authentication checks, login/logout flows, or
33
42
  }
34
43
  ```
35
44
 
36
- The returned object exposes `username`, `role`, and `role.permission` flags.
37
-
38
- 2. **Enable sessions** before using session-based login. Set `authentication.enableSessions: true` in `harperdb-config.yaml`:
39
-
40
- ```yaml
41
- authentication:
42
- enableSessions: true
43
- ```
44
-
45
- 3. **Access login and session helpers** via `getContext()`. The context object exposes `context.login` and `context.session` for sign-in/out flows.
46
- - Call `context.login(username, password)` to verify credentials and establish a session cookie on success.
47
- - To end a session, delete it via `context.session.delete(context.session.id)`.
45
+ 2. **Enable sessions before using login/logout**: Set `authentication.enableSessions: true` in `harperdb-config.yaml`. Without this, `context.login` and `context.session` are unavailable.
48
46
 
49
- 4. **Implement sign-in and sign-out Resources** using the context helpers:
47
+ 3. **Implement login via `getContext()`**: Call `this.getContext()` to obtain the request context, then call `context.login(username, password)` to verify credentials and establish a session cookie.
50
48
 
51
49
  ```javascript
52
50
  export class SignIn extends Resource {
@@ -60,7 +58,11 @@ Apply this rule when implementing authentication checks, login/logout flows, or
60
58
  return new Response('Logged in', { status: 200 });
61
59
  }
62
60
  }
61
+ ```
63
62
 
63
+ 4. **Implement logout**: Delete the session via `context.session.delete(context.session.id)`.
64
+
65
+ ```javascript
64
66
  export class SignOut extends Resource {
65
67
  async post() {
66
68
  const context = this.getContext();
@@ -71,7 +73,96 @@ Apply this rule when implementing authentication checks, login/logout flows, or
71
73
  }
72
74
  ```
73
75
 
74
- 5. **Issue JWTs for non-browser clients** (CLI tools, mobile apps, service-to-service). Cookie-based sessions are intended for browser clients. For other clients, mint tokens programmatically using `server.operation()`:
76
+ Cookie-based sessions are intended for browser clients. For non-browser clients, use JWT issuance (steps below).
77
+
78
+ 5. **Create authentication tokens**: Call `create_authentication_tokens` with credentials. No `Authorization` header is required for this operation.
79
+
80
+ ```json
81
+ {
82
+ "operation": "create_authentication_tokens",
83
+ "username": "username",
84
+ "password": "password"
85
+ }
86
+ ```
87
+
88
+ Response:
89
+
90
+ ```json
91
+ {
92
+ "operation_token": "<jwt-operation-token>",
93
+ "refresh_token": "<jwt-refresh-token>"
94
+ }
95
+ ```
96
+
97
+ 6. **Use the operation token**: Pass it as a `Bearer` token in the `Authorization` header on subsequent requests.
98
+
99
+ ```bash
100
+ curl --location --request POST 'http://localhost:9925' \
101
+ --header 'Content-Type: application/json' \
102
+ --header 'Authorization: Bearer <operation_token>' \
103
+ --data-raw '{
104
+ "operation": "search_by_hash",
105
+ "schema": "dev",
106
+ "table": "dog",
107
+ "hash_values": [1],
108
+ "get_attributes": ["*"]
109
+ }'
110
+ ```
111
+
112
+ 7. **Refresh an expired operation token**: When the `operation_token` expires, use `refresh_operation_token` and pass the `refresh_token` as `Bearer <refresh_token>`.
113
+
114
+ ```bash
115
+ curl --location --request POST 'http://localhost:9925' \
116
+ --header 'Content-Type: application/json' \
117
+ --header 'Authorization: Bearer <refresh_token>' \
118
+ --data-raw '{
119
+ "operation": "refresh_operation_token"
120
+ }'
121
+ ```
122
+
123
+ Response:
124
+
125
+ ```json
126
+ {
127
+ "operation_token": "<new-jwt-operation-token>"
128
+ }
129
+ ```
130
+
131
+ When both tokens have expired, call `create_authentication_tokens` again with username and password.
132
+
133
+ 8. **Mint scoped tokens for limited access**: A super user can embed an inline role in `create_authentication_tokens` using the same `permission` structure as `add_role`. Include `expires_in` to control lifetime. Do not include a `password` field. The `username` is attribution only and must not match an existing user.
134
+
135
+ ```json
136
+ {
137
+ "operation": "create_authentication_tokens",
138
+ "username": "reporting-service",
139
+ "role": {
140
+ "permission": {
141
+ "operations": ["read_only"],
142
+ "dev": {
143
+ "tables": {
144
+ "dog": {
145
+ "read": true,
146
+ "insert": false,
147
+ "update": false,
148
+ "delete": false,
149
+ "attribute_permissions": []
150
+ }
151
+ }
152
+ }
153
+ }
154
+ },
155
+ "expires_in": "7d"
156
+ }
157
+ ```
158
+
159
+ Key constraints for scoped tokens:
160
+ - No refresh token is issued; no user record is created.
161
+ - Scoped tokens cannot be revoked before expiry — choose short `expires_in` values.
162
+ - `super_user` and `cluster_user` are always forced to `false` in the embedded role.
163
+ - In mixed-version clusters, only nodes with scoped-token support accept these tokens; older nodes return 401.
164
+
165
+ 9. **Issue tokens from a custom Resource using `server.operation`**: Import `server` from `harper` and call `server.operation()` to mint tokens programmatically. Pass `true` as the **third argument** to run the operation as the current authenticated user; omit it when supplying credentials directly.
75
166
 
76
167
  ```javascript
77
168
  import { Resource, server } from 'harper';
@@ -115,31 +206,50 @@ Apply this rule when implementing authentication checks, login/logout flows, or
115
206
  }
116
207
  ```
117
208
 
118
- Pass `true` as the third argument to `server.operation()` when the operation should run as the current authenticated user. Omit it or pass `false` when the operation supplies its own credentials.
119
-
120
- 6. **Configure JWT token expiry** in `harperdb-config.yaml` under the `authentication` section:
121
-
122
- ```yaml
123
- authentication:
124
- operationTokenTimeout: 1d
125
- refreshTokenTimeout: 30d
126
- ```
209
+ 10. **Configure token expiry**: Set timeouts in `harper-config.yaml` under the `authentication` section. Values follow the `jsonwebtoken` duration string format (e.g., `1d`, `12h`, `60m`).
127
210
 
128
- Duration strings follow the `jsonwebtoken` package format (e.g., `1d`, `12h`, `60m`).
211
+ ```yaml
212
+ authentication:
213
+ operationTokenTimeout: 1d # Default: 1 day
214
+ refreshTokenTimeout: 30d # Default: 30 days
215
+ ```
129
216
 
130
217
  ## Examples
131
218
 
132
- **Protecting a resource endpoint and returning user info:**
133
-
134
- ```javascript
135
- async get(target) {
136
- const user = this.getCurrentUser();
137
- if (!user) return new Response(null, { status: 401 });
138
- return { username: user.username, role: user.role };
139
- }
219
+ ### Full JWT flow via cURL
220
+
221
+ ```bash
222
+ # Step 1: Create tokens
223
+ curl --location --request POST 'http://localhost:9925' \
224
+ --header 'Content-Type: application/json' \
225
+ --data-raw '{
226
+ "operation": "create_authentication_tokens",
227
+ "username": "username",
228
+ "password": "password"
229
+ }'
230
+
231
+ # Step 2: Use operation token
232
+ curl --location --request POST 'http://localhost:9925' \
233
+ --header 'Content-Type: application/json' \
234
+ --header 'Authorization: Bearer <operation_token>' \
235
+ --data-raw '{
236
+ "operation": "search_by_hash",
237
+ "schema": "dev",
238
+ "table": "dog",
239
+ "hash_values": [1],
240
+ "get_attributes": ["*"]
241
+ }'
242
+
243
+ # Step 3: Refresh when operation token expires
244
+ curl --location --request POST 'http://localhost:9925' \
245
+ --header 'Content-Type: application/json' \
246
+ --header 'Authorization: Bearer <refresh_token>' \
247
+ --data-raw '{
248
+ "operation": "refresh_operation_token"
249
+ }'
140
250
  ```
141
251
 
142
- **Full session-based sign-in/sign-out flow:**
252
+ ### Session-based login/logout Resource
143
253
 
144
254
  ```javascript
145
255
  export class SignIn extends Resource {
@@ -164,27 +274,11 @@ export class SignOut extends Resource {
164
274
  }
165
275
  ```
166
276
 
167
- **JWT token refresh endpoint:**
168
-
169
- ```javascript
170
- export class RefreshJWT extends Resource {
171
- static async post(_target, data) {
172
- const { refresh_token } = await data;
173
- if (!refresh_token) {
174
- return new Response('refresh_token required', { status: 400 });
175
- }
176
- const { operation_token } = await server.operation({
177
- operation: 'refresh_operation_token',
178
- refresh_token,
179
- });
180
- return { operation_token };
181
- }
182
- }
183
- ```
184
-
185
277
  ## Notes
186
278
 
187
- - `getCurrentUser()` and `getContext()` are instance methods; call them with `this` inside non-static Resource methods.
188
- - `enableSessions` must be `true` in config before `context.login` or `context.session` will function.
189
- - Cookie-based sessions target browser clients. Use JWT issuance via `server.operation()` for all other client types.
190
- - When both `operation_token` and `refresh_token` have expired, the client must call `create_authentication_tokens` again with credentials.
279
+ - JWT authentication is **preferred over Basic Auth** when you want to avoid sending credentials on every request, when the client can store tokens, or when making multiple sequential requests. For simple or server-to-server scenarios, use Basic Authentication.
280
+ - Always use **HTTPS** in production to protect tokens in transit.
281
+ - Treat tokens like passwords. If a token is compromised, it remains valid until expiry — use shorter `operationTokenTimeout` values in high-security environments.
282
+ - `enableSessions` must be `true` in config before `context.login` or `context.session` will work.
283
+ - The `third argument` (`true`) to `server.operation` controls whether the operation runs as the current authenticated user. Omit it when the operation body supplies its own credentials.
284
+ - Scoped tokens have a 12KB limit when encoded into an `Authorization` header.