@prosopo/api-express-router 3.1.98 → 3.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.
Files changed (35) hide show
  1. package/.turbo/turbo-build$colon$cjs.log +7 -6
  2. package/.turbo/turbo-build$colon$tsc.log +21 -21
  3. package/.turbo/turbo-build.log +7 -6
  4. package/CHANGELOG.md +35 -0
  5. package/dist/cjs/index.cjs +2 -0
  6. package/dist/cjs/middlewares/authMiddleware.cjs +25 -14
  7. package/dist/cjs/middlewares/jwtReplayGuard.cjs +28 -0
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +2 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/middlewares/authMiddleware.d.ts +7 -1
  13. package/dist/middlewares/authMiddleware.d.ts.map +1 -1
  14. package/dist/middlewares/authMiddleware.js +26 -15
  15. package/dist/middlewares/authMiddleware.js.map +1 -1
  16. package/dist/middlewares/jwtReplayGuard.d.ts +5 -0
  17. package/dist/middlewares/jwtReplayGuard.d.ts.map +1 -0
  18. package/dist/middlewares/jwtReplayGuard.js +28 -0
  19. package/dist/middlewares/jwtReplayGuard.js.map +1 -0
  20. package/dist/tests/apiExpressRouter.test-d.js +1 -1
  21. package/dist/tests/apiExpressRouter.test-d.js.map +1 -1
  22. package/dist/tests/unit/middlewares/authMiddleware.unit.test.js +94 -8
  23. package/dist/tests/unit/middlewares/authMiddleware.unit.test.js.map +1 -1
  24. package/dist/tests/unit/middlewares/jwtReplayGuard.unit.test.d.ts +2 -0
  25. package/dist/tests/unit/middlewares/jwtReplayGuard.unit.test.d.ts.map +1 -0
  26. package/dist/tests/unit/middlewares/jwtReplayGuard.unit.test.js +36 -0
  27. package/dist/tests/unit/middlewares/jwtReplayGuard.unit.test.js.map +1 -0
  28. package/package.json +9 -9
  29. package/src/index.ts +1 -0
  30. package/src/middlewares/authMiddleware.ts +73 -16
  31. package/src/middlewares/jwtReplayGuard.ts +56 -0
  32. package/src/tests/apiExpressRouter.test-d.ts +8 -2
  33. package/src/tests/unit/middlewares/authMiddleware.unit.test.ts +118 -11
  34. package/src/tests/unit/middlewares/jwtReplayGuard.unit.test.ts +53 -0
  35. package/tsconfig.tsbuildinfo +1 -1
@@ -1,5 +1,5 @@
1
1
 
2
- > @prosopo/api-express-router@3.1.98 build:cjs
2
+ > @prosopo/api-express-router@3.2.0 build:cjs
3
3
  > NODE_ENV=${NODE_ENV:-development}; vite build --config vite.cjs.config.ts --mode $NODE_ENV
4
4
 
5
5
  ViteCommonJSConfig: .
@@ -26,7 +26,6 @@ ViteCommonJSConfig: .
26
26
  {
27
27
  externals: [
28
28
  '@prosopo/api-route',
29
- The plugin "vite-tsconfig-paths" is detected. Vite now supports tsconfig paths resolution natively via the resolve.tsconfigPaths option. You can remove the plugin and set resolve.tsconfigPaths: true in your Vite config instead.
30
29
  '@prosopo/logger',
31
30
  '@prosopo/util',
32
31
  '@prosopo/common',
@@ -44,17 +43,19 @@ ViteCommonJSConfig: .
44
43
  '@prosopo/keyring'
45
44
  ]
46
45
  }
46
+ The plugin "vite-tsconfig-paths" is detected. Vite now supports tsconfig paths resolution natively via the resolve.tsconfigPaths option. You can remove the plugin and set resolve.tsconfigPaths: true in your Vite config instead.
47
47
  vite v8.1.5 building ssr environment for production...
48
48
  Bundle build started
49
49
  
50
- ✓ 7 modules transformed.
50
+ ✓ 8 modules transformed.
51
51
  rendering chunks...
52
52
  computing gzip size...
53
53
  dist/cjs/errorHandler.cjs 0.45 kB │ gzip: 0.25 kB
54
54
  dist/cjs/apiExpressRouterFactory.cjs 0.78 kB │ gzip: 0.35 kB
55
- dist/cjs/index.cjs 1.25 kB │ gzip: 0.37 kB
55
+ dist/cjs/middlewares/jwtReplayGuard.cjs 0.97 kB │ gzip: 0.53 kB
56
+ dist/cjs/index.cjs 1.42 kB │ gzip: 0.41 kB
56
57
  dist/cjs/endpointAdapter/apiExpressDefaultEndpointAdapter.cjs 1.49 kB │ gzip: 0.62 kB
57
- dist/cjs/middlewares/authMiddleware.cjs 1.71 kB │ gzip: 0.74 kB
58
58
  dist/cjs/middlewares/requestLoggerMiddleware.cjs 1.81 kB │ gzip: 0.78 kB
59
+ dist/cjs/middlewares/authMiddleware.cjs 2.47 kB │ gzip: 1.01 kB
59
60
 
