@_mustachio/openauth 0.11.0 → 0.13.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 (208) hide show
  1. package/dist/esm/adapters/d1/index.js +18 -9
  2. package/dist/esm/adapters/d1/migrations.js +7 -2
  3. package/dist/esm/adapters/d1/session-store.js +39 -0
  4. package/dist/esm/adapters/d1/session.js +2 -2
  5. package/dist/esm/adapters/durable-object/index.js +4 -0
  6. package/dist/esm/adapters/durable-object/session-store.js +42 -0
  7. package/dist/esm/adapters/dynamo/index.js +24 -7
  8. package/dist/esm/adapters/dynamo/session-store.js +49 -0
  9. package/dist/esm/adapters/kms/index.js +6 -2
  10. package/dist/esm/adapters/kv/index.js +6 -2
  11. package/dist/esm/adapters/memory/index.js +24 -7
  12. package/dist/esm/adapters/memory/session-store.js +23 -0
  13. package/dist/esm/adapters/postgres/executor.js +2 -2
  14. package/dist/esm/adapters/postgres/index.js +33 -10
  15. package/dist/esm/adapters/postgres/migrations.js +8 -2
  16. package/dist/esm/adapters/postgres/session-store.js +42 -0
  17. package/dist/esm/client.js +27 -27
  18. package/dist/esm/domain/authorize.js +2 -2
  19. package/dist/esm/domain/callback.js +121 -7
  20. package/dist/esm/domain/client-auth.js +2 -2
  21. package/dist/esm/domain/crypto.js +14 -14
  22. package/dist/esm/domain/discovery.js +2 -2
  23. package/dist/esm/domain/dpop.js +4 -4
  24. package/dist/esm/domain/id-token.js +5 -5
  25. package/dist/esm/domain/jwt.js +4 -4
  26. package/dist/esm/domain/method-dispatch.js +27 -1
  27. package/dist/esm/domain/method-route.js +80 -1
  28. package/dist/esm/domain/par.js +2 -2
  29. package/dist/esm/domain/pkce.js +2 -2
  30. package/dist/esm/domain/revoke.js +2 -2
  31. package/dist/esm/domain/scim/discovery.js +206 -0
  32. package/dist/esm/domain/scim/filter.js +177 -0
  33. package/dist/esm/domain/scim/handle.js +338 -0
  34. package/dist/esm/domain/scim/patch.js +568 -0
  35. package/dist/esm/domain/scim/resource.js +256 -0
  36. package/dist/esm/domain/state-envelope.js +2 -2
  37. package/dist/esm/domain/token.js +5 -5
  38. package/dist/esm/error.js +8 -8
  39. package/dist/esm/http/cookies.js +19 -2
  40. package/dist/esm/http/errors.js +4 -4
  41. package/dist/esm/http/handlers/authorize.js +3 -19
  42. package/dist/esm/http/handlers/callback.js +7 -1
  43. package/dist/esm/http/handlers/metadata.js +2 -2
  44. package/dist/esm/http/handlers/method-route.js +47 -2
  45. package/dist/esm/http/handlers/revocation.js +2 -2
  46. package/dist/esm/http/handlers/scim.js +59 -0
  47. package/dist/esm/http/middleware/tenant.js +31 -6
  48. package/dist/esm/http/router.js +3 -0
  49. package/dist/esm/http/schemas/authorize.js +2 -2
  50. package/dist/esm/http/schemas/revocation.js +2 -2
  51. package/dist/esm/http/schemas/token.js +7 -7
  52. package/dist/esm/index.js +34 -32
  53. package/dist/esm/methods/oauth2-factory.js +2 -2
  54. package/dist/esm/methods/password-hash.js +2 -2
  55. package/dist/esm/methods/providers/index.js +30 -14
  56. package/dist/esm/methods/saml-sp/acs.js +232 -0
  57. package/dist/esm/methods/saml-sp/attributes.js +77 -0
  58. package/dist/esm/methods/saml-sp/authnrequest.js +63 -0
  59. package/dist/esm/methods/saml-sp/cache-provider.js +33 -0
  60. package/dist/esm/methods/saml-sp/cert-rotation.js +13 -0
  61. package/dist/esm/methods/saml-sp/factory.js +81 -0
  62. package/dist/esm/methods/saml-sp/index.js +7 -0
  63. package/dist/esm/methods/saml-sp/metadata.js +71 -0
  64. package/dist/esm/methods/saml-sp/method.js +29 -0
  65. package/dist/esm/methods/saml-sp/parse-idp-metadata.js +102 -0
  66. package/dist/esm/methods/saml-sp/saml-instance.js +59 -0
  67. package/dist/esm/methods/saml-sp/slo-initiate.js +84 -0
  68. package/dist/esm/methods/saml-sp/sls.js +154 -0
  69. package/dist/esm/methods/saml-sp/types.js +0 -0
  70. package/dist/esm/ports/scim-directory.js +0 -0
  71. package/dist/esm/types/error.js +5 -0
  72. package/dist/esm/types/result.js +3 -3
  73. package/dist/esm/types/scim.js +0 -0
  74. package/dist/esm/ui/forms.js +3 -3
  75. package/dist/esm/ui/index.js +8 -5
  76. package/dist/esm/ui/picker.js +2 -2
  77. package/dist/types/adapters/d1/migrations.d.ts.map +1 -1
  78. package/dist/types/adapters/d1/session-store.d.ts +3 -0
  79. package/dist/types/adapters/d1/session-store.d.ts.map +1 -1
  80. package/dist/types/adapters/durable-object/session-store.d.ts +3 -0
  81. package/dist/types/adapters/durable-object/session-store.d.ts.map +1 -1
  82. package/dist/types/adapters/dynamo/session-store.d.ts +3 -0
  83. package/dist/types/adapters/dynamo/session-store.d.ts.map +1 -1
  84. package/dist/types/adapters/memory/session-store.d.ts +3 -0
  85. package/dist/types/adapters/memory/session-store.d.ts.map +1 -1
  86. package/dist/types/adapters/postgres/migrations.d.ts +1 -1
  87. package/dist/types/adapters/postgres/migrations.d.ts.map +1 -1
  88. package/dist/types/adapters/postgres/session-store.d.ts +3 -0
  89. package/dist/types/adapters/postgres/session-store.d.ts.map +1 -1
  90. package/dist/types/domain/callback.d.ts +16 -1
  91. package/dist/types/domain/callback.d.ts.map +1 -1
  92. package/dist/types/domain/method-dispatch.d.ts.map +1 -1
  93. package/dist/types/domain/method-route.d.ts +57 -1
  94. package/dist/types/domain/method-route.d.ts.map +1 -1
  95. package/dist/types/domain/scim/discovery.d.ts +11 -0
  96. package/dist/types/domain/scim/discovery.d.ts.map +1 -0
  97. package/dist/types/domain/scim/filter.d.ts +51 -0
  98. package/dist/types/domain/scim/filter.d.ts.map +1 -0
  99. package/dist/types/domain/scim/handle.d.ts +35 -0
  100. package/dist/types/domain/scim/handle.d.ts.map +1 -0
  101. package/dist/types/domain/scim/patch.d.ts +73 -0
  102. package/dist/types/domain/scim/patch.d.ts.map +1 -0
  103. package/dist/types/domain/scim/resource.d.ts +56 -0
  104. package/dist/types/domain/scim/resource.d.ts.map +1 -0
  105. package/dist/types/http/context.d.ts +6 -1
  106. package/dist/types/http/context.d.ts.map +1 -1
  107. package/dist/types/http/cookies.d.ts +9 -1
  108. package/dist/types/http/cookies.d.ts.map +1 -1
  109. package/dist/types/http/handlers/callback.d.ts.map +1 -1
  110. package/dist/types/http/handlers/method-route.d.ts.map +1 -1
  111. package/dist/types/http/handlers/scim.d.ts +3 -0
  112. package/dist/types/http/handlers/scim.d.ts.map +1 -0
  113. package/dist/types/http/middleware/tenant.d.ts.map +1 -1
  114. package/dist/types/http/router.d.ts.map +1 -1
  115. package/dist/types/index.d.ts +3 -1
  116. package/dist/types/index.d.ts.map +1 -1
  117. package/dist/types/methods/saml-sp/acs.d.ts +4 -0
  118. package/dist/types/methods/saml-sp/acs.d.ts.map +1 -0
  119. package/dist/types/methods/saml-sp/attributes.d.ts +45 -0
  120. package/dist/types/methods/saml-sp/attributes.d.ts.map +1 -0
  121. package/dist/types/methods/saml-sp/authnrequest.d.ts +4 -0
  122. package/dist/types/methods/saml-sp/authnrequest.d.ts.map +1 -0
  123. package/dist/types/methods/saml-sp/cache-provider.d.ts +38 -0
  124. package/dist/types/methods/saml-sp/cache-provider.d.ts.map +1 -0
  125. package/dist/types/methods/saml-sp/cert-rotation.d.ts +23 -0
  126. package/dist/types/methods/saml-sp/cert-rotation.d.ts.map +1 -0
  127. package/dist/types/methods/saml-sp/factory.d.ts +920 -0
  128. package/dist/types/methods/saml-sp/factory.d.ts.map +1 -0
  129. package/dist/types/methods/saml-sp/index.d.ts +24 -0
  130. package/dist/types/methods/saml-sp/index.d.ts.map +1 -0
  131. package/dist/types/methods/saml-sp/metadata.d.ts +50 -0
  132. package/dist/types/methods/saml-sp/metadata.d.ts.map +1 -0
  133. package/dist/types/methods/saml-sp/method.d.ts +17 -0
  134. package/dist/types/methods/saml-sp/method.d.ts.map +1 -0
  135. package/dist/types/methods/saml-sp/parse-idp-metadata.d.ts +4 -0
  136. package/dist/types/methods/saml-sp/parse-idp-metadata.d.ts.map +1 -0
  137. package/dist/types/methods/saml-sp/saml-instance.d.ts +93 -0
  138. package/dist/types/methods/saml-sp/saml-instance.d.ts.map +1 -0
  139. package/dist/types/methods/saml-sp/slo-initiate.d.ts +4 -0
  140. package/dist/types/methods/saml-sp/slo-initiate.d.ts.map +1 -0
  141. package/dist/types/methods/saml-sp/sls.d.ts +4 -0
  142. package/dist/types/methods/saml-sp/sls.d.ts.map +1 -0
  143. package/dist/types/methods/saml-sp/types.d.ts +299 -0
  144. package/dist/types/methods/saml-sp/types.d.ts.map +1 -0
  145. package/dist/types/ports/audit-log.d.ts +18 -5
  146. package/dist/types/ports/audit-log.d.ts.map +1 -1
  147. package/dist/types/ports/scim-directory.d.ts +104 -0
  148. package/dist/types/ports/scim-directory.d.ts.map +1 -0
  149. package/dist/types/ports/session-store.d.ts +23 -0
  150. package/dist/types/ports/session-store.d.ts.map +1 -1
  151. package/dist/types/types/error.d.ts +5 -0
  152. package/dist/types/types/error.d.ts.map +1 -1
  153. package/dist/types/types/idp.d.ts +83 -0
  154. package/dist/types/types/idp.d.ts.map +1 -1
  155. package/dist/types/types/method.d.ts +109 -0
  156. package/dist/types/types/method.d.ts.map +1 -1
  157. package/dist/types/types/scim.d.ts +269 -0
  158. package/dist/types/types/scim.d.ts.map +1 -0
  159. package/dist/types/types/tenant.d.ts +10 -0
  160. package/dist/types/types/tenant.d.ts.map +1 -1
  161. package/package.json +9 -1
  162. package/src/adapters/d1/migrations.ts +5 -0
  163. package/src/adapters/d1/session-store.ts +68 -0
  164. package/src/adapters/durable-object/session-store.ts +61 -0
  165. package/src/adapters/dynamo/session-store.ts +63 -0
  166. package/src/adapters/memory/session-store.ts +39 -0
  167. package/src/adapters/postgres/migrations.ts +6 -0
  168. package/src/adapters/postgres/session-store.ts +69 -0
  169. package/src/domain/callback.ts +215 -7
  170. package/src/domain/method-dispatch.ts +53 -1
  171. package/src/domain/method-route.ts +190 -1
  172. package/src/domain/scim/discovery.ts +262 -0
  173. package/src/domain/scim/filter.ts +295 -0
  174. package/src/domain/scim/handle.ts +597 -0
  175. package/src/domain/scim/patch.ts +807 -0
  176. package/src/domain/scim/resource.ts +358 -0
  177. package/src/http/context.ts +9 -0
  178. package/src/http/cookies.ts +19 -1
  179. package/src/http/handlers/authorize.ts +2 -22
  180. package/src/http/handlers/callback.ts +9 -1
  181. package/src/http/handlers/method-route.ts +72 -2
  182. package/src/http/handlers/scim.ts +107 -0
  183. package/src/http/middleware/tenant.ts +47 -1
  184. package/src/http/router.ts +9 -0
  185. package/src/index.ts +23 -0
  186. package/src/methods/saml-sp/acs.ts +449 -0
  187. package/src/methods/saml-sp/attributes.ts +146 -0
  188. package/src/methods/saml-sp/authnrequest.ts +129 -0
  189. package/src/methods/saml-sp/cache-provider.ts +76 -0
  190. package/src/methods/saml-sp/cert-rotation.ts +34 -0
  191. package/src/methods/saml-sp/factory.ts +139 -0
  192. package/src/methods/saml-sp/index.ts +34 -0
  193. package/src/methods/saml-sp/metadata.ts +215 -0
  194. package/src/methods/saml-sp/method.ts +65 -0
  195. package/src/methods/saml-sp/parse-idp-metadata.ts +158 -0
  196. package/src/methods/saml-sp/saml-instance.ts +210 -0
  197. package/src/methods/saml-sp/slo-initiate.ts +180 -0
  198. package/src/methods/saml-sp/sls.ts +307 -0
  199. package/src/methods/saml-sp/types.ts +298 -0
  200. package/src/ports/CONSISTENCY.md +52 -24
  201. package/src/ports/audit-log.ts +18 -5
  202. package/src/ports/scim-directory.ts +166 -0
  203. package/src/ports/session-store.ts +24 -0
  204. package/src/types/error.ts +11 -0
  205. package/src/types/idp.ts +86 -0
  206. package/src/types/method.ts +110 -0
  207. package/src/types/scim.ts +280 -0
  208. package/src/types/tenant.ts +10 -0
