@hilbras/keystone 1.9.0 → 2.5.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 (127) hide show
  1. package/CHANGELOG.md +386 -0
  2. package/README.md +150 -32
  3. package/dist/config.d.ts +8 -0
  4. package/dist/config.d.ts.map +1 -1
  5. package/dist/config.js +8 -0
  6. package/dist/config.js.map +1 -1
  7. package/dist/db/migrations/0015_chief_vargas.sql +7 -0
  8. package/dist/db/migrations/0016_gorgeous_charles_xavier.sql +5 -0
  9. package/dist/db/migrations/0017_flashy_nekra.sql +1 -0
  10. package/dist/db/migrations/meta/0015_snapshot.json +4340 -0
  11. package/dist/db/migrations/meta/0016_snapshot.json +4363 -0
  12. package/dist/db/migrations/meta/0017_snapshot.json +4370 -0
  13. package/dist/db/migrations/meta/_journal.json +21 -0
  14. package/dist/db/schema.d.ts +138 -1
  15. package/dist/db/schema.d.ts.map +1 -1
  16. package/dist/db/schema.js +39 -1
  17. package/dist/db/schema.js.map +1 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +9 -2
  20. package/dist/index.js.map +1 -1
  21. package/dist/plugins/headerSanitization.d.ts +15 -0
  22. package/dist/plugins/headerSanitization.d.ts.map +1 -0
  23. package/dist/plugins/headerSanitization.js +39 -0
  24. package/dist/plugins/headerSanitization.js.map +1 -0
  25. package/dist/plugins/mtls.d.ts +21 -2
  26. package/dist/plugins/mtls.d.ts.map +1 -1
  27. package/dist/plugins/mtls.js +94 -47
  28. package/dist/plugins/mtls.js.map +1 -1
  29. package/dist/plugins/rateLimit.d.ts +5 -4
  30. package/dist/plugins/rateLimit.d.ts.map +1 -1
  31. package/dist/plugins/rateLimit.js +11 -8
  32. package/dist/plugins/rateLimit.js.map +1 -1
  33. package/dist/repositories/application.d.ts +1 -1
  34. package/dist/repositories/application.d.ts.map +1 -1
  35. package/dist/repositories/application.js +8 -2
  36. package/dist/repositories/application.js.map +1 -1
  37. package/dist/repositories/session.d.ts +1 -0
  38. package/dist/repositories/session.d.ts.map +1 -1
  39. package/dist/repositories/types.d.ts +5 -1
  40. package/dist/repositories/types.d.ts.map +1 -1
  41. package/dist/routes/admin/organizations.d.ts.map +1 -1
  42. package/dist/routes/admin/organizations.js +31 -2
  43. package/dist/routes/admin/organizations.js.map +1 -1
  44. package/dist/routes/oauth.d.ts.map +1 -1
  45. package/dist/routes/oauth.js +38 -2
  46. package/dist/routes/oauth.js.map +1 -1
  47. package/dist/routes/oauth2.d.ts.map +1 -1
  48. package/dist/routes/oauth2.js +84 -11
  49. package/dist/routes/oauth2.js.map +1 -1
  50. package/dist/routes/saml.d.ts +24 -0
  51. package/dist/routes/saml.d.ts.map +1 -1
  52. package/dist/routes/saml.js +32 -3
  53. package/dist/routes/saml.js.map +1 -1
  54. package/dist/routes/serviceAccounts.d.ts.map +1 -1
  55. package/dist/routes/serviceAccounts.js +61 -1
  56. package/dist/routes/serviceAccounts.js.map +1 -1
  57. package/dist/routes/totp.d.ts.map +1 -1
  58. package/dist/routes/totp.js +4 -5
  59. package/dist/routes/totp.js.map +1 -1
  60. package/dist/sdk/index.d.ts.map +1 -1
  61. package/dist/sdk/index.js.map +1 -1
  62. package/dist/sdk/types.d.ts +4 -1
  63. package/dist/sdk/types.d.ts.map +1 -1
  64. package/dist/services/application/organization.d.ts +5 -1
  65. package/dist/services/application/organization.d.ts.map +1 -1
  66. package/dist/services/application/organization.js.map +1 -1
  67. package/dist/services/connectors/google.d.ts +18 -1
  68. package/dist/services/connectors/google.d.ts.map +1 -1
  69. package/dist/services/connectors/google.js +25 -3
  70. package/dist/services/connectors/google.js.map +1 -1
  71. package/dist/services/connectors/oidc.d.ts +14 -2
  72. package/dist/services/connectors/oidc.d.ts.map +1 -1
  73. package/dist/services/connectors/oidc.js +29 -3
  74. package/dist/services/connectors/oidc.js.map +1 -1
  75. package/dist/services/connectors/types.d.ts +13 -1
  76. package/dist/services/connectors/types.d.ts.map +1 -1
  77. package/dist/services/domain/authentication.d.ts.map +1 -1
  78. package/dist/services/domain/authentication.js +36 -19
  79. package/dist/services/domain/authentication.js.map +1 -1
  80. package/dist/services/domain/organization.d.ts +5 -1
  81. package/dist/services/domain/organization.d.ts.map +1 -1
  82. package/dist/services/domain/organization.js.map +1 -1
  83. package/dist/services/events/types.d.ts +1 -1
  84. package/dist/services/events/types.d.ts.map +1 -1
  85. package/dist/services/events/validate.d.ts.map +1 -1
  86. package/dist/services/events/validate.js +6 -0
  87. package/dist/services/events/validate.js.map +1 -1
  88. package/dist/services/magicLinks.d.ts.map +1 -1
  89. package/dist/services/magicLinks.js +13 -9
  90. package/dist/services/magicLinks.js.map +1 -1
  91. package/dist/services/oauth2.d.ts +72 -2
  92. package/dist/services/oauth2.d.ts.map +1 -1
  93. package/dist/services/oauth2.js +106 -6
  94. package/dist/services/oauth2.js.map +1 -1
  95. package/dist/services/redirectUri.d.ts +56 -0
  96. package/dist/services/redirectUri.d.ts.map +1 -0
  97. package/dist/services/redirectUri.js +129 -0
  98. package/dist/services/redirectUri.js.map +1 -0
  99. package/dist/services/serviceAccounts.d.ts +20 -0
  100. package/dist/services/serviceAccounts.d.ts.map +1 -1
  101. package/dist/services/serviceAccounts.js +47 -1
  102. package/dist/services/serviceAccounts.js.map +1 -1
  103. package/dist/services/sessionRevocation.d.ts +76 -0
  104. package/dist/services/sessionRevocation.d.ts.map +1 -0
  105. package/dist/services/sessionRevocation.js +86 -0
  106. package/dist/services/sessionRevocation.js.map +1 -0
  107. package/dist/services/singleUse.d.ts +68 -0
  108. package/dist/services/singleUse.d.ts.map +1 -0
  109. package/dist/services/singleUse.js +54 -0
  110. package/dist/services/singleUse.js.map +1 -0
  111. package/dist/services/smsOtp.d.ts.map +1 -1
  112. package/dist/services/smsOtp.js +12 -8
  113. package/dist/services/smsOtp.js.map +1 -1
  114. package/dist/services/tokens.d.ts +2 -0
  115. package/dist/services/tokens.d.ts.map +1 -1
  116. package/dist/services/tokens.js +5 -0
  117. package/dist/services/tokens.js.map +1 -1
  118. package/dist/services/trustedProxies.d.ts +90 -0
  119. package/dist/services/trustedProxies.d.ts.map +1 -0
  120. package/dist/services/trustedProxies.js +283 -0
  121. package/dist/services/trustedProxies.js.map +1 -0
  122. package/dist/setup-server.js +2 -1
  123. package/dist/setup-server.js.map +1 -1
  124. package/dist/types.d.ts +2 -2
  125. package/dist/types.d.ts.map +1 -1
  126. package/dist/types.js.map +1 -1
  127. package/package.json +4 -5
