@daltonr/authwrite-express 0.1.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/dist/index.d.ts +24 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/package.json +41 -0
- package/src/index.ts +99 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { RequestHandler, Request, Response } from 'express';
|
|
2
|
+
import type { AuthEvaluator, Decision, Subject, Resource } from '@daltonr/authwrite-core';
|
|
3
|
+
declare global {
|
|
4
|
+
namespace Express {
|
|
5
|
+
interface Request {
|
|
6
|
+
authDecision?: Decision;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export interface AuthMiddlewareConfig<S extends Subject = Subject, R extends Resource = Resource> {
|
|
11
|
+
engine: AuthEvaluator<S, R>;
|
|
12
|
+
subject: (req: Request) => S | Promise<S>;
|
|
13
|
+
resource?: (req: Request) => R | undefined | Promise<R | undefined>;
|
|
14
|
+
action: string | ((req: Request) => string);
|
|
15
|
+
onDeny?: (req: Request, res: Response, decision: Decision) => void | Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export declare function createAuthMiddleware<S extends Subject = Subject, R extends Resource = Resource>(config: AuthMiddlewareConfig<S, R>): RequestHandler;
|
|
18
|
+
export interface ExpressAuthConfig<S extends Subject = Subject, R extends Resource = Resource> {
|
|
19
|
+
engine: AuthEvaluator<S, R>;
|
|
20
|
+
subject: (req: Request) => S | Promise<S>;
|
|
21
|
+
resource?: (req: Request) => R | undefined | Promise<R | undefined>;
|
|
22
|
+
onDeny?: (req: Request, res: Response, decision: Decision) => void | Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
export declare function createExpressAuth<S extends Subject = Subject, R extends Resource = Resource>(base: ExpressAuthConfig<S, R>): (action: string | ((req: Request) => string), overrides?: Partial<ExpressAuthConfig<S, R>>) => RequestHandler;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// ─── Middleware factory ───────────────────────────────────────────────────────
|
|
2
|
+
export function createAuthMiddleware(config) {
|
|
3
|
+
return async (req, res, next) => {
|
|
4
|
+
try {
|
|
5
|
+
const subject = await config.subject(req);
|
|
6
|
+
const resource = config.resource ? await config.resource(req) : undefined;
|
|
7
|
+
const action = typeof config.action === 'function' ? config.action(req) : config.action;
|
|
8
|
+
const decision = await config.engine.evaluate({ subject, resource, action });
|
|
9
|
+
req.authDecision = decision;
|
|
10
|
+
if (decision.allowed) {
|
|
11
|
+
next();
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
if (config.onDeny) {
|
|
15
|
+
await config.onDeny(req, res, decision);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
res.status(403).json({ error: 'forbidden', reason: decision.reason });
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
next(err);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export function createExpressAuth(base) {
|
|
26
|
+
return function auth(action, overrides) {
|
|
27
|
+
return createAuthMiddleware({ ...base, ...overrides, action });
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA0BA,iFAAiF;AAEjF,MAAM,UAAU,oBAAoB,CAGlC,MAAkC;IAClC,OAAO,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAC9B,IAAI,CAAC;YACH,MAAM,OAAO,GAAI,MAAM,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;YAC1C,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;YACzE,MAAM,MAAM,GAAK,OAAO,MAAM,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAA;YAEzF,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAE3E;YAAC,GAA4C,CAAC,YAAY,GAAG,QAAQ,CAAA;YAEtE,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;gBACrB,IAAI,EAAE,CAAA;gBACN,OAAM;YACR,CAAC;YAED,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;gBAClB,MAAM,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAA;gBACvC,OAAM;YACR,CAAC;YAED,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAA;QACvE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,GAAG,CAAC,CAAA;QACX,CAAC;IACH,CAAC,CAAA;AACH,CAAC;AA+BD,MAAM,UAAU,iBAAiB,CAG/B,IAA6B;IAC7B,OAAO,SAAS,IAAI,CAClB,MAA2C,EAC3C,SAA4C;QAE5C,OAAO,oBAAoB,CAAO,EAAE,GAAG,IAAI,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,CAAC,CAAA;IACtE,CAAC,CAAA;AACH,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@daltonr/authwrite-express",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"description": "Express middleware adapter for AuthEngine.",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/richardadalton/authwrite.git",
|
|
10
|
+
"directory": "packages/express"
|
|
11
|
+
},
|
|
12
|
+
"keywords": ["authorization", "authz", "express", "middleware"],
|
|
13
|
+
"sideEffects": false,
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"import": "./dist/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"main": "dist/index.js",
|
|
21
|
+
"types": "dist/index.d.ts",
|
|
22
|
+
"files": ["dist", "src", "README.md", "LICENSE"],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc -p tsconfig.json",
|
|
25
|
+
"clean": "rm -rf dist tsconfig.tsbuildinfo",
|
|
26
|
+
"prepublishOnly": "test -d dist && echo 'dist already built, skipping' || (npm run clean && npm run build)"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@daltonr/authwrite-core": "*"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"express": ">=4.0.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/express": "^5.0.0",
|
|
36
|
+
"express": "^5.0.0"
|
|
37
|
+
},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
}
|
|
41
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { RequestHandler, Request, Response } from 'express'
|
|
2
|
+
import type { AuthEvaluator, Decision, Subject, Resource } from '@daltonr/authwrite-core'
|
|
3
|
+
|
|
4
|
+
// ─── Request augmentation ─────────────────────────────────────────────────────
|
|
5
|
+
|
|
6
|
+
declare global {
|
|
7
|
+
namespace Express {
|
|
8
|
+
interface Request {
|
|
9
|
+
authDecision?: Decision
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// ─── Per-middleware config ────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
export interface AuthMiddlewareConfig<
|
|
17
|
+
S extends Subject = Subject,
|
|
18
|
+
R extends Resource = Resource,
|
|
19
|
+
> {
|
|
20
|
+
engine: AuthEvaluator<S, R>
|
|
21
|
+
subject: (req: Request) => S | Promise<S>
|
|
22
|
+
resource?: (req: Request) => R | undefined | Promise<R | undefined>
|
|
23
|
+
action: string | ((req: Request) => string)
|
|
24
|
+
onDeny?: (req: Request, res: Response, decision: Decision) => void | Promise<void>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ─── Middleware factory ───────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
export function createAuthMiddleware<
|
|
30
|
+
S extends Subject = Subject,
|
|
31
|
+
R extends Resource = Resource,
|
|
32
|
+
>(config: AuthMiddlewareConfig<S, R>): RequestHandler {
|
|
33
|
+
return async (req, res, next) => {
|
|
34
|
+
try {
|
|
35
|
+
const subject = await config.subject(req)
|
|
36
|
+
const resource = config.resource ? await config.resource(req) : undefined
|
|
37
|
+
const action = typeof config.action === 'function' ? config.action(req) : config.action
|
|
38
|
+
|
|
39
|
+
const decision = await config.engine.evaluate({ subject, resource, action })
|
|
40
|
+
|
|
41
|
+
;(req as Request & { authDecision: Decision }).authDecision = decision
|
|
42
|
+
|
|
43
|
+
if (decision.allowed) {
|
|
44
|
+
next()
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (config.onDeny) {
|
|
49
|
+
await config.onDeny(req, res, decision)
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
res.status(403).json({ error: 'forbidden', reason: decision.reason })
|
|
54
|
+
} catch (err) {
|
|
55
|
+
next(err)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ─── Bound auth factory ───────────────────────────────────────────────────────
|
|
61
|
+
//
|
|
62
|
+
// Captures the engine, subject resolver, resource resolver, and default onDeny
|
|
63
|
+
// once. Returns a function that produces middleware for a specific action.
|
|
64
|
+
// Eliminates the wrapper function pattern every integration reinvents:
|
|
65
|
+
//
|
|
66
|
+
// // Before:
|
|
67
|
+
// function authFor(action: string) {
|
|
68
|
+
// return createAuthMiddleware({ engine, subject: getUser, resource: getDoc, action, onDeny })
|
|
69
|
+
// }
|
|
70
|
+
//
|
|
71
|
+
// // After:
|
|
72
|
+
// const auth = createExpressAuth({ engine, subject: getUser, resource: getDoc, onDeny })
|
|
73
|
+
// app.get('/docs/:id', auth('read'), handler)
|
|
74
|
+
// app.post('/docs/:id', auth('write'), handler)
|
|
75
|
+
//
|
|
76
|
+
// Per-route overrides are supported:
|
|
77
|
+
// app.get('/docs/:id/admin', auth('admin-view', { resource: () => undefined }), handler)
|
|
78
|
+
|
|
79
|
+
export interface ExpressAuthConfig<
|
|
80
|
+
S extends Subject = Subject,
|
|
81
|
+
R extends Resource = Resource,
|
|
82
|
+
> {
|
|
83
|
+
engine: AuthEvaluator<S, R>
|
|
84
|
+
subject: (req: Request) => S | Promise<S>
|
|
85
|
+
resource?: (req: Request) => R | undefined | Promise<R | undefined>
|
|
86
|
+
onDeny?: (req: Request, res: Response, decision: Decision) => void | Promise<void>
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function createExpressAuth<
|
|
90
|
+
S extends Subject = Subject,
|
|
91
|
+
R extends Resource = Resource,
|
|
92
|
+
>(base: ExpressAuthConfig<S, R>) {
|
|
93
|
+
return function auth(
|
|
94
|
+
action: string | ((req: Request) => string),
|
|
95
|
+
overrides?: Partial<ExpressAuthConfig<S, R>>,
|
|
96
|
+
): RequestHandler {
|
|
97
|
+
return createAuthMiddleware<S, R>({ ...base, ...overrides, action })
|
|
98
|
+
}
|
|
99
|
+
}
|