@pgpm/jwt-claims 0.4.0 → 0.5.0

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.
package/Makefile CHANGED
@@ -1,5 +1,5 @@
1
1
  EXTENSION = launchql-jwt-claims
2
- DATA = sql/launchql-jwt-claims--0.4.6.sql
2
+ DATA = sql/launchql-jwt-claims--0.4.0.sql
3
3
 
4
4
  PG_CONFIG = pg_config
5
5
  PGXS := $(shell $(PG_CONFIG) --pgxs)
package/README.md CHANGED
@@ -2,4 +2,408 @@
2
2
 
3
3
  JWT claim handling and validation functions.
4
4
 
5
- Provides functions for processing JWT claims, validating tokens, and extracting user information from JWT payloads.
5
+ ## Overview
6
+
7
+ `@pgpm/jwt-claims` provides PostgreSQL functions for extracting and working with JWT (JSON Web Token) claims stored in PostgreSQL session variables. This package enables seamless integration between JWT-based authentication systems and PostgreSQL, allowing database functions to access user context, group memberships, IP addresses, and other JWT payload data.
8
+
9
+ ## Features
10
+
11
+ - **User Context Functions**: Extract user ID from JWT claims
12
+ - **Group Membership**: Access user's group IDs
13
+ - **Request Metadata**: Get IP address and user agent from requests
14
+ - **Database Context**: Access database ID from JWT claims
15
+ - **Type-Safe Extraction**: Proper error handling for invalid claim values
16
+ - **Session Variables**: Uses PostgreSQL's `current_setting()` for claim storage
17
+
18
+ ## Installation
19
+
20
+ If you have `pgpm` installed:
21
+
22
+ ```bash
23
+ pgpm install @pgpm/jwt-claims
24
+ pgpm deploy
25
+ ```
26
+
27
+ This is a quick way to get started. The sections below provide more detailed installation options.
28
+
29
+ ### Prerequisites
30
+
31
+ ```bash
32
+ # Install pgpm globally
33
+ npm install -g pgpm
34
+
35
+ # Start PostgreSQL
36
+ pgpm docker start
37
+
38
+ # Set environment variables
39
+ eval "$(pgpm env)"
40
+ ```
41
+
42
+ ### Deploy
43
+
44
+ #### Option 1: Deploy by installing with pgpm
45
+
46
+ ```bash
47
+ pgpm install @pgpm/jwt-claims
48
+ pgpm deploy
49
+ ```
50
+
51
+ #### Option 2: Deploy from Package Directory
52
+
53
+ ```bash
54
+ cd packages/security/jwt-claims
55
+ pgpm deploy --createdb
56
+ ```
57
+
58
+ #### Option 3: Deploy from Workspace Root
59
+
60
+ ```bash
61
+ # Install workspace dependencies
62
+ pgpm install
63
+
64
+ # Deploy with dependencies
65
+ pgpm deploy mydb1 --yes --createdb
66
+ ```
67
+
68
+ ## Core Functions
69
+
70
+ ### jwt_public.current_user_id()
71
+ Extracts the user ID from JWT claims.
72
+
73
+ **Returns:** `uuid` - The current user's ID, or NULL if not set
74
+
75
+ **Usage:**
76
+ ```sql
77
+ SELECT jwt_public.current_user_id();
78
+ ```
79
+
80
+ **JWT Claim:** `jwt.claims.user_id`
81
+
82
+ ### jwt_public.current_group_ids()
83
+ Extracts the user's group IDs from JWT claims.
84
+
85
+ **Returns:** `uuid[]` - Array of group IDs, or empty array if not set
86
+
87
+ **Usage:**
88
+ ```sql
89
+ SELECT jwt_public.current_group_ids();
90
+ ```
91
+
92
+ **JWT Claim:** `jwt.claims.group_ids`
93
+
94
+ ### jwt_public.current_ip_address()
95
+ Extracts the client's IP address from JWT claims.
96
+
97
+ **Returns:** `text` - The client's IP address, or NULL if not set
98
+
99
+ **Usage:**
100
+ ```sql
101
+ SELECT jwt_public.current_ip_address();
102
+ ```
103
+
104
+ **JWT Claim:** `jwt.claims.ip_address`
105
+
106
+ ### jwt_public.current_user_agent()
107
+ Extracts the client's user agent from JWT claims.
108
+
109
+ **Returns:** `text` - The client's user agent string, or NULL if not set
110
+
111
+ **Usage:**
112
+ ```sql
113
+ SELECT jwt_public.current_user_agent();
114
+ ```
115
+
116
+ **JWT Claim:** `jwt.claims.user_agent`
117
+
118
+ ### jwt_private.current_database_id()
119
+ Extracts the database ID from JWT claims (private function).
120
+
121
+ **Returns:** `uuid` - The database ID, or NULL if not set
122
+
123
+ **Usage:**
124
+ ```sql
125
+ SELECT jwt_private.current_database_id();
126
+ ```
127
+
128
+ **JWT Claim:** `jwt.claims.database_id`
129
+
130
+ ## Usage
131
+
132
+ ### Setting JWT Claims
133
+
134
+ JWT claims are set as PostgreSQL session variables, typically by your authentication middleware:
135
+
136
+ ```sql
137
+ -- Set user ID claim
138
+ SELECT set_config('jwt.claims.user_id', 'user-uuid-here', false);
139
+
140
+ -- Set group IDs claim
141
+ SELECT set_config('jwt.claims.group_ids', '{uuid1,uuid2,uuid3}', false);
142
+
143
+ -- Set IP address claim
144
+ SELECT set_config('jwt.claims.ip_address', '192.168.1.1', false);
145
+
146
+ -- Set user agent claim
147
+ SELECT set_config('jwt.claims.user_agent', 'Mozilla/5.0...', false);
148
+
149
+ -- Set database ID claim
150
+ SELECT set_config('jwt.claims.database_id', 'database-uuid-here', false);
151
+ ```
152
+
153
+ ### Using Claims in Row-Level Security
154
+
155
+ ```sql
156
+ -- Enable RLS on a table
157
+ ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
158
+
159
+ -- Users can only see their own posts
160
+ CREATE POLICY user_posts ON posts
161
+ FOR ALL
162
+ TO authenticated
163
+ USING (user_id = jwt_public.current_user_id());
164
+
165
+ -- Users can see posts from their groups
166
+ CREATE POLICY group_posts ON posts
167
+ FOR SELECT
168
+ TO authenticated
169
+ USING (group_id = ANY(jwt_public.current_group_ids()));
170
+ ```
171
+
172
+ ### Using Claims in Functions
173
+
174
+ ```sql
175
+ -- Function that uses current user ID
176
+ CREATE FUNCTION create_post(title text, content text)
177
+ RETURNS uuid AS $$
178
+ DECLARE
179
+ new_post_id uuid;
180
+ BEGIN
181
+ INSERT INTO posts (user_id, title, content)
182
+ VALUES (jwt_public.current_user_id(), title, content)
183
+ RETURNING id INTO new_post_id;
184
+
185
+ RETURN new_post_id;
186
+ END;
187
+ $$ LANGUAGE plpgsql;
188
+
189
+ -- Function that checks group membership
190
+ CREATE FUNCTION user_in_group(group_id uuid)
191
+ RETURNS boolean AS $$
192
+ BEGIN
193
+ RETURN group_id = ANY(jwt_public.current_group_ids());
194
+ END;
195
+ $$ LANGUAGE plpgsql;
196
+ ```
197
+
198
+ ### Audit Logging with JWT Claims
199
+
200
+ ```sql
201
+ -- Audit log table
202
+ CREATE TABLE audit_log (
203
+ id serial PRIMARY KEY,
204
+ user_id uuid,
205
+ ip_address text,
206
+ user_agent text,
207
+ action text,
208
+ timestamp timestamptz DEFAULT now()
209
+ );
210
+
211
+ -- Trigger function for audit logging
212
+ CREATE FUNCTION log_action()
213
+ RETURNS trigger AS $$
214
+ BEGIN
215
+ INSERT INTO audit_log (user_id, ip_address, user_agent, action)
216
+ VALUES (
217
+ jwt_public.current_user_id(),
218
+ jwt_public.current_ip_address(),
219
+ jwt_public.current_user_agent(),
220
+ TG_OP || ' on ' || TG_TABLE_NAME
221
+ );
222
+ RETURN NEW;
223
+ END;
224
+ $$ LANGUAGE plpgsql;
225
+
226
+ -- Add trigger to table
227
+ CREATE TRIGGER audit_posts
228
+ AFTER INSERT OR UPDATE OR DELETE ON posts
229
+ FOR EACH ROW
230
+ EXECUTE FUNCTION log_action();
231
+ ```
232
+
233
+ ### Multi-Tenancy with Database ID
234
+
235
+ ```sql
236
+ -- Filter data by database ID
237
+ CREATE FUNCTION get_tenant_data()
238
+ RETURNS SETOF my_table AS $$
239
+ BEGIN
240
+ RETURN QUERY
241
+ SELECT * FROM my_table
242
+ WHERE database_id = jwt_private.current_database_id();
243
+ END;
244
+ $$ LANGUAGE plpgsql;
245
+ ```
246
+
247
+ ## Integration with Other Packages
248
+
249
+ ### With @pgpm/stamps
250
+
251
+ The stamps package uses JWT claims for automatic user tracking:
252
+
253
+ ```sql
254
+ -- Stamps automatically uses jwt_public.current_user_id()
255
+ -- for created_by and updated_by columns
256
+ ```
257
+
258
+ ### With @pgpm/achievements
259
+
260
+ The achievements package uses JWT claims for user context:
261
+
262
+ ```sql
263
+ -- Check current user's achievements
264
+ SELECT * FROM status_public.steps_required('newbie');
265
+ -- Uses jwt_public.current_user_id() internally
266
+ ```
267
+
268
+ ### With @pgpm/default-roles
269
+
270
+ Combine JWT claims with role-based access:
271
+
272
+ ```sql
273
+ -- Set role based on JWT claim
274
+ CREATE FUNCTION set_user_role()
275
+ RETURNS void AS $$
276
+ DECLARE
277
+ user_role text;
278
+ BEGIN
279
+ user_role := current_setting('jwt.claims.role', true);
280
+
281
+ IF user_role = 'admin' THEN
282
+ SET LOCAL ROLE administrator;
283
+ ELSIF user_role = 'user' THEN
284
+ SET LOCAL ROLE authenticated;
285
+ ELSE
286
+ SET LOCAL ROLE anonymous;
287
+ END IF;
288
+ END;
289
+ $$ LANGUAGE plpgsql;
290
+ ```
291
+
292
+ ## Error Handling
293
+
294
+ All functions include error handling for invalid claim values:
295
+
296
+ ```sql
297
+ -- If jwt.claims.user_id is not a valid UUID
298
+ SELECT jwt_public.current_user_id();
299
+ -- Returns NULL and raises NOTICE: 'Invalid UUID value'
300
+
301
+ -- If jwt.claims.group_ids is not a valid UUID array
302
+ SELECT jwt_public.current_group_ids();
303
+ -- Returns empty array [] and raises NOTICE: 'Invalid UUID value'
304
+ ```
305
+
306
+ ## Security Considerations
307
+
308
+ 1. **Trust the Source**: Only set JWT claims from trusted authentication middleware
309
+ 2. **Validate Claims**: Always validate JWT signatures before setting claims
310
+ 3. **Session Scope**: Claims are session-scoped and don't persist across connections
311
+ 4. **No Direct Access**: Users cannot directly modify session variables in most configurations
312
+ 5. **Use HTTPS**: Always transmit JWTs over HTTPS to prevent interception
313
+
314
+ ## Dependencies
315
+
316
+ - `@pgpm/types`: Core PostgreSQL types
317
+ - `@pgpm/verify`: Verification utilities
318
+
319
+ ## Testing
320
+
321
+ ```bash
322
+ pnpm test
323
+ ```
324
+
325
+ ## Development
326
+
327
+ See the [Development](#development) section below for information on working with this package.
328
+
329
+ ---
330
+
331
+ ## Development
332
+
333
+ ### **Before You Begin**
334
+
335
+ ```bash
336
+ # 1. Install pgpm
337
+ npm install -g pgpm
338
+
339
+ # 2. Start Postgres (Docker or local)
340
+ pgpm docker start
341
+
342
+ # 3. Load PG* environment variables (PGHOST, PGUSER, ...)
343
+ eval "$(pgpm env)"
344
+ ```
345
+
346
+ ---
347
+
348
+ ### **Starting a New Project**
349
+
350
+ ```bash
351
+ # 1. Create a workspace
352
+ pgpm init --workspace
353
+ cd my-app
354
+
355
+ # 2. Create your first module
356
+ pgpm init
357
+
358
+ # 3. Add a migration
359
+ pgpm add some_change
360
+
361
+ # 4. Deploy (auto-creates database)
362
+ pgpm deploy --createdb
363
+ ```
364
+
365
+ ---
366
+
367
+ ### **Working With an Existing Project**
368
+
369
+ ```bash
370
+ # 1. Clone and enter the project
371
+ git clone <repo> && cd <project>
372
+
373
+ # 2. Install dependencies
374
+ pnpm install
375
+
376
+ # 3. Deploy locally
377
+ pgpm deploy --createdb
378
+ ```
379
+
380
+ ---
381
+
382
+ ### **Testing a Module Inside a Workspace**
383
+
384
+ ```bash
385
+ # 1. Install workspace deps
386
+ pnpm install
387
+
388
+ # 2. Enter the module directory
389
+ cd packages/<some-module>
390
+
391
+ # 3. Run tests in watch mode
392
+ pnpm test:watch
393
+ ```
394
+
395
+ ## Related Tooling
396
+
397
+ * [pgpm](https://github.com/launchql/launchql/tree/main/packages/pgpm): **🖥️ PostgreSQL Package Manager** for modular Postgres development. Works with database workspaces, scaffolding, migrations, seeding, and installing database packages.
398
+ * [pgsql-test](https://github.com/launchql/launchql/tree/main/packages/pgsql-test): **📊 Isolated testing environments** with per-test transaction rollbacks—ideal for integration tests, complex migrations, and RLS simulation.
399
+ * [supabase-test](https://github.com/launchql/launchql/tree/main/packages/supabase-test): **🧪 Supabase-native test harness** preconfigured for the local Supabase stack—per-test rollbacks, JWT/role context helpers, and CI/GitHub Actions ready.
400
+ * [graphile-test](https://github.com/launchql/launchql/tree/main/packages/graphile-test): **🔐 Authentication mocking** for Graphile-focused test helpers and emulating row-level security contexts.
401
+ * [pgsql-parser](https://github.com/launchql/pgsql-parser): **🔄 SQL conversion engine** that interprets and converts PostgreSQL syntax.
402
+ * [libpg-query-node](https://github.com/launchql/libpg-query-node): **🌉 Node.js bindings** for `libpg_query`, converting SQL into parse trees.
403
+ * [pg-proto-parser](https://github.com/launchql/pg-proto-parser): **📦 Protobuf parser** for parsing PostgreSQL Protocol Buffers definitions to generate TypeScript interfaces, utility functions, and JSON mappings for enums.
404
+
405
+ ## Disclaimer
406
+
407
+ AS DESCRIBED IN THE LICENSES, THE SOFTWARE IS PROVIDED "AS IS", AT YOUR OWN RISK, AND WITHOUT WARRANTIES OF ANY KIND.
408
+
409
+ No developer or entity involved in creating this software will be liable for any claims or damages whatsoever associated with your use, inability to use, or your interaction with other users of the code, including any direct, indirect, incidental, special, exemplary, punitive or consequential damages, or loss of profits, cryptocurrencies, tokens, or anything else of value.
@@ -1,6 +1,6 @@
1
1
  # launchql-jwt-claims extension
2
2
  comment = 'launchql-jwt-claims extension'
3
- default_version = '0.4.6'
3
+ default_version = '0.4.0'
4
4
  module_pathname = '$libdir/launchql-jwt-claims'
5
5
  requires = 'plpgsql,uuid-ossp,launchql-verify'
6
6
  relocatable = false
package/package.json CHANGED
@@ -1,21 +1,21 @@
1
1
  {
2
2
  "name": "@pgpm/jwt-claims",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "JWT claim handling and validation functions",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
8
8
  "scripts": {
9
- "bundle": "lql package",
9
+ "bundle": "pgpm package",
10
10
  "test": "jest",
11
11
  "test:watch": "jest --watch"
12
12
  },
13
13
  "devDependencies": {
14
- "@launchql/cli": "^4.9.0"
14
+ "pgpm": "^0.2.0"
15
15
  },
16
16
  "dependencies": {
17
- "@pgpm/types": "0.4.0",
18
- "@pgpm/verify": "0.4.0"
17
+ "@pgpm/types": "0.5.0",
18
+ "@pgpm/verify": "0.5.0"
19
19
  },
20
20
  "repository": {
21
21
  "type": "git",
@@ -25,5 +25,5 @@
25
25
  "bugs": {
26
26
  "url": "https://github.com/launchql/extensions/issues"
27
27
  },
28
- "gitHead": "cc9f52a335caa6e21ee7751b04b77c84ce6cb809"
28
+ "gitHead": "d8eedbb24ad22a106634bc3b919bfb8d41976c16"
29
29
  }
package/sqitch.plan DELETED
@@ -1,13 +0,0 @@
1
- %syntax-version=1.0.0
2
- %project=launchql-jwt-claims
3
- %uri=launchql-jwt-claims
4
-
5
- schemas/jwt_public/schema 2020-12-17T06:47:29Z Dan Lynch <dlynch@Dans-MBP-3> # add schemas/jwt_public/schema
6
- schemas/jwt_private/schema 2020-12-17T06:47:34Z Dan Lynch <dlynch@Dans-MBP-3> # add schemas/jwt_private/schema
7
- schemas/jwt_public/procedures/current_user_id [schemas/jwt_public/schema] 2020-12-17T06:48:56Z Dan Lynch <dlynch@Dans-MBP-3> # add schemas/jwt_public/procedures/current_user_id
8
- schemas/jwt_public/procedures/current_ip_address [schemas/jwt_public/schema] 2020-12-17T23:19:17Z Dan Lynch <dlynch@Dans-MBP-3> # add schemas/jwt_public/procedures/current_ip_address
9
- schemas/jwt_public/procedures/current_user_agent [schemas/jwt_public/schema] 2020-12-17T23:20:04Z Dan Lynch <dlynch@Dans-MBP-3> # add schemas/jwt_public/procedures/current_user_agent
10
- schemas/jwt_private/procedures/current_database_id [schemas/jwt_private/schema] 2020-12-17T23:22:28Z Dan Lynch <dlynch@Dans-MBP-3> # add schemas/jwt_private/procedures/current_database_id
11
- schemas/jwt_public/procedures/current_group_ids [schemas/jwt_public/schema] 2020-12-17T23:30:50Z Dan Lynch <dlynch@Dans-MBP-3> # add schemas/jwt_public/procedures/current_group_ids
12
-
13
- [This file mirrors launchql.plan for harness compatibility during tests and local deploys. Do not hand-edit one without the other.]
File without changes