@@ -1 +1 @@
1
- {"version":3,"file":"oauth2.js","sourceRoot":"","sources":["../../src/services/oauth2.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"oauth2.js","sourceRoot":"","sources":["../../src/services/oauth2.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Redirect URI validation.
3
+ *
4
+ * Redirect URIs are the one place an authorization server sends a user's browser
5
+ * somewhere on its own authority. Anything accepted here becomes a `Location`
6
+ * header the identity provider itself emits, so the accepted set has to be a
7
+ * set a browser cannot be talked out of.
8
+ *
9
+ * Two distinct jobs, kept separate on purpose:
10
+ *
11
+ * - {@link validateRedirectUri} decides whether a URI may be **registered**.
12
+ * - {@link isRedirectUriRegistered} decides whether a URI may be **used**.
13
+ *
14
+ * Matching is exact string comparison. That is the strictest form available and
15
+ * the one RFC 9700 requires: no prefix matching, no normalization, no wildcards.
16
+ * Normalizing before comparing is what turns a strict check into a loose one,
17
+ * because the two sides then stop being compared as the bytes that will
18
+ * actually be sent.
19
+ */
20
+ export interface RedirectUriRejection {
21
+ ok: false;
22
+ reason: string;
23
+ }
24
+ export type RedirectUriCheck = {
25
+ ok: true;
26
+ } | RedirectUriRejection;
27
+ /**
28
+ * Whether a redirect URI may be registered.
29
+ *
30
+ * Rejects, in order of how badly they fail:
31
+ * - `javascript:`, `data:`, and friends — script execution on our own origin
32
+ * - wildcards — accepted by the URL parser but can never match exactly, so they
33
+ * mislead the operator into thinking a redirect is configured when it is not
34
+ * - fragments — never transmitted to the server, so an exact comparison against
35
+ * them can never succeed
36
+ * - embedded credentials — `https://user:pass@host` is a phishing primitive
37
+ * - plaintext HTTP to anything that is not loopback
38
+ */
39
+ export declare function validateRedirectUri(uri: string): RedirectUriCheck;
40
+ /**
41
+ * Validate a whole registration set, returning every problem rather than the
42
+ * first, so an operator fixing a configuration sees all of it at once.
43
+ */
44
+ export declare function validateRedirectUris(uris: string[]): RedirectUriRejection[];
45
+ /**
46
+ * Whether a requested redirect URI is registered.
47
+ *
48
+ * Exact string comparison, deliberately. The requested value is compared as the
49
+ * exact bytes that will be sent in the `Location` header, with no parsing,
50
+ * case-folding, trailing-slash adjustment, or default-port removal. Any of those
51
+ * would widen the check: `https://example.com` and `https://example.com/` are
52
+ * different origins paths-wise, and a comparison that treats them as equal is a
53
+ * comparison that can be made to match something it should not.
54
+ */
55
+ export declare function isRedirectUriRegistered(registered: string[], requested: string | undefined): boolean;
56
+ //# sourceMappingURL=redirectUri.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redirectUri.d.ts","sourceRoot":"","sources":["../../src/services/redirectUri.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,gBAAgB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG,oBAAoB,CAAC;AAcnE;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CA6DjE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,oBAAoB,EAAE,CAiB3E;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAGpG"}
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Redirect URI validation.
3
+ *
4
+ * Redirect URIs are the one place an authorization server sends a user's browser
5
+ * somewhere on its own authority. Anything accepted here becomes a `Location`
6
+ * header the identity provider itself emits, so the accepted set has to be a
7
+ * set a browser cannot be talked out of.
8
+ *
9
+ * Two distinct jobs, kept separate on purpose:
10
+ *
11
+ * - {@link validateRedirectUri} decides whether a URI may be **registered**.
12
+ * - {@link isRedirectUriRegistered} decides whether a URI may be **used**.
13
+ *
14
+ * Matching is exact string comparison. That is the strictest form available and
15
+ * the one RFC 9700 requires: no prefix matching, no normalization, no wildcards.
16
+ * Normalizing before comparing is what turns a strict check into a loose one,
17
+ * because the two sides then stop being compared as the bytes that will
18
+ * actually be sent.
19
+ */
20
+ /** Schemes that would execute script or inline a document if navigated to. */
21
+ const SCRIPT_SCHEMES = new Set(["javascript", "data", "vbscript", "blob", "file"]);
22
+ /**
23
+ * Hostnames that may legitimately be reached over plaintext HTTP, because they
24
+ * never leave the developer's machine.
25
+ */
26
+ const LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "[::1]", "::1", "0.0.0.0"]);
27
+ /** `scheme:` per RFC 3986, which custom app schemes (`myapp://`) must match. */
28
+ const SCHEME = /^([a-z][a-z0-9+.-]*):/i;
29
+ /**
30
+ * Whether a redirect URI may be registered.
31
+ *
32
+ * Rejects, in order of how badly they fail:
33
+ * - `javascript:`, `data:`, and friends — script execution on our own origin
34
+ * - wildcards — accepted by the URL parser but can never match exactly, so they
35
+ * mislead the operator into thinking a redirect is configured when it is not
36
+ * - fragments — never transmitted to the server, so an exact comparison against
37
+ * them can never succeed
38
+ * - embedded credentials — `https://user:pass@host` is a phishing primitive
39
+ * - plaintext HTTP to anything that is not loopback
40
+ */
41
+ export function validateRedirectUri(uri) {
42
+ if (typeof uri !== "string" || uri.trim() === "") {
43
+ return { ok: false, reason: "must be a non-empty string" };
44
+ }
45
+ const value = uri.trim();
46
+ // A wildcard is a valid URL character sequence to the parser, so it has to be
47
+ // rejected explicitly rather than caught by scheme or host rules.
48
+ if (value.includes("*")) {
49
+ return { ok: false, reason: "must not contain a wildcard; register each redirect URI exactly" };
50
+ }
51
+ let url;
52
+ try {
53
+ url = new URL(value);
54
+ }
55
+ catch {
56
+ return { ok: false, reason: "must be a valid absolute URL" };
57
+ }
58
+ const scheme = url.protocol.replace(/:$/, "").toLowerCase();
59
+ if (SCRIPT_SCHEMES.has(scheme)) {
60
+ return {
61
+ ok: false,
62
+ reason: `scheme "${scheme}" would execute script or inline a document and is never a valid redirect target`,
63
+ };
64
+ }
65
+ if (url.hash) {
66
+ return { ok: false, reason: "must not contain a fragment; fragments are not sent to the server" };
67
+ }
68
+ if (url.username || url.password) {
69
+ return { ok: false, reason: "must not embed credentials" };
70
+ }
71
+ if (scheme === "https") {
72
+ return { ok: true };
73
+ }
74
+ if (scheme === "http") {
75
+ const host = url.hostname.toLowerCase();
76
+ if (LOOPBACK_HOSTS.has(host))
77
+ return { ok: true };
78
+ return {
79
+ ok: false,
80
+ reason: "must use https; plaintext http is accepted only for loopback addresses during development",
81
+ };
82
+ }
83
+ // A custom scheme is how native and mobile clients receive the redirect, so it
84
+ // is allowed — provided it is not one of the script-bearing schemes handled
85
+ // above. It must be a reverse-DNS style identifier, not a bare word.
86
+ if (SCHEME.test(value) && url.protocol !== ":") {
87
+ if (!/^[a-z][a-z0-9+.-]*:$/i.test(url.protocol)) {
88
+ return { ok: false, reason: "scheme is malformed" };
89
+ }
90
+ return { ok: true };
91
+ }
92
+ return { ok: false, reason: `scheme "${scheme}" is not supported` };
93
+ }
94
+ /**
95
+ * Validate a whole registration set, returning every problem rather than the
96
+ * first, so an operator fixing a configuration sees all of it at once.
97
+ */
98
+ export function validateRedirectUris(uris) {
99
+ const problems = [];
100
+ for (const uri of uris) {
101
+ const result = validateRedirectUri(uri);
102
+ if (!result.ok)
103
+ problems.push({ ok: false, reason: `${uri}: ${result.reason}` });
104
+ }
105
+ const seen = new Set();
106
+ for (const uri of uris) {
107
+ if (seen.has(uri)) {
108
+ problems.push({ ok: false, reason: `${uri}: registered more than once` });
109
+ }
110
+ seen.add(uri);
111
+ }
112
+ return problems;
113
+ }
114
+ /**
115
+ * Whether a requested redirect URI is registered.
116
+ *
117
+ * Exact string comparison, deliberately. The requested value is compared as the
118
+ * exact bytes that will be sent in the `Location` header, with no parsing,
119
+ * case-folding, trailing-slash adjustment, or default-port removal. Any of those
120
+ * would widen the check: `https://example.com` and `https://example.com/` are
121
+ * different origins paths-wise, and a comparison that treats them as equal is a
122
+ * comparison that can be made to match something it should not.
123
+ */
124
+ export function isRedirectUriRegistered(registered, requested) {
125
+ if (typeof requested !== "string" || requested === "")
126
+ return false;
127
+ return registered.includes(requested);
128
+ }
129
+ //# sourceMappingURL=redirectUri.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redirectUri.js","sourceRoot":"","sources":["../../src/services/redirectUri.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,7 @@ export declare function createServiceAccount(input: {
3
3
  orgId: string;
