@hilbras/keystone 1.9.0 → 2.6.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 (155) hide show
  1. package/CHANGELOG.md +459 -0
  2. package/README.md +163 -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 +11 -2
  20. package/dist/index.js.map +1 -1
  21. package/dist/plugins/auth.d.ts +2 -0
  22. package/dist/plugins/auth.d.ts.map +1 -1
  23. package/dist/plugins/auth.js +21 -7
  24. package/dist/plugins/auth.js.map +1 -1
  25. package/dist/plugins/headerSanitization.d.ts +15 -0
  26. package/dist/plugins/headerSanitization.d.ts.map +1 -0
  27. package/dist/plugins/headerSanitization.js +39 -0
  28. package/dist/plugins/headerSanitization.js.map +1 -0
  29. package/dist/plugins/machinePrincipal.d.ts +28 -0
  30. package/dist/plugins/machinePrincipal.d.ts.map +1 -0
  31. package/dist/plugins/machinePrincipal.js +45 -0
  32. package/dist/plugins/machinePrincipal.js.map +1 -0
  33. package/dist/plugins/mtls.d.ts +21 -2
  34. package/dist/plugins/mtls.d.ts.map +1 -1
  35. package/dist/plugins/mtls.js +94 -47
  36. package/dist/plugins/mtls.js.map +1 -1
  37. package/dist/plugins/rateLimit.d.ts +5 -4
  38. package/dist/plugins/rateLimit.d.ts.map +1 -1
  39. package/dist/plugins/rateLimit.js +11 -8
  40. package/dist/plugins/rateLimit.js.map +1 -1
  41. package/dist/repositories/application.d.ts +1 -1
  42. package/dist/repositories/application.d.ts.map +1 -1
  43. package/dist/repositories/application.js +8 -2
  44. package/dist/repositories/application.js.map +1 -1
  45. package/dist/repositories/session.d.ts +1 -0
  46. package/dist/repositories/session.d.ts.map +1 -1
  47. package/dist/repositories/types.d.ts +5 -1
  48. package/dist/repositories/types.d.ts.map +1 -1
  49. package/dist/routes/admin/organizations.d.ts.map +1 -1
  50. package/dist/routes/admin/organizations.js +31 -2
  51. package/dist/routes/admin/organizations.js.map +1 -1
  52. package/dist/routes/apiKeys.d.ts.map +1 -1
  53. package/dist/routes/apiKeys.js +23 -5
  54. package/dist/routes/apiKeys.js.map +1 -1
  55. package/dist/routes/federation.d.ts.map +1 -1
  56. package/dist/routes/federation.js +8 -2
  57. package/dist/routes/federation.js.map +1 -1
  58. package/dist/routes/oauth.d.ts.map +1 -1
  59. package/dist/routes/oauth.js +38 -2
  60. package/dist/routes/oauth.js.map +1 -1
  61. package/dist/routes/oauth2.d.ts.map +1 -1
  62. package/dist/routes/oauth2.js +92 -13
  63. package/dist/routes/oauth2.js.map +1 -1
  64. package/dist/routes/profile.js +2 -2
  65. package/dist/routes/profile.js.map +1 -1
  66. package/dist/routes/saml.d.ts +24 -0
  67. package/dist/routes/saml.d.ts.map +1 -1
  68. package/dist/routes/saml.js +32 -3
  69. package/dist/routes/saml.js.map +1 -1
  70. package/dist/routes/serviceAccounts.d.ts.map +1 -1
  71. package/dist/routes/serviceAccounts.js +78 -2
  72. package/dist/routes/serviceAccounts.js.map +1 -1
  73. package/dist/routes/sessions.js +3 -3
  74. package/dist/routes/sessions.js.map +1 -1
  75. package/dist/routes/smsOtp.d.ts.map +1 -1
  76. package/dist/routes/smsOtp.js +6 -0
  77. package/dist/routes/smsOtp.js.map +1 -1
  78. package/dist/routes/totp.d.ts.map +1 -1
  79. package/dist/routes/totp.js +24 -10
  80. package/dist/routes/totp.js.map +1 -1
  81. package/dist/routes/webauthn.d.ts.map +1 -1
  82. package/dist/routes/webauthn.js +8 -2
  83. package/dist/routes/webauthn.js.map +1 -1
  84. package/dist/sdk/index.d.ts.map +1 -1
  85. package/dist/sdk/index.js.map +1 -1
  86. package/dist/sdk/types.d.ts +4 -1
  87. package/dist/sdk/types.d.ts.map +1 -1
  88. package/dist/services/application/organization.d.ts +5 -1
  89. package/dist/services/application/organization.d.ts.map +1 -1
  90. package/dist/services/application/organization.js.map +1 -1
  91. package/dist/services/connectors/google.d.ts +18 -1
  92. package/dist/services/connectors/google.d.ts.map +1 -1
  93. package/dist/services/connectors/google.js +25 -3
  94. package/dist/services/connectors/google.js.map +1 -1
  95. package/dist/services/connectors/oidc.d.ts +14 -2
  96. package/dist/services/connectors/oidc.d.ts.map +1 -1
  97. package/dist/services/connectors/oidc.js +29 -3
  98. package/dist/services/connectors/oidc.js.map +1 -1
  99. package/dist/services/connectors/types.d.ts +13 -1
  100. package/dist/services/connectors/types.d.ts.map +1 -1
  101. package/dist/services/domain/authentication.d.ts.map +1 -1
  102. package/dist/services/domain/authentication.js +36 -19
  103. package/dist/services/domain/authentication.js.map +1 -1
  104. package/dist/services/domain/organization.d.ts +5 -1
  105. package/dist/services/domain/organization.d.ts.map +1 -1
  106. package/dist/services/domain/organization.js.map +1 -1
  107. package/dist/services/events/types.d.ts +1 -1
  108. package/dist/services/events/types.d.ts.map +1 -1
  109. package/dist/services/events/validate.d.ts.map +1 -1
  110. package/dist/services/events/validate.js +6 -0
  111. package/dist/services/events/validate.js.map +1 -1
  112. package/dist/services/magicLinks.d.ts.map +1 -1
  113. package/dist/services/magicLinks.js +13 -9
  114. package/dist/services/magicLinks.js.map +1 -1
  115. package/dist/services/oauth2.d.ts +72 -2
  116. package/dist/services/oauth2.d.ts.map +1 -1
  117. package/dist/services/oauth2.js +106 -6
  118. package/dist/services/oauth2.js.map +1 -1
  119. package/dist/services/redirectUri.d.ts +56 -0
  120. package/dist/services/redirectUri.d.ts.map +1 -0
  121. package/dist/services/redirectUri.js +129 -0
  122. package/dist/services/redirectUri.js.map +1 -0
  123. package/dist/services/scopes.d.ts +113 -0
  124. package/dist/services/scopes.d.ts.map +1 -0
  125. package/dist/services/scopes.js +138 -0
  126. package/dist/services/scopes.js.map +1 -0
  127. package/dist/services/serviceAccounts.d.ts +20 -0
  128. package/dist/services/serviceAccounts.d.ts.map +1 -1
  129. package/dist/services/serviceAccounts.js +47 -1
  130. package/dist/services/serviceAccounts.js.map +1 -1
  131. package/dist/services/sessionRevocation.d.ts +76 -0
  132. package/dist/services/sessionRevocation.d.ts.map +1 -0
  133. package/dist/services/sessionRevocation.js +86 -0
  134. package/dist/services/sessionRevocation.js.map +1 -0
  135. package/dist/services/singleUse.d.ts +68 -0
  136. package/dist/services/singleUse.d.ts.map +1 -0
  137. package/dist/services/singleUse.js +54 -0
  138. package/dist/services/singleUse.js.map +1 -0
  139. package/dist/services/smsOtp.d.ts.map +1 -1
  140. package/dist/services/smsOtp.js +12 -8
  141. package/dist/services/smsOtp.js.map +1 -1
  142. package/dist/services/tokens.d.ts +2 -0
  143. package/dist/services/tokens.d.ts.map +1 -1
  144. package/dist/services/tokens.js +5 -0
  145. package/dist/services/tokens.js.map +1 -1
  146. package/dist/services/trustedProxies.d.ts +90 -0
  147. package/dist/services/trustedProxies.d.ts.map +1 -0
  148. package/dist/services/trustedProxies.js +283 -0
  149. package/dist/services/trustedProxies.js.map +1 -0
  150. package/dist/setup-server.js +2 -1
  151. package/dist/setup-server.js.map +1 -1
  152. package/dist/types.d.ts +2 -2
  153. package/dist/types.d.ts.map +1 -1
  154. package/dist/types.js.map +1 -1
  155. package/package.json +4 -5
