@drmhse/authos-node 0.1.4 → 0.1.6

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/README.md CHANGED
@@ -192,6 +192,33 @@ app.get('/org/:slug/data',
192
192
  );
193
193
  ```
194
194
 
195
+ ### requireService(slug, options?)
196
+
197
+ Requires the user's JWT to have a matching service claim. Use this for service-scoped endpoints.
198
+
199
+ ```ts
200
+ app.get('/services/:slug/data',
201
+ requireAuth(),
202
+ requireService((req) => req.params.slug),
203
+ (req, res) => { ... }
204
+ );
205
+ ```
206
+
207
+ ### requireTenant(orgSlug, serviceSlug, options?)
208
+
209
+ Requires the user to belong to a specific org AND service. Provides complete tenant isolation.
210
+
211
+ ```ts
212
+ app.get('/orgs/:org/services/:service/data',
213
+ requireAuth(),
214
+ requireTenant(
215
+ (req) => req.params.org,
216
+ (req) => req.params.service
217
+ ),
218
+ (req, res) => { ... }
219
+ );
220
+ ```
221
+
195
222
  ## Understanding JWT Context
196
223
 
197
224
  The middleware functions check claims embedded in the JWT by the client SDK during login:
@@ -199,7 +226,8 @@ The middleware functions check claims embedded in the JWT by the client SDK duri
199
226
  | SDK Initialization | JWT Claims | Middleware to Use |
200
227
  |-------------------|-----------|------------------|
201
228
  | Platform-level (`baseURL` only) | `is_platform_owner: true` | `requirePlatformOwner()` |
202
- | Multi-tenant (`baseURL` + `org` + `service`) | `org: 'slug'` | `requireOrganization()` |
229
+ | Org-level (`baseURL` + `org`) | `org: 'slug'` | `requireOrganization()` |
230
+ | Service-level (`baseURL` + `org` + `service`) | `org: 'slug'`, `service: 'app'` | `requireTenant()` or `requireService()` |
203
231
  | With permissions | `permissions: ['users:write', ...]` | `requirePermission()` |
204
232
 
205
233
  ### How It Works
@@ -209,13 +237,19 @@ The middleware functions check claims embedded in the JWT by the client SDK duri
209
237
  3. **Server-side**: This package verifies the JWT and middleware checks the claims
210
238
 
211
239
  ```ts
212
- // Example: Route for organization admins only
213
- app.get('/org/:slug/settings',
240
+ // Example: Route for tenant-specific data
241
+ app.get('/orgs/:org/services/:service/data',
214
242
  requireAuth(), // 1. Verify JWT signature
215
- requireOrganization((req) => req.params.slug), // 2. Check org claim matches URL
243
+ requireTenant( // 2. Check org + service claims
244
+ (req) => req.params.org,
245
+ (req) => req.params.service
246
+ ),
216
247
  (req, res) => {
217
- // User is authenticated AND belongs to this org
218
- res.json({ org: req.auth?.claims.org });
248
+ // User is authenticated AND belongs to this org+service
249
+ res.json({
250
+ org: req.auth?.claims.org,
251
+ service: req.auth?.claims.service
252
+ });
219
253
  }
220
254
  );
221
255
 
@@ -19,6 +19,8 @@ declare function createAuthMiddleware(options: AuthOSNodeOptions): {
19
19
  requireAllPermissions: (permissions: string[], permOptions?: RequirePermissionOptions) => RequestHandler;
20
20
  requirePlatformOwner: (permOptions?: RequirePermissionOptions) => RequestHandler;
21
21
  requireOrganization: (getOrgSlug: string | ((req: Request) => string), permOptions?: RequirePermissionOptions) => RequestHandler;
22
+ requireService: (getServiceSlug: string | ((req: Request) => string), permOptions?: RequirePermissionOptions) => RequestHandler;
23
+ requireTenant: (getOrgSlug: string | ((req: Request) => string), getServiceSlug: string | ((req: Request) => string), permOptions?: RequirePermissionOptions) => RequestHandler;
22
24
  };
23
25
 
24
26
  export { createAuthMiddleware };
package/dist/express.d.ts CHANGED
@@ -19,6 +19,8 @@ declare function createAuthMiddleware(options: AuthOSNodeOptions): {
19
19
  requireAllPermissions: (permissions: string[], permOptions?: RequirePermissionOptions) => RequestHandler;
20
20
  requirePlatformOwner: (permOptions?: RequirePermissionOptions) => RequestHandler;
21
21
  requireOrganization: (getOrgSlug: string | ((req: Request) => string), permOptions?: RequirePermissionOptions) => RequestHandler;
22
+ requireService: (getServiceSlug: string | ((req: Request) => string), permOptions?: RequirePermissionOptions) => RequestHandler;
23
+ requireTenant: (getOrgSlug: string | ((req: Request) => string), getServiceSlug: string | ((req: Request) => string), permOptions?: RequirePermissionOptions) => RequestHandler;
22
24
  };
23
25
 
24
26
  export { createAuthMiddleware };
package/dist/express.js CHANGED
@@ -382,13 +382,76 @@ function createAuthMiddleware(options) {
382
382
  next();
383
383
  };
384
384
  }
