@jengolabs/auth-server-sdk 0.1.0 → 0.2.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/README.md ADDED
@@ -0,0 +1,134 @@
1
+ # @jengolabs/auth-server-sdk
2
+
3
+ Server-side SDK for [Jengo Auth](https://github.com/jengolabs/jen-auth) — session verification, middleware for Hono and NestJS, and typed session data.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @jengolabs/auth-server-sdk
9
+ ```
10
+
11
+ ## Quick start
12
+
13
+ ```typescript
14
+ import { AuthClient } from '@jengolabs/auth-server-sdk';
15
+
16
+ const authClient = new AuthClient({
17
+ authServerUrl: 'https://auth.yourdomain.com',
18
+ tenantApiKey: process.env.TENANT_API_KEY,
19
+ appSlug: 'your-app',
20
+ cacheTtlMs: 30_000, // optional — cache verified sessions for 30s
21
+ });
22
+ ```
23
+
24
+ ## Hono middleware
25
+
26
+ ```typescript
27
+ import { Hono } from 'hono';
28
+ import { createHonoAuthMiddleware, requireAppRole } from '@jengolabs/auth-server-sdk';
29
+
30
+ const app = new Hono();
31
+
32
+ app.use('*', createHonoAuthMiddleware({
33
+ authClient,
34
+ publicPaths: ['/health', '/public'],
35
+ }));
36
+
37
+ // Role-based access
38
+ app.get('/admin', requireAppRole('admin', 'owner'), (c) => {
39
+ const user = c.get('user');
40
+ return c.json({ user });
41
+ });
42
+ ```
43
+
44
+ The middleware attaches the following to the Hono context after successful auth:
45
+
46
+ | Key | Type | Description |
47
+ |---|---|---|
48
+ | `user` | `AuthUser` | Authenticated user |
49
+ | `session` | `AuthSessionData` | Session metadata |
50
+ | `appGrant` | `AuthAppGrant` | App-level role grant |
51
+ | `organization` | `object \| undefined` | Organization membership |
52
+
53
+ ## NestJS guard
54
+
55
+ ```bash
56
+ npm install @jengolabs/auth-server-sdk @nestjs/common
57
+ ```
58
+
59
+ ```typescript
60
+ import { Module, APP_GUARD } from '@nestjs/common';
61
+ import { AuthClient } from '@jengolabs/auth-server-sdk';
62
+ import { JengoAuthGuard } from '@jengolabs/auth-server-sdk/nestjs';
63
+
64
+ const authClient = new AuthClient({ ... });
65
+
66
+ @Module({
67
+ providers: [
68
+ { provide: APP_GUARD, useValue: new JengoAuthGuard(authClient) },
69
+ ],
70
+ })
71
+ export class AppModule {}
72
+ ```
73
+
74
+ Access session data in controllers:
75
+
76
+ ```typescript
77
+ import { Controller, Get, Req } from '@nestjs/common';
78
+ import { getUser, getSession, getAppGrant } from '@jengolabs/auth-server-sdk/nestjs';
79
+ import type { Request } from 'express';
80
+
81
+ @Controller('profile')
82
+ export class ProfileController {
83
+ @Get()
84
+ profile(@Req() req: Request) {
85
+ const user = getUser(req); // AuthUser
86
+ const grant = getAppGrant(req); // AuthAppGrant | null
87
+ return { user, grant };
88
+ }
89
+ }
90
+ ```
91
+
92
+ ## Generic / Express / other frameworks
93
+
94
+ ```typescript
95
+ import { verifyRequest } from '@jengolabs/auth-server-sdk';
96
+
97
+ // Works with any framework — pass headers directly
98
+ const session = await verifyRequest(authClient, {
99
+ authorization: req.headers.authorization,
100
+ cookie: req.headers.cookie,
101
+ });
102
+ ```
103
+
104
+ ## AuthClient options
105
+
106
+ | Option | Type | Required | Description |
107
+ |---|---|---|---|
108
+ | `authServerUrl` | `string` | ✓ | Base URL of your Jengo Auth server |
109
+ | `tenantApiKey` | `string` | ✓ | Tenant API key from the admin dashboard |
110
+ | `appSlug` | `string` | ✓ | Application slug to verify access grants |
111
+ | `cacheTtlMs` | `number` | — | Cache verified sessions (ms). Omit to disable |
112
+ | `timeoutMs` | `number` | — | Request timeout in ms (default: 5000) |
113
+ | `onError` | `(err: Error) => void` | — | Error callback for monitoring |
114
+
115
+ ## Error types
116
+
117
+ ```typescript
118
+ import { AuthenticationError, AuthorizationError } from '@jengolabs/auth-server-sdk';
119
+
120
+ try {
121
+ await authClient.verifySession(token);
122
+ } catch (err) {
123
+ if (err instanceof AuthenticationError) {
124
+ // 401 — invalid or expired session
125
+ }
126
+ if (err instanceof AuthorizationError) {
127
+ // 403 — valid session but insufficient access
128
+ }
129
+ }
130
+ ```
131
+
132
+ ## License
133
+
134
+ MIT
@@ -0,0 +1,12 @@
1
+ import type { CanActivate, ExecutionContext } from "@nestjs/common";
2
+ import { AuthClient } from "../client";
3
+ import type { AuthSessionWithGrant, AuthUser, AuthAppGrant } from "../types";
4
+ export declare class JengoAuthGuard implements CanActivate {
5
+ private readonly authClient;
6
+ constructor(authClient: AuthClient);
7
+ canActivate(context: ExecutionContext): Promise<boolean>;
8
+ }
9
+ export declare function getSession(request: object): AuthSessionWithGrant;
10
+ export declare function getUser(request: object): AuthUser;
11
+ export declare function getAppGrant(request: object): AuthAppGrant | null;
12
+ //# sourceMappingURL=nestjs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nestjs.d.ts","sourceRoot":"","sources":["../../src/middleware/nestjs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,OAAO,KAAK,EAAE,oBAAoB,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAyB7E,qBAAa,cAAe,YAAW,WAAW;IACpC,OAAO,CAAC,QAAQ,CAAC,UAAU;gBAAV,UAAU,EAAE,UAAU;IAE7C,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;CAyB/D;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,oBAAoB,CAEhE;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,CAEjD;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAEhE"}
@@ -0,0 +1,59 @@
1
+ import { AuthenticationError, AuthorizationError } from "../errors";
2
+ const SESSION_KEY = "jengoSession";
3
+ function throwHttp(status, message) {
4
+ try {
5
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
6
+ const nestjs = require("@nestjs/common");
7
+ if (status === 401)
8
+ throw new nestjs.UnauthorizedException(message);
9
+ throw new nestjs.ForbiddenException(message);
10
+ }
11
+ catch (err) {
12
+ if (err.code === "MODULE_NOT_FOUND") {
13
+ const fallback = new Error(message);
14
+ Object.assign(fallback, { statusCode: status });
15
+ throw fallback;
16
+ }
17
+ throw err;
18
+ }
19
+ }
20
+ export class JengoAuthGuard {
21
+ authClient;
22
+ constructor(authClient) {
23
+ this.authClient = authClient;
24
+ }
25
+ async canActivate(context) {
26
+ const request = context.switchToHttp().getRequest();
27
+ const authHeader = request.headers["authorization"];
28
+ const cookieHeader = request.headers["cookie"];
29
+ const credential = authHeader ?? cookieHeader ?? null;
30
+ if (!credential) {
31
+ throwHttp(401, "Authentication required");
32
+ }
33
+ try {
34
+ const session = await this.authClient.verifySession(credential);
35
+ if (!session.appGrant) {
36
+ throwHttp(403, "Access denied for this application");
37
+ }
38
+ request[SESSION_KEY] = session;
39
+ return true;
40
+ }
41
+ catch (error) {
42
+ if (error instanceof AuthorizationError)
43
+ throwHttp(403, error.message);
44
+ if (error instanceof AuthenticationError)
45
+ throwHttp(401, error.message);
46
+ throw error;
47
+ }
48
+ }
49
+ }
50
+ export function getSession(request) {
51
+ return request[SESSION_KEY];
52
+ }
53
+ export function getUser(request) {
54
+ return getSession(request).user;
55
+ }
56
+ export function getAppGrant(request) {
57
+ return getSession(request).appGrant;
58
+ }
59
+ //# sourceMappingURL=nestjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nestjs.js","sourceRoot":"","sources":["../../src/middleware/nestjs.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAGpE,MAAM,WAAW,GAAG,cAAc,CAAC;AAOnC,SAAS,SAAS,CAAC,MAAiB,EAAE,OAAe;IACnD,IAAI,CAAC;QACH,iEAAiE;QACjE,MAAM,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;QACzC,IAAI,MAAM,KAAK,GAAG;YAAE,MAAM,IAAI,MAAM,CAAC,qBAAqB,CAAC,OAAO,CAAC,CAAC;QACpE,MAAM,IAAI,MAAM,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;YAC/D,MAAM,QAAQ,GAAG,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;YACpC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC;YAChD,MAAM,QAAQ,CAAC;QACjB,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,OAAO,cAAc;IACI;IAA7B,YAA6B,UAAsB;QAAtB,eAAU,GAAV,UAAU,CAAY;IAAG,CAAC;IAEvD,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAe,CAAC;QACjE,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QACpD,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC/C,MAAM,UAAU,GAAG,UAAU,IAAI,YAAY,IAAI,IAAI,CAAC;QAEtD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,SAAS,CAAC,GAAG,EAAE,yBAAyB,CAAC,CAAC;QAC5C,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;YAEhE,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;gBACtB,SAAS,CAAC,GAAG,EAAE,oCAAoC,CAAC,CAAC;YACvD,CAAC;YAED,OAAO,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC;YAC/B,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,kBAAkB;gBAAE,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;YACvE,IAAI,KAAK,YAAY,mBAAmB;gBAAE,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;YACxE,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF;AAED,MAAM,UAAU,UAAU,CAAC,OAAe;IACxC,OAAQ,OAAmC,CAAC,WAAW,CAAyB,CAAC;AACnF,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,OAAe;IACrC,OAAO,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,OAAO,UAAU,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC;AACtC,CAAC"}
package/package.json CHANGED
@@ -1,25 +1,43 @@
1
1
  {
2
2
  "name": "@jengolabs/auth-server-sdk",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Server-side SDK for Jengo Auth — AuthClient, Hono/Express middleware, typed session verification",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ },
12
+ "./nestjs": {
13
+ "types": "./dist/middleware/nestjs.d.ts",
14
+ "default": "./dist/middleware/nestjs.js"
15
+ }
16
+ },
7
17
  "files": [
8
- "dist/**/*"
18
+ "dist/**/*",
19
+ "README.md"
9
20
  ],
10
21
  "dependencies": {
11
22
  "hono": "^4.7.8"
12
23
  },
13
24
  "devDependencies": {
14
25
  "typescript": "^5.8.3",
15
- "@types/node": "^22.15.21"
26
+ "@types/node": "^22.15.21",
27
+ "@nestjs/common": "^10.0.0",
28
+ "reflect-metadata": "^0.1.13",
29
+ "vitest": "^3.1.1"
16
30
  },
17
31
  "peerDependencies": {
18
- "hono": ">=4.0.0"
32
+ "hono": ">=4.0.0",
33
+ "@nestjs/common": ">=9.0.0"
19
34
  },
20
35
  "peerDependenciesMeta": {
21
36
  "hono": {
22
37
  "optional": true
38
+ },
39
+ "@nestjs/common": {
40
+ "optional": true
23
41
  }
24
42
  },
25
43
  "license": "MIT",
@@ -31,6 +49,8 @@
31
49
  "build": "tsc",
32
50
  "dev": "tsc --watch",
33
51
  "type-check": "tsc --noEmit",
52
+ "test": "vitest run",
53
+ "test:watch": "vitest",
34
54
  "clean": "rm -rf dist"
35
55
  }
36
56
  }