@@ -1,5 +1,5 @@
1
1
  import crypto from "node:crypto";
2
- import { eq, and, gt, isNull, or } from "drizzle-orm";
2
+ import { eq, and, gt, isNull } from "drizzle-orm";
3
3
  import { db } from "../db/index.js";
4
4
  import { oauth2AuthorizationCodes, oauth2Consents, applications, orgMemberships, } from "../db/schema.js";
5
5
  import { config } from "../config.js";
@@ -38,8 +38,11 @@ export async function consumeAuthorizationCode(code, appId, redirectUri) {
38
38
  gt(oauth2AuthorizationCodes.expiresAt, now),
39
39
  isNull(oauth2AuthorizationCodes.usedAt),
40
40
  ];
41
+ // Exact match, no exceptions. `redirect_uri` is required at /authorize, so
42
+ // there is always a stored value to compare against; tolerating a missing one
43
+ // here would let a stolen code be redeemed against any redirect URI.
41
44
  if (redirectUri) {
42
- conditions.push(or(isNull(oauth2AuthorizationCodes.redirectUri), eq(oauth2AuthorizationCodes.redirectUri, redirectUri)));
45
+ conditions.push(eq(oauth2AuthorizationCodes.redirectUri, redirectUri));
43
46
  }
44
47
  const [record] = await db