4
4
  name: string;
5
5
  description?: string;
6
+ certFingerprint?: string | null;
6
7
  }): Promise<ServiceAccount>;
7
8
  export declare function findServiceAccountsByOrgId(orgId: string): Promise<ServiceAccount[]>;
8
9
  export declare function findServiceAccountById(id: string, orgId: string): Promise<ServiceAccount | undefined>;
@@ -11,6 +12,25 @@ export declare function updateServiceAccount(id: string, orgId: string, updates:
11
12
  description: string;
12
13
  isActive: boolean;
13
14
  }>): Promise<ServiceAccount | undefined>;
15
+ /**
16
+ * Bind or clear the client certificate this account authenticates with.
17
+ *
18
+ * Clearing sets the column to null, which removes certificate authentication
19
+ * for the account entirely — an unbound account can only use an API key. The
20
+ * unique index on `cert_fingerprint` is what stops one certificate being bound
21
+ * to two accounts; a conflict surfaces as an error here rather than silently
22
+ * picking a winner.
23
+ *
24
+ * @throws if `fingerprint` is not a well-formed SHA-256 digest, or if the
25
+ * certificate is already bound to a different account.
26
+ */
27
+ export declare function setServiceAccountCertificate(id: string, orgId: string, fingerprint: string | null): Promise<ServiceAccount | undefined>;
28
+ /**
29
+ * Permanently stop an account authenticating. Kept separate from
30
+ * `isActive: false` so a later reactivation cannot silently restore access —
31
+ * reactivation requires clearing `revokedAt` deliberately.
32
+ */
33
+ export declare function revokeServiceAccount(id: string, orgId: string): Promise<ServiceAccount | undefined>;
14
34
  export declare function listServiceAccountApiKeys(serviceAccountId: string): Promise<{
15
35
  id: string;
16
36
  name: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"serviceAccounts.d.ts","sourceRoot":"","sources":["../../src/services/serviceAccounts.ts"],"names":[],"mappings":"AAEA,OAAO,EAA4B,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEhF,wBAAsB,oBAAoB,CAAC,KAAK,EAAE;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,cAAc,CAAC,CAU1B;AAED,wBAAsB,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAMzF;AAED,wBAAsB,sBAAsB,CAC1C,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAarC;AAED,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,GACzE,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAOrC;AAED,wBAAsB,yBAAyB,CAAC,gBAAgB,EAAE,MAAM;;;;;;;;;KAcvE"}
