@robelest/convex-auth 0.0.4-preview.13 → 0.0.4-preview.16

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 (328) hide show
  1. package/README.md +140 -9
  2. package/dist/bin.cjs +5957 -5478
  3. package/dist/client/index.d.ts +3 -7
  4. package/dist/client/index.d.ts.map +1 -1
  5. package/dist/client/index.js +27 -26
  6. package/dist/client/index.js.map +1 -1
  7. package/dist/component/_generated/api.d.ts +14 -0
  8. package/dist/component/_generated/api.d.ts.map +1 -1
  9. package/dist/component/_generated/api.js.map +1 -1
  10. package/dist/component/_generated/component.d.ts +1672 -24
  11. package/dist/component/_generated/component.d.ts.map +1 -1
  12. package/dist/component/convex.config.d.ts +2 -2
  13. package/dist/component/convex.config.d.ts.map +1 -1
  14. package/dist/component/index.d.ts +1 -1
  15. package/dist/component/index.js +2 -2
  16. package/dist/component/model.d.ts +153 -0
  17. package/dist/component/model.d.ts.map +1 -0
  18. package/dist/component/model.js +343 -0
  19. package/dist/component/model.js.map +1 -0
  20. package/dist/component/providers/sso.d.ts +1 -1
  21. package/dist/component/public/enterprise.d.ts +54 -0
  22. package/dist/component/public/enterprise.d.ts.map +1 -0
  23. package/dist/component/public/enterprise.js +515 -0
  24. package/dist/component/public/enterprise.js.map +1 -0
  25. package/dist/component/public/factors.d.ts +52 -0
  26. package/dist/component/public/factors.d.ts.map +1 -0
  27. package/dist/component/public/factors.js +285 -0
  28. package/dist/component/public/factors.js.map +1 -0
  29. package/dist/component/public/groups.d.ts +116 -0
  30. package/dist/component/public/groups.d.ts.map +1 -0
  31. package/dist/component/public/groups.js +596 -0
  32. package/dist/component/public/groups.js.map +1 -0
  33. package/dist/component/public/identity.d.ts +93 -0
  34. package/dist/component/public/identity.d.ts.map +1 -0
  35. package/dist/component/public/identity.js +426 -0
  36. package/dist/component/public/identity.js.map +1 -0
  37. package/dist/component/public/keys.d.ts +41 -0
  38. package/dist/component/public/keys.d.ts.map +1 -0
  39. package/dist/component/public/keys.js +157 -0
  40. package/dist/component/public/keys.js.map +1 -0
  41. package/dist/component/public/shared.d.ts +26 -0
  42. package/dist/component/public/shared.d.ts.map +1 -0
  43. package/dist/component/public/shared.js +32 -0
  44. package/dist/component/public/shared.js.map +1 -0
  45. package/dist/component/public.d.ts +9 -321
  46. package/dist/component/public.d.ts.map +1 -1
  47. package/dist/component/public.js +6 -2145
  48. package/dist/component/schema.d.ts +406 -260
  49. package/dist/component/schema.js +37 -32
  50. package/dist/component/schema.js.map +1 -1
  51. package/dist/component/server/auth.d.ts +161 -15
  52. package/dist/component/server/auth.d.ts.map +1 -1
  53. package/dist/component/server/auth.js +100 -7
  54. package/dist/component/server/auth.js.map +1 -1
  55. package/dist/component/server/cookies.js +3 -0
  56. package/dist/component/server/cookies.js.map +1 -1
  57. package/dist/component/server/db.js +1 -0
  58. package/dist/component/server/db.js.map +1 -1
  59. package/dist/component/server/device.js +3 -1
  60. package/dist/component/server/device.js.map +1 -1
  61. package/dist/component/server/domains/core.js +629 -0
  62. package/dist/component/server/domains/core.js.map +1 -0
  63. package/dist/component/server/domains/sso.js +884 -0
  64. package/dist/component/server/domains/sso.js.map +1 -0
  65. package/dist/component/server/factory.d.ts +136 -0
  66. package/dist/component/server/factory.d.ts.map +1 -0
  67. package/dist/component/server/factory.js +1134 -0
  68. package/dist/component/server/factory.js.map +1 -0
  69. package/dist/component/server/fx.js +2 -1
  70. package/dist/component/server/fx.js.map +1 -1
  71. package/dist/component/server/http.js +287 -0
  72. package/dist/component/server/http.js.map +1 -0
  73. package/dist/component/server/identity.js +13 -0
  74. package/dist/component/server/identity.js.map +1 -0
  75. package/dist/component/server/keys.js +4 -0
  76. package/dist/component/server/keys.js.map +1 -1
  77. package/dist/component/server/mutations/account.js +1 -1
  78. package/dist/component/server/mutations/index.js +2 -2
  79. package/dist/component/server/mutations/index.js.map +1 -1
  80. package/dist/component/server/mutations/invalidate.js +1 -1
  81. package/dist/component/server/mutations/oauth.js +10 -7
  82. package/dist/component/server/mutations/oauth.js.map +1 -1
  83. package/dist/component/server/mutations/refresh.js +1 -1
  84. package/dist/component/server/mutations/register.js +1 -1
  85. package/dist/component/server/mutations/retrieve.js +1 -1
  86. package/dist/component/server/mutations/signature.js +1 -1
  87. package/dist/component/server/mutations/store.js +6 -3
  88. package/dist/component/server/mutations/store.js.map +1 -1
  89. package/dist/component/server/mutations/verify.js +1 -1
  90. package/dist/component/server/oauth.js +3 -0
  91. package/dist/component/server/oauth.js.map +1 -1
  92. package/dist/component/server/passkey.js +3 -2
  93. package/dist/component/server/passkey.js.map +1 -1
  94. package/dist/component/server/provider.js +2 -0
  95. package/dist/component/server/provider.js.map +1 -1
  96. package/dist/component/server/providers.js +10 -0
  97. package/dist/component/server/providers.js.map +1 -1
  98. package/dist/component/server/ratelimit.js +3 -0
  99. package/dist/component/server/ratelimit.js.map +1 -1
  100. package/dist/component/server/redirects.js +2 -0
  101. package/dist/component/server/redirects.js.map +1 -1
  102. package/dist/component/server/refresh.js +5 -0
  103. package/dist/component/server/refresh.js.map +1 -1
  104. package/dist/component/server/sessions.js +5 -0
  105. package/dist/component/server/sessions.js.map +1 -1
  106. package/dist/component/server/signin.js +2 -1
  107. package/dist/component/server/signin.js.map +1 -1
  108. package/dist/component/server/sso.js +166 -19
  109. package/dist/component/server/sso.js.map +1 -1
  110. package/dist/component/server/tokens.js +1 -0
  111. package/dist/component/server/tokens.js.map +1 -1
  112. package/dist/component/server/totp.js +4 -2
  113. package/dist/component/server/totp.js.map +1 -1
  114. package/dist/component/server/types.d.ts +106 -38
  115. package/dist/component/server/types.d.ts.map +1 -1
  116. package/dist/component/server/types.js.map +1 -1
  117. package/dist/component/server/users.js +1 -0
  118. package/dist/component/server/users.js.map +1 -1
  119. package/dist/component/server/utils.js +44 -2
  120. package/dist/component/server/utils.js.map +1 -1
  121. package/dist/providers/anonymous.d.ts +1 -1
  122. package/dist/providers/credentials.d.ts +1 -1
  123. package/dist/providers/password.d.ts +1 -1
  124. package/dist/providers/sso.d.ts +1 -1
  125. package/dist/providers/sso.js.map +1 -1
  126. package/dist/server/auth.d.ts +163 -17
  127. package/dist/server/auth.d.ts.map +1 -1
  128. package/dist/server/auth.js +100 -7
  129. package/dist/server/auth.js.map +1 -1
  130. package/dist/server/cookies.d.ts +1 -38
  131. package/dist/server/cookies.js +3 -0
  132. package/dist/server/cookies.js.map +1 -1
  133. package/dist/server/db.d.ts +1 -125
  134. package/dist/server/db.js +1 -0
  135. package/dist/server/db.js.map +1 -1
  136. package/dist/server/device.d.ts +1 -24
  137. package/dist/server/device.js +3 -1
  138. package/dist/server/device.js.map +1 -1
  139. package/dist/server/domains/core.d.ts +434 -0
  140. package/dist/server/domains/core.d.ts.map +1 -0
  141. package/dist/server/domains/core.js +629 -0
  142. package/dist/server/domains/core.js.map +1 -0
  143. package/dist/server/domains/sso.d.ts +409 -0
  144. package/dist/server/domains/sso.d.ts.map +1 -0
  145. package/dist/server/domains/sso.js +884 -0
  146. package/dist/server/domains/sso.js.map +1 -0
  147. package/dist/server/enterpriseValidators.d.ts +1 -0
  148. package/dist/server/enterpriseValidators.js +60 -0
  149. package/dist/server/enterpriseValidators.js.map +1 -0
  150. package/dist/server/factory.d.ts +136 -0
  151. package/dist/server/factory.d.ts.map +1 -0
  152. package/dist/server/factory.js +1134 -0
  153. package/dist/server/factory.js.map +1 -0
  154. package/dist/server/fx.d.ts +1 -16
  155. package/dist/server/fx.d.ts.map +1 -1
  156. package/dist/server/fx.js +1 -0
  157. package/dist/server/fx.js.map +1 -1
  158. package/dist/server/http.d.ts +59 -0
  159. package/dist/server/http.d.ts.map +1 -0
  160. package/dist/server/http.js +287 -0
  161. package/dist/server/http.js.map +1 -0
  162. package/dist/server/identity.d.ts +1 -0
  163. package/dist/server/identity.js +13 -0
  164. package/dist/server/identity.js.map +1 -0
  165. package/dist/server/index.d.ts +468 -1
  166. package/dist/server/index.d.ts.map +1 -1
  167. package/dist/server/index.js +530 -36
  168. package/dist/server/index.js.map +1 -1
  169. package/dist/server/keys.d.ts +1 -57
  170. package/dist/server/keys.js +4 -0
  171. package/dist/server/keys.js.map +1 -1
  172. package/dist/server/mutations/account.d.ts +7 -7
  173. package/dist/server/mutations/account.d.ts.map +1 -1
  174. package/dist/server/mutations/code.d.ts +13 -13
  175. package/dist/server/mutations/code.d.ts.map +1 -1
  176. package/dist/server/mutations/index.d.ts +107 -107
  177. package/dist/server/mutations/index.d.ts.map +1 -1
  178. package/dist/server/mutations/index.js +1 -1
  179. package/dist/server/mutations/index.js.map +1 -1
  180. package/dist/server/mutations/invalidate.d.ts +5 -5
  181. package/dist/server/mutations/invalidate.d.ts.map +1 -1
  182. package/dist/server/mutations/oauth.d.ts +10 -10
  183. package/dist/server/mutations/oauth.d.ts.map +1 -1
  184. package/dist/server/mutations/oauth.js +9 -6
  185. package/dist/server/mutations/oauth.js.map +1 -1
  186. package/dist/server/mutations/refresh.d.ts +4 -4
  187. package/dist/server/mutations/register.d.ts +12 -12
  188. package/dist/server/mutations/register.d.ts.map +1 -1
  189. package/dist/server/mutations/retrieve.d.ts +7 -7
  190. package/dist/server/mutations/signature.d.ts +5 -5
  191. package/dist/server/mutations/signin.d.ts +6 -6
  192. package/dist/server/mutations/signin.d.ts.map +1 -1
  193. package/dist/server/mutations/signout.d.ts +1 -1
  194. package/dist/server/mutations/store.d.ts +3 -2
  195. package/dist/server/mutations/store.d.ts.map +1 -1
  196. package/dist/server/mutations/store.js +6 -3
  197. package/dist/server/mutations/store.js.map +1 -1
  198. package/dist/server/mutations/verifier.d.ts +1 -1
  199. package/dist/server/mutations/verify.d.ts +11 -11
  200. package/dist/server/mutations/verify.d.ts.map +1 -1
  201. package/dist/server/oauth.d.ts +1 -59
  202. package/dist/server/oauth.js +3 -0
  203. package/dist/server/oauth.js.map +1 -1
  204. package/dist/server/passkey.d.ts.map +1 -1
  205. package/dist/server/passkey.js +3 -2
  206. package/dist/server/passkey.js.map +1 -1
  207. package/dist/server/provider.d.ts +1 -14
  208. package/dist/server/provider.d.ts.map +1 -1
  209. package/dist/server/provider.js +2 -0
  210. package/dist/server/provider.js.map +1 -1
  211. package/dist/server/providers.js +10 -0
  212. package/dist/server/providers.js.map +1 -1
  213. package/dist/server/ratelimit.d.ts +1 -22
  214. package/dist/server/ratelimit.js +3 -0
  215. package/dist/server/ratelimit.js.map +1 -1
  216. package/dist/server/redirects.d.ts +1 -10
  217. package/dist/server/redirects.js +2 -0
  218. package/dist/server/redirects.js.map +1 -1
  219. package/dist/server/refresh.d.ts +1 -37
  220. package/dist/server/refresh.js +5 -0
  221. package/dist/server/refresh.js.map +1 -1
  222. package/dist/server/sessions.d.ts +1 -28
  223. package/dist/server/sessions.js +5 -0
  224. package/dist/server/sessions.js.map +1 -1
  225. package/dist/server/signin.d.ts +1 -55
  226. package/dist/server/signin.js +2 -1
  227. package/dist/server/signin.js.map +1 -1
  228. package/dist/server/sso.d.ts +1 -348
  229. package/dist/server/sso.js +165 -18
  230. package/dist/server/sso.js.map +1 -1
  231. package/dist/server/templates.d.ts +1 -21
  232. package/dist/server/templates.js +1 -0
  233. package/dist/server/templates.js.map +1 -1
  234. package/dist/server/tokens.d.ts +1 -11
  235. package/dist/server/tokens.js +1 -0
  236. package/dist/server/tokens.js.map +1 -1
  237. package/dist/server/totp.d.ts +1 -23
  238. package/dist/server/totp.js +4 -2
  239. package/dist/server/totp.js.map +1 -1
  240. package/dist/server/types.d.ts +114 -77
  241. package/dist/server/types.d.ts.map +1 -1
  242. package/dist/server/types.js.map +1 -1
  243. package/dist/server/users.d.ts +1 -31
  244. package/dist/server/users.js +1 -0
  245. package/dist/server/users.js.map +1 -1
  246. package/dist/server/utils.d.ts +1 -27
  247. package/dist/server/utils.js +44 -2
  248. package/dist/server/utils.js.map +1 -1
  249. package/dist/server/version.d.ts +1 -1
  250. package/dist/server/version.js +1 -1
  251. package/dist/server/version.js.map +1 -1
  252. package/package.json +4 -5
  253. package/src/cli/bin.ts +5 -0
  254. package/src/cli/index.ts +22 -9
  255. package/src/cli/keys.ts +3 -0
  256. package/src/client/index.ts +36 -37
  257. package/src/component/_generated/api.ts +14 -0
  258. package/src/component/_generated/component.ts +2106 -9
  259. package/src/component/index.ts +3 -1
  260. package/src/component/model.ts +441 -0
  261. package/src/component/public/enterprise.ts +753 -0
  262. package/src/component/public/factors.ts +332 -0
  263. package/src/component/public/groups.ts +932 -0
  264. package/src/component/public/identity.ts +566 -0
  265. package/src/component/public/keys.ts +209 -0
  266. package/src/component/public/shared.ts +119 -0
  267. package/src/component/public.ts +5 -2965
  268. package/src/component/schema.ts +68 -63
  269. package/src/providers/sso.ts +1 -1
  270. package/src/server/auth.ts +413 -18
  271. package/src/server/cookies.ts +3 -0
  272. package/src/server/db.ts +3 -0
  273. package/src/server/device.ts +3 -1
  274. package/src/server/domains/core.ts +1071 -0
  275. package/src/server/domains/sso.ts +1749 -0
  276. package/src/server/enterpriseValidators.ts +93 -0
  277. package/src/server/factory.ts +2181 -0
  278. package/src/server/fx.ts +1 -0
  279. package/src/server/http.ts +529 -0
  280. package/src/server/identity.ts +18 -0
  281. package/src/server/index.ts +806 -40
  282. package/src/server/keys.ts +4 -0
  283. package/src/server/mutations/index.ts +1 -1
  284. package/src/server/mutations/oauth.ts +36 -8
  285. package/src/server/mutations/store.ts +6 -3
  286. package/src/server/oauth.ts +6 -0
  287. package/src/server/passkey.ts +3 -2
  288. package/src/server/provider.ts +2 -0
  289. package/src/server/providers.ts +20 -0
  290. package/src/server/ratelimit.ts +3 -0
  291. package/src/server/redirects.ts +2 -0
  292. package/src/server/refresh.ts +5 -0
  293. package/src/server/sessions.ts +5 -0
  294. package/src/server/signin.ts +1 -0
  295. package/src/server/sso.ts +259 -17
  296. package/src/server/templates.ts +1 -0
  297. package/src/server/tokens.ts +1 -0
  298. package/src/server/totp.ts +4 -2
  299. package/src/server/types.ts +178 -83
  300. package/src/server/users.ts +1 -0
  301. package/src/server/utils.ts +71 -1
  302. package/src/server/version.ts +1 -1
  303. package/dist/component/public.js.map +0 -1
  304. package/dist/component/server/implementation.d.ts +0 -1264
  305. package/dist/component/server/implementation.d.ts.map +0 -1
  306. package/dist/component/server/implementation.js +0 -2365
  307. package/dist/component/server/implementation.js.map +0 -1
  308. package/dist/server/cookies.d.ts.map +0 -1
  309. package/dist/server/db.d.ts.map +0 -1
  310. package/dist/server/device.d.ts.map +0 -1
  311. package/dist/server/implementation.d.ts +0 -1264
  312. package/dist/server/implementation.d.ts.map +0 -1
  313. package/dist/server/implementation.js +0 -2365
  314. package/dist/server/implementation.js.map +0 -1
  315. package/dist/server/keys.d.ts.map +0 -1
  316. package/dist/server/oauth.d.ts.map +0 -1
  317. package/dist/server/ratelimit.d.ts.map +0 -1
  318. package/dist/server/redirects.d.ts.map +0 -1
  319. package/dist/server/refresh.d.ts.map +0 -1
  320. package/dist/server/sessions.d.ts.map +0 -1
  321. package/dist/server/signin.d.ts.map +0 -1
  322. package/dist/server/sso.d.ts.map +0 -1
  323. package/dist/server/templates.d.ts.map +0 -1
  324. package/dist/server/tokens.d.ts.map +0 -1
  325. package/dist/server/totp.d.ts.map +0 -1
  326. package/dist/server/users.d.ts.map +0 -1
  327. package/dist/server/utils.d.ts.map +0 -1
  328. package/src/server/implementation.ts +0 -5336