@@ -0,0 +1,256 @@
1
+ // src/domain/scim/resource.ts
2
+ import { err, ok } from "../../types/result";
3
+ var SCIM_USER_SCHEMA = "urn:ietf:params:scim:schemas:core:2.0:User";
4
+ var SCIM_GROUP_SCHEMA = "urn:ietf:params:scim:schemas:core:2.0:Group";
5
+ var SCIM_ENTERPRISE_SCHEMA = "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User";
6
+ var SCIM_LIST_SCHEMA = "urn:ietf:params:scim:api:messages:2.0:ListResponse";
7
+ var SCIM_ERROR_SCHEMA = "urn:ietf:params:scim:api:messages:2.0:Error";
8
+ var SCIM_PATCH_SCHEMA = "urn:ietf:params:scim:api:messages:2.0:PatchOp";
9
+ function scimErrorBody(status, detail, scimType) {
10
+ return {
11
+ schemas: [SCIM_ERROR_SCHEMA],
12
+ status: String(status),
13
+ ...scimType !== undefined ? { scimType } : {},
14
+ detail
15
+ };
16
+ }
17
+ var isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
18
+ var asString = (v) => typeof v === "string" && v.length > 0 ? v : undefined;
19
+ function parseName(v) {
20
+ if (!isRecord(v))
21
+ return;
22
+ const out = {};
23
+ for (const k of [
24
+ "formatted",
25
+ "familyName",
26
+ "givenName",
27
+ "middleName",
28
+ "honorificPrefix",
29
+ "honorificSuffix"
30
+ ]) {
31
+ const s = asString(v[k]);
32
+ if (s !== undefined)
33
+ out[k] = s;
34
+ }
35
+ return Object.keys(out).length > 0 ? out : undefined;
36
+ }
37
+ function parseMultiValues(v) {
38
+ if (!Array.isArray(v))
39
+ return;
40
+ const out = [];
41
+ for (const entry of v) {
42
+ if (!isRecord(entry))
43
+ continue;
44
+ const value = asString(entry["value"]);
45
+ if (value === undefined)
46
+ continue;
47
+ const type = asString(entry["type"]);
48
+ const primary = entry["primary"];
49
+ out.push({
50
+ value,
51
+ ...type !== undefined ? { type } : {},
52
+ ...typeof primary === "boolean" ? { primary } : {}
53
+ });
54
+ }
55
+ return out.length > 0 ? out : undefined;
56
+ }
57
+ function parseEnterprise(v) {
58
+ if (!isRecord(v))
59
+ return;
60
+ const out = {};
61
+ for (const k of [
62
+ "employeeNumber",
63
+ "costCenter",
64
+ "organization",
65
+ "division",
66
+ "department"
67
+ ]) {
68
+ const s = asString(v[k]);
69
+ if (s !== undefined)
70
+ out[k] = s;
71
+ }
72
+ const mgr = v["manager"];
73
+ if (isRecord(mgr)) {
74
+ const value = asString(mgr["value"]);
75
+ const displayName = asString(mgr["displayName"]);
76
+ if (value !== undefined || displayName !== undefined) {
77
+ out.manager = {
78
+ ...value !== undefined ? { value } : {},
79
+ ...displayName !== undefined ? { displayName } : {}
80
+ };
81
+ }
82
+ }
83
+ return Object.keys(out).length > 0 ? out : undefined;
84
+ }
85
+ function parseUserWrite(body) {
86
+ if (!isRecord(body)) {
87
+ return err({
88
+ status: 400,
89
+ scimType: "invalidSyntax",
90
+ detail: "request body must be a JSON object"
91
+ });
92
+ }
93
+ if (body["password"] !== undefined) {
94
+ return err({
95
+ status: 400,
96
+ scimType: "invalidValue",
97
+ detail: "this Service Provider does not accept 'password' over SCIM; " + "credentials are managed by the authentication methods, not the " + "directory feed"
98
+ });
99
+ }
100
+ const userName = asString(body["userName"]);
101
+ if (userName === undefined) {
102
+ return err({
103
+ status: 400,
104
+ scimType: "invalidValue",
105
+ detail: "userName is required and must be a non-empty string"
106
+ });
107
+ }
108
+ const activeRaw = body["active"];
109
+ if (activeRaw !== undefined && typeof activeRaw !== "boolean") {
110
+ return err({
111
+ status: 400,
112
+ scimType: "invalidValue",
113
+ detail: "active must be a boolean"
114
+ });
115
+ }
116
+ const externalId = asString(body["externalId"]);
117
+ const displayName = asString(body["displayName"]);
118
+ const name = parseName(body["name"]);
119
+ const emails = parseMultiValues(body["emails"]);
120
+ const phoneNumbers = parseMultiValues(body["phoneNumbers"]);
121
+ const enterprise = parseEnterprise(body[SCIM_ENTERPRISE_SCHEMA]);
122
+ return ok({
123
+ userName,
124
+ active: activeRaw === undefined ? true : activeRaw,
125
+ ...externalId !== undefined ? { externalId } : {},
126
+ ...displayName !== undefined ? { displayName } : {},
127
+ ...name !== undefined ? { name } : {},
128
+ ...emails !== undefined ? { emails } : {},
129
+ ...phoneNumbers !== undefined ? { phoneNumbers } : {},
130
+ ...enterprise !== undefined ? { enterprise } : {}
131
+ });
132
+ }
133
+ var iso = (ms) => ms === undefined ? undefined : new Date(ms).toISOString();
134
+ function serializeUser(user, baseUrl) {
135
+ const schemas = [SCIM_USER_SCHEMA];
136
+ if (user.enterprise !== undefined)
137
+ schemas.push(SCIM_ENTERPRISE_SCHEMA);
138
+ const created = iso(user.createdAt);
139
+ const lastModified = iso(user.updatedAt);
140
+ return {
141
+ schemas,
142
+ id: user.id,
143
+ ...user.externalId !== undefined ? { externalId: user.externalId } : {},
144
+ userName: user.userName,
145
+ ...user.name !== undefined ? { name: user.name } : {},
146
+ ...user.displayName !== undefined ? { displayName: user.displayName } : {},
147
+ ...user.emails !== undefined ? { emails: user.emails } : {},
148
+ ...user.phoneNumbers !== undefined ? { phoneNumbers: user.phoneNumbers } : {},
149
+ active: user.active,
150
+ ...user.enterprise !== undefined ? { [SCIM_ENTERPRISE_SCHEMA]: user.enterprise } : {},
151
+ meta: {
152
+ resourceType: "User",
153
+ ...created !== undefined ? { created } : {},
154
+ ...lastModified !== undefined ? { lastModified } : {},
155
+ location: `${baseUrl}/Users/${encodeURIComponent(user.id)}`
156
+ }
157
+ };
158
+ }
159
+ function serializeList(users, totalResults, startIndex, baseUrl) {
160
+ return {
161
+ schemas: [SCIM_LIST_SCHEMA],
162
+ totalResults,
163
+ startIndex,
164
+ itemsPerPage: users.length,
165
+ Resources: users.map((u) => serializeUser(u, baseUrl))
166
+ };
167
+ }
168
+ function parseMembers(v) {
169
+ const list = Array.isArray(v) ? v : isRecord(v) ? [v] : undefined;
170
+ if (list === undefined)
171
+ return;
172
+ const out = [];
173
+ for (const entry of list) {
174
+ if (!isRecord(entry))
175
+ continue;
176
+ const value = asString(entry["value"]);
177
+ if (value === undefined)
178
+ continue;
179
+ const display = asString(entry["display"]);
180
+ out.push({ value, ...display !== undefined ? { display } : {} });
181
+ }
182
+ return out;
183
+ }
184
+ function parseGroupWrite(body) {
185
+ if (!isRecord(body)) {
186
+ return err({
187
+ status: 400,
188
+ scimType: "invalidSyntax",
189
+ detail: "request body must be a JSON object"
190
+ });
191
+ }
192
+ const displayName = asString(body["displayName"]);
193
+ if (displayName === undefined) {
194
+ return err({
195
+ status: 400,
196
+ scimType: "invalidValue",
197
+ detail: "displayName is required and must be a non-empty string"
198
+ });
199
+ }
200
+ const externalId = asString(body["externalId"]);
201
+ const members = parseMembers(body["members"]);
202
+ return ok({
203
+ displayName,
204
+ ...externalId !== undefined ? { externalId } : {},
205
+ ...members !== undefined ? { members } : {}
206
+ });
207
+ }
208
+ function serializeGroup(group, baseUrl) {
209
+ const created = iso(group.createdAt);
210
+ const lastModified = iso(group.updatedAt);
211
+ return {
212
+ schemas: [SCIM_GROUP_SCHEMA],
213
+ id: group.id,
214
+ ...group.externalId !== undefined ? { externalId: group.externalId } : {},
215
+ displayName: group.displayName,
216
+ ...group.members !== undefined ? {
217
+ members: group.members.map((m) => ({
218
+ value: m.value,
219
+ ...m.display !== undefined ? { display: m.display } : {},
220
+ $ref: `${baseUrl}/Users/${encodeURIComponent(m.value)}`,
221
+ type: "User"
222
+ }))
223
+ } : {},
224
+ meta: {
225
+ resourceType: "Group",
226
+ ...created !== undefined ? { created } : {},
227
+ ...lastModified !== undefined ? { lastModified } : {},
228
+ location: `${baseUrl}/Groups/${encodeURIComponent(group.id)}`
229
+ }
230
+ };
231
+ }
232
+ function serializeGroupList(groups, totalResults, startIndex, baseUrl) {
233
+ return {
234
+ schemas: [SCIM_LIST_SCHEMA],
235
+ totalResults,
236
+ startIndex,
237
+ itemsPerPage: groups.length,
238
+ Resources: groups.map((g) => serializeGroup(g, baseUrl))
239
+ };
240
+ }
241
+ export {
242
+ SCIM_ENTERPRISE_SCHEMA,
243
+ SCIM_ERROR_SCHEMA,
244
+ SCIM_GROUP_SCHEMA,
245
+ SCIM_LIST_SCHEMA,
246
+ SCIM_PATCH_SCHEMA,
247
+ SCIM_USER_SCHEMA,
248
+ parseGroupWrite,
249
+ parseMembers,
250
+ parseUserWrite,
251
+ scimErrorBody,
252
+ serializeGroup,
253
+ serializeGroupList,
254
+ serializeList,
255
+ serializeUser
256
+ };
@@ -75,6 +75,6 @@ function isEnvelopeShape(value) {
75
75
  return typeof v.tenantId === "string" && typeof v.flowId === "string" && typeof v.nonce === "string" && typeof v.kid === "string";
76
76
  }