60
- ✓ built in 40ms
61
+ ✓ built in 54ms
@@ -1,8 +1,8 @@
1
1
 
2
- > @prosopo/api-express-router@3.1.98 build:tsc
2
+ > @prosopo/api-express-router@3.2.0 build:tsc
3
3
  > tsc --build --verbose
4
4
 
5
- 8:26:05 PM - Projects in this build:
5
+ 12:39:13 PM - Projects in this build:
6
6
  * ../../dev/config/tsconfig.json
7
7
  * ../util/tsconfig.json
8
8
  * ../logger/tsconfig.json
@@ -22,41 +22,41 @@
22
22
  * ../env/tsconfig.json
23
23
  * tsconfig.json
24
24
 
25
- 8:26:05 PM - Project '../../dev/config/tsconfig.json' is up to date because newest input '../../dev/config/src/webpack/webpack.config.ts' is older than output '../../dev/config/tsconfig.tsbuildinfo'
25
+ 12:39:13 PM - Project '../../dev/config/tsconfig.json' is up to date because newest input '../../dev/config/src/webpack/webpack.config.ts' is older than output '../../dev/config/tsconfig.tsbuildinfo'
26
26
 
27
- 8:26:05 PM - Project '../util/tsconfig.json' is up to date because newest input '../util/src/url.ts' is older than output '../util/tsconfig.tsbuildinfo'
27
+ 12:39:13 PM - Project '../util/tsconfig.json' is up to date because newest input '../util/src/url.ts' is older than output '../util/tsconfig.tsbuildinfo'
28
28
 
29
- 8:26:05 PM - Project '../logger/tsconfig.json' is up to date because newest input '../logger/src/index.ts' is older than output '../logger/tsconfig.tsbuildinfo'
29
+ 12:39:14 PM - Project '../logger/tsconfig.json' is up to date because newest input '../logger/src/index.ts' is older than output '../logger/tsconfig.tsbuildinfo'
30
30
 
31
- 8:26:05 PM - Project '../api-route/tsconfig.json' is up to date because newest input '../api-route/src/apiRoutes.ts' is older than output '../api-route/tsconfig.tsbuildinfo'
31
+ 12:39:14 PM - Project '../api-route/tsconfig.json' is up to date because newest input '../api-route/src/apiRoutes.ts' is older than output '../api-route/tsconfig.tsbuildinfo'
32
32
 
33
- 8:26:05 PM - Project '../locale/tsconfig.json' is up to date because newest input '../locale/src/translationKey.ts' is older than output '../locale/tsconfig.tsbuildinfo'
33
+ 12:39:14 PM - Project '../locale/tsconfig.json' is up to date because newest input '../locale/src/translationKey.ts' is older than output '../locale/tsconfig.tsbuildinfo'
34
34
 
35
- 8:26:05 PM - Project '../util-crypto/tsconfig.json' is up to date because newest input '../util-crypto/src/types.ts' is older than output '../util-crypto/tsconfig.tsbuildinfo'
35
+ 12:39:14 PM - Project '../util-crypto/tsconfig.json' is up to date because newest input '../util-crypto/src/types.ts' is older than output '../util-crypto/tsconfig.tsbuildinfo'
36
36
 
37
- 8:26:05 PM - Project '../types/tsconfig.json' is up to date because newest input '../types/src/index.ts' is older than output '../types/tsconfig.tsbuildinfo'
37
+ 12:39:14 PM - Project '../types/tsconfig.json' is up to date because newest input '../types/src/types.test-d.ts' is older than output '../types/tsconfig.tsbuildinfo'
38
38
 
39
- 8:26:05 PM - Project '../common/tsconfig.json' is up to date because newest input '../common/src/index.ts' is older than output '../common/tsconfig.tsbuildinfo'
39
+ 12:39:14 PM - Project '../common/tsconfig.json' is up to date because newest input '../common/src/error.ts' is older than output '../common/tsconfig.tsbuildinfo'
40
40
 
41
- 8:26:05 PM - Project '../ipinfo/tsconfig.json' is up to date because newest input '../ipinfo/src/IpInfoService.ts' is older than output '../ipinfo/tsconfig.tsbuildinfo'
41
+ 12:39:14 PM - Project '../ipinfo/tsconfig.json' is up to date because newest input '../ipinfo/src/index.ts' is older than output '../ipinfo/tsconfig.tsbuildinfo'
42
42
 
43
- 8:26:05 PM - Project '../redis-client/tsconfig.json' is up to date because newest input '../redis-client/src/index.ts' is older than output '../redis-client/tsconfig.tsbuildinfo'
43
+ 12:39:14 PM - Project '../redis-client/tsconfig.json' is up to date because newest input '../redis-client/src/index.ts' is older than output '../redis-client/tsconfig.tsbuildinfo'
44
44
 
45
- 8:26:05 PM - Project '../api/tsconfig.json' is up to date because newest input '../api/src/index.ts' is older than output '../api/tsconfig.tsbuildinfo'
45
+ 12:39:14 PM - Project '../api/tsconfig.json' is up to date because newest input '../api/src/index.ts' is older than output '../api/tsconfig.tsbuildinfo'
46
46
 