package/src/server/sso.ts CHANGED
@@ -24,14 +24,22 @@ function ensureSamlifyValidator() {
24
24
  setSchemaValidator(_samlifyPermissiveValidator);
25
25
  }
26
26
  import { decodeIdToken } from "arctic";
27
- import { createRemoteJWKSet, decodeProtectedHeader, jwtVerify } from "jose";
27
+ import {
28
+ createRemoteJWKSet,
29
+ customFetch,
30
+ decodeProtectedHeader,
31
+ jwtVerify,
32
+ } from "jose";
28
33
 
29
34
  import type {
35
+ EnterprisePolicy,
36
+ EnterprisePolicyPatch,
30
37
  OAuthMaterializedConfig,
31
38
  OAuthProfile,
32
39
  SAMLAttributeMapping,
33
40
  } from "./types";
34
41
 
42
+ /** @internal */
35
43
  export type ParsedSamlMetadata = {
36
44
  issuer: string;
37
45
  sso: {
@@ -48,8 +56,10 @@ export type ParsedSamlMetadata = {
48
56
  wantsSignedAuthnRequests: boolean;
49
57
  };
50
58
 
59
+ /** @internal */
51
60
  export type EnterpriseSamlSource = { kind: "enterprise"; id: string };
52
61
 
62
+ /** @internal */
53
63
  export type EnterpriseSamlRelayState = {
54
64
  source: EnterpriseSamlSource;
55
65
  signature: string;
@@ -58,18 +68,21 @@ export type EnterpriseSamlRelayState = {
58
68
  redirectTo?: string;
59
69
  };
60
70
 
71
+ /** @internal */
61
72
  export type EnterpriseSamlUrls = {
62
73
  metadataUrl: string;
63
74
  acsUrl: string;
64
75
  sloUrl?: string;
65
76
  };
66
77
 
78
+ /** @internal */
67
79
  export type EnterpriseSamlLoadedSource = {
68
80
  source: EnterpriseSamlSource;
69
81
  config: unknown;
70
82
  status?: string;
71
83
  };
72
84
 
85
+ /** @internal */
73
86
  export type EnterpriseSamlHttpRequest = {
74
87
  url: URL;
75
88
  body: Record<string, string>;
@@ -80,35 +93,44 @@ export type EnterpriseSamlHttpRequest = {
80
93
  hasSamlResponse: boolean;
81
94
  };
82
95
 
96
+ /** @internal */
83
97
  export type ScimListRequest = {
84
98
  startIndex: number;
85
99
  count: number;
86
100
  filter?: { attribute: string; value: string };
87
101
  };
88
102
 
103
+ /** @internal */
89
104
  export const SCIM_USER_SCHEMA_ID = "urn:ietf:params:scim:schemas:core:2.0:User";
105
+ /** @internal */
90
106
  export const SCIM_GROUP_SCHEMA_ID =
91
107
  "urn:ietf:params:scim:schemas:core:2.0:Group";
92
108
 
109
+ /** @internal */
93
110
  export const ENTERPRISE_OIDC_PROVIDER_PREFIX = "enterprise:oidc:";
111
+ /** @internal */
94
112
  export const ENTERPRISE_SAML_PROVIDER_PREFIX = "enterprise:saml:";
95
113
  const OIDC_JWKS_CACHE = new Map<
96
114
  string,
97
115
  ReturnType<typeof createRemoteJWKSet>
98
116
  >();
99
117
 
118
+ /** @internal */
100
119
  export function normalizeDomain(domain: string): string {
101
120
  return domain.trim().toLowerCase().replace(/^@+/, "");
102
121
  }
103
122
 
123
+ /** @internal */
104
124
  export function enterpriseOidcProviderId(enterpriseId: string): string {
105
125
  return `${ENTERPRISE_OIDC_PROVIDER_PREFIX}${enterpriseId}`;
106
126
  }
107
127
 
128
+ /** @internal */
108
129
  export function enterpriseSamlProviderId(enterpriseId: string): string {
109
130
  return `${ENTERPRISE_SAML_PROVIDER_PREFIX}${enterpriseId}`;
110
131
  }
111
132
 
133
+ /** @internal */
112
134
  export function getEnterpriseSamlUrls(opts: {
113
135
  rootUrl: string;
114
136
  source: EnterpriseSamlSource;
@@ -124,6 +146,7 @@ export function getEnterpriseSamlUrls(opts: {
124
146
  };
125
147
  }
126
148
 
149
+ /** @internal */
127
150
  export function getEnterpriseOidcUrls(opts: {
128
151
  rootUrl: string;
129
152
  enterpriseId: string;
@@ -135,12 +158,14 @@ export function getEnterpriseOidcUrls(opts: {
135
158
  };
136
159
  }
137
160
 
161
+ /** @internal */
138
162
  export function isEnterpriseSamlSourceActive(
139
163
  source: EnterpriseSamlLoadedSource,
140
164
  ) {
141
165
  return source.status === "active";
142
166
  }
143
167
 
168
+ /** @internal */
144
169
  export function isEnterpriseProviderId(providerId: string): boolean {
145
170
  return (
146
171
  providerId.startsWith(ENTERPRISE_OIDC_PROVIDER_PREFIX) ||
@@ -153,6 +178,132 @@ const asRecord = (value: unknown) =>
153
178
  ? (value as Record<string, any>)
154
179
  : null;
155
180
 
181
+ /** @internal */
182
+ export const DEFAULT_ENTERPRISE_POLICY: EnterprisePolicy = {
183
+ version: 1,
184
+ identity: {
185
+ accountLinking: {
186
+ oidc: "verifiedEmail",
187
+ saml: "verifiedEmail",
188
+ },
189
+ },
190
+ provisioning: {
191
+ scimReuse: {
192
+ user: "externalId",
193
+ },
194
+ jit: {
195
+ mode: "createUserAndMembership",
196
+ defaultRoleIds: [],
197
+ },
198
+ deprovision: {
199
+ mode: "soft",
200
+ },
201
+ },
202
+ };
203
+
204
+ /** @internal */
205
+ export function normalizeEnterprisePolicy(policy: unknown): EnterprisePolicy {
206
+ const input = asRecord(policy) ?? {};
207
+ const identity = asRecord(input.identity) ?? {};
208
+ const accountLinking = asRecord(identity.accountLinking) ?? {};
209
+ const provisioning = asRecord(input.provisioning) ?? {};
210
+ const scimReuse = asRecord(provisioning.scimReuse) ?? {};
211
+ const jit = asRecord(provisioning.jit) ?? {};
212
+ const deprovision = asRecord(provisioning.deprovision) ?? {};
213
+ const extend = asRecord(input.extend) ?? undefined;
214
+
215
+ return {
216
+ version: 1,
217
+ identity: {
218
+ accountLinking: {
219
+ oidc:
220
+ accountLinking.oidc === "none"
221
+ ? "none"
222
+ : DEFAULT_ENTERPRISE_POLICY.identity.accountLinking.oidc,
223
+ saml:
224
+ accountLinking.saml === "none"
225
+ ? "none"
226
+ : DEFAULT_ENTERPRISE_POLICY.identity.accountLinking.saml,
227
+ },
228
+ },
229
+ provisioning: {
230
+ scimReuse: {
231
+ user:
232
+ scimReuse.user === "none"
233
+ ? "none"
234
+ : DEFAULT_ENTERPRISE_POLICY.provisioning.scimReuse.user,
235
+ },
236
+ jit: {
237
+ mode:
238
+ jit.mode === "off" ||
239
+ jit.mode === "createUser" ||
240
+ jit.mode === "createUserAndMembership"
241
+ ? jit.mode
242
+ : DEFAULT_ENTERPRISE_POLICY.provisioning.jit.mode,
243
+ defaultRoleIds: Array.isArray(jit.defaultRoleIds)
244
+ ? Array.from(
245
+ new Set(
246
+ jit.defaultRoleIds.filter(
247
+ (value): value is string =>
248
+ typeof value === "string" && value.length > 0,
249
+ ),
250
+ ),
251
+ )
252
+ : typeof jit.defaultRole === "string" && jit.defaultRole.length > 0
253
+ ? [jit.defaultRole]
254
+ : DEFAULT_ENTERPRISE_POLICY.provisioning.jit.defaultRoleIds,
255
+ },
256
+ deprovision: {
257
+ mode:
258
+ deprovision.mode === "hard"
259
+ ? "hard"
260
+ : DEFAULT_ENTERPRISE_POLICY.provisioning.deprovision.mode,
261
+ },
262
+ },
263
+ ...(extend ? { extend } : {}),
264
+ };
265
+ }
266
+
267
+ /** @internal */
268
+ export function patchEnterprisePolicy(
269
+ current: unknown,
270
+ patch: EnterprisePolicyPatch,
271
+ ): EnterprisePolicy {
272
+ const base = normalizeEnterprisePolicy(current);
273
+ return normalizeEnterprisePolicy({
274
+ ...base,
275
+ ...patch,
276
+ identity: {
277
+ ...base.identity,
278
+ ...patch.identity,
279
+ accountLinking: {
280
+ ...base.identity.accountLinking,
281
+ ...patch.identity?.accountLinking,
282
+ },
283
+ },
284
+ provisioning: {
285
+ ...base.provisioning,
286
+ ...patch.provisioning,
287
+ scimReuse: {
288
+ ...base.provisioning.scimReuse,
289
+ ...patch.provisioning?.scimReuse,
290
+ },
291
+ jit: {
292
+ ...base.provisioning.jit,
293
+ ...patch.provisioning?.jit,
294
+ },
295
+ deprovision: {
296
+ ...base.provisioning.deprovision,
297
+ ...patch.provisioning?.deprovision,
298
+ },
299
+ },
300
+ extend:
301
+ patch.extend === undefined
302
+ ? base.extend
303
+ : { ...base.extend, ...patch.extend },
304
+ });
305
+ }
306
+
156
307
  const getProtocolConfig = (config: unknown, protocol: "oidc" | "saml") => {
157
308
  const base = asRecord(config);
158
309
  const direct = base?.[protocol];
@@ -160,14 +311,35 @@ const getProtocolConfig = (config: unknown, protocol: "oidc" | "saml") => {
160
311
  return asRecord(direct) ?? asRecord(viaProtocols) ?? {};
161
312
  };
162
313
 
314
+ /** @internal */
163
315
  export function getOidcConfig(config: unknown): Record<string, any> {
164
316
  return getProtocolConfig(config, "oidc");
165
317
  }
166
318
 
319
+ /** @internal */
320
+ export function getPublicOidcConfig(config: unknown): Record<string, any> {
321
+ const oidc = getOidcConfig(config);
322
+ const { clientSecret: _clientSecret, ...publicOidc } = oidc;
323
+ return publicOidc;
324
+ }
325
+
326
+ /** @internal */
327
+ export function withOidcSecretState(
328
+ config: Record<string, any>,
329
+ hasClientSecret: boolean,
330
+ ) {
331
+ return {
332
+ ...config,
333
+ hasClientSecret,
334
+ };
335
+ }
336
+
337
+ /** @internal */
167
338
  export function getSamlConfig(config: unknown): Record<string, any> {
168
339
  return getProtocolConfig(config, "saml");
169
340
  }
170
341
 
342
+ /** @internal */
171
343
  export function upsertProtocolConfig(
172
344
  config: unknown,
173
345
  protocol: "oidc" | "saml",
@@ -182,6 +354,7 @@ export function upsertProtocolConfig(
182
354
  return { ...base, protocols };
183
355
  }
184
356
 
357
+ /** @internal */
185
358
  export function createSamlPostBindingResponse(opts: {
186
359
  endpoint: string;
187
360
  parameter: "SAMLRequest" | "SAMLResponse";
@@ -200,6 +373,7 @@ export function createSamlPostBindingResponse(opts: {
200
373
  );
201
374
  }
202
375
 
376
+ /** @internal */
203
377
  export function decodeRelayState(
204
378
  value: string | null,
205
379
  ): Record<string, unknown> {
@@ -215,6 +389,7 @@ export function decodeRelayState(
215
389
  }
216
390
  }
217
391
 
392
+ /** @internal */
218
393
  export function encodeEnterpriseSamlRelayState(
219
394
  value: EnterpriseSamlRelayState,
220
395
  ) {
@@ -231,6 +406,7 @@ export function encodeEnterpriseSamlRelayState(
231
406
  );
232
407
  }
233
408
 
409
+ /** @internal */
234
410
  export function decodeEnterpriseSamlRelayStateOrThrow(
235
411
  value: string | null,
236
412
  ): EnterpriseSamlRelayState {
@@ -261,6 +437,7 @@ export function decodeEnterpriseSamlRelayStateOrThrow(
261
437
  };
262
438
  }
263
439
 
440
+ /** @internal */
264
441
  export async function readRequestBody(
265
442
  request: Request,
266
443
  ): Promise<Record<string, string>> {
@@ -279,6 +456,7 @@ export async function readRequestBody(
279
456
  return {};
280
457
  }
281
458
 
459
+ /** @internal */
282
460
  export async function readEnterpriseSamlHttpRequest(
283
461
  request: Request,
284
462
  ): Promise<EnterpriseSamlHttpRequest> {
@@ -319,11 +497,13 @@ async function discoverOidcConfiguration(config: Record<string, any>) {
319
497
  throw new Error("Enterprise OIDC requires an issuer or discoveryUrl.");
320
498
  }
321
499
 
500
+ const oidcFetch = createEnterpriseOidcFetch(config, config.issuer);
501
+
322
502
  return await Fx.run(
323
503
  Fx.defer(() =>
324
504
  Fx.from({
325
505
  ok: async () => {
326
- const response = await fetch(discoveryUrl);
506
+ const response = await oidcFetch(discoveryUrl);
327
507
  if (!response.ok) {
328
508
  throw new Error(
329
509
  `Failed to discover OIDC configuration: ${response.status}`,
@@ -360,11 +540,46 @@ async function discoverOidcConfiguration(config: Record<string, any>) {
360
540
  );
361
541
  }
362
542
 
363
- function getOidcJwks(url: string) {
364
- let jwks = OIDC_JWKS_CACHE.get(url);
543
+ function createEnterpriseOidcFetch(
544
+ config: Record<string, any>,
545
+ discoveredIssuer?: string,
546
+ ) {
547
+ const runtimeOrigin =
548
+ typeof config.discoveryUrl === "string"
549
+ ? new URL(config.discoveryUrl).origin
550
+ : undefined;
551
+ const externalHost =
552
+ typeof config.issuer === "string"
553
+ ? new URL(config.issuer).host
554
+ : typeof discoveredIssuer === "string"
555
+ ? new URL(discoveredIssuer).host
556
+ : undefined;
557
+
558
+ return async (input: string | URL, init?: RequestInit) => {
559
+ const url = new URL(typeof input === "string" ? input : input.toString());
560
+ const rewrittenUrl =
561
+ runtimeOrigin !== undefined && url.origin !== runtimeOrigin
562
+ ? new URL(`${runtimeOrigin}${url.pathname}${url.search}`)
563
+ : url;
564
+ const headers = new Headers(init?.headers);
565
+ if (runtimeOrigin !== undefined && externalHost !== undefined) {
566
+ headers.set("host", externalHost);
567
+ }
568
+ return await fetch(rewrittenUrl, { ...init, headers });
569
+ };
570
+ }
571
+
572
+ function getOidcJwks(
573
+ url: string,
574
+ fetchImpl?: ReturnType<typeof createEnterpriseOidcFetch>,
575
+ ) {
576
+ const cacheKey = fetchImpl ? `${url}::custom` : url;
577
+ let jwks = OIDC_JWKS_CACHE.get(cacheKey);
365
578
  if (!jwks) {
366
- jwks = createRemoteJWKSet(new URL(url));
367
- OIDC_JWKS_CACHE.set(url, jwks);
579
+ jwks = fetchImpl
580
+ ? createRemoteJWKSet(new URL(url), { [customFetch]: fetchImpl })
581
+ : createRemoteJWKSet(new URL(url));
582
+ OIDC_JWKS_CACHE.set(cacheKey, jwks);
368
583
  }
369
584
  return jwks;
370
585
  }
@@ -378,10 +593,11 @@ function userInfoProfileFx(opts: {
378
593
  accessToken: string;
379
594
  verifiedClaims: Record<string, unknown>;
380
595
  verifiedProfile: OAuthProfile & { emailVerified?: boolean };
596
+ fetchImpl?: ReturnType<typeof createEnterpriseOidcFetch>;
381
597
  }) {
382
598
  return Fx.from({
383
599
  ok: async () => {
384
- const response = await fetch(opts.endpoint, {
600
+ const response = await (opts.fetchImpl ?? fetch)(opts.endpoint, {
385
601
  headers: { Authorization: `Bearer ${opts.accessToken}` },
386
602
  });
387
603
  if (!response.ok) {
@@ -438,6 +654,7 @@ function userInfoProfileFx(opts: {
438
654
  );
439
655
  }
440
656
 
657
+ /** @internal */
441
658
  export async function createEnterpriseOidcProvider(
442
659
  config: Record<string, any>,
443
660
  redirectUri: string,
@@ -478,6 +695,10 @@ export async function createEnterpriseOidcProvider(
478
695
  : [];
479
696
  const userinfoEndpoint =
480
697
  (discovery.userinfo_endpoint as string | undefined) ?? undefined;
698
+ const oidcFetch = createEnterpriseOidcFetch(
699
+ config,
700
+ discovery.issuer as string,
701
+ );
481
702
  const scopes = Array.isArray(config.scopes)
482
703
  ? config.scopes.filter(
483
704
  (value: unknown): value is string => typeof value === "string",
@@ -501,7 +722,7 @@ export async function createEnterpriseOidcProvider(
501
722
  ...getIssuerCandidates(discoveredIssuer),
502
723
  ]),
503
724
  );
504
- const jwks = getOidcJwks(jwksUri);
725
+ const jwks = getOidcJwks(jwksUri, oidcFetch);
505
726
  let verifiedClaims: Record<string, unknown> | null = null;
506
727
  let verifiedProfile: (OAuthProfile & { emailVerified?: boolean }) | null =
507
728
  null;
@@ -563,7 +784,7 @@ export async function createEnterpriseOidcProvider(
563
784
  if (codeVerifier) {
564
785
  body.set("code_verifier", codeVerifier);
565
786
  }
566
- const response = await fetch(tokenEndpoint, {
787
+ const response = await oidcFetch(tokenEndpoint, {
567
788
  method: "POST",
568
789
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
569
790
  body,
@@ -692,6 +913,7 @@ export async function createEnterpriseOidcProvider(
692
913
  accessToken: tokens.accessToken(),
693
914
  verifiedClaims,
694
915
  verifiedProfile,
916
+ fetchImpl: oidcFetch,
695
917
  }),
696
918
  );
697
919
  if (userInfoProfile !== null) {
@@ -705,18 +927,23 @@ export async function createEnterpriseOidcProvider(
705
927
  return { provider, oauthConfig };
706
928
  }
707
929
 
930
+ /** @internal */
708
931
  export function createSyntheticOAuthMaterializedConfig(
709
932
  providerId: string,
933
+ options?: {
934
+ accountLinking?: OAuthMaterializedConfig["accountLinking"];
935
+ },
710
936
  ): OAuthMaterializedConfig {
711
937
  return {
712
938
  id: providerId,
713
939
  type: "oauth",
714
940
  provider: null,
715
941
  scopes: [],
716
- accountLinking: "verifiedEmail",
942
+ accountLinking: options?.accountLinking ?? "verifiedEmail",
717
943
  };
718
944
  }
719
945
 
946
+ /** @internal */
720
947
  export function parseSamlIdpMetadata(metadata: string): ParsedSamlMetadata {
721
948
  const idp = IdentityProvider({ metadata });
722
949
  const entityMeta = idp.entityMeta;
@@ -745,6 +972,7 @@ export function parseSamlIdpMetadata(metadata: string): ParsedSamlMetadata {
745
972
  };
746
973
  }
747
974
 
975
+ /** @internal */
748
976
  export function createServiceProviderMetadata(opts: {
749
977
  entityId: string;
750
978
  acsUrl: string;
@@ -789,6 +1017,7 @@ export function createServiceProviderMetadata(opts: {
789
1017
  return sp.getMetadata();
790
1018
  }
791
1019
 
1020
+ /** @internal */
792
1021
  export function createEnterpriseSamlMetadataXml(opts: {
793
1022
  rootUrl: string;
794
1023
  source: EnterpriseSamlSource;
@@ -803,6 +1032,7 @@ export function createEnterpriseSamlMetadataXml(opts: {
803
1032
  );
804
1033
  }
805
1034
 
1035
+ /** @internal */
806
1036
  export function getSamlServiceProviderOptions(opts: {
807
1037
  rootUrl: string;
808
1038
  source: EnterpriseSamlSource;
@@ -835,6 +1065,7 @@ export function getSamlServiceProviderOptions(opts: {
835
1065
  };
836
1066
  }
837
1067
 
1068
+ /** @internal */
838
1069
  export function createSamlServiceProvider(opts: {
839
1070
  entityId: string;
840
1071
  acsUrl: string;
@@ -874,6 +1105,7 @@ export function createSamlServiceProvider(opts: {
874
1105
  });
875
1106
  }
876
1107
 
1108
+ /** @internal */
877
1109
  export function createEnterpriseSamlRuntime(opts: {
878
1110
  rootUrl: string;
879
1111
  source: EnterpriseSamlSource;
@@ -904,6 +1136,7 @@ export function createEnterpriseSamlRuntime(opts: {
904
1136
  };
905
1137
  }
906
1138
 
1139
+ /** @internal */
907
1140
  export function createEnterpriseSamlSignInRequest(opts: {
908
1141
  rootUrl: string;
909
1142
  source: EnterpriseSamlSource;
@@ -951,6 +1184,7 @@ export function createEnterpriseSamlSignInRequest(opts: {
951
1184
  };
952
1185
  }
953
1186
 
1187
+ /** @internal */
954
1188
  export async function parseEnterpriseSamlLoginResponse(opts: {
955
1189
  request: Request;
956
1190
  rootUrl: string;
@@ -1026,6 +1260,7 @@ function warnDeprecatedSamlAlgorithms(parsed: any) {
1026
1260
  }
1027
1261
  }
1028
1262
 
1263
+ /** @internal */
1029
1264
  export function validateEnterpriseSamlLoginRelayState(opts: {
1030
1265
  relayState: EnterpriseSamlRelayState;
1031
1266
  source: EnterpriseSamlSource;
@@ -1040,6 +1275,7 @@ export function validateEnterpriseSamlLoginRelayState(opts: {
1040
1275
  }
1041
1276
  }
1042
1277
 
1278
+ /** @internal */
1043
1279
  export async function parseEnterpriseSamlLogoutMessage(opts: {
1044
1280
  request: Request;
1045
1281
  rootUrl: string;
@@ -1071,23 +1307,23 @@ export async function parseEnterpriseSamlLogoutMessage(opts: {
1071
1307
  };
1072
1308
  }
1073
1309
 
1310
+ /** @internal */
1074
1311
  export async function createEnterpriseOidcRuntime(opts: {
1075
1312
  rootUrl: string;
1076
1313
  enterpriseId: string;
1077
- config: unknown;
1314
+ oidc: Record<string, any>;
1078
1315
  }) {
1079
- const oidc = getOidcConfig(opts.config);
1080
1316
  const providerId = enterpriseOidcProviderId(opts.enterpriseId);
1081
1317
  const urls = getEnterpriseOidcUrls({
1082
1318
  rootUrl: opts.rootUrl,
1083
1319
  enterpriseId: opts.enterpriseId,
1084
1320
  });
1085
1321
  const { provider, oauthConfig } = await createEnterpriseOidcProvider(
1086
- oidc,
1322
+ opts.oidc,
1087
1323
  urls.callbackUrl,
1088
1324
  );
1089
1325
  return {
1090
- oidc,
1326
+ oidc: opts.oidc,
1091
1327
  providerId,
1092
1328
  provider,
1093
1329
  oauthConfig,
@@ -1095,6 +1331,7 @@ export async function createEnterpriseOidcRuntime(opts: {
1095
1331
  };
1096
1332
  }
1097
1333
 
1334
+ /** @internal */
1098
1335
  export function profileFromSamlExtract(
1099
1336
  extract: any,
1100
1337
  mapping?: SAMLAttributeMapping,
@@ -1145,15 +1382,15 @@ export function profileFromSamlExtract(
1145
1382
  };
1146
1383
  }
1147
1384
 
1385
+ /** @internal */
1148
1386
  export function parseScimPath(pathname: string) {
1149
1387
  const parts = pathname.split("/").filter(Boolean);
1150
- const [api, auth, enterprise, enterpriseId, protocol, version, ...rest] =
1151
- parts;
1388
+ const [api, auth, sso, enterpriseId, protocol, version, ...rest] = parts;
1152
1389
 
1153
1390
  if (
1154
1391
  api !== "api" ||
1155
1392
  auth !== "auth" ||
1156
- enterprise !== "enterprise" ||
1393
+ sso !== "sso" ||
1157
1394
  !enterpriseId ||
1158
1395
  enterpriseId === "setup" ||
1159
1396
  protocol !== "scim" ||
@@ -1173,6 +1410,7 @@ export function parseScimPath(pathname: string) {
1173
1410
  };
1174
1411
  }
1175
1412
 
1413
+ /** @internal */
1176
1414
  export function parseScimListRequest(url: URL): ScimListRequest {
1177
1415
  const startIndex = Math.max(
1178
1416
  1,
@@ -1195,6 +1433,7 @@ export function parseScimListRequest(url: URL): ScimListRequest {
1195
1433
  return { startIndex, count, filter };
1196
1434
  }
1197
1435
 
1436
+ /** @internal */
1198
1437
  export function scimJson(data: unknown, status = 200, headers?: HeadersInit) {
1199
1438
  const responseHeaders = new Headers({
1200
1439
  "Content-Type": "application/scim+json",
@@ -1210,6 +1449,7 @@ export function scimJson(data: unknown, status = 200, headers?: HeadersInit) {
1210
1449
  });
1211
1450
  }
1212
1451
 
1452
+ /** @internal */
1213
1453
  export function scimError(status: number, scimType: string, detail: string) {
1214
1454
  return scimJson(
1215
1455
  {
@@ -1222,6 +1462,7 @@ export function scimError(status: number, scimType: string, detail: string) {
1222
1462
  );
1223
1463
  }
1224
1464
 
1465
+ /** @internal */
1225
1466
  export function serializeScimUser(args: {
1226
1467
  id: string;
1227
1468
  user: Record<string, any>;
@@ -1253,6 +1494,7 @@ export function serializeScimUser(args: {
1253
1494
  };
1254
1495
  }
1255
1496
 
1497
+ /** @internal */
1256
1498
  export function serializeScimGroup(args: {
1257
1499
  id: string;
1258
1500
  group: Record<string, any>;
@@ -15,6 +15,7 @@
15
15
  * Used by the auto-registered `email` provider when `email` is
16
16
  * configured in `createAuth(...)`.
17
17
  */
18
+ /** @internal */
18
19
  export function defaultMagicLinkEmail(url: string, host: string): string {
19
20
  const escapedHost = host.replace(
20
21
  /[&<>"']/g,
@@ -10,6 +10,7 @@ const TOKEN_JTI_LENGTH = 24;
10
10
  const TOKEN_JTI_ALPHABET =
11
11
  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
12
12
 
13
+ /** @internal */
13
14
  export async function generateToken(
14
15
  args: {
15
16
  userId: GenericId<"User">;
@@ -12,6 +12,7 @@ import { verifyTOTPWithGracePeriod, createTOTPKeyURI } from "@oslojs/otp";
12
12
  import type { Fx as FxType } from "@robelest/fx";
13
13
 
14
14
  import { AuthError, Fx } from "./fx";
15
+ import { userIdFromIdentitySubject } from "./identity";
15
16
  import { callSignIn, callVerifier } from "./mutations/index";
16
17
  import { callVerifierSignature } from "./mutations/signature";
17
18
  import { TotpProviderConfig, GenericActionCtxWithAuthConfig } from "./types";
@@ -134,6 +135,7 @@ const resolveTotpDispatchFx = (
134
135
  ),
135
136
  );
136
137
 
138
+ /** @internal */
137
139
  export const handleTotp = (
138
140
  ctx: EnrichedActionCtx,
139
141
  provider: TotpProviderConfig,
@@ -152,7 +154,7 @@ export const handleTotp = (
152
154
  Fx.chain((identity) =>
153
155
  identity === null
154
156
  ? Fx.fail(new AuthError("TOTP_AUTH_REQUIRED"))
155
- : Fx.succeed(identity.subject.split("|")[0]!),
157
+ : Fx.succeed(userIdFromIdentitySubject(identity.subject)),
156
158
  ),
157
159
  Fx.chain((userId) =>
158
160
  Fx.from({
@@ -224,7 +226,7 @@ export const handleTotp = (
224
226
  Fx.chain((identity) =>
225
227
  identity === null
226
228
  ? Fx.fail(new AuthError("TOTP_AUTH_REQUIRED"))
227
- : Fx.succeed(identity.subject.split("|")[0]!),
229
+ : Fx.succeed(userIdFromIdentitySubject(identity.subject)),
228
230
  ),
229
231
  Fx.chain((userId) =>
230
232
  Fx.from({