77
77
  export {
78
- verifyStateEnvelope,
79
- mintStateEnvelope
78
+ mintStateEnvelope,
79
+ verifyStateEnvelope
80
80
  };
@@ -237,10 +237,10 @@ async function hashTokenForAudit(token) {
237
237
  return base64url.encode(await sha256(utf8.encode(token))).slice(0, 16);
238
238
  }
239
239
  export {
240
- saveEncryptedCode,
241
- mintTokens,
242
- hashClientSecret,
243
- exchangeCode,
240
+ DEFAULT_ACCESS_TTL_MS,
244
241
  DEFAULT_REFRESH_TTL_MS,
245
- DEFAULT_ACCESS_TTL_MS
242
+ exchangeCode,
243
+ hashClientSecret,
244
+ mintTokens,
245
+ saveEncryptedCode
246
246
  };
package/dist/esm/error.js CHANGED
@@ -61,13 +61,13 @@ class InvalidAuthorizationCodeError extends Error {
61
61
  }
62
62
  }
63
63
  export {
64
- UnknownStateError,
65
- UnauthorizedClientError,
66
- OauthError,
67
- MissingProviderError,
68
- MissingParameterError,
69
- InvalidSubjectError,
70
- InvalidRefreshTokenError,
64
+ InvalidAccessTokenError,
71
65
  InvalidAuthorizationCodeError,
72
- InvalidAccessTokenError
66
+ InvalidRefreshTokenError,
67
+ InvalidSubjectError,
68
+ MissingParameterError,
69
+ MissingProviderError,
70
+ OauthError,
71
+ UnauthorizedClientError,
72
+ UnknownStateError
73
73
  };