47
- 8:26:05 PM - Project '../user-access-policy/tsconfig.json' is up to date because newest input '../user-access-policy/src/ruleRecord.ts' is older than output '../user-access-policy/tsconfig.tsbuildinfo'
47
+ 12:39:14 PM - Project '../user-access-policy/tsconfig.json' is up to date because newest input '../user-access-policy/src/transformRule.ts' is older than output '../user-access-policy/tsconfig.tsbuildinfo'
48
48
 
49
- 8:26:05 PM - Project '../types-database/tsconfig.json' is up to date because newest input '../types-database/src/index.ts' is older than output '../types-database/tsconfig.tsbuildinfo'
49
+ 12:39:14 PM - Project '../types-database/tsconfig.json' is up to date because newest input '../types-database/src/index.ts' is older than output '../types-database/tsconfig.tsbuildinfo'
50
50
 
51
- 8:26:05 PM - Project '../database/tsconfig.json' is up to date because newest input '../database/src/tests/mongoGlobalSetup.ts' is older than output '../database/tsconfig.tsbuildinfo'
51
+ 12:39:14 PM - Project '../database/tsconfig.json' is up to date because newest input '../database/src/tests/unit/captchaLabel.unit.test.ts' is older than output '../database/tsconfig.tsbuildinfo'
52
52
 
53
- 8:26:05 PM - Project '../keyring/tsconfig.json' is up to date because newest input '../keyring/src/keyring/keyring.test.ts' is older than output '../keyring/tsconfig.tsbuildinfo'
53
+ 12:39:14 PM - Project '../keyring/tsconfig.json' is up to date because newest input '../keyring/src/pair/decode.ts' is older than output '../keyring/tsconfig.tsbuildinfo'
54
54
 
55
- 8:26:05 PM - Project '../types-env/tsconfig.json' is up to date because newest input '../types-env/src/env.ts' is older than output '../types-env/tsconfig.tsbuildinfo'
55
+ 12:39:14 PM - Project '../types-env/tsconfig.json' is up to date because newest input '../types-env/src/index.ts' is older than output '../types-env/tsconfig.tsbuildinfo'
56
56
 
57
- 8:26:05 PM - Project '../env/tsconfig.json' is up to date because newest input '../env/src/env.ts' is older than output '../env/tsconfig.tsbuildinfo'
57
+ 12:39:14 PM - Project '../env/tsconfig.json' is up to date because newest input '../env/src/env.ts' is older than output '../env/tsconfig.tsbuildinfo'
58
58
 
59
- 8:26:05 PM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
59
+ 12:39:14 PM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
60
60
 
61
- 8:26:05 PM - Building project '/home/runner/work/captcha/captcha/packages/api-express-router/tsconfig.json'...
61
+ 12:39:14 PM - Building project '/home/runner/work/captcha/captcha/packages/api-express-router/tsconfig.json'...
62
62
 
@@ -1,9 +1,9 @@
1
1
 
2
- > @prosopo/api-express-router@3.1.98 build
2
+ > @prosopo/api-express-router@3.2.0 build
3
3
  > npm run build:cross-env -- --mode ${NODE_ENV:-development}
4
4
 
5
5
 
6
- > @prosopo/api-express-router@3.1.98 build:cross-env
6
+ > @prosopo/api-express-router@3.2.0 build:cross-env
7
7
  > vite build --config vite.esm.config.ts --mode production
8
8
 
9
9
  ViteEsmConfig: .
@@ -51,15 +51,16 @@ ViteEsmConfig: .
51
51
  vite v8.1.5 building ssr environment for production...
52
52
  Bundle build started
53
53
  
54
- ✓ 7 modules transformed.
54
+ ✓ 8 modules transformed.
55
55
  rendering chunks...
56
56
  computing gzip size...
57
57
  dist/_virtual/_rolldown/runtime.js 0.04 kB │ gzip: 0.06 kB
58
58
  dist/errorHandler.js 0.42 kB │ gzip: 0.24 kB
59
59
  dist/apiExpressRouterFactory.js 0.71 kB │ gzip: 0.34 kB
60
- dist/index.js 0.84 kB │ gzip: 0.31 kB
60
+ dist/index.js 0.94 kB │ gzip: 0.34 kB
61
+ dist/middlewares/jwtReplayGuard.js 0.95 kB │ gzip: 0.53 kB
61
62
  dist/endpointAdapter/apiExpressDefaultEndpointAdapter.js 1.30 kB │ gzip: 0.59 kB
62
- dist/middlewares/authMiddleware.js 1.60 kB │ gzip: 0.72 kB
63
63
  dist/middlewares/requestLoggerMiddleware.js 1.75 kB │ gzip: 0.76 kB
64
+ dist/middlewares/authMiddleware.js 2.34 kB │ gzip: 0.98 kB
64
65
 
65
- ✓ built in 35ms
66
+ ✓ built in 49ms
package/CHANGELOG.md CHANGED
@@ -1,5 +1,40 @@
1
1
  # @prosopo/api-express-router
2
2
 
