@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 +134 -0
- package/dist/middleware/nestjs.d.ts +12 -0
- package/dist/middleware/nestjs.d.ts.map +1 -0
- package/dist/middleware/nestjs.js +59 -0
- package/dist/middleware/nestjs.js.map +1 -0
- package/package.json +24 -4
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.
|
|
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
|
}
|