1
+ {"version":3,"file":"serviceAccounts.d.ts","sourceRoot":"","sources":["../../src/services/serviceAccounts.ts"],"names":[],"mappings":"AAGA,OAAO,EAA4B,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEhF,wBAAsB,oBAAoB,CAAC,KAAK,EAAE;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC,GAAG,OAAO,CAAC,cAAc,CAAC,CAW1B;AAED,wBAAsB,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAMzF;AAED,wBAAsB,sBAAsB,CAC1C,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAarC;AAED,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,GACzE,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAOrC;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,4BAA4B,CAChD,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,GAAG,IAAI,GACzB,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAmBrC;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAOrC;AAED,wBAAsB,yBAAyB,CAAC,gBAAgB,EAAE,MAAM;;;;;;;;;KAcvE"}
@@ -1,4 +1,5 @@
1
- import { eq, and, sql } from "drizzle-orm";
1
+ import { eq, and, isNull, sql } from "drizzle-orm";
2
+ import { canonicalFingerprint, isValidFingerprint } from "./trustedProxies.js";
2
3
  import { db } from "../db/index.js";
3
4
  import { serviceAccounts, apiKeys } from "../db/schema.js";
4
5
  export async function createServiceAccount(input) {
@@ -8,6 +9,7 @@ export async function createServiceAccount(input) {
8
9
  orgId: input.orgId,
9
10
  name: input.name,
10
11
  description: input.description ?? null,
12
+ certFingerprint: input.certFingerprint ?? null,
11
13
  })
