@noodleseed/one 0.99.0 → 0.100.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 (178) hide show
  1. package/dist/commands/auth-ops.d.ts.map +1 -1
  2. package/dist/commands/auth-ops.js +6 -1
  3. package/dist/commands/auth-ops.js.map +1 -1
  4. package/dist/commands/auth-service-principals.d.ts +3 -0
  5. package/dist/commands/auth-service-principals.d.ts.map +1 -0
  6. package/dist/commands/auth-service-principals.js +380 -0
  7. package/dist/commands/auth-service-principals.js.map +1 -0
  8. package/dist/commands/catalog-data-auth-discovery.d.ts.map +1 -1
  9. package/dist/commands/catalog-data-auth-discovery.js +187 -2
  10. package/dist/commands/catalog-data-auth-discovery.js.map +1 -1
  11. package/dist/commands/catalog.d.ts.map +1 -1
  12. package/dist/commands/catalog.js +1 -0
  13. package/dist/commands/catalog.js.map +1 -1
  14. package/node_modules/@noodle-borg/auth/dist/index.d.ts +1 -1
  15. package/node_modules/@noodle-borg/auth/dist/index.d.ts.map +1 -1
  16. package/node_modules/@noodle-borg/auth/dist/index.js.map +1 -1
  17. package/node_modules/@noodle-borg/auth/dist/jwt-issuer.d.ts +5 -1
  18. package/node_modules/@noodle-borg/auth/dist/jwt-issuer.d.ts.map +1 -1
  19. package/node_modules/@noodle-borg/auth/dist/jwt-issuer.js +6 -0
  20. package/node_modules/@noodle-borg/auth/dist/jwt-issuer.js.map +1 -1
  21. package/node_modules/@noodle-borg/auth/dist/verify.d.ts +9 -2
  22. package/node_modules/@noodle-borg/auth/dist/verify.d.ts.map +1 -1
  23. package/node_modules/@noodle-borg/auth/dist/verify.js +25 -1
  24. package/node_modules/@noodle-borg/auth/dist/verify.js.map +1 -1
  25. package/node_modules/@noodle-borg/capabilities/dist/index.d.ts.map +1 -1
  26. package/node_modules/@noodle-borg/capabilities/dist/index.js +7 -0
  27. package/node_modules/@noodle-borg/capabilities/dist/index.js.map +1 -1
  28. package/node_modules/@noodle-borg/module/dist/contract.d.ts +1 -1
  29. package/node_modules/@noodle-borg/module/dist/contract.d.ts.map +1 -1
  30. package/node_modules/@noodle-borg/protocol/dist/index.d.ts +1 -0
  31. package/node_modules/@noodle-borg/protocol/dist/index.d.ts.map +1 -1
  32. package/node_modules/@noodle-borg/protocol/dist/index.js +1 -0
  33. package/node_modules/@noodle-borg/protocol/dist/index.js.map +1 -1
  34. package/node_modules/@noodle-borg/protocol/dist/sdk-server.d.ts +2 -0
  35. package/node_modules/@noodle-borg/protocol/dist/sdk-server.d.ts.map +1 -1
  36. package/node_modules/@noodle-borg/protocol/dist/sdk-server.js.map +1 -1
  37. package/node_modules/@noodle-borg/protocol/dist/tool-confirmation.d.ts +6 -0
  38. package/node_modules/@noodle-borg/protocol/dist/tool-confirmation.d.ts.map +1 -0
  39. package/node_modules/@noodle-borg/protocol/dist/tool-confirmation.js +6 -0
  40. package/node_modules/@noodle-borg/protocol/dist/tool-confirmation.js.map +1 -0
  41. package/node_modules/@noodle-borg/protocol/dist/tool-interaction.d.ts +1 -1
  42. package/node_modules/@noodle-borg/protocol/dist/tool-interaction.d.ts.map +1 -1
  43. package/node_modules/@noodle-borg/protocol/dist/tool-interaction.js +2 -2
  44. package/node_modules/@noodle-borg/protocol/dist/tool-interaction.js.map +1 -1
  45. package/node_modules/@noodle-borg/protocol/dist/v2/confirmation.d.ts +1 -1
  46. package/node_modules/@noodle-borg/protocol/dist/v2/confirmation.d.ts.map +1 -1
  47. package/node_modules/@noodle-borg/protocol/dist/v2/confirmation.js +2 -2
  48. package/node_modules/@noodle-borg/protocol/dist/v2/confirmation.js.map +1 -1
  49. package/node_modules/@noodle-borg/protocol/dist/v2/input-required.d.ts.map +1 -1
  50. package/node_modules/@noodle-borg/protocol/dist/v2/input-required.js +5 -0
  51. package/node_modules/@noodle-borg/protocol/dist/v2/input-required.js.map +1 -1
  52. package/node_modules/@noodle-borg/protocol/dist/v2/server.d.ts.map +1 -1
  53. package/node_modules/@noodle-borg/protocol/dist/v2/server.js +13 -9
  54. package/node_modules/@noodle-borg/protocol/dist/v2/server.js.map +1 -1
  55. package/node_modules/@noodle-borg/runtime/dist/connector/types.d.ts +1 -1
  56. package/node_modules/@noodle-borg/runtime/dist/connector/types.d.ts.map +1 -1
  57. package/node_modules/@noodle-borg/service/dist/index.d.ts +4 -0
  58. package/node_modules/@noodle-borg/service/dist/index.d.ts.map +1 -1
  59. package/node_modules/@noodle-borg/service/dist/index.js +4 -0
  60. package/node_modules/@noodle-borg/service/dist/index.js.map +1 -1
  61. package/node_modules/@noodle-borg/service/dist/oauth/app.d.ts +2 -0
  62. package/node_modules/@noodle-borg/service/dist/oauth/app.d.ts.map +1 -1
  63. package/node_modules/@noodle-borg/service/dist/oauth/app.js +29 -0
  64. package/node_modules/@noodle-borg/service/dist/oauth/app.js.map +1 -1
  65. package/node_modules/@noodle-borg/service/dist/oauth/metadata.d.ts +1 -1
  66. package/node_modules/@noodle-borg/service/dist/oauth/metadata.d.ts.map +1 -1
  67. package/node_modules/@noodle-borg/service/dist/oauth/metadata.js +19 -2
  68. package/node_modules/@noodle-borg/service/dist/oauth/metadata.js.map +1 -1
  69. package/node_modules/@noodle-borg/service/dist/oauth/platform-principal-guard.d.ts.map +1 -1
  70. package/node_modules/@noodle-borg/service/dist/oauth/platform-principal-guard.js +3 -1
  71. package/node_modules/@noodle-borg/service/dist/oauth/platform-principal-guard.js.map +1 -1
  72. package/node_modules/@noodle-borg/service/dist/oauth/provider.d.ts +5 -0
  73. package/node_modules/@noodle-borg/service/dist/oauth/provider.d.ts.map +1 -1
  74. package/node_modules/@noodle-borg/service/dist/oauth/provider.js +11 -1
  75. package/node_modules/@noodle-borg/service/dist/oauth/provider.js.map +1 -1
  76. package/node_modules/@noodle-borg/service/dist/oauth/service-bootstrap.d.ts +8 -1
  77. package/node_modules/@noodle-borg/service/dist/oauth/service-bootstrap.d.ts.map +1 -1
  78. package/node_modules/@noodle-borg/service/dist/oauth/service-bootstrap.js +60 -0
  79. package/node_modules/@noodle-borg/service/dist/oauth/service-bootstrap.js.map +1 -1
  80. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-client-auth.d.ts +22 -0
  81. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-client-auth.d.ts.map +1 -0
  82. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-client-auth.js +199 -0
  83. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-client-auth.js.map +1 -0
  84. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-credentials.d.ts +14 -0
  85. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-credentials.d.ts.map +1 -0
  86. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-credentials.js +119 -0
  87. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-credentials.js.map +1 -0
  88. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-guard.d.ts +16 -0
  89. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-guard.d.ts.map +1 -0
  90. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-guard.js +34 -0
  91. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-guard.js.map +1 -0
  92. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-resource.d.ts +14 -0
  93. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-resource.d.ts.map +1 -0
  94. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-resource.js +53 -0
  95. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-resource.js.map +1 -0
  96. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-store-postgres-schema.d.ts +4 -0
  97. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-store-postgres-schema.d.ts.map +1 -0
  98. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-store-postgres-schema.js +98 -0
  99. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-store-postgres-schema.js.map +1 -0
  100. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-store-postgres.d.ts +22 -0
  101. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-store-postgres.d.ts.map +1 -0
  102. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-store-postgres.js +359 -0
  103. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-store-postgres.js.map +1 -0
  104. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-store.d.ts +165 -0
  105. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-store.d.ts.map +1 -0
  106. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-store.js +349 -0
  107. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-store.js.map +1 -0
  108. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-token-handler.d.ts +20 -0
  109. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-token-handler.d.ts.map +1 -0
  110. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-token-handler.js +175 -0
  111. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-token-handler.js.map +1 -0
  112. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-token-issuer.d.ts +12 -0
  113. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-token-issuer.d.ts.map +1 -0
  114. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-token-issuer.js +20 -0
  115. package/node_modules/@noodle-borg/service/dist/oauth/service-principal-token-issuer.js.map +1 -0
  116. package/node_modules/@noodle-borg/service/dist/oauth/token-issuer.d.ts +9 -3
  117. package/node_modules/@noodle-borg/service/dist/oauth/token-issuer.d.ts.map +1 -1
  118. package/node_modules/@noodle-borg/service/dist/oauth/token-issuer.js +6 -0
  119. package/node_modules/@noodle-borg/service/dist/oauth/token-issuer.js.map +1 -1
  120. package/node_modules/@noodle-borg/service/dist/options.d.ts +5 -0
  121. package/node_modules/@noodle-borg/service/dist/options.d.ts.map +1 -1
  122. package/node_modules/@noodle-borg/service/dist/routes/feedback-operations-dispatch.d.ts +2 -1
  123. package/node_modules/@noodle-borg/service/dist/routes/feedback-operations-dispatch.d.ts.map +1 -1
  124. package/node_modules/@noodle-borg/service/dist/routes/feedback-operations-dispatch.js +2 -1
  125. package/node_modules/@noodle-borg/service/dist/routes/feedback-operations-dispatch.js.map +1 -1
  126. package/node_modules/@noodle-borg/service/dist/routes/service-principal-contracts.d.ts +24 -0
  127. package/node_modules/@noodle-borg/service/dist/routes/service-principal-contracts.d.ts.map +1 -0
  128. package/node_modules/@noodle-borg/service/dist/routes/service-principal-contracts.js +55 -0
  129. package/node_modules/@noodle-borg/service/dist/routes/service-principal-contracts.js.map +1 -0
  130. package/node_modules/@noodle-borg/service/dist/routes/service-principal-paths.d.ts +29 -0
  131. package/node_modules/@noodle-borg/service/dist/routes/service-principal-paths.d.ts.map +1 -0
  132. package/node_modules/@noodle-borg/service/dist/routes/service-principal-paths.js +63 -0
  133. package/node_modules/@noodle-borg/service/dist/routes/service-principal-paths.js.map +1 -0
  134. package/node_modules/@noodle-borg/service/dist/routes/service-principals-dispatch.d.ts +27 -0
  135. package/node_modules/@noodle-borg/service/dist/routes/service-principals-dispatch.d.ts.map +1 -0
  136. package/node_modules/@noodle-borg/service/dist/routes/service-principals-dispatch.js +33 -0
  137. package/node_modules/@noodle-borg/service/dist/routes/service-principals-dispatch.js.map +1 -0
  138. package/node_modules/@noodle-borg/service/dist/routes/service-principals.d.ts +17 -0
  139. package/node_modules/@noodle-borg/service/dist/routes/service-principals.d.ts.map +1 -0
  140. package/node_modules/@noodle-borg/service/dist/routes/service-principals.js +203 -0
  141. package/node_modules/@noodle-borg/service/dist/routes/service-principals.js.map +1 -0
  142. package/node_modules/@noodle-borg/service/dist/routes/slack-feedback-actions.d.ts +2 -0
  143. package/node_modules/@noodle-borg/service/dist/routes/slack-feedback-actions.d.ts.map +1 -1
  144. package/node_modules/@noodle-borg/service/dist/routes/slack-feedback-actions.js +44 -14
  145. package/node_modules/@noodle-borg/service/dist/routes/slack-feedback-actions.js.map +1 -1
  146. package/node_modules/@noodle-borg/service/dist/serve.d.ts.map +1 -1
  147. package/node_modules/@noodle-borg/service/dist/serve.js +4 -1
  148. package/node_modules/@noodle-borg/service/dist/serve.js.map +1 -1
  149. package/node_modules/@noodle-borg/service/dist/service-principal-data-plane.d.ts +12 -0
  150. package/node_modules/@noodle-borg/service/dist/service-principal-data-plane.d.ts.map +1 -0
  151. package/node_modules/@noodle-borg/service/dist/service-principal-data-plane.js +71 -0
  152. package/node_modules/@noodle-borg/service/dist/service-principal-data-plane.js.map +1 -0
  153. package/node_modules/@noodle-borg/service/dist/service.d.ts +1 -3
  154. package/node_modules/@noodle-borg/service/dist/service.d.ts.map +1 -1
  155. package/node_modules/@noodle-borg/service/dist/service.js +9 -5
  156. package/node_modules/@noodle-borg/service/dist/service.js.map +1 -1
  157. package/node_modules/@noodle-borg/service/package.json +1 -0
  158. package/node_modules/@noodle-borg/transport-http/dist/handler.d.ts +5 -0
  159. package/node_modules/@noodle-borg/transport-http/dist/handler.d.ts.map +1 -1
  160. package/node_modules/@noodle-borg/transport-http/dist/handler.js +4 -0
  161. package/node_modules/@noodle-borg/transport-http/dist/handler.js.map +1 -1
  162. package/node_modules/@noodle-borg/transport-http/dist/identity-authorization.d.ts +5 -0
  163. package/node_modules/@noodle-borg/transport-http/dist/identity-authorization.d.ts.map +1 -1
  164. package/node_modules/@noodle-borg/transport-http/dist/identity-authorization.js +16 -0
  165. package/node_modules/@noodle-borg/transport-http/dist/identity-authorization.js.map +1 -1
  166. package/node_modules/@noodle-borg/transport-http/dist/index.d.ts +2 -0
  167. package/node_modules/@noodle-borg/transport-http/dist/index.d.ts.map +1 -1
  168. package/node_modules/@noodle-borg/transport-http/dist/index.js +1 -0
  169. package/node_modules/@noodle-borg/transport-http/dist/index.js.map +1 -1
  170. package/node_modules/@noodle-borg/transport-http/dist/serve-request.d.ts +3 -0
  171. package/node_modules/@noodle-borg/transport-http/dist/serve-request.d.ts.map +1 -1
  172. package/node_modules/@noodle-borg/transport-http/dist/serve-request.js +56 -2
  173. package/node_modules/@noodle-borg/transport-http/dist/serve-request.js.map +1 -1
  174. package/node_modules/@noodle-borg/transport-http/dist/service-principal-authorization.d.ts +31 -0
  175. package/node_modules/@noodle-borg/transport-http/dist/service-principal-authorization.d.ts.map +1 -0
  176. package/node_modules/@noodle-borg/transport-http/dist/service-principal-authorization.js +51 -0
  177. package/node_modules/@noodle-borg/transport-http/dist/service-principal-authorization.js.map +1 -0
  178. package/package.json +1 -1