3
+ ## 3.2.0
4
+ ### Minor Changes
5
+
6
+ - ede4352: Admin tokens can now be bound to one provider and used only once. `jwtVerify` takes optional checks for the `aud` claim and for the longest allowed lifetime, and the provider's admin check uses them. A token that names an audience must name this provider: its host, `https://` plus its host, or one of the values in `PROSOPO_ADMIN_JWT_AUDIENCE`. A token that carries a `jti` is accepted once per provider process. Tokens may live at most one hour (`PROSOPO_ADMIN_JWT_MAX_LIFETIME_SECONDS`).
7
+
8
+ Migration: tokens without `aud` or `jti` are still accepted, so current callers keep working. Callers should add both, for example `pair.jwtIssue({ expiresIn }, { aud: provider.url, jti: randomUUID() })`, minting one token per provider and per request. Callers that reuse one token for several requests to the same provider must mint a new one per request before they add `jti`. Once every caller sends `aud`, set `PROSOPO_ADMIN_JWT_REQUIRE_AUDIENCE=true` to refuse tokens without it.
9
+
10
+ ### Patch Changes
11
+
12
+ - 9ed0afd: 401 responses no longer include server internals. The admin auth check returned the whole error object, which included the request's i18n instance with its settings and file paths (about 16 KB per response), or whatever a failing check had thrown. It now returns `{ error: { code: 401, key, message } }`, the same shape as other API errors. The domain check returned unexpected errors, such as database errors, in the response body. It now returns a fixed message and logs the error.
13
+ - Updated dependencies [ede4352]
14
+ - Updated dependencies [de6bb08]
15
+ - Updated dependencies [2145922]
16
+ - Updated dependencies [b017dfb]
17
+ - Updated dependencies [8ed0eb8]
18
+ - Updated dependencies [06784d0]
19
+ - Updated dependencies [06784d0]
20
+ - Updated dependencies [8d7ba8c]
21
+ - Updated dependencies [995e954]
22
+ - Updated dependencies [97a799e]
23
+ - Updated dependencies [9fc1e8a]
24
+ - Updated dependencies [4461043]
25
+ - Updated dependencies [92edebd]
26
+ - Updated dependencies [54a07f3]
27
+ - Updated dependencies [b75e9b7]
28
+ - Updated dependencies [6d5b7f5]
29
+ - @prosopo/util-crypto@13.6.0
30
+ - @prosopo/types@5.12.0
31
+ - @prosopo/util@3.3.13
32
+ - @prosopo/locale@3.6.2
33
+ - @prosopo/common@3.1.61
34
+ - @prosopo/env@3.6.68
35
+ - @prosopo/logger@2.1.2
36
+ - @prosopo/api-route@2.6.62
37
+
3
38
  ## 3.1.98
4
39
  ### Patch Changes
5
40
 
@@ -3,6 +3,7 @@ const require_errorHandler = require("./errorHandler.cjs");
3
3
  const require_apiExpressRouterFactory = require("./apiExpressRouterFactory.cjs");
4
4
  const require_apiExpressDefaultEndpointAdapter = require("./endpointAdapter/apiExpressDefaultEndpointAdapter.cjs");
5
5
  const require_authMiddleware = require("./middlewares/authMiddleware.cjs");
6
+ const require_jwtReplayGuard = require("./middlewares/jwtReplayGuard.cjs");
6
7
  const require_requestLoggerMiddleware = require("./middlewares/requestLoggerMiddleware.cjs");
7
8
  //#region src/index.ts
8
9
  var apiExpressRouterFactory = new require_apiExpressRouterFactory.ApiExpressRouterFactory();
@@ -13,6 +14,7 @@ var createApiExpressDefaultEndpointAdapter = (logLevel, errorStatusCode = 500) =
13
14
  exports.apiExpressRouterFactory = apiExpressRouterFactory;
14
15
  exports.authMiddleware = require_authMiddleware.authMiddleware;
15
16
  exports.createApiExpressDefaultEndpointAdapter = createApiExpressDefaultEndpointAdapter;
17
+ exports.createMemoryJwtReplayGuard = require_jwtReplayGuard.createMemoryJwtReplayGuard;
16
18
  exports.handleErrors = require_errorHandler.handleErrors;
17
19
  exports.requestLoggerMiddleware = require_requestLoggerMiddleware.requestLoggerMiddleware;
18
20
  exports.verifySignature = require_authMiddleware.verifySignature;
@@ -1,36 +1,47 @@
1
1
  let _prosopo_common = require("@prosopo/common");
2
2
  let _polkadot_util = require("@polkadot/util");
3
+ let _prosopo_locale = require("@prosopo/locale");
3
4
  //#region src/middlewares/authMiddleware.ts
