@boopkit/cap-oauth-server 0.1.4 → 0.3.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 (81) hide show
  1. package/capability.json +14 -2
  2. package/dist/authorization-code-service.d.ts +40 -11
  3. package/dist/authorization-code-service.d.ts.map +1 -1
  4. package/dist/authorization-code-service.js +93 -41
  5. package/dist/authorization-code-service.js.map +1 -1
  6. package/dist/bearer.d.ts +27 -4
  7. package/dist/bearer.d.ts.map +1 -1
  8. package/dist/bearer.js +4 -4
  9. package/dist/bearer.js.map +1 -1
  10. package/dist/client-prune.d.ts +102 -0
  11. package/dist/client-prune.d.ts.map +1 -0
  12. package/dist/client-prune.js +51 -0
  13. package/dist/client-prune.js.map +1 -0
  14. package/dist/client-registry-service.d.ts +78 -7
  15. package/dist/client-registry-service.d.ts.map +1 -1
  16. package/dist/client-registry-service.js +200 -52
  17. package/dist/client-registry-service.js.map +1 -1
  18. package/dist/config.d.ts +6 -0
  19. package/dist/config.d.ts.map +1 -1
  20. package/dist/config.js +1 -0
  21. package/dist/config.js.map +1 -1
  22. package/dist/connections-controller.js +1 -1
  23. package/dist/connections-controller.js.map +1 -1
  24. package/dist/consent-controller.d.ts +80 -6
  25. package/dist/consent-controller.d.ts.map +1 -1
  26. package/dist/consent-controller.js +134 -18
  27. package/dist/consent-controller.js.map +1 -1
  28. package/dist/discovery-controller.d.ts +2 -1
  29. package/dist/discovery-controller.d.ts.map +1 -1
  30. package/dist/discovery-controller.js +14 -4
  31. package/dist/discovery-controller.js.map +1 -1
  32. package/dist/discovery.d.ts +26 -0
  33. package/dist/discovery.d.ts.map +1 -1
  34. package/dist/discovery.js +55 -1
  35. package/dist/discovery.js.map +1 -1
  36. package/dist/errors.d.ts +38 -0
  37. package/dist/errors.d.ts.map +1 -1
  38. package/dist/errors.js +59 -0
  39. package/dist/errors.js.map +1 -1
  40. package/dist/grant-service.d.ts +11 -4
  41. package/dist/grant-service.d.ts.map +1 -1
  42. package/dist/grant-service.js +6 -3
  43. package/dist/grant-service.js.map +1 -1
  44. package/dist/index.d.ts +10 -5
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +18 -3
  47. package/dist/index.js.map +1 -1
  48. package/dist/metadata-document.d.ts +22 -0
  49. package/dist/metadata-document.d.ts.map +1 -1
  50. package/dist/metadata-document.js +44 -2
  51. package/dist/metadata-document.js.map +1 -1
  52. package/dist/outbound-target.d.ts +48 -0
  53. package/dist/outbound-target.d.ts.map +1 -0
  54. package/dist/outbound-target.js +211 -0
  55. package/dist/outbound-target.js.map +1 -0
  56. package/dist/rate-limit.d.ts +53 -11
  57. package/dist/rate-limit.d.ts.map +1 -1
  58. package/dist/rate-limit.js +27 -4
  59. package/dist/rate-limit.js.map +1 -1
  60. package/dist/redirect-uri.d.ts +17 -9
  61. package/dist/redirect-uri.d.ts.map +1 -1
  62. package/dist/redirect-uri.js +26 -10
  63. package/dist/redirect-uri.js.map +1 -1
  64. package/dist/registration-controller.d.ts +10 -0
  65. package/dist/registration-controller.d.ts.map +1 -1
  66. package/dist/registration-controller.js +10 -0
  67. package/dist/registration-controller.js.map +1 -1
  68. package/dist/same-site.d.ts +54 -0
  69. package/dist/same-site.d.ts.map +1 -0
  70. package/dist/same-site.js +124 -0
  71. package/dist/same-site.js.map +1 -0
  72. package/dist/token-controller.d.ts +21 -0
  73. package/dist/token-controller.d.ts.map +1 -1
  74. package/dist/token-controller.js +42 -18
  75. package/dist/token-controller.js.map +1 -1
  76. package/package.json +2 -2
  77. package/scaffold/reference/oauth-server.ref.md +276 -10
  78. package/scaffold/src/middleware/oauth-server.ts +33 -17
  79. package/scaffold/src/scripts/prune-oauth-clients.ts +86 -0
  80. package/scaffold/src/services/oauth-server.service.ts +24 -0
  81. package/scaffold/views/oauth-consent-error.hbs +10 -3
@@ -1,11 +1,32 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_REGISTRATION_RATE_WINDOW_MS = exports.DEFAULT_REGISTRATION_RATE_LIMIT = void 0;
4
- exports.createRegistrationRateLimiter = createRegistrationRateLimiter;
3
+ exports.createRegistrationRateLimiter = exports.DEFAULT_METADATA_RESOLUTION_RATE_LIMIT = exports.DEFAULT_REGISTRATION_RATE_WINDOW_MS = exports.DEFAULT_REGISTRATION_RATE_LIMIT = void 0;
4
+ exports.rateLimitAddress = rateLimitAddress;
5
+ exports.createFixedWindowRateLimiter = createFixedWindowRateLimiter;
5
6
  exports.DEFAULT_REGISTRATION_RATE_LIMIT = 10;