12
14
  .returning();
13
15
  return record;
@@ -35,6 +37,50 @@ export async function updateServiceAccount(id, orgId, updates) {
35
37
  .returning();
36
38
  return updated;
37
39
  }
40
+ /**
41
+ * Bind or clear the client certificate this account authenticates with.
42
+ *
43
+ * Clearing sets the column to null, which removes certificate authentication
44
+ * for the account entirely — an unbound account can only use an API key. The
45
+ * unique index on `cert_fingerprint` is what stops one certificate being bound
46
+ * to two accounts; a conflict surfaces as an error here rather than silently
47
+ * picking a winner.
48
+ *
49
+ * @throws if `fingerprint` is not a well-formed SHA-256 digest, or if the
50
+ * certificate is already bound to a different account.
51
+ */
52
+ export async function setServiceAccountCertificate(id, orgId, fingerprint) {
53
+ // Canonicalize here rather than trusting the caller. The hex and
54
+ // colon-separated spellings of one certificate must not become two bindings,
55
+ // and this is the only place every caller is guaranteed to pass through.
56
+ let canonical = null;
57
+ if (fingerprint !== null) {
58
+ const trimmed = fingerprint.trim();
59
+ if (!isValidFingerprint(trimmed)) {
60
+ throw new Error("fingerprint must be a SHA-256 digest (64 hex characters)");
61
+ }
62
+ canonical = canonicalFingerprint(trimmed);
63
+ }
64
+ const [updated] = await db
65
+ .update(serviceAccounts)
66
+ .set({ certFingerprint: canonical, updatedAt: sql `now()` })
67
+ .where(and(eq(serviceAccounts.id, id), eq(serviceAccounts.orgId, orgId)))
68
+ .returning();
69
+ return updated;
70
+ }
71
+ /**
72
+ * Permanently stop an account authenticating. Kept separate from
73
+ * `isActive: false` so a later reactivation cannot silently restore access —
74
+ * reactivation requires clearing `revokedAt` deliberately.
75
+ */
76
+ export async function revokeServiceAccount(id, orgId) {
77
+ const [updated] = await db
78
+ .update(serviceAccounts)
79
+ .set({ isActive: false, revokedAt: sql `now()`, updatedAt: sql `now()` })
80
+ .where(and(eq(serviceAccounts.id, id), eq(serviceAccounts.orgId, orgId), isNull(serviceAccounts.revokedAt)))
81
+ .returning();
82
+ return updated;
83
+ }
38
84
  export async function listServiceAccountApiKeys(serviceAccountId) {
39
85
  return db
40
86
  .select({
@@ -1 +1 @@
1
- {"version":3,"file":"serviceAccounts.js","sourceRoot":"","sources":["../../src/services/serviceAccounts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,OAAO,EAAuB,MAAM,iBAAiB,CAAC;AAEhF,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,KAI1C;IACC,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE;SACtB,MAAM,CAAC,eAAe,CAAC;SACvB,MAAM,CAAC;QACN,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,WAAW,EAAE,KAAK,CAAC,WAAW,IAAI,IAAI;KACvC,CAAC;SACD,SAAS,EAAE,CAAC;IACf,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAAC,KAAa;IAC5D,OAAO,EAAE;SACN,MAAM,EAAE;SACR,IAAI,CAAC,eAAe,CAAC;SACrB,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;SAChF,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,EAAU,EACV,KAAa;IAEb,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE;SACtB,MAAM,EAAE;SACR,IAAI,CAAC,eAAe,CAAC;SACrB,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,eAAe,CAAC,EAAE,EAAE,EAAE,CAAC,EAC1B,EAAE,CAAC,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,EAChC,EAAE,CAAC,eAAe,CAAC,QAAQ,EAAE,IAAI,CAAC,CACnC,CACF;SACA,KAAK,CAAC,CAAC,CAAC,CAAC;IACZ,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,EAAU,EACV,KAAa,EACb,OAA0E;IAE1E,MAAM,CAAC,OAAO,CAAC,GAAG,MAAM,EAAE;SACvB,MAAM,CAAC,eAAe,CAAC;SACvB,GAAG,CAAC,EAAE,GAAG,OAAO,EAAE,SAAS,EAAE,GAAG,CAAA,OAAO,EAAE,CAAC;SAC1C,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,eAAe,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;SACxE,SAAS,EAAE,CAAC;IACf,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,gBAAwB;IACtE,OAAO,EAAE;SACN,MAAM,CAAC;QACN,EAAE,EAAE,OAAO,CAAC,EAAE;QACd,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,SAAS,EAAE,OAAO,CAAC,SAAS;KAC7B,CAAC;SACD,IAAI,CAAC,OAAO,CAAC;SACb,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,CAAC,CAAC;AAC3D,CAAC"}
1
+ {"version":3,"file":"serviceAccounts.js","sourceRoot":"","sources":["../../src/services/serviceAccounts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Centralized credential revocation.
3
+ *
4
+ * Every "this account should stop being logged in" operation routes through
5
+ * here. Before this existed, revocation was open-coded per call site, and the
6
+ * most important call site was missing entirely: completing a password reset
7
+ * changed the password and left every existing session and refresh token
8
+ * working. That defeats the purpose of a reset, which is the standard response
9
+ * to a suspected compromise — the intruder keeps their session.
10
+ *
11
+ * The primitives below are deliberately separate so a caller can be precise
12
+ * about what it invalidates, and so the composite operations are visibly
13
+ * composed rather than reimplemented at each site.
14
+ */
15
+ export interface RevocationCounts {
16
+ refreshTokens: number;
17
+ sessions: number;
18
+ recoveryCredentials: number;
19
+ }
20
+ export interface RevokeOptions {
21
+ /**
22
+ * Keep this session and its refresh token alive, for a change the user is
23
+ * making to their own account.
24
+ *
25
+ * Never pass it on a reset the user did not initiate themselves: the current
26
+ * session is precisely what an attacker would be holding, and preserving it
27
+ * would leave them in.
28
+ */
29
+ exceptSessionId?: string;
30
+ /** Keep this refresh token alive, for the same reason. */
31
+ exceptRefreshTokenId?: string;
32
+ }
33
+ /**
34
+ * Revoke the user's refresh tokens, so no further access token can be minted
35
+ * from an existing session.
36
+ */
37
+ export declare function revokeRefreshTokens(userId: string, options?: RevokeOptions): Promise<number>;
38
+ /**
39
+ * Revoke the user's authentication sessions.
40
+ *
41
+ * A revoked session stops authorising requests immediately; it is not merely
42
+ * hidden from a session list.
43
+ */
44
+ export declare function revokeAuthenticationSessions(userId: string, options?: RevokeOptions): Promise<number>;
45
+ /**
46
+ * Revoke outstanding recovery credentials.
47
+ *
48
+ * A reset token already sitting in someone's inbox stays valid until it
49
+ * expires unless it is spent here. Someone who intercepted an earlier reset
50
+ * email could otherwise complete it after the user had already recovered.
51
+ */
52
+ export declare function revokeRecoveryCredentials(userId: string): Promise<number>;
53
+ /**
54
+ * Log the user out everywhere: sessions and refresh tokens together.
55
+ *
56
+ * This is what most credential changes want. Revoking only one leaves a live
57
+ * credential behind: a session with no refresh token still authorises requests
58
+ * until it expires, and a refresh token with no session can still mint access
59
+ * tokens.
60
+ */
61
+ export declare function revokeUserSessions(userId: string, options?: RevokeOptions): Promise<RevocationCounts>;
62
+ /**
63
+ * Everything: sessions, refresh tokens, and outstanding recovery credentials.
64
+ *
65
+ * Used after a password reset.
66
+ *
67
+ * API keys are deliberately **not** revoked. They are separately issued,
68
+ * long-lived credentials belonging to integrations rather than to the person,
69
+ * and silently killing them on a password reset breaks deployments without
70
+ * improving the account's own security. The residual gap is real — an API key
71
+ * minted by an attacker who already held the password survives — and the right
72
+ * answer is key expiry and rotation rather than coupling key lifetime to a
73
+ * human's password.
74
+ */
75
+ export declare function revokeAllUserCredentials(userId: string, options?: RevokeOptions): Promise<RevocationCounts>;
76
+ //# sourceMappingURL=sessionRevocation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionRevocation.d.ts","sourceRoot":"","sources":["../../src/services/sessionRevocation.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,0DAA0D;IAC1D,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAID;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAWjB;AAED;;;;;GAKG;AACH,wBAAsB,4BAA4B,CAChD,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAWjB;AAED;;;;;;GAMG;AACH,wBAAsB,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAO/E;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,gBAAgB,CAAC,CAI3B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,gBAAgB,CAAC,CAK3B"}
@@ -0,0 +1,86 @@
1
+ import { and, eq, isNull, ne } from "drizzle-orm";
2
+ import { db } from "../db/index.js";
3
+ import { passwordResetTokens, refreshTokens, userSessions } from "../db/schema.js";
4
+ const EMPTY = { refreshTokens: 0, sessions: 0, recoveryCredentials: 0 };
5
+ /**
6
+ * Revoke the user's refresh tokens, so no further access token can be minted
7
+ * from an existing session.
8
+ */
9
+ export async function revokeRefreshTokens(userId, options = {}) {
10
+ const conditions = [eq(refreshTokens.userId, userId), isNull(refreshTokens.revokedAt)];
11
+ if (options.exceptRefreshTokenId) {
12
+ conditions.push(ne(refreshTokens.id, options.exceptRefreshTokenId));
13
+ }
14
+ const rows = await db
15
+ .update(refreshTokens)
16
+ .set({ revokedAt: new Date() })
17
+ .where(and(...conditions))
18
+ .returning({ id: refreshTokens.id });
19
+ return rows.length;
20
+ }
21
+ /**
22
+ * Revoke the user's authentication sessions.
23
+ *
24
+ * A revoked session stops authorising requests immediately; it is not merely
25
+ * hidden from a session list.
26
+ */
27
+ export async function revokeAuthenticationSessions(userId, options = {}) {
28
+ const conditions = [eq(userSessions.userId, userId), isNull(userSessions.revokedAt)];
29
+ if (options.exceptSessionId) {
30
+ conditions.push(ne(userSessions.id, options.exceptSessionId));
31
+ }
32
+ const rows = await db
33
+ .update(userSessions)
34
+ .set({ revokedAt: new Date() })
35
+ .where(and(...conditions))
36
+ .returning({ id: userSessions.id });
37
+ return rows.length;
38
+ }
39
+ /**
40
+ * Revoke outstanding recovery credentials.
41
+ *
42
+ * A reset token already sitting in someone's inbox stays valid until it
43
+ * expires unless it is spent here. Someone who intercepted an earlier reset
44
+ * email could otherwise complete it after the user had already recovered.
45
+ */
46
+ export async function revokeRecoveryCredentials(userId) {
47
+ const rows = await db
48
+ .update(passwordResetTokens)
49
+ .set({ usedAt: new Date() })
50
+ .where(and(eq(passwordResetTokens.userId, userId), isNull(passwordResetTokens.usedAt)))
51
+ .returning({ id: passwordResetTokens.id });
52
+ return rows.length;
53
+ }
54
+ /**
55
+ * Log the user out everywhere: sessions and refresh tokens together.
56
+ *
57
+ * This is what most credential changes want. Revoking only one leaves a live
58
+ * credential behind: a session with no refresh token still authorises requests
59
+ * until it expires, and a refresh token with no session can still mint access
60
+ * tokens.
61
+ */
62
+ export async function revokeUserSessions(userId, options = {}) {
63
+ const sessions = await revokeAuthenticationSessions(userId, options);
64
+ const refreshTokensRevoked = await revokeRefreshTokens(userId, options);
65
+ return { ...EMPTY, sessions, refreshTokens: refreshTokensRevoked };
66
+ }
67
+ /**
68
+ * Everything: sessions, refresh tokens, and outstanding recovery credentials.
69
+ *
70
+ * Used after a password reset.
71
+ *
72
+ * API keys are deliberately **not** revoked. They are separately issued,
73
+ * long-lived credentials belonging to integrations rather than to the person,
74
+ * and silently killing them on a password reset breaks deployments without
75
+ * improving the account's own security. The residual gap is real — an API key
76
+ * minted by an attacker who already held the password survives — and the right
77
+ * answer is key expiry and rotation rather than coupling key lifetime to a
78
+ * human's password.
79
+ */
80
+ export async function revokeAllUserCredentials(userId, options = {}) {
81
+ const sessions = await revokeAuthenticationSessions(userId, options);
82
+ const refreshTokensRevoked = await revokeRefreshTokens(userId, options);
83
+ const recoveryCredentials = await revokeRecoveryCredentials(userId);
84
+ return { sessions, refreshTokens: refreshTokensRevoked, recoveryCredentials };
85
+ }
86
+ //# sourceMappingURL=sessionRevocation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionRevocation.js","sourceRoot":"","sources":["../../src/services/sessionRevocation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ import { magicLinks, passwordResetTokens, smsOtpCodes } from "../db/schema.js";
2
+ /**
3
+ * Atomic consumption of single-use credentials.
4
+ *
5
+ * A single-use credential has one dangerous shape, and it is always the same:
6
+ *
7
+ * ```text
8
+ * SELECT ... WHERE used_at IS NULL <- conditional
9
+ * if (!row) return
10
+ * UPDATE ... SET used_at = now() <- UNCONDITIONAL: the race
11
+ * ```
12
+ *
13
+ * Between the SELECT and the UPDATE, any number of concurrent requests can pass
14
+ * the same check. A magic link redeemed by ten parallel requests logs in ten
15
+ * times; a password reset token spent ten times over writes ten different
16
+ * passwords, last writer winning.
17
+ *
18
+ * The fix is to make the write itself the gate:
19
+ *
20
+ * ```text
21
+ * UPDATE ... SET used_at = now()
22
+ * WHERE token_hash = ? AND expires_at > now() AND used_at IS NULL
23
+ * RETURNING ...
24
+ * ```
25
+ *
26
+ * PostgreSQL evaluates that predicate while holding a row lock, so exactly one
27
+ * transaction can update the row and see a returned row. Everyone else matches
28
+ * zero rows. The claim and the validation are one statement, so there is no
29
+ * window between them to lose.
30
+ *
31
+ * All consumption goes through here so the pattern exists once. A credential
32
+ * that consumes its own token inline is a credential that can be raced.
33
+ */
34
+ export type ConsumeOutcome =
35
+ /** This caller claimed the credential. */
36
+ "consumed"
37
+ /** A row exists but was already spent. Somebody replayed it. */
38
+ | "replayed"
39
+ /** A row exists but has passed its expiry. */
40
+ | "expired"
41
+ /** No row for this value. */
42
+ | "not_found";
43
+ export interface ConsumeResult<T> {
44
+ outcome: ConsumeOutcome;
45
+ /** Present only when `outcome` is `consumed`. */
46
+ record?: T;
47
+ /**
48
+ * Why the credential was refused. Set for `replayed` and `expired`; omitted
49
+ * for `not_found`, so a caller cannot distinguish "never existed" from
50
+ * "deleted", which is itself information.
51
+ */
52
+ reason?: "replayed" | "expired";
53
+ }
54
+ export type MagicLinkRow = typeof magicLinks.$inferSelect;
55
+ export type PasswordResetTokenRow = typeof passwordResetTokens.$inferSelect;
56
+ export type SmsOtpCodeRow = typeof smsOtpCodes.$inferSelect;
57
+ /** Atomically consume a magic link. */
58
+ export declare function consumeMagicLinkRow(tokenHash: string, now: Date): Promise<ConsumeResult<MagicLinkRow>>;
59
+ /** Atomically consume a password reset token. */
60
+ export declare function consumePasswordResetTokenRow(tokenHash: string, now: Date): Promise<ConsumeResult<PasswordResetTokenRow>>;
61
+ /**
62
+ * Atomically consume an SMS OTP code.
63
+ *
64
+ * Scoped to the user as well as the code. A code is six digits, so a lookup on
65
+ * the hash alone could let one user's code be spent against another's row.
66
+ */
67
+ export declare function consumeSmsOtpCodeRow(userId: string, codeHash: string, now: Date): Promise<ConsumeResult<SmsOtpCodeRow>>;
68
+ //# sourceMappingURL=singleUse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"singleUse.d.ts","sourceRoot":"","sources":["../../src/services/singleUse.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,MAAM,MAAM,cAAc;AACxB,0CAA0C;AACxC,UAAU;AACZ,gEAAgE;GAC9D,UAAU;AACZ,8CAA8C;GAC5C,SAAS;AACX,6BAA6B;GAC3B,WAAW,CAAC;AAEhB,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,OAAO,EAAE,cAAc,CAAC;IACxB,iDAAiD;IACjD,MAAM,CAAC,EAAE,CAAC,CAAC;IACX;;;;OAIG;IACH,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;CACjC;AAuED,MAAM,MAAM,YAAY,GAAG,OAAO,UAAU,CAAC,YAAY,CAAC;AAC1D,MAAM,MAAM,qBAAqB,GAAG,OAAO,mBAAmB,CAAC,YAAY,CAAC;AAC5E,MAAM,MAAM,aAAa,GAAG,OAAO,WAAW,CAAC,YAAY,CAAC;AAE5D,uCAAuC;AACvC,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,IAAI,GACR,OAAO,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC,CAEtC;AAED,iDAAiD;AACjD,wBAAgB,4BAA4B,CAC1C,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,IAAI,GACR,OAAO,CAAC,aAAa,CAAC,qBAAqB,CAAC,CAAC,CAO/C;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,IAAI,GACR,OAAO,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAQvC"}