@caronte-sdk/node 0.2.0 → 0.2.2
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 +34 -34
- package/dist/adapters/apollo.cjs +9 -9
- package/dist/adapters/apollo.cjs.map +1 -1
- package/dist/adapters/apollo.d.cts +10 -10
- package/dist/adapters/apollo.d.ts +10 -10
- package/dist/adapters/apollo.js +8 -8
- package/dist/adapters/apollo.js.map +1 -1
- package/dist/adapters/express.cjs +5 -5
- package/dist/adapters/express.cjs.map +1 -1
- package/dist/adapters/express.d.cts +6 -6
- package/dist/adapters/express.d.ts +6 -6
- package/dist/adapters/express.js +5 -5
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/fastify.cjs +9 -9
- package/dist/adapters/fastify.cjs.map +1 -1
- package/dist/adapters/fastify.d.cts +7 -7
- package/dist/adapters/fastify.d.ts +7 -7
- package/dist/adapters/fastify.js +9 -9
- package/dist/adapters/fastify.js.map +1 -1
- package/dist/adapters/nest/index.cjs +38 -38
- package/dist/adapters/nest/index.cjs.map +1 -1
- package/dist/adapters/nest/index.d.cts +15 -15
- package/dist/adapters/nest/index.d.ts +15 -15
- package/dist/adapters/nest/index.js +24 -24
- package/dist/adapters/nest/index.js.map +1 -1
- package/dist/chunk-VEDVQOBD.js +21 -0
- package/dist/chunk-VEDVQOBD.js.map +1 -0
- package/dist/{chunk-G73YTEJQ.js → chunk-XMA2NAR7.js} +15 -15
- package/dist/chunk-XMA2NAR7.js.map +1 -0
- package/dist/{client-Cgm1csuM.d.cts → client-TcOSwgrn.d.cts} +5 -5
- package/dist/{client-Cgm1csuM.d.ts → client-TcOSwgrn.d.ts} +5 -5
- package/dist/index.cjs +24 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -9
- package/dist/index.d.ts +9 -9
- package/dist/index.js +2 -2
- package/package.json +3 -4
- package/dist/chunk-AKUM2UEO.js +0 -21
- package/dist/chunk-AKUM2UEO.js.map +0 -1
- package/dist/chunk-G73YTEJQ.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @
|
|
1
|
+
# @caronte-sdk/node
|
|
2
2
|
|
|
3
3
|
Node.js client for the **Argos** authorizer — handles app authentication,
|
|
4
4
|
JWT validation, permission checking and automatic operation sync.
|
|
@@ -6,12 +6,12 @@ JWT validation, permission checking and automatic operation sync.
|
|
|
6
6
|
## Installation
|
|
7
7
|
|
|
8
8
|
```bash
|
|
9
|
-
npm install @
|
|
9
|
+
npm install @caronte-sdk/node
|
|
10
10
|
# With framework-specific adapter
|
|
11
|
-
npm install @
|
|
12
|
-
npm install @
|
|
13
|
-
npm install @
|
|
14
|
-
npm install @
|
|
11
|
+
npm install @caronte-sdk/node express # Express
|
|
12
|
+
npm install @caronte-sdk/node fastify # Fastify
|
|
13
|
+
npm install @caronte-sdk/node @nestjs/common # NestJS
|
|
14
|
+
npm install @caronte-sdk/node @apollo/server # Apollo Server
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
## Quick start
|
|
@@ -20,24 +20,24 @@ npm install @argos/node @apollo/server # Apollo Server
|
|
|
20
20
|
|
|
21
21
|
```ts
|
|
22
22
|
import express from 'express';
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
23
|
+
import { CaronteClient, getRegistry } from '@caronte-sdk/node';
|
|
24
|
+
import { caronte } from '@caronte-sdk/node/express';
|
|
25
25
|
|
|
26
|
-
const client = new
|
|
26
|
+
const client = new CaronteClient({
|
|
27
27
|
authorizerUrl: process.env.AUTHORIZER_URL!,
|
|
28
28
|
realmId: process.env.REALM_ID!,
|
|
29
29
|
appId: process.env.APP_ID!,
|
|
30
30
|
secret: process.env.APP_SECRET!,
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
-
const { middleware, guard } =
|
|
33
|
+
const { middleware, guard } = caronte(client);
|
|
34
34
|
|
|
35
35
|
const app = express();
|
|
36
36
|
app.use(express.json(), middleware);
|
|
37
37
|
|
|
38
38
|
app.get('/health', guard('health:check', 'public'), (_req, res) => res.json({ status: 'ok' }));
|
|
39
39
|
app.get('/tasks', guard('tasks:list', 'private'), (_req, res) => res.json(tasks));
|
|
40
|
-
app.post('/tasks', guard('tasks:create', 'protected'), (req, res) => { /* req.
|
|
40
|
+
app.post('/tasks', guard('tasks:create', 'protected'), (req, res) => { /* req.caronteUser */ });
|
|
41
41
|
|
|
42
42
|
for (const op of getRegistry()) client.registerOperation(op);
|
|
43
43
|
await client.startup();
|
|
@@ -49,22 +49,22 @@ app.listen(3001);
|
|
|
49
49
|
|
|
50
50
|
```ts
|
|
51
51
|
import Fastify from 'fastify';
|
|
52
|
-
import {
|
|
53
|
-
import {
|
|
52
|
+
import { CaronteClient, getRegistry } from '@caronte-sdk/node';
|
|
53
|
+
import { caronteFastifyPlugin } from '@caronte-sdk/node/fastify';
|
|
54
54
|
|
|
55
|
-
const client = new
|
|
55
|
+
const client = new CaronteClient({ ... });
|
|
56
56
|
const fastify = Fastify();
|
|
57
57
|
|
|
58
|
-
await fastify.register(
|
|
58
|
+
await fastify.register(caronteFastifyPlugin, { client });
|
|
59
59
|
|
|
60
60
|
fastify.get('/health',
|
|
61
|
-
{ preHandler: fastify.
|
|
61
|
+
{ preHandler: fastify.caronteGuard('health:check', 'public') },
|
|
62
62
|
async () => ({ status: 'ok' }),
|
|
63
63
|
);
|
|
64
64
|
|
|
65
65
|
fastify.get('/tasks',
|
|
66
|
-
{ preHandler: fastify.
|
|
67
|
-
async (req) => { /* req.
|
|
66
|
+
{ preHandler: fastify.caronteGuard('tasks:list', 'private') },
|
|
67
|
+
async (req) => { /* req.caronteUser */ },
|
|
68
68
|
);
|
|
69
69
|
|
|
70
70
|
for (const op of getRegistry()) client.registerOperation(op);
|
|
@@ -78,11 +78,11 @@ await fastify.listen({ port: 3002 });
|
|
|
78
78
|
```ts
|
|
79
79
|
// app.module.ts
|
|
80
80
|
import { Module } from '@nestjs/common';
|
|
81
|
-
import {
|
|
81
|
+
import { CaronteModule } from '@caronte-sdk/node/nest';
|
|
82
82
|
|
|
83
83
|
@Module({
|
|
84
84
|
imports: [
|
|
85
|
-
|
|
85
|
+
CaronteModule.forRootAsync({
|
|
86
86
|
authorizerUrl: process.env.AUTHORIZER_URL!,
|
|
87
87
|
realmId: process.env.REALM_ID!,
|
|
88
88
|
appId: process.env.APP_ID!,
|
|
@@ -96,11 +96,11 @@ export class AppModule {}
|
|
|
96
96
|
```ts
|
|
97
97
|
// tasks.controller.ts
|
|
98
98
|
import { Controller, Get, Post, UseGuards } from '@nestjs/common';
|
|
99
|
-
import {
|
|
100
|
-
import type { TokenClaims } from '@
|
|
99
|
+
import { CaronteGuard, CaronteUser, Operation } from '@caronte-sdk/node/nest';
|
|
100
|
+
import type { TokenClaims } from '@caronte-sdk/node';
|
|
101
101
|
|
|
102
102
|
@Controller('tasks')
|
|
103
|
-
@UseGuards(
|
|
103
|
+
@UseGuards(CaronteGuard)
|
|
104
104
|
export class TasksController {
|
|
105
105
|
|
|
106
106
|
@Get()
|
|
@@ -109,7 +109,7 @@ export class TasksController {
|
|
|
109
109
|
|
|
110
110
|
@Post()
|
|
111
111
|
@Operation('tasks:create', 'protected')
|
|
112
|
-
create(@
|
|
112
|
+
create(@CaronteUser() user: TokenClaims) {
|
|
113
113
|
// user.sub, user.groups
|
|
114
114
|
}
|
|
115
115
|
}
|
|
@@ -119,23 +119,23 @@ export class TasksController {
|
|
|
119
119
|
|
|
120
120
|
```ts
|
|
121
121
|
import { ApolloServer } from '@apollo/server';
|
|
122
|
-
import {
|
|
123
|
-
import {
|
|
122
|
+
import { CaronteClient, getRegistry } from '@caronte-sdk/node';
|
|
123
|
+
import { carontePlugin, createGuard, type CaronteContext } from '@caronte-sdk/node/apollo';
|
|
124
124
|
|
|
125
|
-
const client = new
|
|
125
|
+
const client = new CaronteClient({ ... });
|
|
126
126
|
const guard = createGuard(client);
|
|
127
127
|
|
|
128
128
|
const resolvers = {
|
|
129
129
|
Query: {
|
|
130
130
|
health: guard('health:check', 'public', () => 'ok'),
|
|
131
|
-
tasks: guard('tasks:list', 'private', (_p, _a, ctx:
|
|
132
|
-
const user = ctx.
|
|
131
|
+
tasks: guard('tasks:list', 'private', (_p, _a, ctx: CaronteContext) => {
|
|
132
|
+
const user = ctx.caronteUser; // TokenClaims
|
|
133
133
|
return tasks;
|
|
134
134
|
}),
|
|
135
135
|
},
|
|
136
136
|
Mutation: {
|
|
137
|
-
createTask: guard('tasks:create', 'protected', (_p, { input }, ctx:
|
|
138
|
-
// ctx.
|
|
137
|
+
createTask: guard('tasks:create', 'protected', (_p, { input }, ctx: CaronteContext) => {
|
|
138
|
+
// ctx.caronteUser is the authenticated user
|
|
139
139
|
}),
|
|
140
140
|
},
|
|
141
141
|
};
|
|
@@ -143,10 +143,10 @@ const resolvers = {
|
|
|
143
143
|
for (const op of getRegistry()) client.registerOperation(op);
|
|
144
144
|
await client.startup();
|
|
145
145
|
|
|
146
|
-
const server = new ApolloServer<
|
|
146
|
+
const server = new ApolloServer<CaronteContext>({
|
|
147
147
|
typeDefs,
|
|
148
148
|
resolvers,
|
|
149
|
-
plugins: [
|
|
149
|
+
plugins: [carontePlugin(client)],
|
|
150
150
|
});
|
|
151
151
|
```
|
|
152
152
|
|
|
@@ -191,7 +191,7 @@ On `startup()`, the client:
|
|
|
191
191
|
On each request, the adapter:
|
|
192
192
|
- Validates the Bearer token using the cached JWKS
|
|
193
193
|
- Checks `TokenClaims.groups` against the operation's `allowedGroups`
|
|
194
|
-
- Injects `
|
|
194
|
+
- Injects `caronteUser` (`TokenClaims`) into the request context
|
|
195
195
|
|
|
196
196
|
## License
|
|
197
197
|
|
package/dist/adapters/apollo.cjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
// src/exceptions.ts
|
|
4
|
-
var
|
|
4
|
+
var CaronteError = class extends Error {
|
|
5
5
|
constructor(message) {
|
|
6
6
|
super(message);
|
|
7
7
|
this.name = this.constructor.name;
|
|
8
8
|
}
|
|
9
9
|
};
|
|
10
|
-
var
|
|
10
|
+
var CaronteTokenError = class extends CaronteError {
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
// src/registry.ts
|
|
@@ -27,7 +27,7 @@ function registerOperation(op) {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
// src/adapters/apollo.ts
|
|
30
|
-
function
|
|
30
|
+
function carontePlugin(client) {
|
|
31
31
|
return {
|
|
32
32
|
async requestDidStart() {
|
|
33
33
|
return {
|
|
@@ -38,10 +38,10 @@ function argosPlugin(client) {
|
|
|
38
38
|
const [scheme, token] = auth.split(" ");
|
|
39
39
|
if (scheme?.toLowerCase() === "bearer" && token) {
|
|
40
40
|
try {
|
|
41
|
-
ctx.contextValue.
|
|
41
|
+
ctx.contextValue.caronteUser = await client.validateToken(token);
|
|
42
42
|
} catch (err) {
|
|
43
|
-
if (err instanceof
|
|
44
|
-
ctx.contextValue.
|
|
43
|
+
if (err instanceof CaronteTokenError) {
|
|
44
|
+
ctx.contextValue.caronteUser = void 0;
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
}
|
|
@@ -56,8 +56,8 @@ function createGuard(client) {
|
|
|
56
56
|
registerOperation({ identifier: id, method: resolvedMethod, level });
|
|
57
57
|
return (parent, args, context, info) => {
|
|
58
58
|
if (level === "public") return resolver(parent, args, context, info);
|
|
59
|
-
if (!context.
|
|
60
|
-
if (!client.checkPermission(context.
|
|
59
|
+
if (!context.caronteUser) throw new Error("Unauthorized: Bearer token required.");
|
|
60
|
+
if (!client.checkPermission(context.caronteUser, id, resolvedMethod)) {
|
|
61
61
|
throw new Error("Forbidden: Insufficient permissions.");
|
|
62
62
|
}
|
|
63
63
|
return resolver(parent, args, context, info);
|
|
@@ -65,7 +65,7 @@ function createGuard(client) {
|
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
exports.
|
|
68
|
+
exports.carontePlugin = carontePlugin;
|
|
69
69
|
exports.createGuard = createGuard;
|
|
70
70
|
//# sourceMappingURL=apollo.cjs.map
|
|
71
71
|
//# sourceMappingURL=apollo.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/exceptions.ts","../../src/registry.ts","../../src/adapters/apollo.ts"],"names":[],"mappings":";;;AAAO,IAAM,
|
|
1
|
+
{"version":3,"sources":["../../src/exceptions.ts","../../src/registry.ts","../../src/adapters/apollo.ts"],"names":[],"mappings":"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","file":"apollo.cjs","sourcesContent":["export class CaronteError extends Error {\n constructor(message: string) {\n super(message);\n this.name = this.constructor.name;\n }\n}\n\nexport class CaronteAuthError extends CaronteError {}\nexport class CaronteTokenError extends CaronteError {}\nexport class CaronteForbiddenError extends CaronteError {}\nexport class CaronteSyncError extends CaronteError {}\nexport class CaronteConfigError extends CaronteError {}","import type { OperationDescriptor } from './models.js';\n\nconst _registry: OperationDescriptor[] = [];\n\n/** Infer operation method from the last segment of the operation id.\n * e.g. \"tasks:list\" → \"read\", \"tasks:create\" → \"write\", \"tasks:delete\" → \"delete\"\n */\nexport function detectMethod(id: string): string {\n const last = (id.split(':').pop() ?? '').toLowerCase();\n if (/^(get|list|fetch|read)/.test(last)) return 'read';\n if (/^(delete|remove|destroy)/.test(last)) return 'delete';\n if (/(stream|subscribe|watch|listen)/.test(last)) return 'stream';\n return 'write';\n}\n\n/** Register an operation in the global registry (used by startup to sync). */\nexport function registerOperation(op: OperationDescriptor): void {\n const exists = _registry.some(\n o => o.identifier === op.identifier && o.method === op.method,\n );\n if (!exists) _registry.push(op);\n}\n\nexport function getRegistry(): OperationDescriptor[] {\n return [..._registry];\n}\n\nexport function clearRegistry(): void {\n _registry.length = 0;\n}","import type { ApolloServerPlugin, BaseContext, GraphQLRequestContext } from '@apollo/server';\nimport { CaronteClient } from '../client.js';\nimport { CaronteTokenError } from '../exceptions.js';\nimport type { TokenClaims } from '../models.js';\nimport { detectMethod, registerOperation } from '../registry.js';\n\nexport interface CaronteContext extends BaseContext {\n caronteUser?: TokenClaims;\n}\n\n/**\n * Apollo Server plugin for caronte.\n *\n * Validates the Bearer token on every request and stores the claims in\n * `context.caronteUser`. Use `createGuard(client)` to enforce per-resolver\n * permissions.\n */\nexport function carontePlugin(client: CaronteClient): ApolloServerPlugin<CaronteContext> {\n return {\n async requestDidStart() {\n return {\n async didResolveOperation(ctx: GraphQLRequestContext<CaronteContext>) {\n const httpAuth = ctx.request.http?.headers.get('authorization') ?? '';\n const wsAuth = ((ctx.contextValue as any)?.connectionParams?.Authorization as string | undefined) ?? '';\n const auth = httpAuth || wsAuth;\n\n const [scheme, token] = auth.split(' ');\n if (scheme?.toLowerCase() === 'bearer' && token) {\n try {\n ctx.contextValue.caronteUser = await client.validateToken(token);\n } catch (err) {\n if (err instanceof CaronteTokenError) {\n ctx.contextValue.caronteUser = undefined;\n }\n }\n }\n },\n };\n },\n };\n}\n\n/**\n * Factory that returns a per-resolver guard bound to a `CaronteClient`.\n *\n * @example\n * ```ts\n * const guard = createGuard(client);\n *\n * const resolvers = {\n * Query: {\n * tasks: guard('tasks:list', 'private', async (_parent, _args, context) => {\n * const user = context.caronteUser;\n * return db.tasks.findAll();\n * }),\n * },\n * };\n * ```\n */\nexport function createGuard(client: CaronteClient) {\n return function guard<TParent, TArgs, TContext extends CaronteContext, TReturn>(\n id: string,\n level: string,\n resolver: (parent: TParent, args: TArgs, context: TContext, info: unknown) => TReturn,\n method?: string,\n ): (parent: TParent, args: TArgs, context: TContext, info: unknown) => TReturn {\n const resolvedMethod = method ?? detectMethod(id);\n registerOperation({ identifier: id, method: resolvedMethod, level });\n\n return (parent, args, context, info) => {\n if (level === 'public') return resolver(parent, args, context, info);\n if (!context.caronteUser) throw new Error('Unauthorized: Bearer token required.');\n if (!client.checkPermission(context.caronteUser, id, resolvedMethod)) {\n throw new Error('Forbidden: Insufficient permissions.');\n }\n return resolver(parent, args, context, info);\n };\n };\n}"]}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { BaseContext, ApolloServerPlugin } from '@apollo/server';
|
|
2
|
-
import { T as TokenClaims,
|
|
2
|
+
import { T as TokenClaims, C as CaronteClient } from '../client-TcOSwgrn.cjs';
|
|
3
3
|
|
|
4
|
-
interface
|
|
5
|
-
|
|
4
|
+
interface CaronteContext extends BaseContext {
|
|
5
|
+
caronteUser?: TokenClaims;
|
|
6
6
|
}
|
|
7
7
|
/**
|
|
8
|
-
* Apollo Server plugin for
|
|
8
|
+
* Apollo Server plugin for caronte.
|
|
9
9
|
*
|
|
10
10
|
* Validates the Bearer token on every request and stores the claims in
|
|
11
|
-
* `context.
|
|
11
|
+
* `context.caronteUser`. Use `createGuard(client)` to enforce per-resolver
|
|
12
12
|
* permissions.
|
|
13
13
|
*/
|
|
14
|
-
declare function
|
|
14
|
+
declare function carontePlugin(client: CaronteClient): ApolloServerPlugin<CaronteContext>;
|
|
15
15
|
/**
|
|
16
|
-
* Factory that returns a per-resolver guard bound to a `
|
|
16
|
+
* Factory that returns a per-resolver guard bound to a `CaronteClient`.
|
|
17
17
|
*
|
|
18
18
|
* @example
|
|
19
19
|
* ```ts
|
|
@@ -22,13 +22,13 @@ declare function argosPlugin(client: ArgosClient): ApolloServerPlugin<ArgosConte
|
|
|
22
22
|
* const resolvers = {
|
|
23
23
|
* Query: {
|
|
24
24
|
* tasks: guard('tasks:list', 'private', async (_parent, _args, context) => {
|
|
25
|
-
* const user = context.
|
|
25
|
+
* const user = context.caronteUser;
|
|
26
26
|
* return db.tasks.findAll();
|
|
27
27
|
* }),
|
|
28
28
|
* },
|
|
29
29
|
* };
|
|
30
30
|
* ```
|
|
31
31
|
*/
|
|
32
|
-
declare function createGuard(client:
|
|
32
|
+
declare function createGuard(client: CaronteClient): <TParent, TArgs, TContext extends CaronteContext, TReturn>(id: string, level: string, resolver: (parent: TParent, args: TArgs, context: TContext, info: unknown) => TReturn, method?: string) => (parent: TParent, args: TArgs, context: TContext, info: unknown) => TReturn;
|
|
33
33
|
|
|
34
|
-
export { type
|
|
34
|
+
export { type CaronteContext, carontePlugin, createGuard };
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { BaseContext, ApolloServerPlugin } from '@apollo/server';
|
|
2
|
-
import { T as TokenClaims,
|
|
2
|
+
import { T as TokenClaims, C as CaronteClient } from '../client-TcOSwgrn.js';
|
|
3
3
|
|
|
4
|
-
interface
|
|
5
|
-
|
|
4
|
+
interface CaronteContext extends BaseContext {
|
|
5
|
+
caronteUser?: TokenClaims;
|
|
6
6
|
}
|
|
7
7
|
/**
|
|
8
|
-
* Apollo Server plugin for
|
|
8
|
+
* Apollo Server plugin for caronte.
|
|
9
9
|
*
|
|
10
10
|
* Validates the Bearer token on every request and stores the claims in
|
|
11
|
-
* `context.
|
|
11
|
+
* `context.caronteUser`. Use `createGuard(client)` to enforce per-resolver
|
|
12
12
|
* permissions.
|
|
13
13
|
*/
|
|
14
|
-
declare function
|
|
14
|
+
declare function carontePlugin(client: CaronteClient): ApolloServerPlugin<CaronteContext>;
|
|
15
15
|
/**
|
|
16
|
-
* Factory that returns a per-resolver guard bound to a `
|
|
16
|
+
* Factory that returns a per-resolver guard bound to a `CaronteClient`.
|
|
17
17
|
*
|
|
18
18
|
* @example
|
|
19
19
|
* ```ts
|
|
@@ -22,13 +22,13 @@ declare function argosPlugin(client: ArgosClient): ApolloServerPlugin<ArgosConte
|
|
|
22
22
|
* const resolvers = {
|
|
23
23
|
* Query: {
|
|
24
24
|
* tasks: guard('tasks:list', 'private', async (_parent, _args, context) => {
|
|
25
|
-
* const user = context.
|
|
25
|
+
* const user = context.caronteUser;
|
|
26
26
|
* return db.tasks.findAll();
|
|
27
27
|
* }),
|
|
28
28
|
* },
|
|
29
29
|
* };
|
|
30
30
|
* ```
|
|
31
31
|
*/
|
|
32
|
-
declare function createGuard(client:
|
|
32
|
+
declare function createGuard(client: CaronteClient): <TParent, TArgs, TContext extends CaronteContext, TReturn>(id: string, level: string, resolver: (parent: TParent, args: TArgs, context: TContext, info: unknown) => TReturn, method?: string) => (parent: TParent, args: TArgs, context: TContext, info: unknown) => TReturn;
|
|
33
33
|
|
|
34
|
-
export { type
|
|
34
|
+
export { type CaronteContext, carontePlugin, createGuard };
|
package/dist/adapters/apollo.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CaronteTokenError } from '../chunk-VEDVQOBD.js';
|
|
2
2
|
import { detectMethod, registerOperation } from '../chunk-HV6X5RJY.js';
|
|
3
3
|
|
|
4
4
|
// src/adapters/apollo.ts
|
|
5
|
-
function
|
|
5
|
+
function carontePlugin(client) {
|
|
6
6
|
return {
|
|
7
7
|
async requestDidStart() {
|
|
8
8
|
return {
|
|
@@ -13,10 +13,10 @@ function argosPlugin(client) {
|
|
|
13
13
|
const [scheme, token] = auth.split(" ");
|
|
14
14
|
if (scheme?.toLowerCase() === "bearer" && token) {
|
|
15
15
|
try {
|
|
16
|
-
ctx.contextValue.
|
|
16
|
+
ctx.contextValue.caronteUser = await client.validateToken(token);
|
|
17
17
|
} catch (err) {
|
|
18
|
-
if (err instanceof
|
|
19
|
-
ctx.contextValue.
|
|
18
|
+
if (err instanceof CaronteTokenError) {
|
|
19
|
+
ctx.contextValue.caronteUser = void 0;
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
}
|
|
@@ -31,8 +31,8 @@ function createGuard(client) {
|
|
|
31
31
|
registerOperation({ identifier: id, method: resolvedMethod, level });
|
|
32
32
|
return (parent, args, context, info) => {
|
|
33
33
|
if (level === "public") return resolver(parent, args, context, info);
|
|
34
|
-
if (!context.
|
|
35
|
-
if (!client.checkPermission(context.
|
|
34
|
+
if (!context.caronteUser) throw new Error("Unauthorized: Bearer token required.");
|
|
35
|
+
if (!client.checkPermission(context.caronteUser, id, resolvedMethod)) {
|
|
36
36
|
throw new Error("Forbidden: Insufficient permissions.");
|
|
37
37
|
}
|
|
38
38
|
return resolver(parent, args, context, info);
|
|
@@ -40,6 +40,6 @@ function createGuard(client) {
|
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
export {
|
|
43
|
+
export { carontePlugin, createGuard };
|
|
44
44
|
//# sourceMappingURL=apollo.js.map
|
|
45
45
|
//# sourceMappingURL=apollo.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/apollo.ts"],"names":[],"mappings":";;;;AAiBO,SAAS,
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/apollo.ts"],"names":[],"mappings":";;;;AAiBO,SAAS,cAAc,MAAA,EAA2D;AACvF,EAAA,OAAO;AAAA,IACL,MAAM,eAAA,GAAkB;AACtB,MAAA,OAAO;AAAA,QACL,MAAM,oBAAoB,GAAA,EAA4C;AACpE,UAAA,MAAM,WAAY,GAAA,CAAI,OAAA,CAAQ,MAAM,OAAA,CAAQ,GAAA,CAAI,eAAe,CAAA,IAAK,EAAA;AACpE,UAAA,MAAM,MAAA,GAAc,GAAA,CAAI,YAAA,EAAsB,gBAAA,EAAkB,aAAA,IAAwC,EAAA;AACxG,UAAA,MAAM,OAAY,QAAA,IAAY,MAAA;AAE9B,UAAA,MAAM,CAAC,MAAA,EAAQ,KAAK,CAAA,GAAI,IAAA,CAAK,MAAM,GAAG,CAAA;AACtC,UAAA,IAAI,MAAA,EAAQ,WAAA,EAAY,KAAM,QAAA,IAAY,KAAA,EAAO;AAC/C,YAAA,IAAI;AACF,cAAA,GAAA,CAAI,YAAA,CAAa,WAAA,GAAc,MAAM,MAAA,CAAO,cAAc,KAAK,CAAA;AAAA,YACjE,SAAS,GAAA,EAAK;AACZ,cAAA,IAAI,eAAe,iBAAA,EAAmB;AACpC,gBAAA,GAAA,CAAI,aAAa,WAAA,GAAc,MAAA;AAAA,cACjC;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,OACF;AAAA,IACF;AAAA,GACF;AACF;AAmBO,SAAS,YAAY,MAAA,EAAuB;AACjD,EAAA,OAAO,SAAS,KAAA,CACd,EAAA,EACA,KAAA,EACA,UACA,MAAA,EAC6E;AAC7E,IAAA,MAAM,cAAA,GAAiB,MAAA,IAAU,YAAA,CAAa,EAAE,CAAA;AAChD,IAAA,iBAAA,CAAkB,EAAE,UAAA,EAAY,EAAA,EAAI,MAAA,EAAQ,cAAA,EAAgB,OAAO,CAAA;AAEnE,IAAA,OAAO,CAAC,MAAA,EAAQ,IAAA,EAAM,OAAA,EAAS,IAAA,KAAS;AACtC,MAAA,IAAI,UAAU,QAAA,EAAU,OAAO,SAAS,MAAA,EAAQ,IAAA,EAAM,SAAS,IAAI,CAAA;AACnE,MAAA,IAAI,CAAC,OAAA,CAAQ,WAAA,EAAa,MAAM,IAAI,MAAM,sCAAsC,CAAA;AAChF,MAAA,IAAI,CAAC,MAAA,CAAO,eAAA,CAAgB,QAAQ,WAAA,EAAa,EAAA,EAAI,cAAc,CAAA,EAAG;AACpE,QAAA,MAAM,IAAI,MAAM,sCAAsC,CAAA;AAAA,MACxD;AACA,MAAA,OAAO,QAAA,CAAS,MAAA,EAAQ,IAAA,EAAM,OAAA,EAAS,IAAI,CAAA;AAAA,IAC7C,CAAA;AAAA,EACF,CAAA;AACF","file":"apollo.js","sourcesContent":["import type { ApolloServerPlugin, BaseContext, GraphQLRequestContext } from '@apollo/server';\nimport { CaronteClient } from '../client.js';\nimport { CaronteTokenError } from '../exceptions.js';\nimport type { TokenClaims } from '../models.js';\nimport { detectMethod, registerOperation } from '../registry.js';\n\nexport interface CaronteContext extends BaseContext {\n caronteUser?: TokenClaims;\n}\n\n/**\n * Apollo Server plugin for caronte.\n *\n * Validates the Bearer token on every request and stores the claims in\n * `context.caronteUser`. Use `createGuard(client)` to enforce per-resolver\n * permissions.\n */\nexport function carontePlugin(client: CaronteClient): ApolloServerPlugin<CaronteContext> {\n return {\n async requestDidStart() {\n return {\n async didResolveOperation(ctx: GraphQLRequestContext<CaronteContext>) {\n const httpAuth = ctx.request.http?.headers.get('authorization') ?? '';\n const wsAuth = ((ctx.contextValue as any)?.connectionParams?.Authorization as string | undefined) ?? '';\n const auth = httpAuth || wsAuth;\n\n const [scheme, token] = auth.split(' ');\n if (scheme?.toLowerCase() === 'bearer' && token) {\n try {\n ctx.contextValue.caronteUser = await client.validateToken(token);\n } catch (err) {\n if (err instanceof CaronteTokenError) {\n ctx.contextValue.caronteUser = undefined;\n }\n }\n }\n },\n };\n },\n };\n}\n\n/**\n * Factory that returns a per-resolver guard bound to a `CaronteClient`.\n *\n * @example\n * ```ts\n * const guard = createGuard(client);\n *\n * const resolvers = {\n * Query: {\n * tasks: guard('tasks:list', 'private', async (_parent, _args, context) => {\n * const user = context.caronteUser;\n * return db.tasks.findAll();\n * }),\n * },\n * };\n * ```\n */\nexport function createGuard(client: CaronteClient) {\n return function guard<TParent, TArgs, TContext extends CaronteContext, TReturn>(\n id: string,\n level: string,\n resolver: (parent: TParent, args: TArgs, context: TContext, info: unknown) => TReturn,\n method?: string,\n ): (parent: TParent, args: TArgs, context: TContext, info: unknown) => TReturn {\n const resolvedMethod = method ?? detectMethod(id);\n registerOperation({ identifier: id, method: resolvedMethod, level });\n\n return (parent, args, context, info) => {\n if (level === 'public') return resolver(parent, args, context, info);\n if (!context.caronteUser) throw new Error('Unauthorized: Bearer token required.');\n if (!client.checkPermission(context.caronteUser, id, resolvedMethod)) {\n throw new Error('Forbidden: Insufficient permissions.');\n }\n return resolver(parent, args, context, info);\n };\n };\n}"]}
|
|
@@ -17,13 +17,13 @@ function registerOperation(op) {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
// src/adapters/express.ts
|
|
20
|
-
function
|
|
20
|
+
function caronte(client) {
|
|
21
21
|
const middleware = async (req, _res, next) => {
|
|
22
22
|
const auth = req.headers.authorization ?? "";
|
|
23
23
|
const [scheme, token] = auth.split(" ");
|
|
24
24
|
if (scheme?.toLowerCase() === "bearer" && token) {
|
|
25
25
|
try {
|
|
26
|
-
req.
|
|
26
|
+
req.caronteUser = await client.validateToken(token);
|
|
27
27
|
} catch {
|
|
28
28
|
}
|
|
29
29
|
}
|
|
@@ -37,11 +37,11 @@ function argos(client) {
|
|
|
37
37
|
next();
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
40
|
-
if (!req.
|
|
40
|
+
if (!req.caronteUser) {
|
|
41
41
|
res.status(401).json({ error: "unauthorized", detail: "Bearer token required." });
|
|
42
42
|
return;
|
|
43
43
|
}
|
|
44
|
-
const allowed = client.checkPermission(req.
|
|
44
|
+
const allowed = client.checkPermission(req.caronteUser, id, resolvedMethod);
|
|
45
45
|
if (!allowed) {
|
|
46
46
|
res.status(403).json({ error: "forbidden", detail: "Insufficient permissions." });
|
|
47
47
|
return;
|
|
@@ -52,6 +52,6 @@ function argos(client) {
|
|
|
52
52
|
return { middleware, guard };
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
exports.
|
|
55
|
+
exports.caronte = caronte;
|
|
56
56
|
//# sourceMappingURL=express.cjs.map
|
|
57
57
|
//# sourceMappingURL=express.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/registry.ts","../../src/adapters/express.ts"],"names":[],"mappings":";;;AAEA,IAAM,YAAmC,EAAC;AAKnC,SAAS,aAAa,EAAA,EAAoB;AAC/C,EAAA,MAAM,IAAA,GAAA,CAAQ,GAAG,KAAA,CAAM,GAAG,EAAE,GAAA,EAAI,IAAK,IAAI,WAAA,EAAY;AACrD,EAAA,IAAI,wBAAA,CAAyB,IAAA,CAAK,IAAI,CAAA,EAAS,OAAO,MAAA;AACtD,EAAA,IAAI,0BAAA,CAA2B,IAAA,CAAK,IAAI,CAAA,EAAQ,OAAO,QAAA;AACvD,EAAA,IAAI,iCAAA,CAAkC,IAAA,CAAK,IAAI,CAAA,EAAG,OAAO,QAAA;AACzD,EAAA,OAAO,OAAA;AACT;AAGO,SAAS,kBAAkB,EAAA,EAA+B;AAC/D,EAAA,MAAM,SAAS,SAAA,CAAU,IAAA;AAAA,IACvB,OAAK,CAAA,CAAE,UAAA,KAAe,GAAG,UAAA,IAAc,CAAA,CAAE,WAAW,EAAA,CAAG;AAAA,GACzD;AACA,EAAA,IAAI,CAAC,MAAA,EAAQ,SAAA,CAAU,IAAA,CAAK,EAAE,CAAA;AAChC;;;ACCO,SAAS,
|
|
1
|
+
{"version":3,"sources":["../../src/registry.ts","../../src/adapters/express.ts"],"names":[],"mappings":";;;AAEA,IAAM,YAAmC,EAAC;AAKnC,SAAS,aAAa,EAAA,EAAoB;AAC/C,EAAA,MAAM,IAAA,GAAA,CAAQ,GAAG,KAAA,CAAM,GAAG,EAAE,GAAA,EAAI,IAAK,IAAI,WAAA,EAAY;AACrD,EAAA,IAAI,wBAAA,CAAyB,IAAA,CAAK,IAAI,CAAA,EAAS,OAAO,MAAA;AACtD,EAAA,IAAI,0BAAA,CAA2B,IAAA,CAAK,IAAI,CAAA,EAAQ,OAAO,QAAA;AACvD,EAAA,IAAI,iCAAA,CAAkC,IAAA,CAAK,IAAI,CAAA,EAAG,OAAO,QAAA;AACzD,EAAA,OAAO,OAAA;AACT;AAGO,SAAS,kBAAkB,EAAA,EAA+B;AAC/D,EAAA,MAAM,SAAS,SAAA,CAAU,IAAA;AAAA,IACvB,OAAK,CAAA,CAAE,UAAA,KAAe,GAAG,UAAA,IAAc,CAAA,CAAE,WAAW,EAAA,CAAG;AAAA,GACzD;AACA,EAAA,IAAI,CAAC,MAAA,EAAQ,SAAA,CAAU,IAAA,CAAK,EAAE,CAAA;AAChC;;;ACCO,SAAS,QAAQ,MAAA,EAA0C;AAEhE,EAAA,MAAM,UAAA,GAAa,OAAO,GAAA,EAAc,IAAA,EAAgB,IAAA,KAAsC;AAC5F,IAAA,MAAM,IAAA,GAAO,GAAA,CAAI,OAAA,CAAQ,aAAA,IAAiB,EAAA;AAC1C,IAAA,MAAM,CAAC,MAAA,EAAQ,KAAK,CAAA,GAAI,IAAA,CAAK,MAAM,GAAG,CAAA;AACtC,IAAA,IAAI,MAAA,EAAQ,WAAA,EAAY,KAAM,QAAA,IAAY,KAAA,EAAO;AAC/C,MAAA,IAAI;AACF,QAAA,GAAA,CAAI,WAAA,GAAc,MAAM,MAAA,CAAO,aAAA,CAAc,KAAK,CAAA;AAAA,MACpD,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AACA,IAAA,IAAA,EAAK;AAAA,EACP,CAAA;AAEA,EAAA,MAAM,KAAA,GAAQ,CAAC,EAAA,EAAY,KAAA,EAAe,MAAA,KAAoB;AAC5D,IAAA,MAAM,cAAA,GAAiB,MAAA,IAAU,YAAA,CAAa,EAAE,CAAA;AAChD,IAAA,iBAAA,CAAkB,EAAE,UAAA,EAAY,EAAA,EAAI,MAAA,EAAQ,cAAA,EAAgB,OAAO,CAAA;AAEnE,IAAA,OAAO,CAAC,GAAA,EAAc,GAAA,EAAe,IAAA,KAA6B;AAChE,MAAA,IAAI,UAAU,QAAA,EAAU;AAAE,QAAA,IAAA,EAAK;AAAG,QAAA;AAAA,MAAQ;AAE1C,MAAA,IAAI,CAAC,IAAI,WAAA,EAAa;AACpB,QAAA,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,EAAE,KAAA,EAAO,cAAA,EAAgB,MAAA,EAAQ,wBAAA,EAA0B,CAAA;AAChF,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,UAAU,MAAA,CAAO,eAAA,CAAgB,GAAA,CAAI,WAAA,EAAa,IAAI,cAAc,CAAA;AAC1E,MAAA,IAAI,CAAC,OAAA,EAAS;AACZ,QAAA,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,EAAE,KAAA,EAAO,WAAA,EAAa,MAAA,EAAQ,2BAAA,EAA6B,CAAA;AAChF,QAAA;AAAA,MACF;AAEA,MAAA,IAAA,EAAK;AAAA,IACP,CAAA;AAAA,EACF,CAAA;AAEA,EAAA,OAAO,EAAE,YAAY,KAAA,EAAM;AAC7B","file":"express.cjs","sourcesContent":["import type { OperationDescriptor } from './models.js';\n\nconst _registry: OperationDescriptor[] = [];\n\n/** Infer operation method from the last segment of the operation id.\n * e.g. \"tasks:list\" → \"read\", \"tasks:create\" → \"write\", \"tasks:delete\" → \"delete\"\n */\nexport function detectMethod(id: string): string {\n const last = (id.split(':').pop() ?? '').toLowerCase();\n if (/^(get|list|fetch|read)/.test(last)) return 'read';\n if (/^(delete|remove|destroy)/.test(last)) return 'delete';\n if (/(stream|subscribe|watch|listen)/.test(last)) return 'stream';\n return 'write';\n}\n\n/** Register an operation in the global registry (used by startup to sync). */\nexport function registerOperation(op: OperationDescriptor): void {\n const exists = _registry.some(\n o => o.identifier === op.identifier && o.method === op.method,\n );\n if (!exists) _registry.push(op);\n}\n\nexport function getRegistry(): OperationDescriptor[] {\n return [..._registry];\n}\n\nexport function clearRegistry(): void {\n _registry.length = 0;\n}","import type { NextFunction, Request, Response } from 'express';\nimport { CaronteClient } from '../client.js';\nimport { CaronteTokenError } from '../exceptions.js';\nimport type { TokenClaims } from '../models.js';\nimport { detectMethod, registerOperation } from '../registry.js';\n\ndeclare global {\n namespace Express {\n interface Request {\n caronteUser?: TokenClaims;\n }\n }\n}\n\nexport interface CaronteMiddleware {\n /** Global middleware — validates the Bearer token and injects `req.caronteUser`. */\n middleware: (req: Request, res: Response, next: NextFunction) => Promise<void>;\n /** Per-route guard — registers the operation and enforces permission at request time. */\n guard: (id: string, level: string, method?: string) =>\n (req: Request, res: Response, next: NextFunction) => void;\n}\n\nexport function caronte(client: CaronteClient): CaronteMiddleware {\n\n const middleware = async (req: Request, _res: Response, next: NextFunction): Promise<void> => {\n const auth = req.headers.authorization ?? '';\n const [scheme, token] = auth.split(' ');\n if (scheme?.toLowerCase() === 'bearer' && token) {\n try {\n req.caronteUser = await client.validateToken(token);\n } catch {\n // invalid token — caronteUser stays undefined; guard handles the 401\n }\n }\n next();\n };\n\n const guard = (id: string, level: string, method?: string) => {\n const resolvedMethod = method ?? detectMethod(id);\n registerOperation({ identifier: id, method: resolvedMethod, level });\n\n return (req: Request, res: Response, next: NextFunction): void => {\n if (level === 'public') { next(); return; }\n\n if (!req.caronteUser) {\n res.status(401).json({ error: 'unauthorized', detail: 'Bearer token required.' });\n return;\n }\n\n const allowed = client.checkPermission(req.caronteUser, id, resolvedMethod);\n if (!allowed) {\n res.status(403).json({ error: 'forbidden', detail: 'Insufficient permissions.' });\n return;\n }\n\n next();\n };\n };\n\n return { middleware, guard };\n}"]}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { Request, Response, NextFunction } from 'express';
|
|
2
|
-
import { T as TokenClaims,
|
|
2
|
+
import { T as TokenClaims, C as CaronteClient } from '../client-TcOSwgrn.cjs';
|
|
3
3
|
|
|
4
4
|
declare global {
|
|
5
5
|
namespace Express {
|
|
6
6
|
interface Request {
|
|
7
|
-
|
|
7
|
+
caronteUser?: TokenClaims;
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
|
-
interface
|
|
12
|
-
/** Global middleware — validates the Bearer token and injects `req.
|
|
11
|
+
interface CaronteMiddleware {
|
|
12
|
+
/** Global middleware — validates the Bearer token and injects `req.caronteUser`. */
|
|
13
13
|
middleware: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
14
14
|
/** Per-route guard — registers the operation and enforces permission at request time. */
|
|
15
15
|
guard: (id: string, level: string, method?: string) => (req: Request, res: Response, next: NextFunction) => void;
|
|
16
16
|
}
|
|
17
|
-
declare function
|
|
17
|
+
declare function caronte(client: CaronteClient): CaronteMiddleware;
|
|
18
18
|
|
|
19
|
-
export { type
|
|
19
|
+
export { type CaronteMiddleware, caronte };
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { Request, Response, NextFunction } from 'express';
|
|
2
|
-
import { T as TokenClaims,
|
|
2
|
+
import { T as TokenClaims, C as CaronteClient } from '../client-TcOSwgrn.js';
|
|
3
3
|
|
|
4
4
|
declare global {
|
|
5
5
|
namespace Express {
|
|
6
6
|
interface Request {
|
|
7
|
-
|
|
7
|
+
caronteUser?: TokenClaims;
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
|
-
interface
|
|
12
|
-
/** Global middleware — validates the Bearer token and injects `req.
|
|
11
|
+
interface CaronteMiddleware {
|
|
12
|
+
/** Global middleware — validates the Bearer token and injects `req.caronteUser`. */
|
|
13
13
|
middleware: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
14
14
|
/** Per-route guard — registers the operation and enforces permission at request time. */
|
|
15
15
|
guard: (id: string, level: string, method?: string) => (req: Request, res: Response, next: NextFunction) => void;
|
|
16
16
|
}
|
|
17
|
-
declare function
|
|
17
|
+
declare function caronte(client: CaronteClient): CaronteMiddleware;
|
|
18
18
|
|
|
19
|
-
export { type
|
|
19
|
+
export { type CaronteMiddleware, caronte };
|
package/dist/adapters/express.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { detectMethod, registerOperation } from '../chunk-HV6X5RJY.js';
|
|
2
2
|
|
|
3
3
|
// src/adapters/express.ts
|
|
4
|
-
function
|
|
4
|
+
function caronte(client) {
|
|
5
5
|
const middleware = async (req, _res, next) => {
|
|
6
6
|
const auth = req.headers.authorization ?? "";
|
|
7
7
|
const [scheme, token] = auth.split(" ");
|
|
8
8
|
if (scheme?.toLowerCase() === "bearer" && token) {
|
|
9
9
|
try {
|
|
10
|
-
req.
|
|
10
|
+
req.caronteUser = await client.validateToken(token);
|
|
11
11
|
} catch {
|
|
12
12
|
}
|
|
13
13
|
}
|
|
@@ -21,11 +21,11 @@ function argos(client) {
|
|
|
21
21
|
next();
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
24
|
-
if (!req.
|
|
24
|
+
if (!req.caronteUser) {
|
|
25
25
|
res.status(401).json({ error: "unauthorized", detail: "Bearer token required." });
|
|
26
26
|
return;
|
|
27
27
|
}
|
|
28
|
-
const allowed = client.checkPermission(req.
|
|
28
|
+
const allowed = client.checkPermission(req.caronteUser, id, resolvedMethod);
|
|
29
29
|
if (!allowed) {
|
|
30
30
|
res.status(403).json({ error: "forbidden", detail: "Insufficient permissions." });
|
|
31
31
|
return;
|
|
@@ -36,6 +36,6 @@ function argos(client) {
|
|
|
36
36
|
return { middleware, guard };
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
export {
|
|
39
|
+
export { caronte };
|
|
40
40
|
//# sourceMappingURL=express.js.map
|
|
41
41
|
//# sourceMappingURL=express.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/express.ts"],"names":[],"mappings":";;;AAsBO,SAAS,
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/express.ts"],"names":[],"mappings":";;;AAsBO,SAAS,QAAQ,MAAA,EAA0C;AAEhE,EAAA,MAAM,UAAA,GAAa,OAAO,GAAA,EAAc,IAAA,EAAgB,IAAA,KAAsC;AAC5F,IAAA,MAAM,IAAA,GAAO,GAAA,CAAI,OAAA,CAAQ,aAAA,IAAiB,EAAA;AAC1C,IAAA,MAAM,CAAC,MAAA,EAAQ,KAAK,CAAA,GAAI,IAAA,CAAK,MAAM,GAAG,CAAA;AACtC,IAAA,IAAI,MAAA,EAAQ,WAAA,EAAY,KAAM,QAAA,IAAY,KAAA,EAAO;AAC/C,MAAA,IAAI;AACF,QAAA,GAAA,CAAI,WAAA,GAAc,MAAM,MAAA,CAAO,aAAA,CAAc,KAAK,CAAA;AAAA,MACpD,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AACA,IAAA,IAAA,EAAK;AAAA,EACP,CAAA;AAEA,EAAA,MAAM,KAAA,GAAQ,CAAC,EAAA,EAAY,KAAA,EAAe,MAAA,KAAoB;AAC5D,IAAA,MAAM,cAAA,GAAiB,MAAA,IAAU,YAAA,CAAa,EAAE,CAAA;AAChD,IAAA,iBAAA,CAAkB,EAAE,UAAA,EAAY,EAAA,EAAI,MAAA,EAAQ,cAAA,EAAgB,OAAO,CAAA;AAEnE,IAAA,OAAO,CAAC,GAAA,EAAc,GAAA,EAAe,IAAA,KAA6B;AAChE,MAAA,IAAI,UAAU,QAAA,EAAU;AAAE,QAAA,IAAA,EAAK;AAAG,QAAA;AAAA,MAAQ;AAE1C,MAAA,IAAI,CAAC,IAAI,WAAA,EAAa;AACpB,QAAA,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,EAAE,KAAA,EAAO,cAAA,EAAgB,MAAA,EAAQ,wBAAA,EAA0B,CAAA;AAChF,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,UAAU,MAAA,CAAO,eAAA,CAAgB,GAAA,CAAI,WAAA,EAAa,IAAI,cAAc,CAAA;AAC1E,MAAA,IAAI,CAAC,OAAA,EAAS;AACZ,QAAA,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,EAAE,KAAA,EAAO,WAAA,EAAa,MAAA,EAAQ,2BAAA,EAA6B,CAAA;AAChF,QAAA;AAAA,MACF;AAEA,MAAA,IAAA,EAAK;AAAA,IACP,CAAA;AAAA,EACF,CAAA;AAEA,EAAA,OAAO,EAAE,YAAY,KAAA,EAAM;AAC7B","file":"express.js","sourcesContent":["import type { NextFunction, Request, Response } from 'express';\nimport { CaronteClient } from '../client.js';\nimport { CaronteTokenError } from '../exceptions.js';\nimport type { TokenClaims } from '../models.js';\nimport { detectMethod, registerOperation } from '../registry.js';\n\ndeclare global {\n namespace Express {\n interface Request {\n caronteUser?: TokenClaims;\n }\n }\n}\n\nexport interface CaronteMiddleware {\n /** Global middleware — validates the Bearer token and injects `req.caronteUser`. */\n middleware: (req: Request, res: Response, next: NextFunction) => Promise<void>;\n /** Per-route guard — registers the operation and enforces permission at request time. */\n guard: (id: string, level: string, method?: string) =>\n (req: Request, res: Response, next: NextFunction) => void;\n}\n\nexport function caronte(client: CaronteClient): CaronteMiddleware {\n\n const middleware = async (req: Request, _res: Response, next: NextFunction): Promise<void> => {\n const auth = req.headers.authorization ?? '';\n const [scheme, token] = auth.split(' ');\n if (scheme?.toLowerCase() === 'bearer' && token) {\n try {\n req.caronteUser = await client.validateToken(token);\n } catch {\n // invalid token — caronteUser stays undefined; guard handles the 401\n }\n }\n next();\n };\n\n const guard = (id: string, level: string, method?: string) => {\n const resolvedMethod = method ?? detectMethod(id);\n registerOperation({ identifier: id, method: resolvedMethod, level });\n\n return (req: Request, res: Response, next: NextFunction): void => {\n if (level === 'public') { next(); return; }\n\n if (!req.caronteUser) {\n res.status(401).json({ error: 'unauthorized', detail: 'Bearer token required.' });\n return;\n }\n\n const allowed = client.checkPermission(req.caronteUser, id, resolvedMethod);\n if (!allowed) {\n res.status(403).json({ error: 'forbidden', detail: 'Insufficient permissions.' });\n return;\n }\n\n next();\n };\n };\n\n return { middleware, guard };\n}"]}
|