4
- var authMiddleware = (pair, authAccount) => {
5
+ var verifyWith = (key, jwt, options) => {
6
+ if (!key) return void 0;
7
+ const result = key.jwtVerify(jwt, options.verify);
8
+ return result.isValid ? result : void 0;
9
+ };
10
+ var isReplay = (result, options) => {
11
+ const payload = result.payload;
12
+ if (!options.replayGuard || !payload || typeof payload.jti !== "string") return false;
13
+ return !options.replayGuard.claim(`${payload.sub}:${payload.jti}`, payload.exp);
14
+ };
15
+ var authMiddleware = (pair, authAccount, options = {}) => {
5
16
  return async (req, res, next) => {
6
17
  try {
7
18
  const jwt = extractJWT(req);
8
- if (authAccount?.jwtVerify(jwt).isValid) {
19
+ const verified = verifyWith(authAccount, jwt, options) ?? verifyWith(pair, jwt, options);
20
+ if (verified && !isReplay(verified, options)) {
9
21
  next();
10
22
  return;
11
23
  }
12
- if (pair?.jwtVerify(jwt).isValid) {
13
- next();
14
- return;
15
- }
16
- res.status(401).json({ error: new _prosopo_common.ProsopoEnvError("API.UNAUTHORIZED", { context: {
17
- i18n: req.i18n,
18
- code: 401
19
- } }) });
24
+ unauthorized(req, res, "API.UNAUTHORIZED");
20
25
  return;
21
26
  } catch (err) {
22
27
  req.logger.error(() => ({
23
28
  err,
24
29
  msg: "Auth Middleware Error"
25
30
  }));
26
- res.status(401).json({
27
- error: "Unauthorized",
28
- message: err
29
- });
31
+ unauthorized(req, res, translationKeyOf(err));
30
32
  return;
31
33
  }
32
34
  };
33
35
  };
36
+ var translationKeyOf = (err) => err instanceof _prosopo_common.ProsopoBaseError && err.translationKey !== void 0 && (0, _prosopo_locale.isTranslationKey)(err.translationKey) ? err.translationKey : "API.UNAUTHORIZED";
37
+ var unauthorized = (req, res, key) => {
38
+ const body = { error: {
39
+ code: 401,
40
+ key,
41
+ message: req.i18n ? req.i18n.t(key) : key
42
+ } };
43
+ res.status(401).json(body);
44
+ };
34
45
  var extractJWT = (req) => {
35
46
  const authHeader = req.headers.Authorization || req.headers.authorization;
36
47
  if (!authHeader || typeof authHeader !== "string") throw new _prosopo_common.ProsopoApiError("GENERAL.MISSING_AUTH_HEADER", { context: {
@@ -0,0 +1,28 @@
1
+ //#region src/middlewares/jwtReplayGuard.ts
2
+ /**
3
+ * Per-process set of token ids seen until their expiry. Only tokens signed by
4
+ * an admin key reach it, so the size cap guards against a misbehaving issuer
5
+ * rather than an attacker; when full the oldest entry is dropped.
6
+ */
7
+ var createMemoryJwtReplayGuard = (maxEntries = 1e4, nowSeconds = () => Date.now() / 1e3) => {
8
+ const seen = /* @__PURE__ */ new Map();
9
+ const sweep = (now) => {
10
+ for (const [id, exp] of seen) if (exp < now) seen.delete(id);
11
+ };
12
+ return { claim(id, expiresAtSeconds) {
13
+ const now = nowSeconds();
14
+ const previous = seen.get(id);
15
+ if (previous !== void 0 && previous >= now) return false;
16
+ if (seen.size >= maxEntries) {
17
+ sweep(now);
18
+ if (seen.size >= maxEntries) {
19
+ const oldest = seen.keys().next();
20
+ if (!oldest.done) seen.delete(oldest.value);
21
+ }
22
+ }
23
+ seen.set(id, expiresAtSeconds);
24
+ return true;
25
+ } };
26
+ };
27
+ //#endregion
28
+ exports.createMemoryJwtReplayGuard = createMemoryJwtReplayGuard;
package/dist/index.d.ts CHANGED
@@ -6,5 +6,6 @@ declare const createApiExpressDefaultEndpointAdapter: (logLevel: LogLevel, error
6
6
  export { apiExpressRouterFactory, createApiExpressDefaultEndpointAdapter, type ApiExpressEndpointAdapter, };
7
7
  export * from "./errorHandler.js";
8
8
  export * from "./middlewares/authMiddleware.js";
9
+ export * from "./middlewares/jwtReplayGuard.js";
9
10
  export * from "./middlewares/requestLoggerMiddleware.js";
10
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAEvE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gDAAgD,CAAC;AAEhG,QAAA,MAAM,uBAAuB,yBAAgC,CAAC;AAE9D,QAAA,MAAM,sCAAsC,aACjC,QAAQ,+BAEhB,yBAEF,CAAC;AAEF,OAAO,EACN,uBAAuB,EACvB,sCAAsC,EACtC,KAAK,yBAAyB,GAC9B,CAAC;AAEF,cAAc,mBAAmB,CAAC;AAClC,cAAc,iCAAiC,CAAC;AAChD,cAAc,0CAA0C,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAEvE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gDAAgD,CAAC;AAEhG,QAAA,MAAM,uBAAuB,yBAAgC,CAAC;AAE9D,QAAA,MAAM,sCAAsC,aACjC,QAAQ,+BAEhB,yBAEF,CAAC;AAEF,OAAO,EACN,uBAAuB,EACvB,sCAAsC,EACtC,KAAK,yBAAyB,GAC9B,CAAC;AAEF,cAAc,mBAAmB,CAAC;AAClC,cAAc,iCAAiC,CAAC;AAChD,cAAc,iCAAiC,CAAC;AAChD,cAAc,0CAA0C,CAAC"}
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ import { handleErrors } from "./errorHandler.js";
3
3
  import { ApiExpressRouterFactory } from "./apiExpressRouterFactory.js";
4
4
  import { ApiExpressDefaultEndpointAdapter } from "./endpointAdapter/apiExpressDefaultEndpointAdapter.js";
5
5
  import { authMiddleware, verifySignature } from "./middlewares/authMiddleware.js";
6
+ import { createMemoryJwtReplayGuard } from "./middlewares/jwtReplayGuard.js";
6
7
  import { requestLoggerMiddleware } from "./middlewares/requestLoggerMiddleware.js";
7
8
  //#region src/index.ts
8
9
  var apiExpressRouterFactory = new ApiExpressRouterFactory();
@@ -10,4 +11,4 @@ var createApiExpressDefaultEndpointAdapter = (logLevel, errorStatusCode = 500) =
10
11
  return new ApiExpressDefaultEndpointAdapter(logLevel, errorStatusCode);
11
12
  };
12
13
  //#endregion
13
- export { apiExpressRouterFactory, authMiddleware, createApiExpressDefaultEndpointAdapter, handleErrors, requestLoggerMiddleware, verifySignature };
14
+ export { apiExpressRouterFactory, authMiddleware, createApiExpressDefaultEndpointAdapter, createMemoryJwtReplayGuard, handleErrors, requestLoggerMiddleware, verifySignature };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,gCAAgC,EAAE,MAAM,uDAAuD,CAAC;AAGzG,MAAM,uBAAuB,GAAG,IAAI,uBAAuB,EAAE,CAAC;AAE9D,MAAM,sCAAsC,GAAG,CAC9C,QAAkB,EAClB,eAAe,GAAG,GAAG,EACO,EAAE;IAC9B,OAAO,IAAI,gCAAgC,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;AACxE,CAAC,CAAC;AAEF,OAAO,EACN,uBAAuB,EACvB,sCAAsC,GAEtC,CAAC;AAEF,cAAc,mBAAmB,CAAC;AAClC,cAAc,iCAAiC,CAAC;AAChD,cAAc,0CAA0C,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,gCAAgC,EAAE,MAAM,uDAAuD,CAAC;AAGzG,MAAM,uBAAuB,GAAG,IAAI,uBAAuB,EAAE,CAAC;AAE9D,MAAM,sCAAsC,GAAG,CAC9C,QAAkB,EAClB,eAAe,GAAG,GAAG,EACO,EAAE;IAC9B,OAAO,IAAI,gCAAgC,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;AACxE,CAAC,CAAC;AAEF,OAAO,EACN,uBAAuB,EACvB,sCAAsC,GAEtC,CAAC;AAEF,cAAc,mBAAmB,CAAC;AAClC,cAAc,iCAAiC,CAAC;AAChD,cAAc,iCAAiC,CAAC;AAChD,cAAc,0CAA0C,CAAC"}
@@ -1,5 +1,11 @@
1
1
  import type { KeyringPair } from "@prosopo/types";
2
+ import type { JWTVerifyOptions } from "@prosopo/util-crypto";
2
3
  import type { NextFunction, Request, Response } from "express";
3
- export declare const authMiddleware: (pair: KeyringPair | undefined, authAccount?: KeyringPair | undefined) => (req: Request, res: Response, next: NextFunction) => Promise<void>;
4
+ import type { JwtReplayGuard } from "./jwtReplayGuard.js";
5
+ export type AuthMiddlewareOptions = {
6
+ verify?: JWTVerifyOptions;
7
+ replayGuard?: JwtReplayGuard;
8
+ };
9
+ export declare const authMiddleware: (pair: KeyringPair | undefined, authAccount?: KeyringPair | undefined, options?: AuthMiddlewareOptions) => (req: Request, res: Response, next: NextFunction) => Promise<void>;
4
10
  export declare const verifySignature: (signature: string, message: string, pair: KeyringPair) => void;
5
11
  //# sourceMappingURL=authMiddleware.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"authMiddleware.d.ts","sourceRoot":"","sources":["../../src/middlewares/authMiddleware.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE/D,eAAO,MAAM,cAAc,SACpB,WAAW,GAAG,SAAS,gBACf,WAAW,GAAG,SAAS,WAElB,OAAO,OAAO,QAAQ,QAAQ,YAAY,kBA4B7D,CAAC;AAsBF,eAAO,MAAM,eAAe,cAChB,MAAM,WACR,MAAM,QACT,WAAW,SAejB,CAAC"}
1
+ {"version":3,"file":"authMiddleware.d.ts","sourceRoot":"","sources":["../../src/middlewares/authMiddleware.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAgB,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,KAAK,EAEX,gBAAgB,EAEhB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,MAAM,MAAM,qBAAqB,GAAG;IAEnC,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAK1B,WAAW,CAAC,EAAE,cAAc,CAAC;CAC7B,CAAC;AA0BF,eAAO,MAAM,cAAc,SACpB,WAAW,GAAG,SAAS,gBACf,WAAW,GAAG,SAAS,YAC5B,qBAAqB,WAEX,OAAO,OAAO,QAAQ,QAAQ,YAAY,kBAsB7D,CAAC;AA4CF,eAAO,MAAM,eAAe,cAChB,MAAM,WACR,MAAM,QACT,WAAW,SAejB,CAAC"}
@@ -1,36 +1,47 @@
1
- import { ProsopoApiError, ProsopoEnvError } from "@prosopo/common";
1
+ import { ProsopoApiError, ProsopoBaseError } from "@prosopo/common";
2
2
  import { hexToU8a } from "@polkadot/util";
3
+ import { isTranslationKey } from "@prosopo/locale";
3
4
  //#region src/middlewares/authMiddleware.ts
4
- var authMiddleware = (pair, authAccount) => {
5
+ var verifyWith = (key, jwt, options) => {
6
+ if (!key) return void 0;
7
+ const result = key.jwtVerify(jwt, options.verify);
8
+ return result.isValid ? result : void 0;
9
+ };
10
+ var isReplay = (result, options) => {
11
+ const payload = result.payload;
12
+ if (!options.replayGuard || !payload || typeof payload.jti !== "string") return false;
13
+ return !options.replayGuard.claim(`${payload.sub}:${payload.jti}`, payload.exp);
14
+ };
15
+ var authMiddleware = (pair, authAccount, options = {}) => {
5
16
  return async (req, res, next) => {
6
17
  try {
7
18
  const jwt = extractJWT(req);
8
- if (authAccount?.jwtVerify(jwt).isValid) {
19
+ const verified = verifyWith(authAccount, jwt, options) ?? verifyWith(pair, jwt, options);
20
+ if (verified && !isReplay(verified, options)) {
9
21
  next();
10
22
  return;
11
23
  }
12
- if (pair?.jwtVerify(jwt).isValid) {
13
- next();
14
- return;
15
- }
16
- res.status(401).json({ error: new ProsopoEnvError("API.UNAUTHORIZED", { context: {
17
- i18n: req.i18n,
18
- code: 401
19
- } }) });
24
+ unauthorized(req, res, "API.UNAUTHORIZED");
20
25
  return;
21
26
  } catch (err) {
22
27
  req.logger.error(() => ({
23
28
  err,
24
29
  msg: "Auth Middleware Error"
25
30
  }));
26
- res.status(401).json({
27
- error: "Unauthorized",
28
- message: err
29
- });
31
+ unauthorized(req, res, translationKeyOf(err));
30
32
  return;
31
33
  }
32
34
  };
33
35
  };
36
+ var translationKeyOf = (err) => err instanceof ProsopoBaseError && err.translationKey !== void 0 && isTranslationKey(err.translationKey) ? err.translationKey : "API.UNAUTHORIZED";
37
+ var unauthorized = (req, res, key) => {
38
+ const body = { error: {
39
+ code: 401,
40
+ key,
41
+ message: req.i18n ? req.i18n.t(key) : key
42
+ } };
43
+ res.status(401).json(body);
44
+ };
34
45
  var extractJWT = (req) => {
35
46
  const authHeader = req.headers.Authorization || req.headers.authorization;
36
47
  if (!authHeader || typeof authHeader !== "string") throw new ProsopoApiError("GENERAL.MISSING_AUTH_HEADER", { context: {
@@ -1 +1 @@
1
- {"version":3,"file":"authMiddleware.js","sourceRoot":"","sources":["../../src/middlewares/authMiddleware.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAKnE,MAAM,CAAC,MAAM,cAAc,GAAG,CAC7B,IAA6B,EAC7B,WAAqC,EACpC,EAAE;IACH,OAAO,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;QAChE,IAAI,CAAC;YACJ,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;YAE5B,IAAI,KAAkC,CAAC;YAEvC,IAAI,WAAW,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;gBACzC,IAAI,EAAE,CAAC;gBACP,OAAO;YACR,CAAC;YAED,IAAI,IAAI,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;gBAClC,IAAI,EAAE,CAAC;gBACP,OAAO;YACR,CAAC;YAED,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACpB,KAAK,EAAE,IAAI,eAAe,CAAC,KAAK,IAAI,kBAAkB,EAAE;oBACvD,OAAO,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE;iBACtC,CAAC;aACF,CAAC,CAAC;YACH,OAAO;QACR,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,uBAAuB,EAAE,CAAC,CAAC,CAAC;YAChE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;YAC9D,OAAO;QACR,CAAC;IACF,CAAC,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,UAAU,GAAG,CAAC,GAAY,EAAE,EAAE;IACnC,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,IAAI,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC;IAE1E,IAAI,CAAC,UAAU,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE,CAAC;QACnD,MAAM,IAAI,eAAe,CAAC,6BAA6B,EAAE;YACxD,OAAO,EAAE,EAAE,KAAK,EAAE,8BAA8B,EAAE,IAAI,EAAE,GAAG,EAAE;SAC7D,CAAC,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IAE9C,IAAI,CAAC,GAAG,EAAE,CAAC;QACV,MAAM,IAAI,eAAe,CAAC,qBAAqB,EAAE;YAChD,OAAO,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,EAAE;SAC5C,CAAC,CAAC;IACJ,CAAC;IAED,OAAO,GAAU,CAAC;AACnB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG,CAC9B,SAAiB,EACjB,OAAe,EACf,IAAiB,EAChB,EAAE;IACH,MAAM,KAAK,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;IAElC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,eAAe,CAAC,2BAA2B,EAAE;YACtD,OAAO,EAAE;gBACR,KAAK,EAAE,+BAA+B;gBACtC,IAAI,EAAE,GAAG;gBACT,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,OAAO;gBACP,SAAS;aACT;SACD,CAAC,CAAC;IACJ,CAAC;AACF,CAAC,CAAC"}
1
+ {"version":3,"file":"authMiddleware.js","sourceRoot":"","sources":["../../src/middlewares/authMiddleware.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export interface JwtReplayGuard {
2
+ claim(id: string, expiresAtSeconds: number): boolean;
3
+ }
4
+ export declare const createMemoryJwtReplayGuard: (maxEntries?: number, nowSeconds?: () => number) => JwtReplayGuard;
5
+ //# sourceMappingURL=jwtReplayGuard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwtReplayGuard.d.ts","sourceRoot":"","sources":["../../src/middlewares/jwtReplayGuard.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,cAAc;IAK9B,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC;CACrD;AAOD,eAAO,MAAM,0BAA0B,qCAE1B,MAAM,MAAM,KACtB,cAyBF,CAAC"}
@@ -0,0 +1,28 @@
1
+ //#region src/middlewares/jwtReplayGuard.ts
2
+ /**
3
+ * Per-process set of token ids seen until their expiry. Only tokens signed by
4
+ * an admin key reach it, so the size cap guards against a misbehaving issuer
5
+ * rather than an attacker; when full the oldest entry is dropped.
6
+ */
7
+ var createMemoryJwtReplayGuard = (maxEntries = 1e4, nowSeconds = () => Date.now() / 1e3) => {
8
+ const seen = /* @__PURE__ */ new Map();
9
+ const sweep = (now) => {
10
+ for (const [id, exp] of seen) if (exp < now) seen.delete(id);
11
+ };
12
+ return { claim(id, expiresAtSeconds) {
13
+ const now = nowSeconds();
14
+ const previous = seen.get(id);
15
+ if (previous !== void 0 && previous >= now) return false;
16
+ if (seen.size >= maxEntries) {
17
+ sweep(now);
18
+ if (seen.size >= maxEntries) {
19
+ const oldest = seen.keys().next();
20
+ if (!oldest.done) seen.delete(oldest.value);
21
+ }
22
+ }
23
+ seen.set(id, expiresAtSeconds);
24
+ return true;
25
+ } };
26
+ };
27
+ //#endregion
28
+ export { createMemoryJwtReplayGuard };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwtReplayGuard.js","sourceRoot":"","sources":["../../src/middlewares/jwtReplayGuard.ts"],"names":[],"mappings":"AA2BA,MAAM,CAAC,MAAM,0BAA0B,GAAG,CACzC,UAAU,GAAG,MAAM,EACnB,aAA2B,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,EACjC,EAAE;IACnB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEvC,MAAM,KAAK,GAAG,CAAC,GAAW,EAAQ,EAAE;QACnC,KAAK,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;YAC9B,IAAI,GAAG,GAAG,GAAG;gBAAE,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAChC,CAAC;IACF,CAAC,CAAC;IAEF,OAAO;QACN,KAAK,CAAC,EAAU,EAAE,gBAAwB;YACzC,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;YACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC9B,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,IAAI,GAAG;gBAAE,OAAO,KAAK,CAAC;YAC5D,IAAI,IAAI,CAAC,IAAI,IAAI,UAAU,EAAE,CAAC;gBAC7B,KAAK,CAAC,GAAG,CAAC,CAAC;gBACX,IAAI,IAAI,CAAC,IAAI,IAAI,UAAU,EAAE,CAAC;oBAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;oBAClC,IAAI,CAAC,MAAM,CAAC,IAAI;wBAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC7C,CAAC;YACF,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;YAC/B,OAAO,IAAI,CAAC;QACb,CAAC;KACD,CAAC;AACH,CAAC,CAAC"}
@@ -41,7 +41,7 @@ describe("the endpoint adapter interface", () => {
41
41
  });
42
42
  });
43
43
  describe("the middleware", () => {
44
- test("authMiddleware takes an optional pair and an optional auth account", () => {
44
+ test("authMiddleware takes an optional pair, auth account and options", () => {
45
45
  expectTypeOf(authMiddleware).parameters.toEqualTypeOf();
46
46
  });
47
47
  test("authMiddleware returns an async express handler", () => {
@@ -1 +1 @@
1
- {"version":3,"file":"apiExpressRouter.test-d.js","sourceRoot":"","sources":["../../src/tests/apiExpressRouter.test-d.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"apiExpressRouter.test-d.js","sourceRoot":"","sources":["../../src/tests/apiExpressRouter.test-d.ts"],"names":[],"mappings":"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"}