@@ -67,6 +67,22 @@ var STRIPPED_HEADERS = [
67
67
  "permissions-policy",
68
68
  "cache-control"
69
69
  ];
70
+ function cacheControlHeader(cache) {
71
+ if (!cache)
72
+ return "no-store";
73
+ if ((cache.maxAge ?? -1) === 0)
74
+ return "no-store";
75
+ const parts = [];
76
+ if (cache.isPrivate)
77
+ parts.push("private");
78
+ if (cache.maxAge !== undefined)
79
+ parts.push(`max-age=${cache.maxAge}`);
80
+ if (cache.sMaxAge !== undefined)
81
+ parts.push(`s-maxage=${cache.sMaxAge}`);
82
+ if (cache.immutable)
83
+ parts.push("immutable");
84
+ return parts.length ? parts.join(", ") : "no-store";
85
+ }
70
86
  function applyResponsePolicy(response, opts = {}) {
71
87
  const headers = new Headers(response.headers);
72
88
  const stripped = [];
@@ -92,7 +108,8 @@ function applyResponsePolicy(response, opts = {}) {
92
108
  });
93
109
  }
94
110
  export {
95
- serializeSetCookie,
111
+ applyResponsePolicy,
112
+ cacheControlHeader,
96
113
  parseCookieHeader,
97
- applyResponsePolicy
114
+ serializeSetCookie
98
115
  };