@@ -0,0 +1,199 @@
1
+ import { decodeJwt, decodeProtectedHeader, importJWK, jwtVerify } from 'jose';
2
+ import { secretDigestMatches } from './service-principal-credentials.js';
3
+ const ASSERTION_TYPE = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer';
4
+ const PRINCIPAL_ID = /^spn_[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
5
+ const SECRET = /^[A-Za-z0-9_-]{43}$/;
6
+ const MAX_ASSERTION_BYTES = 16 * 1024;
7
+ const MAX_BASIC_BYTES = 4096;
8
+ const MAX_ASSERTION_SECONDS = 300;
9
+ const MAX_FUTURE_IAT_SECONDS = 60;
10
+ export class ServicePrincipalClientAuthError extends Error {
11
+ oauthError = 'invalid_client';
12
+ status = 401;
13
+ constructor() {
14
+ super('invalid client authentication');
15
+ this.name = 'ServicePrincipalClientAuthError';
16
+ }
17
+ }
18
+ /** Authenticate one bounded machine client without disclosing client or credential existence. */
19
+ export async function authenticateServicePrincipalClient(request, store, tokenEndpoint, nowSeconds) {
20
+ try {
21
+ validateClock(nowSeconds);
22
+ const authorization = oneAuthorization(request.authorization);
23
+ const hasAssertion = request.body.client_assertion !== undefined ||
24
+ request.body.client_assertion_type !== undefined;
25
+ if (request.body.client_secret !== undefined)
26
+ throw new Error('unsupported secret transport');
27
+ if (authorization !== undefined) {
28
+ if (hasAssertion)
29
+ throw new Error('conflicting authentication');
30
+ return await authenticateBasic(authorization, request.body, store, nowSeconds);
31
+ }
32
+ if (!hasAssertion)
33
+ throw new Error('missing authentication');
34
+ return await authenticateAssertion(request.body, store, tokenEndpoint, nowSeconds);
35
+ }
36
+ catch {
37
+ throw new ServicePrincipalClientAuthError();
38
+ }
39
+ }
40
+ async function authenticateBasic(authorization, body, store, nowSeconds) {
41
+ if (!authorization.startsWith('Basic ') ||
42
+ Buffer.byteLength(authorization, 'utf8') > MAX_BASIC_BYTES) {
43
+ throw new Error('invalid Basic authorization');
44
+ }
45
+ const encoded = authorization.slice('Basic '.length);
46
+ if (!isCanonicalBase64(encoded))
47
+ throw new Error('invalid Basic encoding');
48
+ const decoded = Buffer.from(encoded, 'base64').toString('utf8');
49
+ const separator = decoded.indexOf(':');
50
+ if (separator < 1 || decoded.indexOf(':', separator + 1) !== -1) {
51
+ throw new Error('invalid Basic credentials');
52
+ }
53
+ const clientId = decoded.slice(0, separator);
54
+ const secret = decoded.slice(separator + 1);
55
+ if (!PRINCIPAL_ID.test(clientId) || !SECRET.test(secret))
56
+ throw new Error('invalid client');
57
+ const bodyClientId = optionalString(body.client_id);
58
+ if (bodyClientId !== undefined && bodyClientId !== clientId)
59
+ throw new Error('client mismatch');
60
+ const client = await store.loadActiveClient(clientId, nowSeconds * 1000);
61
+ if (client === undefined)
62
+ throw new Error('unknown client');
63
+ let match;
64
+ for (const credential of client.credentials) {
65
+ if (credential.kind !== 'client_secret')
66
+ continue;
67
+ if (secretDigestMatches(secret, credential.secretDigest))
68
+ match = credential;
69
+ }
70
+ if (match === undefined)
71
+ throw new Error('invalid secret');
72
+ return verified(client, match.credentialId);
73
+ }
74
+ async function authenticateAssertion(body, store, tokenEndpoint, nowSeconds) {
75
+ if (body.client_assertion_type !== ASSERTION_TYPE)
76
+ throw new Error('invalid assertion type');
77
+ const assertion = requiredString(body.client_assertion);
78
+ if (Buffer.byteLength(assertion, 'utf8') > MAX_ASSERTION_BYTES) {
79
+ throw new Error('assertion too large');
80
+ }
81
+ const decoded = decodeJwt(assertion);
82
+ const clientId = assertionClientId(decoded, body.client_id);
83
+ const header = decodeProtectedHeader(assertion);
84
+ if (header.alg !== 'RS256' && header.alg !== 'ES256')
85
+ throw new Error('unsupported algorithm');
86
+ if (header.kid !== undefined && (typeof header.kid !== 'string' || header.kid.length > 200)) {
87
+ throw new Error('invalid kid');
88
+ }
89
+ const client = await store.loadActiveClient(clientId, nowSeconds * 1000);
90
+ if (client === undefined)
91
+ throw new Error('unknown client');
92
+ const candidates = client.credentials.filter((credential) => credential.kind === 'public_jwk' &&
93
+ credential.algorithm === header.alg &&
94
+ (header.kid === undefined || credential.kid === header.kid));
95
+ if (candidates.length === 0 || candidates.length > 5)
96
+ throw new Error('invalid key set');
97
+ const audiences = acceptedAudiences(tokenEndpoint);
98
+ for (const credential of candidates) {
99
+ if (credential.kind !== 'public_jwk')
100
+ continue;
101
+ try {
102
+ const key = await importJWK(credential.publicJwk, credential.algorithm);
103
+ const verifiedAssertion = await jwtVerify(assertion, key, {
104
+ algorithms: [credential.algorithm],
105
+ currentDate: new Date(nowSeconds * 1000),
106
+ });
107
+ const replay = validateAssertionClaims(verifiedAssertion.payload, clientId, audiences, nowSeconds);
108
+ return verified(client, credential.credentialId, replay);
109
+ }
110
+ catch {
111
+ // The active public-key set is bounded to five; try every eligible key without disclosure.
112
+ }
113
+ }
114
+ throw new Error('invalid assertion');
115
+ }
116
+ function validateAssertionClaims(payload, clientId, acceptedAudience, nowSeconds) {
117
+ if (payload.iss !== clientId || payload.sub !== clientId)
118
+ throw new Error('invalid subject');
119
+ if (typeof payload.aud !== 'string' || !acceptedAudience.has(payload.aud)) {
120
+ throw new Error('invalid audience');
121
+ }
122
+ if (!Number.isInteger(payload.iat) || !Number.isInteger(payload.exp)) {
123
+ throw new Error('invalid assertion clock');
124
+ }
125
+ const issuedAt = payload.iat;
126
+ const expiresAt = payload.exp;
127
+ if (issuedAt > nowSeconds + MAX_FUTURE_IAT_SECONDS ||
128
+ expiresAt <= nowSeconds ||
129
+ expiresAt <= issuedAt ||
130
+ expiresAt - issuedAt > MAX_ASSERTION_SECONDS) {
131
+ throw new Error('invalid assertion lifetime');
132
+ }
133
+ if (typeof payload.jti !== 'string' || payload.jti.length < 1 || payload.jti.length > 200) {
134
+ throw new Error('invalid assertion jti');
135
+ }
136
+ return { jti: payload.jti, expiresAt };
137
+ }
138
+ function assertionClientId(payload, bodyClientId) {
139
+ if (typeof payload.iss !== 'string' ||
140
+ payload.iss !== payload.sub ||
141
+ !PRINCIPAL_ID.test(payload.iss)) {
142
+ throw new Error('invalid client identifier');
143
+ }
144
+ const explicit = optionalString(bodyClientId);
145
+ if (explicit !== undefined && explicit !== payload.iss)
146
+ throw new Error('client mismatch');
147
+ return payload.iss;
148
+ }
149
+ function verified(client, credentialId, assertion) {
150
+ return {
151
+ principalId: client.principal.principalId,
152
+ org: client.principal.org,
153
+ credentialId,
154
+ ...(assertion === undefined ? {} : { assertion }),
155
+ };
156
+ }
157
+ function oneAuthorization(value) {
158
+ if (value === undefined)
159
+ return undefined;
160
+ if (typeof value !== 'string')
161
+ throw new Error('duplicate authorization');
162
+ return value;
163
+ }
164
+ function optionalString(value) {
165
+ if (value === undefined)
166
+ return undefined;
167
+ return requiredString(value);
168
+ }
169
+ function requiredString(value) {
170
+ if (typeof value !== 'string' || value.length === 0)
171
+ throw new Error('invalid string');
172
+ return value;
173
+ }
174
+ function isCanonicalBase64(value) {
175
+ if (value.length === 0 || value.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(value)) {
176
+ return false;
177
+ }
178
+ try {
179
+ return Buffer.from(value, 'base64').toString('base64') === value;
180
+ }
181
+ catch {
182
+ return false;
183
+ }
184
+ }
185
+ function acceptedAudiences(tokenEndpoint) {
186
+ const endpoint = new URL(tokenEndpoint);
187
+ if (!endpoint.pathname.endsWith('/token') || endpoint.search || endpoint.hash) {
188
+ throw new Error('invalid token endpoint');
189
+ }
190
+ const issuer = new URL(endpoint.href);
191
+ issuer.pathname = issuer.pathname.slice(0, -'/token'.length) || '/';
192
+ const issuerValue = issuer.href.endsWith('/') ? issuer.href.slice(0, -1) : issuer.href;
193
+ return new Set([endpoint.href, issuerValue]);
194
+ }
195
+ function validateClock(value) {
196
+ if (!Number.isInteger(value) || value < 0)
197
+ throw new Error('invalid clock');
198
+ }
199
+ //# sourceMappingURL=service-principal-client-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-principal-client-auth.js","sourceRoot":"","sources":["../../src/oauth/service-principal-client-auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export type ServicePrincipalSigningAlgorithm = 'RS256' | 'ES256';
2
+ export interface ValidatedPublicJwk {
3
+ readonly algorithm: ServicePrincipalSigningAlgorithm;
4
+ readonly publicJwk: Readonly<Record<string, unknown>>;
5
+ }
6
+ /** Generate the one-time 256-bit secret returned by the management API. */
7
+ export declare function createClientSecret(): string;
8
+ /** Hash a high-entropy client secret for durable storage. */
9
+ export declare function digestClientSecret(secret: string): string;
10
+ /** Compare a presented secret with one canonical SHA-256 digest in constant time. */
11
+ export declare function secretDigestMatches(secret: string, expectedDigest: string): boolean;
12
+ /** Validate and import-check one public verification JWK before it reaches a store. */
13
+ export declare function validatePublicJwk(value: unknown, algorithm: string): Promise<ValidatedPublicJwk>;
14
+ //# sourceMappingURL=service-principal-credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-principal-credentials.d.ts","sourceRoot":"","sources":["../../src/oauth/service-principal-credentials.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,gCAAgC,GAAG,OAAO,GAAG,OAAO,CAAC;AAEjE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,SAAS,EAAE,gCAAgC,CAAC;IACrD,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACvD;AAKD,2EAA2E;AAC3E,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,6DAA6D;AAC7D,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,qFAAqF;AACrF,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAOnF;AAED,uFAAuF;AACvF,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,kBAAkB,CAAC,CA2B7B"}
@@ -0,0 +1,119 @@
1
+ import { createHash, randomBytes, timingSafeEqual } from 'node:crypto';
2
+ import { importJWK } from 'jose';
3
+ const MAX_PUBLIC_JWK_BYTES = 16 * 1024;
4
+ const PRIVATE_JWK_MEMBERS = ['d', 'p', 'q', 'dp', 'dq', 'qi', 'oth', 'k'];
5
+ /** Generate the one-time 256-bit secret returned by the management API. */
6
+ export function createClientSecret() {
7
+ return randomBytes(32).toString('base64url');
8
+ }
9
+ /** Hash a high-entropy client secret for durable storage. */
10
+ export function digestClientSecret(secret) {
11
+ return createHash('sha256').update(secret, 'utf8').digest('base64url');
12
+ }
13
+ /** Compare a presented secret with one canonical SHA-256 digest in constant time. */
14
+ export function secretDigestMatches(secret, expectedDigest) {
15
+ if (!/^[A-Za-z0-9_-]{43}$/.test(expectedDigest))
16
+ return false;
17
+ const expected = Buffer.from(expectedDigest, 'base64url');
18
+ const actual = Buffer.from(digestClientSecret(secret), 'base64url');
19
+ return (expected.length === 32 && actual.length === expected.length && timingSafeEqual(actual, expected));
20
+ }
21
+ /** Validate and import-check one public verification JWK before it reaches a store. */
22
+ export async function validatePublicJwk(value, algorithm) {
23
+ if (algorithm !== 'RS256' && algorithm !== 'ES256') {
24
+ throw new Error('service-principal JWK algorithm must be RS256 or ES256');
25
+ }
26
+ if (!isRecord(value))
27
+ throw new Error('service-principal public JWK must be an object');
28
+ const serialized = serializeJwk(value);
29
+ if (Buffer.byteLength(serialized, 'utf8') > MAX_PUBLIC_JWK_BYTES) {
30
+ throw new Error('service-principal public JWK must be at most 16 KiB');
31
+ }
32
+ for (const member of PRIVATE_JWK_MEMBERS) {
33
+ if (Object.hasOwn(value, member)) {
34
+ throw new Error(`service-principal public JWK must not contain private member ${member}`);
35
+ }
36
+ }
37
+ validateCommonJwkMetadata(value, algorithm);
38
+ if (algorithm === 'RS256')
39
+ validateRsaJwk(value);
40
+ else
41
+ validateEcJwk(value);
42
+ try {
43
+ await importJWK(value, algorithm);
44
+ }
45
+ catch {
46
+ throw new Error(`service-principal ${algorithm} public JWK is malformed`);
47
+ }
48
+ return Object.freeze({
49
+ algorithm,
50
+ publicJwk: Object.freeze(structuredClone(value)),
51
+ });
52
+ }
53
+ function serializeJwk(value) {
54
+ try {
55
+ return JSON.stringify(value);
56
+ }
57
+ catch {
58
+ throw new Error('service-principal public JWK must be JSON serializable');
59
+ }
60
+ }
61
+ function validateCommonJwkMetadata(jwk, algorithm) {
62
+ if (jwk.alg !== undefined && jwk.alg !== algorithm) {
63
+ throw new Error('service-principal JWK declared algorithm does not match its pinned algorithm');
64
+ }
65
+ if (jwk.use !== undefined && jwk.use !== 'sig') {
66
+ throw new Error('service-principal JWK use must be sig');
67
+ }
68
+ if (jwk.key_ops !== undefined &&
69
+ (!Array.isArray(jwk.key_ops) || jwk.key_ops.length !== 1 || jwk.key_ops[0] !== 'verify')) {
70
+ throw new Error('service-principal JWK key_ops must contain only verify');
71
+ }
72
+ if (jwk.kid !== undefined &&
73
+ (typeof jwk.kid !== 'string' || jwk.kid.length === 0 || jwk.kid.length > 200)) {
74
+ throw new Error('service-principal JWK kid must be between 1 and 200 characters');
75
+ }
76
+ }
77
+ function validateRsaJwk(jwk) {
78
+ if (jwk.kty !== 'RSA' || typeof jwk.n !== 'string' || typeof jwk.e !== 'string') {
79
+ throw new Error('RS256 requires an RSA public JWK');
80
+ }
81
+ const modulus = decodeCanonicalBase64url(jwk.n, 'RSA modulus');
82
+ const bits = modulusBitLength(modulus);
83
+ if (bits < 2048 || bits > 4096) {
84
+ throw new Error('RS256 RSA modulus must be between 2048 and 4096 bits');
85
+ }
86
+ decodeCanonicalBase64url(jwk.e, 'RSA exponent');
87
+ }
88
+ function validateEcJwk(jwk) {
89
+ if (jwk.kty !== 'EC' ||
90
+ jwk.crv !== 'P-256' ||
91
+ typeof jwk.x !== 'string' ||
92
+ typeof jwk.y !== 'string') {
93
+ throw new Error('ES256 requires a P-256 EC public JWK');
94
+ }
95
+ if (decodeCanonicalBase64url(jwk.x, 'EC x coordinate').length !== 32 ||
96
+ decodeCanonicalBase64url(jwk.y, 'EC y coordinate').length !== 32) {
97
+ throw new Error('ES256 requires 256-bit EC coordinates');
98
+ }
99
+ }
100
+ function decodeCanonicalBase64url(value, label) {
101
+ if (!/^[A-Za-z0-9_-]+$/.test(value))
102
+ throw new Error(`${label} must be base64url`);
103
+ const decoded = Buffer.from(value, 'base64url');
104
+ if (decoded.length === 0 || decoded.toString('base64url') !== value) {
105
+ throw new Error(`${label} must be canonical base64url`);
106
+ }
107
+ return decoded;
108
+ }
109
+ function modulusBitLength(value) {
110
+ const firstNonZero = value.findIndex((byte) => byte !== 0);
111
+ if (firstNonZero === -1)
112
+ return 0;
113
+ const first = value[firstNonZero];
114
+ return (value.length - firstNonZero - 1) * 8 + (32 - Math.clz32(first));
115
+ }
116
+ function isRecord(value) {
117
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
118
+ }
119
+ //# sourceMappingURL=service-principal-credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-principal-credentials.js","sourceRoot":"","sources":["../../src/oauth/service-principal-credentials.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { TokenVerifier, VerifiedTokenEnvelope } from '@noodle-borg/auth';
2
+ import type { OwnerTokenVerification, OwnerTokenVerifier } from '@noodle-borg/module';
3
+ import type { ServicePrincipalStore } from './service-principal-store.js';
4
+ export interface ResolvedServicePrincipalTarget {
5
+ readonly org: string;
6
+ readonly app: string;
7
+ readonly environment: string;
8
+ }
9
+ type ServiceCapableTokenVerifier = (token: string, resource: string) => Promise<VerifiedTokenEnvelope | OwnerTokenVerification | null>;
10
+ /**
11
+ * Revalidate the private principal/grant/credential binding for every machine-authenticated data-plane
12
+ * request. The private binding is consumed here and never projected into the protocol caller context.
13
+ */
14
+ export declare function guardServicePrincipalAccessTokenVerifier(verifier: TokenVerifier | ServiceCapableTokenVerifier, store: ServicePrincipalStore | undefined, resolveResource: (resource: string) => Promise<ResolvedServicePrincipalTarget | undefined>, now?: () => number): OwnerTokenVerifier;
15
+ export {};
16
+ //# sourceMappingURL=service-principal-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-principal-guard.d.ts","sourceRoot":"","sources":["../../src/oauth/service-principal-guard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC9E,OAAO,KAAK,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACtF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAE1E,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,KAAK,2BAA2B,GAAG,CACjC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC,qBAAqB,GAAG,sBAAsB,GAAG,IAAI,CAAC,CAAC;AAEpE;;;GAGG;AACH,wBAAgB,wCAAwC,CACtD,QAAQ,EAAE,aAAa,GAAG,2BAA2B,EACrD,KAAK,EAAE,qBAAqB,GAAG,SAAS,EACxC,eAAe,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,8BAA8B,GAAG,SAAS,CAAC,EAC1F,GAAG,GAAE,MAAM,MAAiB,GAC3B,kBAAkB,CAyBpB"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Revalidate the private principal/grant/credential binding for every machine-authenticated data-plane
3
+ * request. The private binding is consumed here and never projected into the protocol caller context.
4
+ */
5
+ export function guardServicePrincipalAccessTokenVerifier(verifier, store, resolveResource, now = Date.now) {
6
+ return async (token, resource) => {
7
+ const verification = await verifier(token, resource);
8
+ if (verification === null || verification.caller.identityKind !== 'service') {
9
+ return verification;
10
+ }
11
+ const binding = 'servicePrincipal' in verification ? verification.servicePrincipal : undefined;
12
+ if (binding === undefined || store === undefined)
13
+ return null;
14
+ try {
15
+ const target = await resolveResource(resource);
16
+ if (target === undefined)
17
+ return null;
18
+ const valid = await store.validateAccessBinding({
19
+ principalId: verification.caller.subject,
20
+ grantId: binding.grantId,
21
+ credentialId: binding.credentialId,
22
+ org: target.org,
23
+ app: target.app,
24
+ environment: target.environment,
25
+ now: now(),
26
+ });
27
+ return valid ? { caller: verification.caller } : null;
28
+ }
29
+ catch {
30
+ return null;
31
+ }
32
+ };
33
+ }
34
+ //# sourceMappingURL=service-principal-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-principal-guard.js","sourceRoot":"","sources":["../../src/oauth/service-principal-guard.ts"],"names":[],"mappings":"AAeA;;;GAGG;AACH,MAAM,UAAU,wCAAwC,CACtD,QAAqD,EACrD,KAAwC,EACxC,eAA0F,EAC1F,MAAoB,IAAI,CAAC,GAAG;IAE5B,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE;QAC/B,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACrD,IAAI,YAAY,KAAK,IAAI,IAAI,YAAY,CAAC,MAAM,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;YAC5E,OAAO,YAAY,CAAC;QACtB,CAAC;QACD,MAAM,OAAO,GAAG,kBAAkB,IAAI,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS,CAAC;QAC/F,IAAI,OAAO,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QAC9D,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAC;YAC/C,IAAI,MAAM,KAAK,SAAS;gBAAE,OAAO,IAAI,CAAC;YACtC,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,qBAAqB,CAAC;gBAC9C,WAAW,EAAE,YAAY,CAAC,MAAM,CAAC,OAAO;gBACxC,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,YAAY,EAAE,OAAO,CAAC,YAAY;gBAClC,GAAG,EAAE,MAAM,CAAC,GAAG;gBACf,GAAG,EAAE,MAAM,CAAC,GAAG;gBACf,WAAW,EAAE,MAAM,CAAC,WAAW;gBAC/B,GAAG,EAAE,GAAG,EAAE;aACX,CAAC,CAAC;YACH,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACxD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,14 @@
1
+ import { type TenantRouteRef } from '@noodle-borg/module';
2
+ import type { ServerRegistry } from '../registry.js';
3
+ export interface ServicePrincipalResourceRouting {
4
+ readonly legacyOrigin: string;
5
+ readonly allowedBaseDomains: readonly string[];
6
+ }
7
+ export interface ResolvedServicePrincipalResource {
8
+ readonly resource: string;
9
+ readonly tenant: TenantRouteRef;
10
+ readonly deploymentId?: string;
11
+ }
12
+ /** Resolve only a byte-exact canonical MCP resource that is actively served right now. */
13
+ export declare function resolveServicePrincipalResource(resource: string, registry: ServerRegistry, routing: ServicePrincipalResourceRouting): Promise<ResolvedServicePrincipalResource | undefined>;
14
+ //# sourceMappingURL=service-principal-resource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-principal-resource.d.ts","sourceRoot":"","sources":["../../src/oauth/service-principal-resource.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGrD,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,kBAAkB,EAAE,SAAS,MAAM,EAAE,CAAC;CAChD;AAED,MAAM,WAAW,gCAAgC;IAC/C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,0FAA0F;AAC1F,wBAAsB,+BAA+B,CACnD,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,gCAAgC,GAAG,SAAS,CAAC,CAavD"}
@@ -0,0 +1,53 @@
1
+ import { formatTenantMcpUrl, parseLegacyTenantMcpPath, parsePublicTenantMcpUrl, } from '@noodle-borg/module';
2
+ import { tenantMcpUrl } from '../registry-helpers.js';
3
+ /** Resolve only a byte-exact canonical MCP resource that is actively served right now. */
4
+ export async function resolveServicePrincipalResource(resource, registry, routing) {
5
+ const tenant = exactTenant(resource, routing);
6
+ if (tenant === undefined)
7
+ return undefined;
8
+ const target = tenant.serverVersion === undefined
9
+ ? await registry.getActiveByTenant(tenant)
10
+ : await registry.getActiveByTenantVersion(tenant, tenant.serverVersion);
11
+ if (target === undefined)
12
+ return undefined;
13
+ return {
14
+ resource,
15
+ tenant,
16
+ ...(target.deploymentId === undefined ? {} : { deploymentId: target.deploymentId }),
17
+ };
18
+ }
19
+ function exactTenant(resource, routing) {
20
+ let url;
21
+ try {
22
+ url = new URL(resource);
23
+ }
24
+ catch {
25
+ return undefined;
26
+ }
27
+ if (url.search !== '' || url.hash !== '')
28
+ return undefined;
29
+ const publicTenant = parsePublicTenantMcpUrl(resource, routing.allowedBaseDomains);
30
+ if (publicTenant !== undefined) {
31
+ const exact = routing.allowedBaseDomains.some((baseDomain) => formatTenantMcpUrl(baseDomain, publicTenant) === resource);
32
+ return exact ? publicTenant : undefined;
33
+ }
34
+ let legacyOrigin;
35
+ try {
36
+ legacyOrigin = new URL(routing.legacyOrigin);
37
+ }
38
+ catch {
39
+ return undefined;
40
+ }
41
+ if (legacyOrigin.pathname !== '/' ||
42
+ legacyOrigin.search !== '' ||
43
+ legacyOrigin.hash !== '' ||
44
+ url.origin !== legacyOrigin.origin) {
45
+ return undefined;
46
+ }
47
+ const legacyTenant = parseLegacyTenantMcpPath(url.pathname);
48
+ if (legacyTenant === undefined)
49
+ return undefined;
50
+ const canonical = tenantMcpUrl(legacyOrigin.origin, legacyTenant, legacyTenant.serverVersion);
51
+ return canonical === resource ? legacyTenant : undefined;
52
+ }
53
+ //# sourceMappingURL=service-principal-resource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-principal-resource.js","sourceRoot":"","sources":["../../src/oauth/service-principal-resource.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,wBAAwB,EACxB,uBAAuB,GAExB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAatD,0FAA0F;AAC1F,MAAM,CAAC,KAAK,UAAU,+BAA+B,CACnD,QAAgB,EAChB,QAAwB,EACxB,OAAwC;IAExC,MAAM,MAAM,GAAG,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC9C,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC3C,MAAM,MAAM,GACV,MAAM,CAAC,aAAa,KAAK,SAAS;QAChC,CAAC,CAAC,MAAM,QAAQ,CAAC,iBAAiB,CAAC,MAAM,CAAC;QAC1C,CAAC,CAAC,MAAM,QAAQ,CAAC,wBAAwB,CAAC,MAAM,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC;IAC5E,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC3C,OAAO;QACL,QAAQ;QACR,MAAM;QACN,GAAG,CAAC,MAAM,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC;KACpF,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAClB,QAAgB,EAChB,OAAwC;IAExC,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE,IAAI,GAAG,CAAC,IAAI,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IAE3D,MAAM,YAAY,GAAG,uBAAuB,CAAC,QAAQ,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACnF,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,OAAO,CAAC,kBAAkB,CAAC,IAAI,CAC3C,CAAC,UAAU,EAAE,EAAE,CAAC,kBAAkB,CAAC,UAAU,EAAE,YAAY,CAAC,KAAK,QAAQ,CAC1E,CAAC;QACF,OAAO,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC;IAC1C,CAAC;IAED,IAAI,YAAiB,CAAC;IACtB,IAAI,CAAC;QACH,YAAY,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IACE,YAAY,CAAC,QAAQ,KAAK,GAAG;QAC7B,YAAY,CAAC,MAAM,KAAK,EAAE;QAC1B,YAAY,CAAC,IAAI,KAAK,EAAE;QACxB,GAAG,CAAC,MAAM,KAAK,YAAY,CAAC,MAAM,EAClC,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,YAAY,GAAG,wBAAwB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC5D,IAAI,YAAY,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACjD,MAAM,SAAS,GAAG,YAAY,CAAC,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE,YAAY,CAAC,aAAa,CAAC,CAAC;IAC9F,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3D,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { Pool } from 'pg';
2
+ /** Additively installs the durable service-principal and assertion-replay tables. */
3
+ export declare function ensureServicePrincipalSchema(pool: Pool): Promise<void>;
4
+ //# sourceMappingURL=service-principal-store-postgres-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-principal-store-postgres-schema.d.ts","sourceRoot":"","sources":["../../src/oauth/service-principal-store-postgres-schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAE/B,qFAAqF;AACrF,wBAAsB,4BAA4B,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CA+F5E"}
@@ -0,0 +1,98 @@
1
+ /** Additively installs the durable service-principal and assertion-replay tables. */
2
+ export async function ensureServicePrincipalSchema(pool) {
3
+ await pool.query(`
4
+ CREATE TABLE IF NOT EXISTS oauth_service_principals (
5
+ principal_id text PRIMARY KEY
6
+ CHECK (principal_id ~ '^spn_[0-9a-f-]{36}$'),
7
+ org_slug text NOT NULL,
8
+ name text NOT NULL CHECK (char_length(name) BETWEEN 1 AND 80),
9
+ status text NOT NULL CHECK (status IN ('active', 'revoked')),
10
+ created_by_subject text NOT NULL,
11
+ created_at timestamptz NOT NULL,
12
+ updated_at timestamptz NOT NULL,
13
+ revoked_at timestamptz,
14
+ revoked_by_subject text,
15
+ UNIQUE (principal_id, org_slug)
16
+ )
17
+ `);
18
+ await pool.query(`
19
+ CREATE TABLE IF NOT EXISTS oauth_service_principal_grants (
20
+ grant_id text PRIMARY KEY
21
+ CHECK (grant_id ~ '^spg_[0-9a-f-]{36}$'),
22
+ principal_id text NOT NULL,
23
+ org_slug text NOT NULL,
24
+ app_slug text NOT NULL,
25
+ environment text NOT NULL,
26
+ scopes text[] NOT NULL CHECK (cardinality(scopes) <= 64),
27
+ status text NOT NULL CHECK (status IN ('active', 'revoked')),
28
+ created_by_subject text NOT NULL,
29
+ created_at timestamptz NOT NULL,
30
+ updated_at timestamptz NOT NULL,
31
+ revoked_at timestamptz,
32
+ revoked_by_subject text,
33
+ FOREIGN KEY (principal_id, org_slug)
34
+ REFERENCES oauth_service_principals (principal_id, org_slug)
35
+ ON DELETE RESTRICT
36
+ )
37
+ `);
38
+ await pool.query(`
39
+ CREATE UNIQUE INDEX IF NOT EXISTS oauth_service_principal_active_grant_target_uq
40
+ ON oauth_service_principal_grants (principal_id, app_slug, environment)
41
+ WHERE status = 'active'
42
+ `);
43
+ await pool.query(`
44
+ CREATE INDEX IF NOT EXISTS oauth_service_principal_grants_lookup_idx
45
+ ON oauth_service_principal_grants (principal_id, status)
46
+ `);
47
+ await pool.query(`
48
+ CREATE TABLE IF NOT EXISTS oauth_service_principal_credentials (
49
+ credential_id text PRIMARY KEY
50
+ CHECK (credential_id ~ '^spc_[0-9a-f-]{36}$'),
51
+ principal_id text NOT NULL,
52
+ org_slug text NOT NULL,
53
+ kind text NOT NULL CHECK (kind IN ('public_jwk', 'client_secret')),
54
+ label text NOT NULL CHECK (char_length(label) BETWEEN 1 AND 80),
55
+ algorithm text CHECK (algorithm IN ('RS256', 'ES256')),
56
+ kid text CHECK (kid IS NULL OR char_length(kid) BETWEEN 1 AND 200),
57
+ public_jwk jsonb,
58
+ secret_digest text,
59
+ status text NOT NULL CHECK (status IN ('active', 'revoked')),
60
+ created_by_subject text NOT NULL,
61
+ created_at timestamptz NOT NULL,
62
+ updated_at timestamptz NOT NULL,
63
+ expires_at timestamptz,
64
+ revoked_at timestamptz,
65
+ revoked_by_subject text,
66
+ FOREIGN KEY (principal_id, org_slug)
67
+ REFERENCES oauth_service_principals (principal_id, org_slug)
68
+ ON DELETE RESTRICT,
69
+ CHECK (
70
+ (kind = 'public_jwk' AND algorithm IS NOT NULL AND public_jwk IS NOT NULL
71
+ AND secret_digest IS NULL)
72
+ OR
73
+ (kind = 'client_secret' AND algorithm IS NULL AND kid IS NULL
74
+ AND public_jwk IS NULL AND secret_digest IS NOT NULL)
75
+ )
76
+ )
77
+ `);
78
+ await pool.query(`
79
+ CREATE INDEX IF NOT EXISTS oauth_service_principal_active_credentials_idx
80
+ ON oauth_service_principal_credentials (principal_id, kind, expires_at)
81
+ WHERE status = 'active'
82
+ `);
83
+ await pool.query(`
84
+ CREATE TABLE IF NOT EXISTS oauth_client_assertion_nonces (
85
+ credential_id text NOT NULL
86
+ REFERENCES oauth_service_principal_credentials (credential_id) ON DELETE CASCADE,
87
+ jti text NOT NULL CHECK (char_length(jti) BETWEEN 1 AND 200),
88
+ expires_at timestamptz NOT NULL,
89
+ created_at timestamptz NOT NULL,
90
+ PRIMARY KEY (credential_id, jti)
91
+ )
92
+ `);
93
+ await pool.query(`
94
+ CREATE INDEX IF NOT EXISTS oauth_client_assertion_nonces_expiry_idx
95
+ ON oauth_client_assertion_nonces (expires_at)
96
+ `);
97
+ }
98
+ //# sourceMappingURL=service-principal-store-postgres-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-principal-store-postgres-schema.js","sourceRoot":"","sources":["../../src/oauth/service-principal-store-postgres-schema.ts"],"names":[],"mappings":"AAEA,qFAAqF;AACrF,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAAC,IAAU;IAC3D,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;;;;;GAchB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;;;;;;;;;;GAmBhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;GAIhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;GAGhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;GAIhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;GAShB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;GAGhB,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { Pool } from 'pg';
2
+ import { type ActiveServicePrincipalClient, type AssertionJtiInput, type CreateServicePrincipalGrantInput, type CreateServicePrincipalInput, type CreateStoredCredentialInput, type CredentialMutation, type GrantMutation, type PrincipalMutation, type PrincipalRef, type ServicePrincipalAccessBinding, type ServicePrincipalCredentialRecord, type ServicePrincipalGrantRecord, type ServicePrincipalRecord, type ServicePrincipalStore, type ServicePrincipalView } from './service-principal-store.js';
3
+ /** Durable, organization-isolated service-principal lifecycle and assertion replay store. */
4
+ export declare class PostgresServicePrincipalStore implements ServicePrincipalStore {
5
+ #private;
6
+ constructor(pool: Pool, options?: {
7
+ readonly now?: () => Date;
8
+ });
9
+ ensureSchema(): Promise<void>;
10
+ createPrincipal(input: CreateServicePrincipalInput): Promise<ServicePrincipalRecord>;
11
+ listPrincipals(org: string): Promise<readonly ServicePrincipalRecord[]>;
12
+ getPrincipal(input: PrincipalRef): Promise<ServicePrincipalView | undefined>;
13
+ revokePrincipal(input: PrincipalMutation): Promise<boolean>;
14
+ createGrant(input: CreateServicePrincipalGrantInput): Promise<ServicePrincipalGrantRecord>;
15
+ revokeGrant(input: GrantMutation): Promise<boolean>;
16
+ createCredential(input: CreateStoredCredentialInput): Promise<ServicePrincipalCredentialRecord>;
17
+ revokeCredential(input: CredentialMutation): Promise<boolean>;
18
+ loadActiveClient(clientId: string, now: number): Promise<ActiveServicePrincipalClient | undefined>;
19
+ validateAccessBinding(input: ServicePrincipalAccessBinding): Promise<boolean>;
20
+ consumeAssertionJti(input: AssertionJtiInput): Promise<boolean>;
21
+ }
22
+ //# sourceMappingURL=service-principal-store-postgres.d.ts.map