6
7
  exports.DEFAULT_REGISTRATION_RATE_WINDOW_MS = 60 * 60 * 1000;
8
+ /**
9
+ * First-sight metadata-document resolutions allowed per address per window.
10
+ *
11
+ * The same number as registration, and for the same reason: reaching it is not
12
+ * something an honest browser does. A resolution is charged only when the
13
+ * `client_id` is an https URL with **no row yet** — every later visit to the
14
+ * same client is served from the row and costs nothing — so ten distinct,
15
+ * never-before-seen connectors an hour from one address is already generous.
16
+ */
17
+ exports.DEFAULT_METADATA_RESOLUTION_RATE_LIMIT = 10;
7
18
  const DEFAULT_MAX_TRACKED_ADDRESSES = 10000;
8
- function createRegistrationRateLimiter(options = {}) {
19
+ /**
20
+ * The address a limiter counts against.
21
+ *
22
+ * Exported so the middleware and the consent handlers cannot pick different
23
+ * answers — two limiters keyed differently are two quotas, and the one an
24
+ * attacker would find is whichever is keyed more loosely.
25
+ */
26
+ function rateLimitAddress(req) {
27
+ return req.ip ?? req.socket?.remoteAddress ?? 'unknown';
28
+ }
29
+ function createFixedWindowRateLimiter(options = {}) {
9
30
  const limit = options.limit ?? exports.DEFAULT_REGISTRATION_RATE_LIMIT;
10
31
  const windowMs = options.windowMs ?? exports.DEFAULT_REGISTRATION_RATE_WINDOW_MS;
11
32
  const now = options.now ?? (() => Date.now());
@@ -36,7 +57,7 @@ function createRegistrationRateLimiter(options = {}) {
36
57
  return { allowed: true, retryAfterSeconds: 0 };
37
58
  }
38
59
  const middleware = (req, res, next) => {
39
- const address = req.ip ?? req.socket?.remoteAddress ?? 'unknown';
60
+ const address = rateLimitAddress(req);
40
61
  const verdict = check(address);
41
62
  if (verdict.allowed)
42
63
  return next();
@@ -52,4 +73,6 @@ function createRegistrationRateLimiter(options = {}) {
52
73
  reset: () => windows.clear(),
53
74
  };
54
75
  }
76
+ /** The registration endpoint's name for the same factory. */
77
+ exports.createRegistrationRateLimiter = createFixedWindowRateLimiter;
55
78
  //# sourceMappingURL=rate-limit.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":";;;AA8DA,sEAqDC;AArEY,QAAA,+BAA+B,GAAG,EAAE,CAAC;AACrC,QAAA,mCAAmC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAClE,MAAM,6BAA6B,GAAG,KAAM,CAAC;AAc7C,SAAgB,6BAA6B,CAC3C,UAA8B,EAAE;IAEhC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,uCAA+B,CAAC;IAC/D,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,2CAAmC,CAAC;IACzE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC9C,MAAM,UAAU,GAAG,OAAO,CAAC,mBAAmB,IAAI,6BAA6B,CAAC;IAEhF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE1C,SAAS,KAAK,CAAC,OAAe;QAC5B,MAAM,EAAE,GAAG,GAAG,EAAE,CAAC;QACjB,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAEtC,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;YACxC,wEAAwE;YACxE,yEAAyE;YACzE,sBAAsB;YACtB,IAAI,OAAO,CAAC,IAAI,IAAI,UAAU,EAAE,CAAC;gBAC/B,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;gBACrC,IAAI,CAAC,MAAM,CAAC,IAAI;oBAAE,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACjD,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,GAAG,QAAQ,EAAE,CAAC,CAAC;YAC3D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC,EAAE,CAAC;QACjD,CAAC;QAED,QAAQ,CAAC,KAAK,IAAI,CAAC,CAAC;QACpB,IAAI,QAAQ,CAAC,KAAK,GAAG,KAAK,EAAE,CAAC;YAC3B,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,iBAAiB,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,OAAO,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;aAC1E,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC,EAAE,CAAC;IACjD,CAAC;IAED,MAAM,UAAU,GAAmB,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;QACrF,MAAM,OAAO,GAAG,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,MAAM,EAAE,aAAa,IAAI,SAAS,CAAC;QACjE,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;QAC/B,IAAI,OAAO,CAAC,OAAO;YAAE,OAAO,IAAI,EAAE,CAAC;QAEnC,GAAG,CAAC,SAAS,CAAC,aAAa,EAAE,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC;QAChE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YACnB,KAAK,EAAE,yBAAyB;YAChC,iBAAiB,EAAE,mEAAmE;SACvF,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,OAAO;QACL,UAAU;QACV,KAAK;QACL,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE;KAC7B,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"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"}
@@ -25,16 +25,24 @@
25
25
  *
26
26
  * ## The matching rule
27
27
  *
28
- * **Exact string match**, with exactly one exception: a registered
29
- * `loopback-http` entry matches a presented URI that differs only in port. That
30
- * is RFC 8252 §7.3, and it is what Claude Code's published Client ID Metadata
31
- * Document requires — it binds an ephemeral loopback port at connect time, so
32
- * the port it will present is not knowable at registration.
28
+ * **Exact string match**, with exactly one exception: a registered entry whose
29
+ * *host* is `localhost` or `127.0.0.1` matches a presented URI that differs only
30
+ * in port. That is RFC 8252 §7.3, and it is what Claude Code's published Client
31
+ * ID Metadata Document requires — it binds an ephemeral loopback port at connect
32
+ * time, so the port it will present is not knowable at registration.
33
33
  *
34
- * The exception is scoped to `loopback-http` and not merely to a loopback
35
- * *host*. A private-use scheme has no TCP port to be ephemeral: the operating
36
- * system routes it by scheme name, so `myapp://localhost/cb` gets the plain
37
- * exact rule even though its host happens to read as loopback.
34
+ * The key is the host and not the `loopback-http` shape, so an
35
+ * `https://localhost/cb` registration gets its port relaxed too. §7.3 is about
36
+ * loopback **http**, so an argument exists for narrowing the exception to `http`
37
+ * alone — but a port on the loopback interface is ephemeral whichever scheme
38
+ * terminates it, and narrowing would break an operator running a local client
39
+ * over TLS for no security gain. That is a decision, not an oversight, and the
40
+ * test table pins it.
41
+ *
42
+ * A private-use scheme is refused the exception even so, because it has no TCP
43
+ * port to be ephemeral: the operating system routes it by scheme name, so
44
+ * `myapp://localhost/cb` gets the plain exact rule even though its host happens
45
+ * to read as loopback.
38
46
  *
39
47
  * The exception lives here, in one function with its own test table, rather
40
48
  * than being threaded through the authorization controller as a special case.
@@ -1 +1 @@
1
- {"version":3,"file":"redirect-uri.d.ts","sourceRoot":"","sources":["../src/redirect-uri.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAsDH,4DAA4D;AAC5D,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,eAAe,GAAG,aAAa,CAAC;AAEzE;;;;;;GAMG;AACH,MAAM,MAAM,yBAAyB,GACjC;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,gBAAgB,CAAC;IAAC,GAAG,EAAE,GAAG,CAAA;CAAE,GACrD;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AA6BxC;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,yBAAyB,CAsC5E;AAiBD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CA+C/E;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAE/F;AAED,0DAA0D;AAC1D,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD"}
1
+ {"version":3,"file":"redirect-uri.d.ts","sourceRoot":"","sources":["../src/redirect-uri.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AA8DH,4DAA4D;AAC5D,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,eAAe,GAAG,aAAa,CAAC;AAEzE;;;;;;GAMG;AACH,MAAM,MAAM,yBAAyB,GACjC;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,gBAAgB,CAAC;IAAC,GAAG,EAAE,GAAG,CAAA;CAAE,GACrD;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AA6BxC;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,yBAAyB,CAsC5E;AAiBD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CA+C/E;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAE/F;AAED,0DAA0D;AAC1D,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD"}
@@ -26,16 +26,24 @@
26
26
  *
27
27
  * ## The matching rule
28
28
  *
29
- * **Exact string match**, with exactly one exception: a registered
30
- * `loopback-http` entry matches a presented URI that differs only in port. That
31
- * is RFC 8252 §7.3, and it is what Claude Code's published Client ID Metadata
32
- * Document requires — it binds an ephemeral loopback port at connect time, so
33
- * the port it will present is not knowable at registration.
29
+ * **Exact string match**, with exactly one exception: a registered entry whose
30
+ * *host* is `localhost` or `127.0.0.1` matches a presented URI that differs only
31
+ * in port. That is RFC 8252 §7.3, and it is what Claude Code's published Client
32
+ * ID Metadata Document requires — it binds an ephemeral loopback port at connect
33
+ * time, so the port it will present is not knowable at registration.
34
34
  *
35
- * The exception is scoped to `loopback-http` and not merely to a loopback
36
- * *host*. A private-use scheme has no TCP port to be ephemeral: the operating
37
- * system routes it by scheme name, so `myapp://localhost/cb` gets the plain
38
- * exact rule even though its host happens to read as loopback.
35
+ * The key is the host and not the `loopback-http` shape, so an
36
+ * `https://localhost/cb` registration gets its port relaxed too. §7.3 is about
37
+ * loopback **http**, so an argument exists for narrowing the exception to `http`
38
+ * alone — but a port on the loopback interface is ephemeral whichever scheme
39
+ * terminates it, and narrowing would break an operator running a local client
40
+ * over TLS for no security gain. That is a decision, not an oversight, and the
41
+ * test table pins it.
42
+ *
43
+ * A private-use scheme is refused the exception even so, because it has no TCP
44
+ * port to be ephemeral: the operating system routes it by scheme name, so
45
+ * `myapp://localhost/cb` gets the plain exact rule even though its host happens
46
+ * to read as loopback.
39
47
  *
40
48
  * The exception lives here, in one function with its own test table, rather
41
49
  * than being threaded through the authorization controller as a special case.
@@ -50,7 +58,15 @@ exports.classifyRedirectUri = classifyRedirectUri;
50
58
  exports.matchRedirectUri = matchRedirectUri;
51
59
  exports.matchesAnyRedirectUri = matchesAnyRedirectUri;
52
60
  exports.isLoopbackHost = isLoopbackHost;
53
- /** Hosts RFC 8252 §7.3 lets a native app bind an ephemeral port on. */
61
+ /**
62
+ * Hosts a native app may bind an ephemeral port on (RFC 8252 §7.3).
63
+ *
64
+ * The IPv6 loopback `[::1]` is deliberately absent. §7.3 contemplates it, so
65
+ * this refuses a real client — but it refuses it at *registration*, where
66
+ * `validateRedirectUris` calls `http://[::1]:52731/cb` http on a non-loopback
67
+ * host, rather than silently at match time. The reference page says so under
68
+ * "Redirect URIs".
69
+ */
54
70
  const LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1']);
55
71
  /**
56
72
  * Schemes a private-use redirect may not claim, on three separate grounds.
@@ -1 +1 @@
1
- {"version":3,"file":"redirect-uri.js","sourceRoot":"","sources":["../src/redirect-uri.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"redirect-uri.js","sourceRoot":"","sources":["../src/redirect-uri.ts"],"names":[],"mappings":"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"}
@@ -33,6 +33,16 @@ export interface RegistrationHandlerDeps {
33
33
  *
34
34
  * `client_secret` is deliberately absent rather than null: this server issues
35
35
  * only public clients, and a client that found the field would try to use it.
36
+ *
37
+ * `registration_access_token` and `registration_client_uri` are absent for the
38
+ * same reason one step further out. They are RFC 7592's credential and address
39
+ * for managing a registration afterwards, and this server implements no such
40
+ * surface — `PUT`/`DELETE /oauth/register/:client_id` are unmounted by decision,
41
+ * not by omission. Emitting either field would hand a client a credential and a
42
+ * URL that resolve to a 404, which is worse than saying nothing: a client that
43
+ * reads them plans to re-register on failure rather than to re-register at all.
44
+ * If that decision is ever reversed, these two fields and the routes ship
45
+ * together or neither ships.
36
46
  */
37
47
  export declare function toRegistrationResponse(client: OauthClient): Record<string, unknown>;
38
48
  export declare function createRegistrationHandlers(deps: RegistrationHandlerDeps): {
@@ -1 +1 @@
1
- {"version":3,"file":"registration-controller.d.ts","sourceRoot":"","sources":["../src/registration-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAQ,KAAK,cAAc,EAAE,MAAM,SAAS,CAAC;AAE5D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAE1E,OAAO,EAAiC,KAAK,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACzF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,WAAW,uBAAuB;IACtC,cAAc,EAAE,qBAAqB,CAAC;CACvC;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAkBnF;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,uBAAuB,GAAG;IACzE,QAAQ,EAAE,cAAc,CAAC;CAC1B,CA6BA;AAED,MAAM,WAAW,yBAA0B,SAAQ,uBAAuB;IACxE;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,kBAAkB,GAAG,KAAK,CAAC;IACvC,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,yBAAyB,GAAG,MAAM,CAYnF"}
1
+ {"version":3,"file":"registration-controller.d.ts","sourceRoot":"","sources":["../src/registration-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAQ,KAAK,cAAc,EAAE,MAAM,SAAS,CAAC;AAE5D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAE1E,OAAO,EAAiC,KAAK,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACzF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,WAAW,uBAAuB;IACtC,cAAc,EAAE,qBAAqB,CAAC;CACvC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAkBnF;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,uBAAuB,GAAG;IACzE,QAAQ,EAAE,cAAc,CAAC;CAC1B,CA6BA;AAED,MAAM,WAAW,yBAA0B,SAAQ,uBAAuB;IACxE;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,kBAAkB,GAAG,KAAK,CAAC;IACvC,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,yBAAyB,GAAG,MAAM,CAYnF"}
@@ -11,6 +11,16 @@ const rate_limit_js_1 = require("./rate-limit.js");
11
11
  *
12
12
  * `client_secret` is deliberately absent rather than null: this server issues
13
13
  * only public clients, and a client that found the field would try to use it.
14
+ *
15
+ * `registration_access_token` and `registration_client_uri` are absent for the
16
+ * same reason one step further out. They are RFC 7592's credential and address
17
+ * for managing a registration afterwards, and this server implements no such
18
+ * surface — `PUT`/`DELETE /oauth/register/:client_id` are unmounted by decision,
19
+ * not by omission. Emitting either field would hand a client a credential and a
20
+ * URL that resolve to a 404, which is worse than saying nothing: a client that
21
+ * reads them plans to re-register on failure rather than to re-register at all.
22
+ * If that decision is ever reversed, these two fields and the routes ship
23
+ * together or neither ships.
14
24
  */
15
25
  function toRegistrationResponse(client) {
16
26
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"registration-controller.js","sourceRoot":"","sources":["../src/registration-controller.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"registration-controller.js","sourceRoot":"","sources":["../src/registration-controller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Did this request come from a page this server served?
3
+ *
4
+ * ## Why the consent form needs its own answer
5
+ *
6
+ * `POST /oauth/authorize` mints an authorization code, and the only credential
7
+ * it needs is the session cookie the browser attaches by itself. That is the
8
+ * exact shape cross-site request forgery exploits: an attacker's page
9
+ * auto-submits the form, the victim's browser attaches the cookie, and a code
10
+ * for the victim's account is redirected to a `redirect_uri` the attacker
11
+ * registered.
12
+ *
13
+ * PKCE does not help. It binds a code to whoever *asked* for it, and here the
14
+ * attacker is the client — they generate the challenge and hold the verifier.
15
+ * `state` does not help either: it is the client's CSRF token, echoed back
16
+ * untouched, and on this attack the attacker is the one who chose it. Neither
17
+ * mechanism was ever pointed at this direction.
18
+ *
19
+ * What was holding the line was a browser default — an unlabelled cookie is
20
+ * treated as `SameSite=Lax`, and Lax is not sent on a cross-site POST. The
21
+ * cookie is now labelled explicitly (see `createConsentHandlers`), but a
22
+ * defence that lives entirely in the user agent is one this server cannot
23
+ * assert, cannot test, and does not own. This is the server's own answer.
24
+ *
25
+ * ## What it reads, and why that is enough
26
+ *
27
+ * `Origin` and `Sec-Fetch-Site` are both **forbidden header names**: page
28
+ * script cannot set or remove either one, so on a form post from a hostile
29
+ * page the browser attaches them truthfully. An attacker can no more suppress
30
+ * them than they can read the cookie.
31
+ *
32
+ * A request carrying neither is accepted. It is not a browser, or it is one
33
+ * too old to label anything — and a forgery needs a browser, because a browser
34
+ * is the only thing that will attach somebody else's cookie. Refusing here
35
+ * would break every non-browser caller and buy nothing.
36
+ */
37
+ /** The parts of a request this check reads. `express.Request` satisfies it. */
38
+ export interface SameSiteRequest {
39
+ headers: Record<string, string | string[] | undefined>;
40
+ /** Express's resolved hostname — `X-Forwarded-Host` under `trust proxy`. */
41
+ hostname?: string;
42
+ }
43
+ /**
44
+ * Whether this request may be treated as originating from this site.
45
+ *
46
+ * `allowedOrigins` are origins the application declares as its own, matched
47
+ * exactly and before anything else. They exist for a deployment this function
48
+ * cannot work out on its own — a proxy that rewrites `Host` without setting
49
+ * `X-Forwarded-Host`, or a consent page deliberately served from a sibling
50
+ * host. Naming one is a decision about what counts as this application, so it
51
+ * is the application's to make and nothing here guesses at it.
52
+ */
53
+ export declare function isSameSiteRequest(req: SameSiteRequest, allowedOrigins?: readonly string[]): boolean;
54
+ //# sourceMappingURL=same-site.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"same-site.d.ts","sourceRoot":"","sources":["../src/same-site.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,+EAA+E;AAC/E,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACvD,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AA6DD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,eAAe,EACpB,cAAc,GAAE,SAAS,MAAM,EAAO,GACrC,OAAO,CAcT"}
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ /**
3
+ * Did this request come from a page this server served?
4
+ *
5
+ * ## Why the consent form needs its own answer
6
+ *
7
+ * `POST /oauth/authorize` mints an authorization code, and the only credential
8
+ * it needs is the session cookie the browser attaches by itself. That is the
9
+ * exact shape cross-site request forgery exploits: an attacker's page
10
+ * auto-submits the form, the victim's browser attaches the cookie, and a code
11
+ * for the victim's account is redirected to a `redirect_uri` the attacker
12
+ * registered.
13
+ *
14
+ * PKCE does not help. It binds a code to whoever *asked* for it, and here the
15
+ * attacker is the client — they generate the challenge and hold the verifier.
16
+ * `state` does not help either: it is the client's CSRF token, echoed back
17
+ * untouched, and on this attack the attacker is the one who chose it. Neither
18
+ * mechanism was ever pointed at this direction.
19
+ *
20
+ * What was holding the line was a browser default — an unlabelled cookie is
21
+ * treated as `SameSite=Lax`, and Lax is not sent on a cross-site POST. The
22
+ * cookie is now labelled explicitly (see `createConsentHandlers`), but a
23
+ * defence that lives entirely in the user agent is one this server cannot
24
+ * assert, cannot test, and does not own. This is the server's own answer.
25
+ *
26
+ * ## What it reads, and why that is enough
27
+ *
28
+ * `Origin` and `Sec-Fetch-Site` are both **forbidden header names**: page
29
+ * script cannot set or remove either one, so on a form post from a hostile
30
+ * page the browser attaches them truthfully. An attacker can no more suppress
31
+ * them than they can read the cookie.
32
+ *
33
+ * A request carrying neither is accepted. It is not a browser, or it is one
34
+ * too old to label anything — and a forgery needs a browser, because a browser
35
+ * is the only thing that will attach somebody else's cookie. Refusing here
36
+ * would break every non-browser caller and buy nothing.
37
+ */
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.isSameSiteRequest = isSameSiteRequest;
40
+ function headerOf(req, name) {
41
+ const value = req.headers?.[name];
42
+ const first = Array.isArray(value) ? value[0] : value;
43
+ return typeof first === 'string' && first !== '' ? first : undefined;
44
+ }
45
+ /**
46
+ * Whether `origin` names the host this request arrived on.
47
+ *
48
+ * The comparison is on host, not on the whole origin: the scheme is the
49
+ * deployment's business — a boop application in development is served over
50
+ * http — and a wrong scheme on the right host means a network attacker, which
51
+ * is `Secure` on the cookie rather than this function.
52
+ *
53
+ * `Host` is checked first and including the port, because a different port is a
54
+ * different origin and on a shared development host it is a different
55
+ * application altogether.
56
+ *
57
+ * The fallback to `req.hostname` is for a deployment behind a proxy that
58
+ * rewrites `Host`: with `trust proxy` set, express resolves
59
+ * `X-Forwarded-Host` there, which is the public name the browser used. It
60
+ * compares hostnames only, because `X-Forwarded-Host` frequently carries no
61
+ * port — and it is reached **only when express resolved a different name than
62
+ * `Host` carried**, i.e. when a proxy really did rewrite something. Otherwise
63
+ * an unproxied request whose port did not match would be rescued by a
64
+ * comparison that ignores the port, and the check above would mean nothing.
65
+ */
66
+ function matchesOwnHost(origin, req) {
67
+ let url;
68
+ try {
69
+ url = new URL(origin);
70
+ }
71
+ catch {
72
+ // `null`, an empty string, anything malformed. Not our host.
73
+ return false;
74
+ }
75
+ const host = headerOf(req, 'host');
76
+ const hostname = typeof req.hostname === 'string' ? req.hostname : undefined;
77
+ if (host !== undefined) {
78
+ if (url.host.toLowerCase() === host.toLowerCase())
79
+ return true;
80
+ if (hostname === undefined || hostname === '')
81
+ return false;
82
+ // Nothing was rewritten — the mismatch is real, and it is the port.
83
+ if (hostname.toLowerCase() === hostnameOf(host))
84
+ return false;
85
+ }
86
+ if (hostname === undefined || hostname === '')
87
+ return false;
88
+ return url.hostname.toLowerCase() === hostname.toLowerCase();
89
+ }
90
+ /** The host-header's name without its port. `undefined` if it will not parse. */
91
+ function hostnameOf(host) {
92
+ try {
93
+ return new URL(`http://${host}`).hostname.toLowerCase();
94
+ }
95
+ catch {
96
+ return undefined;
97
+ }
98
+ }
99
+ /**
100
+ * Whether this request may be treated as originating from this site.
101
+ *
102
+ * `allowedOrigins` are origins the application declares as its own, matched
103
+ * exactly and before anything else. They exist for a deployment this function
104
+ * cannot work out on its own — a proxy that rewrites `Host` without setting
105
+ * `X-Forwarded-Host`, or a consent page deliberately served from a sibling
106
+ * host. Naming one is a decision about what counts as this application, so it
107
+ * is the application's to make and nothing here guesses at it.
108
+ */
109
+ function isSameSiteRequest(req, allowedOrigins = []) {
110
+ const origin = headerOf(req, 'origin');
111
+ if (origin !== undefined && allowedOrigins.includes(origin))
112
+ return true;
113
+ // Refuse on any label that says this did not come from us, whether or not an
114
+ // Origin is present. `same-site` is refused alongside `cross-site`: this form
115
+ // only ever posts to the page that rendered it, so a sibling subdomain is as
116
+ // much a stranger here as anywhere else.
117
+ const site = headerOf(req, 'sec-fetch-site');
118
+ if (site !== undefined && site !== 'same-origin' && site !== 'none')
119
+ return false;
120
+ if (origin !== undefined)
121
+ return matchesOwnHost(origin, req);
122
+ return true;
123
+ }
124
+ //# sourceMappingURL=same-site.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"same-site.js","sourceRoot":"","sources":["../src/same-site.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;;AA8EH,8CAiBC;AAtFD,SAAS,QAAQ,CAAC,GAAoB,EAAE,IAAY;IAClD,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACtD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACvE,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,SAAS,cAAc,CAAC,MAAc,EAAE,GAAoB;IAC1D,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,6DAA6D;QAC7D,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACnC,MAAM,QAAQ,GAAG,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IAE7E,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,IAAI,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,WAAW,EAAE;YAAE,OAAO,IAAI,CAAC;QAC/D,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,EAAE;YAAE,OAAO,KAAK,CAAC;QAC5D,oEAAoE;QACpE,IAAI,QAAQ,CAAC,WAAW,EAAE,KAAK,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,CAAC;IAChE,CAAC;IAED,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IAC5D,OAAO,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,QAAQ,CAAC,WAAW,EAAE,CAAC;AAC/D,CAAC;AAED,iFAAiF;AACjF,SAAS,UAAU,CAAC,IAAY;IAC9B,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;IAC1D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,SAAgB,iBAAiB,CAC/B,GAAoB,EACpB,iBAAoC,EAAE;IAEtC,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACvC,IAAI,MAAM,KAAK,SAAS,IAAI,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IAEzE,6EAA6E;IAC7E,8EAA8E;IAC9E,6EAA6E;IAC7E,yCAAyC;IACzC,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;IAC7C,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,aAAa,IAAI,IAAI,KAAK,MAAM;QAAE,OAAO,KAAK,CAAC;IAElF,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,cAAc,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAE7D,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -41,6 +41,27 @@ import { type TokenService } from './token-service.js';
41
41
  * Superseded rows are bounded: they age out under the same
42
42
  * `createdAt + refresh ttl` rule as any other token, and revoking the grant
43
43
  * end-dates all of them at once.
44
+ *
45
+ * ## And so does replay of an authorization code, for the same reason
46
+ *
47
+ * RFC 6749 §4.1.2 and §10.5 ask for both halves: deny the replayed code, AND
48
+ * revoke the tokens already issued from it. Denying alone is the worse of the
49
+ * two failures, because the redemption that succeeded was the ATTACKER'S. A
50
+ * code stolen off a loopback callback and raced to this endpoint leaves the
51
+ * attacker holding a working token pair, while the legitimate client's
52
+ * redemption comes back as a generic `invalid_grant` that reads exactly like an
53
+ * ordinary expiry — nobody is told anything, and nothing is undone.
54
+ *
55
+ * So a second redemption of one code ends the grant with `reason:
56
+ * "code_replay"` and sweeps its tokens, the same write `refresh_reuse` makes.
57
+ * The grant ended is the one belonging to the client the CODE was issued to,
58
+ * which `redeem` reports back — never the `client_id` the replay happened to
59
+ * carry, or a stolen code would be a way to disconnect a bystander.
60
+ *
61
+ * The one honest limit: a replay arriving after the code's own 120 seconds
62
+ * resolves to nobody, so there is no grant to reach and the answer is a bare
63
+ * `invalid_grant`. Every replay that is a race — which is every replay that
64
+ * matters — arrives well inside that window.
44
65
  */
45
66
  /** RFC 6749 §5.1's success body. */
46
67
  export interface TokenResponse {
@@ -1 +1 @@
1
- {"version":3,"file":"token-controller.d.ts","sourceRoot":"","sources":["../src/token-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAkC,KAAK,cAAc,EAAiB,MAAM,SAAS,CAAC;AAErG,OAAO,KAAK,EAAE,wBAAwB,EAAiB,MAAM,iCAAiC,CAAC;AAE/F,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,oBAAoB,CAAC;AAIzE,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,oBAAoB,CAAC;AAI5B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,oCAAoC;AACpC,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,QAAQ,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,cAAc,EAAE,qBAAqB,CAAC;IACtC,YAAY,EAAE,YAAY,CAAC;IAC3B,YAAY,EAAE,YAAY,CAAC;IAC3B,wBAAwB,EAAE,wBAAwB,CAAC;CACpD;AAED,wEAAwE;AACxE,eAAO,MAAM,oBAAoB,mBAAmB,CAAC;AAWrD,iFAAiF;AACjF,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAG7D;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,gBAAgB,GAAG;IAAE,KAAK,EAAE,cAAc,CAAA;CAAE,CAiMrF;AAED,MAAM,WAAW,kBAAmB,SAAQ,gBAAgB;IAC1D,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,CAarE"}
1
+ {"version":3,"file":"token-controller.d.ts","sourceRoot":"","sources":["../src/token-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAkC,KAAK,cAAc,EAAiB,MAAM,SAAS,CAAC;AAErG,OAAO,KAAK,EAAE,wBAAwB,EAAiB,MAAM,iCAAiC,CAAC;AAE/F,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,oBAAoB,CAAC;AAIzE,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,oBAAoB,CAAC;AAI5B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AAEH,oCAAoC;AACpC,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,QAAQ,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,cAAc,EAAE,qBAAqB,CAAC;IACtC,YAAY,EAAE,YAAY,CAAC;IAC3B,YAAY,EAAE,YAAY,CAAC;IAC3B,wBAAwB,EAAE,wBAAwB,CAAC;CACpD;AAED,wEAAwE;AACxE,eAAO,MAAM,oBAAoB,mBAAmB,CAAC;AAYrD,iFAAiF;AACjF,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAG7D;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,gBAAgB,GAAG;IAAE,KAAK,EAAE,cAAc,CAAA;CAAE,CA0NrF;AAED,MAAM,WAAW,kBAAmB,SAAQ,gBAAgB;IAC1D,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,CAarE"}
@@ -16,7 +16,7 @@ exports.OFFLINE_ACCESS_SCOPE = 'offline_access';
16
16
  /** Redemption failures, mapped to the wire. */
17
17
  const REDEEM_ERROR = {
18
18
  unknown_code: 'the authorization code is unknown, expired or already used',
19
- already_redeemed: 'the authorization code has already been redeemed',
19
+ already_redeemed: 'this authorization code has already been redeemed; anything it granted has been revoked',
20
20
  expired: 'the authorization code has expired',
21
21
  client_mismatch: 'the authorization code was issued to a different client',
22
22
  redirect_uri_mismatch: 'redirect_uri does not match the one the code was issued for',
@@ -43,6 +43,36 @@ function createTokenHandlers(deps) {
43
43
  await tokenService.revokeTokensForClient(userId, grant.client_public_id);
44
44
  await grantService.end(userId, grant.grant_id, reason);
45
45
  }
46
+ /**
47
+ * A code was presented twice: end the grant the first redemption created.
48
+ *
49
+ * Idempotent and quiet by design. `findActive` answers `null` when the first
50
+ * redemption never got as far as a grant — a replay of a code whose original
51
+ * redemption failed PKCE, or a third presentation after the second already
52
+ * revoked — and there is nothing to do in either case. The refusal below is
53
+ * the same either way, so the caller learns nothing from which happened.
54
+ *
55
+ * A lost revision race is swallowed for the same reason. Something else wrote
56
+ * `oauth-grants` between the read and the write — a person disconnecting, or a
57
+ * second replay a millisecond behind this one — and in every version of that
58
+ * the grant is being ended anyway. Turning it into a 500 would answer a
59
+ * REPLAY with a server error, which is the one answer that tells an attacker
60
+ * something. The tokens are already swept by then regardless.
61
+ */
62
+ async function revokeForCodeReplay(userId, clientPublicId) {
63
+ if (userId === undefined || clientPublicId === undefined)
64
+ return;
65
+ const grant = await grantService.findActive(userId, clientPublicId);
66
+ if (!grant)
67
+ return;
68
+ try {
69
+ await revokeGrant(userId, grant, 'code_replay');
70
+ }
71
+ catch (err) {
72
+ if (!(0, user_store_js_1.isStaleWriteError)(err))
73
+ throw err;
74
+ }
75
+ }
46
76
  /** Mint the pair and answer. One place, so the two grants cannot drift. */
47
77
  async function issueTokens(userId, client, grant) {
48
78
  const access = await tokenService.mint(userId, token_service_js_1.OAUTH_ACCESS_DOMAIN, client.publicId);
@@ -71,24 +101,18 @@ function createTokenHandlers(deps) {
71
101
  // PKCE is required, not optional: OAuth 2.1 requires it for public clients
72
102
  // and every client here is public.
73
103
  const verifier = (0, body_field_js_1.required)(body, 'code_verifier');
74
- let redeemed;
75
- try {
76
- redeemed = await authorizationCodeService.redeem({
77
- code,
78
- clientId: client.clientId,
79
- clientPublicId: client.publicId,
80
- redirectUri,
81
- });
82
- }
83
- catch (err) {
84
- // The single-use claim is the property write, so losing its revision
85
- // check means another request redeemed this code first.
86
- if ((0, user_store_js_1.isStaleWriteError)(err)) {
87
- throw new oauth_error_js_1.OAuthError('invalid_grant', 'the authorization code has already been redeemed');
88
- }
89
- throw err;
90
- }
104
+ const redeemed = await authorizationCodeService.redeem({
105
+ code,
106
+ clientId: client.clientId,
107
+ clientPublicId: client.publicId,
108
+ redirectUri,
109
+ });
91
110
  if (!redeemed.ok) {
111
+ if (redeemed.reason === 'already_redeemed') {
112
+ // The replay. See the header for why denying it is not enough, and why
113
+ // the client revoked is the code's own rather than this request's.
114
+ await revokeForCodeReplay(redeemed.userId, redeemed.clientPublicId);
115
+ }
92
116
  throw new oauth_error_js_1.OAuthError('invalid_grant', REDEEM_ERROR[redeemed.reason]);
93
117
  }
94
118
  const { record, userId } = redeemed;
@@ -1 +1 @@
1
- {"version":3,"file":"token-controller.js","sourceRoot":"","sources":["../src/token-controller.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"token-controller.js","sourceRoot":"","sources":["../src/token-controller.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boopkit/cap-oauth-server",
3
- "version": "0.1.4",
3
+ "version": "0.3.0",
4
4
  "description": "Make a boop app an OAuth 2.1 authorization server and resource server, so an MCP client can act on behalf of one of its users",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -20,10 +20,10 @@
20
20
  "build": "tsc -p tsconfig.json"
21
21
  },
22
22
  "dependencies": {
23
- "@boopkit/cap-users": "^0.8.0",
24
23
  "@boopkit/logger": "^0.4.0"
25
24
  },
26
25
  "peerDependencies": {
26
+ "@boopkit/cap-users": ">=0.9.0 <1",
27
27
  "express": ">=4.0.0"
28
28
  },
29
29
  "devDependencies": {