@@ -63,9 +63,9 @@ function isNonRecoverable(error) {
63
63
  return false;
64
64
  }
65
65
  export {
66
- tokenEndpointErrorResponse,
67
- publicErrorCode,
68
- isNonRecoverable,
66
+ authorizeDirectErrorResponse,
69
67
  authorizeRedirectErrorResponse,
70
- authorizeDirectErrorResponse
68
+ isNonRecoverable,
69
+ publicErrorCode,
70
+ tokenEndpointErrorResponse
71
71
  };
@@ -4,7 +4,7 @@ import { startAuthorize } from "../../domain/authorize";
4
4
  import { asTenantId } from "../../types/tenant";
5
5
  import { authError } from "../../types/error";
6
6
  import { renderPicker as renderDefaultPicker } from "../../ui/picker";
7
- import { applyResponsePolicy } from "../cookies";
7
+ import { applyResponsePolicy, cacheControlHeader } from "../cookies";
8
8
  import {
9
9
  authorizeDirectErrorResponse,
10
10
  authorizeRedirectErrorResponse,
@@ -48,7 +48,7 @@ function makeAuthorizeHandler(deps) {
48
48
  }
49
49
  const q = parsed.data;
50
50
  if (raw.request_uri === undefined) {
51
- const client = tenant.config.clients.find((c2) => c2.id === q.client_id);
51
+ const client = tenant.config.clients.find((c) => c.id === q.client_id);
52
52
  if (client?.requirePushedAuthorizationRequests) {
53
53
  return authorizeDirectErrorResponse(authError.invalidRequest("this client requires Pushed Authorization Requests (RFC 9126)", "request_uri"));
54
54
  }
@@ -123,7 +123,7 @@ function makeAuthorizeHandler(deps) {
123
123
  return applyResponsePolicy(out.response, {
124
124
  setCookies: [flowCookie, ...out.setCookies],
125
125
  cookieDefaults: deps.cookieDefaults,
126
- cacheControl: cacheControlFor(out.cache)
126
+ cacheControl: cacheControlHeader(out.cache)
127
127
  });
128
128
  }
129
129
  case "issue-code": {
@@ -229,22 +229,6 @@ function clearFlowCookie() {
229
229
  maxAge: 0
230
230
  };
231
231
  }
232
- function cacheControlFor(cache) {
233
- if (!cache)
234
- return "no-store";
235
- if ((cache.maxAge ?? -1) === 0)
236
- return "no-store";
237
- const parts = [];
238
- if (cache.isPrivate)
239
- parts.push("private");
240
- if (cache.maxAge !== undefined)
241
- parts.push(`max-age=${cache.maxAge}`);
242
- if (cache.sMaxAge !== undefined)
243
- parts.push(`s-maxage=${cache.sMaxAge}`);
244
- if (cache.immutable)
245
- parts.push("immutable");
246
- return parts.length ? parts.join(", ") : "no-store";
247
- }
248
232
  export {
249
233
  makeAuthorizeHandler
250
234
  };
@@ -5,7 +5,13 @@ import { applyResponsePolicy } from "../cookies";
5
5
  import { authorizeDirectErrorResponse } from "../errors";
6
6
  function makeCallbackHandler(deps) {
7
7
  return async (c) => {
8
- const result = await handleCallback({ rawRequest: c.req.raw, cookies: c.get("cookies") }, {
8
+ const tenant = c.get("tenant");
9
+ const result = await handleCallback({
10
+ rawRequest: c.req.raw,
11
+ cookies: c.get("cookies"),
12
+ ...tenant ? { tenant } : {},
13
+ issuerUrl: c.get("issuerUrl")
14
+ }, {
9
15
  configStore: deps.configStore,
10
16
  sessionStore: deps.sessionStore,
11
17
  tokenStore: deps.tokenStore,
@@ -32,6 +32,6 @@ function makeJwksHandler(deps) {
32
32
  };
33
33
  }
34
34
  export {
35
- makeJwksHandler,
36
- makeDiscoveryHandler
35
+ makeDiscoveryHandler,
36
+ makeJwksHandler
37
37
  };
@@ -1,8 +1,11 @@
1
1
  // src/http/handlers/method-route.ts
2
- import { handleMethodRoute } from "../../domain/method-route";
2
+ import {
3
+ handleMethodRoute,
4
+ handlePublicMethodRoute
5
+ } from "../../domain/method-route";
3
6
  import { authError } from "../../types/error";
4
7
  import { isErr } from "../../types/result";
5
- import { applyResponsePolicy } from "../cookies";
8
+ import { applyResponsePolicy, cacheControlHeader } from "../cookies";
6
9
  import { authorizeDirectErrorResponse } from "../errors";
7
10
  function makeMethodRouteHandler(deps) {
8
11
  return async (c) => {
@@ -10,6 +13,48 @@ function makeMethodRouteHandler(deps) {
10
13
  if (!tenant) {
11
14
  return authorizeDirectErrorResponse(authError.invalidRequest("tenant unresolved"));
12
15
  }
16
+ {
17
+ const u = new URL(c.req.url);
18
+ const segs = u.pathname.split("/").filter(Boolean);
19
+ if (segs.length >= 3 && segs[0] === "m") {
20
+ const methodId = segs[1];
21
+ const subPath = "/" + segs.slice(2).join("/");
22
+ const httpMethod = c.req.method.toUpperCase() === "POST" ? "POST" : "GET";
23
+ const routeKey = `${httpMethod} ${subPath}`;
24
+ const resolved = await deps.methodCache.resolve(tenant.config, methodId);
25
+ if (!isErr(resolved) && resolved.value.publicRoutes?.includes(routeKey)) {
26
+ const issuerUrl = c.get("issuerUrl");
27
+ const callbackHost = deps.callbackHostFor?.(tenant.id) ?? new URL(issuerUrl).host;
28
+ const callbackUrl = `${new URL(issuerUrl).protocol}//${callbackHost}/cb/${methodId}`;
29
+ const pub = await handlePublicMethodRoute({
30
+ rawRequest: c.req.raw,
31
+ tenant,
32
+ method: resolved.value,
33
+ route: routeKey,
34
+ subPath,
35
+ dispatch: { state: "", callbackUrl, issuerUrl }
36
+ }, {
37
+ sessionStore: deps.sessionStore,
38
+ tokenStore: deps.tokenStore,
39
+ clock: deps.clock,
40
+ ...deps.auditLog ? { auditLog: deps.auditLog } : {},
41
+ ...deps.onLogout ? { onLogout: deps.onLogout } : {}
42
+ });
43
+ if (isErr(pub))
44
+ return authorizeDirectErrorResponse(pub.error);
45
+ if (pub.value.kind === "denied") {
46
+ return applyResponsePolicy(new Response(`access_denied: ${pub.value.reason}`, {
47
+ status: 403,
48
+ headers: { "content-type": "text/plain" }
49
+ }), { cookieDefaults: deps.cookieDefaults });
50
+ }
51
+ return applyResponsePolicy(pub.value.response, {
52
+ cacheControl: cacheControlHeader(pub.value.cache),
53
+ cookieDefaults: deps.cookieDefaults
54
+ });
55
+ }
56
+ }
57
+ }
13
58
  const cookies = c.get("cookies");
14
59
  const flowId = cookies.get("idp.flow");
15
60
  if (!flowId) {
@@ -93,6 +93,6 @@ async function readForm(req) {
93
93
  return new URLSearchParams(await req.text());
94
94
  }
95
95
  export {
96
- makeRevokeHandler,
97
- makeIntrospectHandler
96
+ makeIntrospectHandler,
97
+ makeRevokeHandler
98
98
  };
@@ -0,0 +1,59 @@
1
+ // src/http/handlers/scim.ts
2
+ import { handleScimRequest } from "../../domain/scim/handle";
3
+ import { scimErrorBody } from "../../domain/scim/resource";
4
+ var SCIM_CONTENT_TYPE = "application/scim+json;charset=utf-8";
5
+ var MOUNT = "/scim/v2";
6
+ function scimResponse(status, body, extra = {}) {
7
+ if (body === null || status === 204) {
8
+ return new Response(null, { status, headers: extra });
9
+ }
10
+ return new Response(JSON.stringify(body), {
11
+ status,
12
+ headers: {
13
+ "content-type": SCIM_CONTENT_TYPE,
14
+ "cache-control": "no-store",
15
+ ...extra
16
+ }
17
+ });
18
+ }
19
+ function makeScimHandler(deps) {
20
+ return async (c) => {
21
+ if (!deps.scimDirectory) {
22
+ return scimResponse(501, scimErrorBody(501, "SCIM is not configured on this deployment (no scimDirectory " + "was supplied to createIdP)"));
23
+ }
24
+ const tenant = c.get("tenant");
25
+ if (!tenant) {
26
+ return scimResponse(403, scimErrorBody(403, "SCIM provisioning is not enabled for this tenant"));
27
+ }
28
+ const url = new URL(c.req.url);
29
+ const path = url.pathname.startsWith(MOUNT) ? url.pathname.slice(MOUNT.length) : url.pathname;
30
+ const method = c.req.method.toUpperCase();
31
+ let body = null;
32
+ if (method === "POST" || method === "PUT" || method === "PATCH") {
33
+ const raw = await c.req.raw.text();
34
+ if (raw.length > 0) {
35
+ try {
36
+ body = JSON.parse(raw);
37
+ } catch {
38
+ return scimResponse(400, scimErrorBody(400, "request body is not valid JSON", "invalidSyntax"));
39
+ }
40
+ }
41
+ }
42
+ const issuer = c.get("issuerUrl");
43
+ const base = issuer.endsWith("/") ? issuer.slice(0, -1) : issuer;
44
+ const result = await handleScimRequest({
45
+ tenant,
46
+ method,
47
+ path,
48
+ query: url.searchParams,
49
+ body,
50
+ authorization: c.req.header("authorization") ?? null,
51
+ baseUrl: `${base}${MOUNT}`,
52
+ directory: deps.scimDirectory
53
+ });
54
+ return scimResponse(result.status, result.body, result.headers ?? {});
55
+ };
56
+ }
57
+ export {
58
+ makeScimHandler
59
+ };
@@ -7,6 +7,25 @@ import {
7
7
  tokenEndpointErrorResponse
8
8
  } from "../errors";
9
9
  import { authError } from "../../types/error";
10
+ function isScimPath(pathname) {
11
+ return pathname === "/scim/v2" || pathname.startsWith("/scim/v2/");
12
+ }
13
+ function scimTenantErrorResponse(error) {
14
+ const operational = error.code === "server_error" || error.code === "internal_error";
15
+ const status = operational ? 500 : 403;
16
+ const detail = operational ? "the configuration store backing this endpoint failed the request" : "SCIM provisioning is not enabled for this tenant";
17
+ return new Response(JSON.stringify({
18
+ schemas: ["urn:ietf:params:scim:api:messages:2.0:Error"],
19
+ status: String(status),
20
+ detail
21
+ }), {
22
+ status,
23
+ headers: {
24
+ "content-type": "application/scim+json;charset=utf-8",
25
+ "cache-control": "no-store"
26
+ }
27
+ });
28
+ }
10
29
  function isCallbackPath(pathname) {
11
30
  return pathname.startsWith("/cb/");
12
31
  }
@@ -27,12 +46,12 @@ function tenantMiddleware(deps) {
27
46
  const recovery = await runCallbackRecovery(c.req.raw, deps);
28
47
  c.set("recovery", recovery);
29
48
  if (recovery.kind !== "fresh-request") {
30
- const cfg2 = await deps.configStore.getTenantConfig(recovery.tenantId);
31
- if (isErr(cfg2)) {
32
- return tokenEndpointErrorResponse(cfg2.error);
49
+ const cfg = await deps.configStore.getTenantConfig(recovery.tenantId);
50
+ if (isErr(cfg)) {
51
+ return tokenEndpointErrorResponse(cfg.error);
33
52
  }
34
53
  const customCb = deps.buildCustomContext ? await deps.buildCustomContext(c.req.raw) : {};
35
- c.set("tenant", buildTenantContext(c.req.raw, recovery.tenantId, cfg2.value, customCb));
54
+ c.set("tenant", buildTenantContext(c.req.raw, recovery.tenantId, cfg.value, customCb));
36
55
  return next();
37
56
  }
38
57
  }
@@ -41,6 +60,9 @@ function tenantMiddleware(deps) {
41
60
  if (url.pathname === "/authorize") {
42
61
  return authorizeDirectErrorResponse(resolved.error);
43
62
  }
63
+ if (isScimPath(url.pathname)) {
64
+ return scimTenantErrorResponse(resolved.error);
65
+ }
44
66
  return tokenEndpointErrorResponse(resolved.error);
45
67
  }
46
68
  const tenantId = resolved.value;
@@ -50,6 +72,9 @@ function tenantMiddleware(deps) {
50
72
  if (url.pathname === "/authorize") {
51
73
  return authorizeDirectErrorResponse(err);
52
74
  }
75
+ if (isScimPath(url.pathname)) {
76
+ return scimTenantErrorResponse(cfg.error);
77
+ }
53
78
  return tokenEndpointErrorResponse(err);
54
79
  }
55
80
  const custom = deps.buildCustomContext ? await deps.buildCustomContext(c.req.raw) : {};
@@ -97,6 +122,6 @@ async function extractStateParam(req) {
97
122
  }
98
123
  }
99
124
  export {
100
- tenantMiddleware,
101
- bootstrapMiddleware
125
+ bootstrapMiddleware,
126
+ tenantMiddleware
102
127
  };
@@ -7,6 +7,7 @@ import { makeDiscoveryHandler, makeJwksHandler } from "./handlers/metadata";
7
7
  import { makeMethodRouteHandler } from "./handlers/method-route";
8
8
  import { makeParHandler } from "./handlers/par";
9
9
  import { makeRegisterHandler } from "./handlers/register";
10
+ import { makeScimHandler } from "./handlers/scim";
10
11
  import { makeIntrospectHandler, makeRevokeHandler } from "./handlers/revocation";
11
12
  import { makeTokenHandler } from "./handlers/token";
12
13
  import { makeUserinfoHandler } from "./handlers/userinfo";
@@ -34,6 +35,8 @@ function buildRouter(deps) {
34
35
  app.use("/end_session", tenantMiddleware(deps));
35
36
  app.get("/end_session", makeEndSessionHandler(deps));
36
37
  app.post("/end_session", makeEndSessionHandler(deps));
38
+ app.use("/scim/v2/*", tenantMiddleware(deps));
39
+ app.all("/scim/v2/*", makeScimHandler(deps));
37
40
  app.use("/par", tenantMiddleware(deps));
38
41
  app.post("/par", makeParHandler(deps));
39
42
  app.use("/register", tenantMiddleware(deps));
@@ -25,6 +25,6 @@ var authorizeRequestUriQuerySchema = z.object({
25
25
  request_uri: z.string({ required_error: "missing request_uri" }).min(1)
26
26
  }).passthrough();
27
27
  export {
28
- authorizeRequestUriQuerySchema,
29
- authorizeQuerySchema
28
+ authorizeQuerySchema,
29
+ authorizeRequestUriQuerySchema
30
30
  };
@@ -13,6 +13,6 @@ var introspectBodySchema = z.object({
13
13
  client_secret: z.string().optional()
14
14
  });
15
15
  export {
16
- revokeBodySchema,
17
- introspectBodySchema
16
+ introspectBodySchema,
17
+ revokeBodySchema
18
18
  };