45
48
  .update(oauth2AuthorizationCodes)
@@ -48,13 +51,92 @@ export async function consumeAuthorizationCode(code, appId, redirectUri) {
48
51
  .returning();
49
52
  return record;
50
53
  }
51
- export function verifyPKCE(challenge, method, verifier) {
52
- if (!challenge)
53
- return true;
54
+ /**
55
+ * Verify a PKCE code verifier against a stored challenge.
56
+ *
57
+ * `requireChallenge` exists because the previous shape of this function was the
58
+ * whole PKCE story at the token endpoint, and it was not a check at all:
59
+ *
60
+ * ```ts
61
+ * if (!challenge) return true; // no challenge was registered -> accept
62
+ * ```
63
+ *
64
+ * A client that omitted `code_challenge` at `/authorize` and then sent any
65
+ * string as `code_verifier` at `/token` passed. The endpoint required a
66
+ * verifier to be *present*, so the requirement looked enforced while proving
67
+ * nothing. For a public client — an SPA or a mobile app that cannot hold a
68
+ * secret — PKCE is the only thing standing between an intercepted authorization
69
+ * code and a stolen account, so "optional" meant "unprotected".
70
+ *
71
+ * The two failure modes are now separated deliberately:
72
+ * - no challenge and `requireChallenge` false: a confidential client that chose
73
+ * not to use PKCE. Permitted, because it authenticates with a secret.
74
+ * - a challenge but a mismatched or malformed verifier: always a failure.
75
+ */
76
+ export function verifyPKCE(challenge, method, verifier, options = {}) {
77
+ if (!challenge) {
78
+ // No challenge to verify against. Only acceptable when PKCE was not
79
+ // required for this client.
80
+ return !options.requireChallenge;
81
+ }
82
+ if (typeof verifier !== "string" || verifier === "")
83
+ return false;
54
84
  if (method?.toLowerCase() !== "s256")
55
85
  return false;
56
86
  const hash = crypto.createHash("sha256").update(verifier).digest("base64url");
57
- return hash === challenge;
87
+ return timingSafeEqual(hash, challenge);
88
+ }
89
+ /** Constant-time comparison, so a verifier cannot be recovered byte by byte. */
90
+ function timingSafeEqual(a, b) {
91
+ const bufA = Buffer.from(a, "utf8");
92
+ const bufB = Buffer.from(b, "utf8");
93
+ if (bufA.length !== bufB.length)
94
+ return false;
95
+ return crypto.timingSafeEqual(bufA, bufB);
96
+ }
97
+ /**
98
+ * A client that cannot keep a secret must use PKCE.
99
+ *
100
+ * Today every application has a non-null `client_secret_hash`, so this reduces to
101
+ * "the client has no usable secret" — which is the honest test regardless of how
102
+ * the column is populated, and stays correct if secretless public clients are
103
+ * introduced later.
104
+ */
105
+ export function requiresPkce(clientSecretHash) {
106
+ return !clientSecretHash || clientSecretHash === "";
107
+ }
108
+ /**
109
+ * Reduce requested scopes to the ones actually granted.
110
+ *
111
+ * The effective set is the intersection of what the client is registered to ask
112
+ * for, what it asked for, and what the user consented to. Previously the
113
+ * client's `scope` parameter was stored verbatim and the only filter applied was
114
+ * consent, so a client could put any string it liked into the issued token.
115
+ *
116
+ * Rejecting rather than silently dropping is deliberate: a client asking for
117
+ * something it was never registered for is a misconfiguration or an attempt to
118
+ * escalate, and quietly handing back a lesser scope set hides both. The error
119
+ * names the offending scope so the client can be fixed.
120
+ *
121
+ * An empty `allowedScopes` means "no restriction registered", which preserves
122
+ * every existing application. Once an application has an allowlist, it is
123
+ * enforced.
124
+ */
125
+ export function resolveEffectiveScopes(input) {
126
+ const requested = [...new Set(input.requested.filter(Boolean))];
127
+ const allowed = new Set(input.allowed);
128
+ const consented = input.consented === null ? null : new Set(input.consented);
129
+ const effective = [];
130
+ for (const scope of requested) {
131
+ if (allowed.size > 0 && !allowed.has(scope)) {
132
+ return { ok: false, error: "invalid_scope", scope };
133
+ }
134
+ if (consented && !consented.has(scope)) {
135
+ return { ok: false, error: "invalid_scope", scope };
136
+ }
137
+ effective.push(scope);
138
+ }
139
+ return { ok: true, scopes: effective };
58
140
  }
59
141
  export async function hasConsent(userId, appId, requestedScopes) {
60
142
  if (requestedScopes.length === 0)
@@ -69,6 +151,21 @@ export async function hasConsent(userId, appId, requestedScopes) {
69
151
  const granted = new Set(consent.scopes);
70
152
  return requestedScopes.every((scope) => granted.has(scope));
71
153
  }
154
+ /**
155
+ * The user's current consent record, or undefined if they have never consented
156
+ * or have revoked it.
157
+ *
158
+ * Kept separate from `hasConsent` because the scope intersection needs the
159
+ * granted set itself, not a yes/no answer to it.
160
+ */
161
+ export async function findConsent(userId, appId) {
162
+ const [consent] = await db
163
+ .select()
164
+ .from(oauth2Consents)
165
+ .where(and(eq(oauth2Consents.appId, appId), eq(oauth2Consents.userId, userId), isNull(oauth2Consents.revokedAt)))
166
+ .limit(1);
167
+ return consent;
168
+ }
72
169
  export async function grantConsent(userId, appId, scopes) {
73
170
  const now = new Date();
74
171
  const [record] = await db
@@ -114,6 +211,9 @@ export async function createTokenResponse(user, app, scopes, opts = {}) {
114
211
  appId: app.id,
115
212
  orgId: app.orgId,
116
213
  clientId: app.clientId,
214
+ // The granted set is persisted on the refresh token so it survives rotation
215
+ // instead of being dropped at the first refresh.
216
+ ...(scopes.length > 0 ? { scopes } : {}),
117
217
  ...(opts.mfaFactor ? { mfaFactor: opts.mfaFactor } : {}),
118
218
  };
119
219
  const tokenSet = await createTokenSet(user, opts.ip, opts.userAgent, tokenOpts, opts.deviceFingerprint);
@@ -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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"}
@@ -0,0 +1,113 @@
1
+ /**
2
+ * API key scope registry.
3
+ *
4
+ * API key scopes used to be decorative. `requireScopes` existed and was
5
+ * decorated onto the Fastify instance, but no route referenced it, so a key
6
+ * issued with the default `["api:read"]` carried exactly the same authority as
7
+ * the user who created it. Scopes were also free-form: a caller could request
8
+ * any string at key creation, including the literal `"service_account"` that
9
+ * `requireScopes` treated as a wildcard.
10
+ *
11
+ * A registry fixes both halves. A scope that is not defined here cannot be
12
+ * granted, so there is no string an attacker can invent; and a route that needs a
13
+ * scope names it from this list, so enforcement cannot silently drift out of
14
+ * date the way an unreferenced decorator did.
15
+ */
16
+ /** Every scope Keystone recognises. Order is the order shown to operators. */
17
+ export declare const API_KEY_SCOPES: {
18
+ /** Read the caller's own profile, sessions, and keys. */
19
+ readonly "profile:read": "Read your own profile and identity";
20
+ readonly "profile:write": "Update your own profile";
21
+ /** Read sessions belonging to the caller. */
22
+ readonly "sessions:read": "List your own sessions";
23
+ readonly "sessions:revoke": "Revoke your own sessions";
24
+ /** Manage the caller's own API keys. */
25
+ readonly "api_keys:read": "List your own API keys";
26
+ readonly "api_keys:revoke": "Revoke your own API keys";
27
+ /** Read organization members and applications. */
28
+ readonly "organizations:read": "Read organization membership and applications";
29
+ /** Manage organization members, applications, and roles. */
30
+ readonly "organizations:write": "Manage organization membership and applications";
31
+ /** Read and manage webhooks. */
32
+ readonly "webhooks:manage": "Create, update, and delete webhooks";
33
+ /** Read and manage workflows. */
34
+ readonly "workflows:read": "Read workflows and their runs";
35
+ readonly "workflows:write": "Create and update workflows";
36
+ /** Second-factor management. Never appropriate for a machine credential. */
37
+ readonly "mfa:manage": "Enroll, verify, and disable TOTP and backup codes";
38
+ };
39
+ export type ApiKeyScope = keyof typeof API_KEY_SCOPES;
40
+ export declare function isApiKeyScope(value: string): value is ApiKeyScope;
41
+ export declare function knownScopes(): ApiKeyScope[];
42
+ /** Human-readable description of a scope, for API responses and dashboards. */
43
+ export declare function describeScope(scope: string): string | undefined;
44
+ /**
45
+ * Scopes granted to a service account by default.
46
+ *
47
+ * Deliberately excludes anything interactive: a machine credential has no user
48
+ * present to complete a second factor, no real profile, and no sessions worth
49
+ * listing. A service account that needs more is granted it explicitly.
50
+ */
51
+ export declare const SERVICE_ACCOUNT_DEFAULT_SCOPES: ApiKeyScope[];
52
+ /**
53
+ * Scopes a new personal API key receives when the caller names none.
54
+ *
55
+ * Read-only and confined to the caller's own resources. The previous default,
56
+ * `["api:read"]`, was not in the registry at all — which is a fair indicator that
57
+ * nothing was checking it.
58
+ */
59
+ export declare const PERSONAL_KEY_DEFAULT_SCOPES: ApiKeyScope[];
60
+ /**
61
+ * Scopes that may never be granted to a machine credential.
62
+ *
63
+ * `mfa:manage` because a second factor is enrolled by a person holding an
64
+ * authenticator; `MfaRequiredError` is raised when a token is minted for a user
65
+ * with TOTP enabled and no recorded factor, and a service account has no
66
+ * authenticator to record.
67
+ *
68
+ * `profile:*` because a service account's "profile" is a synthesized object with
69
+ * an id of `sa:<uuid>` that matches no user row. Reading it returns nothing real,
70
+ * and writing it would update zero rows -- so the scope is meaningless for a
71
+ * machine and misleading in an audit log that shows what a key was used for.
72
+ */
73
+ export declare const HUMAN_ONLY_SCOPES: readonly ApiKeyScope[];
74
+ export interface ScopeValidation {
75
+ ok: boolean;
76
+ /** Unknown scopes, which must be refused rather than stored. */
77
+ unknown: string[];
78
+ /** Scopes refused because a service account may not hold them. */
79
+ forbiddenForServiceAccount: string[];
80
+ /** The accepted set, de-duplicated and in registry order. */
81
+ scopes: ApiKeyScope[];
82
+ }
83
+ /**
84
+ * Validate a requested scope set.
85
+ *
86
+ * Unknown scopes are refused rather than dropped. Silently discarding a scope a
87
+ * caller asked for hides both a typo and an attempt to smuggle an unrecognised
88
+ * privilege past a review, and it leaves the caller believing they hold
89
+ * something they do not.
90
+ */
91
+ export declare function validateScopes(requested: string[] | undefined, options?: {
92
+ principal: "user" | "service_account";
93
+ fallback?: ApiKeyScope[];
94
+ }): ScopeValidation;
95
+ /**
96
+ * Narrow a key's stored scopes to what the caller is actually entitled to.
97
+ *
98
+ * A key never gains authority from its own record. The effective set is what the
99
+ * key holds *intersected with* what its principal could do directly, so revoking
100
+ * a role or removing a membership takes effect on the key too rather than
101
+ * leaving a stale grant behind.
102
+ */
103
+ export declare function intersectScopes(granted: string[] | null | undefined, principalAllows: string[]): ApiKeyScope[];
104
+ /**
105
+ * Grant check used by `requireScopes`.
106
+ *
107
+ * Explicit allow-list membership only. The previous implementation also accepted
108
+ * a key whose scopes merely *contained* the string `"service_account"`, which is
109
+ * a bypass by construction: that string was client-suppliable at key creation, so
110
+ * any key created with `scopes: ["service_account"]` satisfied every check.
111
+ */
112
+ export declare function hasScopes(granted: string[] | null | undefined, required: readonly string[]): boolean;
113
+ //# sourceMappingURL=scopes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scopes.d.ts","sourceRoot":"","sources":["../../src/services/scopes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,8EAA8E;AAC9E,eAAO,MAAM,cAAc;IACzB,yDAAyD;aACzD,cAAc,EAAE,oCAAoC;aACpD,eAAe,EAAE,yBAAyB;IAC1C,6CAA6C;aAC7C,eAAe,EAAE,wBAAwB;aACzC,iBAAiB,EAAE,0BAA0B;IAC7C,wCAAwC;aACxC,eAAe,EAAE,wBAAwB;aACzC,iBAAiB,EAAE,0BAA0B;IAC7C,kDAAkD;aAClD,oBAAoB,EAAE,+CAA+C;IACrE,4DAA4D;aAC5D,qBAAqB,EAAE,iDAAiD;IACxE,gCAAgC;aAChC,iBAAiB,EAAE,qCAAqC;IACxD,iCAAiC;aACjC,gBAAgB,EAAE,+BAA+B;aACjD,iBAAiB,EAAE,6BAA6B;IAChD,4EAA4E;aAC5E,YAAY,EAAE,mDAAmD;CACzD,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,cAAc,CAAC;AAItD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,WAAW,CAEjE;AAED,wBAAgB,WAAW,IAAI,WAAW,EAAE,CAE3C;AAED,+EAA+E;AAC/E,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE/D;AAED;;;;;;GAMG;AACH,eAAO,MAAM,8BAA8B,EAAE,WAAW,EAA2B,CAAC;AAEpF;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,EAAE,WAAW,EAAuD,CAAC;AAE7G;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAAS,WAAW,EAInD,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,OAAO,CAAC;IACZ,gEAAgE;IAChE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,kEAAkE;IAClE,0BAA0B,EAAE,MAAM,EAAE,CAAC;IACrC,6DAA6D;IAC7D,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EAAE,GAAG,SAAS,EAC/B,OAAO,GAAE;IAAE,SAAS,EAAE,MAAM,GAAG,iBAAiB,CAAC;IAAC,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAA;CAA0B,GACnG,eAAe,CAwBjB;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,EAAE,eAAe,EAAE,MAAM,EAAE,GAAG,WAAW,EAAE,CAI9G;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAIpG"}
@@ -0,0 +1,138 @@
1
+ /**
2
+ * API key scope registry.
3
+ *
4
+ * API key scopes used to be decorative. `requireScopes` existed and was
5
+ * decorated onto the Fastify instance, but no route referenced it, so a key
6
+ * issued with the default `["api:read"]` carried exactly the same authority as
7
+ * the user who created it. Scopes were also free-form: a caller could request
8
+ * any string at key creation, including the literal `"service_account"` that
9
+ * `requireScopes` treated as a wildcard.
10
+ *
11
+ * A registry fixes both halves. A scope that is not defined here cannot be
12
+ * granted, so there is no string an attacker can invent; and a route that needs a
13
+ * scope names it from this list, so enforcement cannot silently drift out of
14
+ * date the way an unreferenced decorator did.
15
+ */
16
+ /** Every scope Keystone recognises. Order is the order shown to operators. */
17
+ export const API_KEY_SCOPES = {
18
+ /** Read the caller's own profile, sessions, and keys. */
19
+ "profile:read": "Read your own profile and identity",
20
+ "profile:write": "Update your own profile",
21
+ /** Read sessions belonging to the caller. */
22
+ "sessions:read": "List your own sessions",
23
+ "sessions:revoke": "Revoke your own sessions",
24
+ /** Manage the caller's own API keys. */
25
+ "api_keys:read": "List your own API keys",
26
+ "api_keys:revoke": "Revoke your own API keys",
27
+ /** Read organization members and applications. */
28
+ "organizations:read": "Read organization membership and applications",
29
+ /** Manage organization members, applications, and roles. */
30
+ "organizations:write": "Manage organization membership and applications",
31
+ /** Read and manage webhooks. */
32
+ "webhooks:manage": "Create, update, and delete webhooks",
33
+ /** Read and manage workflows. */
34
+ "workflows:read": "Read workflows and their runs",
35
+ "workflows:write": "Create and update workflows",
36
+ /** Second-factor management. Never appropriate for a machine credential. */
37
+ "mfa:manage": "Enroll, verify, and disable TOTP and backup codes",
38
+ };
39
+ const ALL_SCOPES = Object.keys(API_KEY_SCOPES);
40
+ export function isApiKeyScope(value) {
41
+ return Object.prototype.hasOwnProperty.call(API_KEY_SCOPES, value);
42
+ }
43
+ export function knownScopes() {
44
+ return [...ALL_SCOPES];
45
+ }
46
+ /** Human-readable description of a scope, for API responses and dashboards. */
47
+ export function describeScope(scope) {
48
+ return isApiKeyScope(scope) ? API_KEY_SCOPES[scope] : undefined;
49
+ }
50
+ /**
51
+ * Scopes granted to a service account by default.
52
+ *
53
+ * Deliberately excludes anything interactive: a machine credential has no user
54
+ * present to complete a second factor, no real profile, and no sessions worth
55
+ * listing. A service account that needs more is granted it explicitly.
56
+ */
57
+ export const SERVICE_ACCOUNT_DEFAULT_SCOPES = ["organizations:read"];
58
+ /**
59
+ * Scopes a new personal API key receives when the caller names none.
60
+ *
61
+ * Read-only and confined to the caller's own resources. The previous default,
62
+ * `["api:read"]`, was not in the registry at all — which is a fair indicator that
63
+ * nothing was checking it.
64
+ */
65
+ export const PERSONAL_KEY_DEFAULT_SCOPES = ["profile:read", "sessions:read", "api_keys:read"];
66
+ /**
67
+ * Scopes that may never be granted to a machine credential.
68
+ *
69
+ * `mfa:manage` because a second factor is enrolled by a person holding an
70
+ * authenticator; `MfaRequiredError` is raised when a token is minted for a user
71
+ * with TOTP enabled and no recorded factor, and a service account has no
72
+ * authenticator to record.
73
+ *
74
+ * `profile:*` because a service account's "profile" is a synthesized object with
75
+ * an id of `sa:<uuid>` that matches no user row. Reading it returns nothing real,
76
+ * and writing it would update zero rows -- so the scope is meaningless for a
77
+ * machine and misleading in an audit log that shows what a key was used for.
78
+ */
79
+ export const HUMAN_ONLY_SCOPES = [
80
+ "mfa:manage",
81
+ "profile:read",
82
+ "profile:write",
83
+ ];
84
+ /**
85
+ * Validate a requested scope set.
86
+ *
87
+ * Unknown scopes are refused rather than dropped. Silently discarding a scope a
88
+ * caller asked for hides both a typo and an attempt to smuggle an unrecognised
89
+ * privilege past a review, and it leaves the caller believing they hold
90
+ * something they do not.
91
+ */
92
+ export function validateScopes(requested, options = { principal: "user" }) {
93
+ const wanted = requested && requested.length > 0
94
+ ? requested
95
+ : (options.fallback ??
96
+ (options.principal === "service_account" ? SERVICE_ACCOUNT_DEFAULT_SCOPES : PERSONAL_KEY_DEFAULT_SCOPES));
97
+ const unknown = wanted.filter((scope) => !isApiKeyScope(scope));
98
+ const known = wanted.filter(isApiKeyScope);
99
+ const forbidden = options.principal === "service_account"
100
+ ? known.filter((scope) => HUMAN_ONLY_SCOPES.includes(scope))
101
+ : [];
102
+ const allowed = known.filter((scope) => !forbidden.includes(scope));
103
+ return {
104
+ ok: unknown.length === 0 && forbidden.length === 0,
105
+ unknown,
106
+ forbiddenForServiceAccount: forbidden,
107
+ // Registry order, so two equivalent requests produce identical rows.
108
+ scopes: ALL_SCOPES.filter((scope) => allowed.includes(scope)),
109
+ };
110
+ }
111
+ /**
112
+ * Narrow a key's stored scopes to what the caller is actually entitled to.
113
+ *
114
+ * A key never gains authority from its own record. The effective set is what the
115
+ * key holds *intersected with* what its principal could do directly, so revoking
116
+ * a role or removing a membership takes effect on the key too rather than
117
+ * leaving a stale grant behind.
118
+ */
119
+ export function intersectScopes(granted, principalAllows) {
120
+ const held = new Set((granted ?? []).filter(isApiKeyScope));
121
+ const allowed = new Set(principalAllows.filter(isApiKeyScope));
122
+ return ALL_SCOPES.filter((scope) => held.has(scope) && allowed.has(scope));
123
+ }
124
+ /**
125
+ * Grant check used by `requireScopes`.
126
+ *
127
+ * Explicit allow-list membership only. The previous implementation also accepted
128
+ * a key whose scopes merely *contained* the string `"service_account"`, which is
129
+ * a bypass by construction: that string was client-suppliable at key creation, so
130
+ * any key created with `scopes: ["service_account"]` satisfied every check.
131
+ */
132
+ export function hasScopes(granted, required) {
133
+ if (required.length === 0)
134
+ return true;
135
+ const held = new Set(granted ?? []);
136
+ return required.every((scope) => held.has(scope));
137
+ }
138
+ //# sourceMappingURL=scopes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scopes.js","sourceRoot":"","sources":["../../src/services/scopes.ts"],"names":[],"mappings":"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"}