385
+ function requireService(getServiceSlug, permOptions = {}) {
386
+ const { message = "Service access required" } = permOptions;
387
+ return (req, res, next) => {
388
+ if (!req.auth) {
389
+ res.status(401).json({
390
+ error: "Unauthorized",
391
+ message: "Authentication required",
392
+ code: "NOT_AUTHENTICATED"
393
+ });
394
+ return;
395
+ }
396
+ const requiredService = typeof getServiceSlug === "function" ? getServiceSlug(req) : getServiceSlug;
397
+ const userService = req.auth.claims.service;
398
+ if (!userService || userService !== requiredService) {
399
+ res.status(403).json({
400
+ error: "Forbidden",
401
+ message,
402
+ code: "WRONG_SERVICE",
403
+ required: requiredService
404
+ });
405
+ return;
406
+ }
407
+ next();
408
+ };
409
+ }
410
+ function requireTenant(getOrgSlug, getServiceSlug, permOptions = {}) {
411
+ const { message = "Tenant access required" } = permOptions;
412
+ return (req, res, next) => {
413
+ if (!req.auth) {
414
+ res.status(401).json({
415
+ error: "Unauthorized",
416
+ message: "Authentication required",
417
+ code: "NOT_AUTHENTICATED"
418
+ });
419
+ return;
420
+ }
421
+ const requiredOrg = typeof getOrgSlug === "function" ? getOrgSlug(req) : getOrgSlug;
422
+ const requiredService = typeof getServiceSlug === "function" ? getServiceSlug(req) : getServiceSlug;
423
+ const userOrg = req.auth.claims.org;
424
+ const userService = req.auth.claims.service;
425
+ if (!userOrg || userOrg !== requiredOrg) {
426
+ res.status(403).json({
427
+ error: "Forbidden",
428
+ message,
429
+ code: "WRONG_ORGANIZATION",
430
+ required: { org: requiredOrg, service: requiredService }
431
+ });
432
+ return;
433
+ }
434
+ if (!userService || userService !== requiredService) {
435
+ res.status(403).json({
436
+ error: "Forbidden",
437
+ message,
438
+ code: "WRONG_SERVICE",
439
+ required: { org: requiredOrg, service: requiredService }
440
+ });
441
+ return;
442
+ }
443
+ next();
444
+ };
445
+ }
385
446
  return {
386
447
  requireAuth,
387
448
  requirePermission,
388
449
  requireAnyPermission,
389
450
  requireAllPermissions,
390
451
  requirePlatformOwner,
391
- requireOrganization
452
+ requireOrganization,
453
+ requireService,
454
+ requireTenant
392
455
  };
393
456
  }
394
457
 
package/dist/express.mjs CHANGED
@@ -360,13 +360,76 @@ function createAuthMiddleware(options) {
360
360
  next();
361
361
  };
362
362
  }
363
+ function requireService(getServiceSlug, permOptions = {}) {
364
+ const { message = "Service access required" } = permOptions;
365
+ return (req, res, next) => {
366
+ if (!req.auth) {
367
+ res.status(401).json({
368
+ error: "Unauthorized",
369
+ message: "Authentication required",
370
+ code: "NOT_AUTHENTICATED"
371
+ });
372
+ return;
373
+ }
374
+ const requiredService = typeof getServiceSlug === "function" ? getServiceSlug(req) : getServiceSlug;
375
+ const userService = req.auth.claims.service;
376
+ if (!userService || userService !== requiredService) {
377
+ res.status(403).json({
378
+ error: "Forbidden",
379
+ message,
380
+ code: "WRONG_SERVICE",
381
+ required: requiredService
382
+ });
383
+ return;
384
+ }
385
+ next();
386
+ };
387
+ }
388
+ function requireTenant(getOrgSlug, getServiceSlug, permOptions = {}) {
389
+ const { message = "Tenant access required" } = permOptions;
390
+ return (req, res, next) => {
391
+ if (!req.auth) {
392
+ res.status(401).json({
393
+ error: "Unauthorized",
394
+ message: "Authentication required",
395
+ code: "NOT_AUTHENTICATED"
396
+ });
397
+ return;
398
+ }
399
+ const requiredOrg = typeof getOrgSlug === "function" ? getOrgSlug(req) : getOrgSlug;
400
+ const requiredService = typeof getServiceSlug === "function" ? getServiceSlug(req) : getServiceSlug;
401
+ const userOrg = req.auth.claims.org;
402
+ const userService = req.auth.claims.service;
403
+ if (!userOrg || userOrg !== requiredOrg) {
404
+ res.status(403).json({
405
+ error: "Forbidden",
406
+ message,
407
+ code: "WRONG_ORGANIZATION",
408
+ required: { org: requiredOrg, service: requiredService }
409
+ });
410
+ return;
411
+ }
412
+ if (!userService || userService !== requiredService) {
413
+ res.status(403).json({
414
+ error: "Forbidden",
415
+ message,
416
+ code: "WRONG_SERVICE",
417
+ required: { org: requiredOrg, service: requiredService }
418
+ });
419
+ return;
420
+ }
421
+ next();
422
+ };
423
+ }
363
424
  return {
364
425
  requireAuth,
365
426
  requirePermission,
366
427
  requireAnyPermission,
367
428
  requireAllPermissions,
368
429
  requirePlatformOwner,
369
- requireOrganization
430
+ requireOrganization,
431
+ requireService,
432
+ requireTenant
370
433
  };
371
434
  }
372
435
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drmhse/authos-node",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Node.js server adapter for AuthOS authentication - Express middleware and token verification",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -58,7 +58,7 @@
58
58
  }
59
59
  },
60
60
  "dependencies": {
61
- "@drmhse/sso-sdk": "^0.3.10"
61
+ "@drmhse/sso-sdk": "^0.3.13"
62
62
  },
63
63
  "devDependencies": {
64
64
  "@types/express": "^5.0.0",