@open-mercato/enterprise 0.4.6-develop-15c18897fc → 0.4.6-develop-34aa847ce6

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 (195) hide show
  1. package/dist/index.js +1 -1
  2. package/dist/index.js.map +2 -2
  3. package/dist/modules/sso/acl.js +11 -0
  4. package/dist/modules/sso/acl.js.map +7 -0
  5. package/dist/modules/sso/api/admin-context.js +27 -0
  6. package/dist/modules/sso/api/admin-context.js.map +7 -0
  7. package/dist/modules/sso/api/callback/oidc/route.js +103 -0
  8. package/dist/modules/sso/api/callback/oidc/route.js.map +7 -0
  9. package/dist/modules/sso/api/config/[id]/activate/route.js +49 -0
  10. package/dist/modules/sso/api/config/[id]/activate/route.js.map +7 -0
  11. package/dist/modules/sso/api/config/[id]/domains/route.js +96 -0
  12. package/dist/modules/sso/api/config/[id]/domains/route.js.map +7 -0
  13. package/dist/modules/sso/api/config/[id]/route.js +103 -0
  14. package/dist/modules/sso/api/config/[id]/route.js.map +7 -0
  15. package/dist/modules/sso/api/config/[id]/test/route.js +41 -0
  16. package/dist/modules/sso/api/config/[id]/test/route.js.map +7 -0
  17. package/dist/modules/sso/api/config/route.js +83 -0
  18. package/dist/modules/sso/api/config/route.js.map +7 -0
  19. package/dist/modules/sso/api/error-handler.js +28 -0
  20. package/dist/modules/sso/api/error-handler.js.map +7 -0
  21. package/dist/modules/sso/api/hrd/route.js +52 -0
  22. package/dist/modules/sso/api/hrd/route.js.map +7 -0
  23. package/dist/modules/sso/api/initiate/route.js +66 -0
  24. package/dist/modules/sso/api/initiate/route.js.map +7 -0
  25. package/dist/modules/sso/api/scim/context.js +68 -0
  26. package/dist/modules/sso/api/scim/context.js.map +7 -0
  27. package/dist/modules/sso/api/scim/logs/route.js +65 -0
  28. package/dist/modules/sso/api/scim/logs/route.js.map +7 -0
  29. package/dist/modules/sso/api/scim/tokens/[id]/route.js +42 -0
  30. package/dist/modules/sso/api/scim/tokens/[id]/route.js.map +7 -0
  31. package/dist/modules/sso/api/scim/tokens/route.js +83 -0
  32. package/dist/modules/sso/api/scim/tokens/route.js.map +7 -0
  33. package/dist/modules/sso/api/scim/v2/ServiceProviderConfig/route.js +42 -0
  34. package/dist/modules/sso/api/scim/v2/ServiceProviderConfig/route.js.map +7 -0
  35. package/dist/modules/sso/api/scim/v2/Users/[id]/route.js +94 -0
  36. package/dist/modules/sso/api/scim/v2/Users/[id]/route.js.map +7 -0
  37. package/dist/modules/sso/api/scim/v2/Users/route.js +86 -0
  38. package/dist/modules/sso/api/scim/v2/Users/route.js.map +7 -0
  39. package/dist/modules/sso/backend/page.js +173 -0
  40. package/dist/modules/sso/backend/page.js.map +7 -0
  41. package/dist/modules/sso/backend/page.meta.js +31 -0
  42. package/dist/modules/sso/backend/page.meta.js.map +7 -0
  43. package/dist/modules/sso/backend/sso/config/[id]/page.js +749 -0
  44. package/dist/modules/sso/backend/sso/config/[id]/page.js.map +7 -0
  45. package/dist/modules/sso/backend/sso/config/[id]/page.meta.js +19 -0
  46. package/dist/modules/sso/backend/sso/config/[id]/page.meta.js.map +7 -0
  47. package/dist/modules/sso/backend/sso/config/new/page.js +381 -0
  48. package/dist/modules/sso/backend/sso/config/new/page.js.map +7 -0
  49. package/dist/modules/sso/backend/sso/config/new/page.meta.js +19 -0
  50. package/dist/modules/sso/backend/sso/config/new/page.meta.js.map +7 -0
  51. package/dist/modules/sso/data/entities.js +299 -0
  52. package/dist/modules/sso/data/entities.js.map +7 -0
  53. package/dist/modules/sso/data/validators.js +114 -0
  54. package/dist/modules/sso/data/validators.js.map +7 -0
  55. package/dist/modules/sso/di.js +26 -0
  56. package/dist/modules/sso/di.js.map +7 -0
  57. package/dist/modules/sso/events.js +24 -0
  58. package/dist/modules/sso/events.js.map +7 -0
  59. package/dist/modules/sso/i18n/de.json +146 -0
  60. package/dist/modules/sso/i18n/en.json +146 -0
  61. package/dist/modules/sso/i18n/es.json +146 -0
  62. package/dist/modules/sso/i18n/pl.json +146 -0
  63. package/dist/modules/sso/index.js +11 -0
  64. package/dist/modules/sso/index.js.map +7 -0
  65. package/dist/modules/sso/lib/domains.js +30 -0
  66. package/dist/modules/sso/lib/domains.js.map +7 -0
  67. package/dist/modules/sso/lib/oidc-provider.js +140 -0
  68. package/dist/modules/sso/lib/oidc-provider.js.map +7 -0
  69. package/dist/modules/sso/lib/registry.js +15 -0
  70. package/dist/modules/sso/lib/registry.js.map +7 -0
  71. package/dist/modules/sso/lib/scim-filter.js +43 -0
  72. package/dist/modules/sso/lib/scim-filter.js.map +7 -0
  73. package/dist/modules/sso/lib/scim-mapper.js +49 -0
  74. package/dist/modules/sso/lib/scim-mapper.js.map +7 -0
  75. package/dist/modules/sso/lib/scim-patch.js +63 -0
  76. package/dist/modules/sso/lib/scim-patch.js.map +7 -0
  77. package/dist/modules/sso/lib/scim-response.js +34 -0
  78. package/dist/modules/sso/lib/scim-response.js.map +7 -0
  79. package/dist/modules/sso/lib/scim-utils.js +9 -0
  80. package/dist/modules/sso/lib/scim-utils.js.map +7 -0
  81. package/dist/modules/sso/lib/state-cookie.js +67 -0
  82. package/dist/modules/sso/lib/state-cookie.js.map +7 -0
  83. package/dist/modules/sso/lib/types.js +1 -0
  84. package/dist/modules/sso/lib/types.js.map +7 -0
  85. package/dist/modules/sso/migrations/Migration20260219000000_sso.js +20 -0
  86. package/dist/modules/sso/migrations/Migration20260219000000_sso.js.map +7 -0
  87. package/dist/modules/sso/migrations/Migration20260222000000_sso_add_name.js +13 -0
  88. package/dist/modules/sso/migrations/Migration20260222000000_sso_add_name.js.map +7 -0
  89. package/dist/modules/sso/migrations/Migration20260222000001_sso_partial_unique_org.js +15 -0
  90. package/dist/modules/sso/migrations/Migration20260222000001_sso_partial_unique_org.js.map +7 -0
  91. package/dist/modules/sso/migrations/Migration20260223000000_scim_tables.js +22 -0
  92. package/dist/modules/sso/migrations/Migration20260223000000_scim_tables.js.map +7 -0
  93. package/dist/modules/sso/migrations/Migration20260224000000_sso_external_id.js +15 -0
  94. package/dist/modules/sso/migrations/Migration20260224000000_sso_external_id.js.map +7 -0
  95. package/dist/modules/sso/migrations/Migration20260224100000_sso_role_grants.js +17 -0
  96. package/dist/modules/sso/migrations/Migration20260224100000_sso_role_grants.js.map +7 -0
  97. package/dist/modules/sso/migrations/Migration20260224200000_drop_default_role_id.js +13 -0
  98. package/dist/modules/sso/migrations/Migration20260224200000_drop_default_role_id.js.map +7 -0
  99. package/dist/modules/sso/migrations/Migration20260225000000_sso_identities_partial_unique.js +23 -0
  100. package/dist/modules/sso/migrations/Migration20260225000000_sso_identities_partial_unique.js.map +7 -0
  101. package/dist/modules/sso/migrations/Migration20260305000000_sso_role_grants_org_id.js +14 -0
  102. package/dist/modules/sso/migrations/Migration20260305000000_sso_role_grants_org_id.js.map +7 -0
  103. package/dist/modules/sso/services/accountLinkingService.js +298 -0
  104. package/dist/modules/sso/services/accountLinkingService.js.map +7 -0
  105. package/dist/modules/sso/services/hrdService.js +18 -0
  106. package/dist/modules/sso/services/hrdService.js.map +7 -0
  107. package/dist/modules/sso/services/scimService.js +372 -0
  108. package/dist/modules/sso/services/scimService.js.map +7 -0
  109. package/dist/modules/sso/services/scimTokenService.js +94 -0
  110. package/dist/modules/sso/services/scimTokenService.js.map +7 -0
  111. package/dist/modules/sso/services/ssoConfigService.js +254 -0
  112. package/dist/modules/sso/services/ssoConfigService.js.map +7 -0
  113. package/dist/modules/sso/services/ssoService.js +125 -0
  114. package/dist/modules/sso/services/ssoService.js.map +7 -0
  115. package/dist/modules/sso/setup.js +47 -0
  116. package/dist/modules/sso/setup.js.map +7 -0
  117. package/dist/modules/sso/subscribers/user-deleted-cleanup.js +21 -0
  118. package/dist/modules/sso/subscribers/user-deleted-cleanup.js.map +7 -0
  119. package/dist/modules/sso/widgets/injection/login-sso/widget.client.js +106 -0
  120. package/dist/modules/sso/widgets/injection/login-sso/widget.client.js.map +7 -0
  121. package/dist/modules/sso/widgets/injection/login-sso/widget.js +16 -0
  122. package/dist/modules/sso/widgets/injection/login-sso/widget.js.map +7 -0
  123. package/dist/modules/sso/widgets/injection-table.js +14 -0
  124. package/dist/modules/sso/widgets/injection-table.js.map +7 -0
  125. package/package.json +5 -4
  126. package/src/index.ts +1 -1
  127. package/src/modules/sso/acl.ts +7 -0
  128. package/src/modules/sso/api/admin-context.ts +36 -0
  129. package/src/modules/sso/api/callback/oidc/route.ts +115 -0
  130. package/src/modules/sso/api/config/[id]/activate/route.ts +53 -0
  131. package/src/modules/sso/api/config/[id]/domains/route.ts +107 -0
  132. package/src/modules/sso/api/config/[id]/route.ts +114 -0
  133. package/src/modules/sso/api/config/[id]/test/route.ts +44 -0
  134. package/src/modules/sso/api/config/route.ts +88 -0
  135. package/src/modules/sso/api/error-handler.ts +36 -0
  136. package/src/modules/sso/api/hrd/route.ts +55 -0
  137. package/src/modules/sso/api/initiate/route.ts +70 -0
  138. package/src/modules/sso/api/scim/context.ts +85 -0
  139. package/src/modules/sso/api/scim/logs/route.ts +69 -0
  140. package/src/modules/sso/api/scim/tokens/[id]/route.ts +45 -0
  141. package/src/modules/sso/api/scim/tokens/route.ts +89 -0
  142. package/src/modules/sso/api/scim/v2/ServiceProviderConfig/route.ts +40 -0
  143. package/src/modules/sso/api/scim/v2/Users/[id]/route.ts +103 -0
  144. package/src/modules/sso/api/scim/v2/Users/route.ts +94 -0
  145. package/src/modules/sso/backend/page.meta.ts +29 -0
  146. package/src/modules/sso/backend/page.tsx +232 -0
  147. package/src/modules/sso/backend/sso/config/[id]/page.meta.ts +15 -0
  148. package/src/modules/sso/backend/sso/config/[id]/page.tsx +1024 -0
  149. package/src/modules/sso/backend/sso/config/new/page.meta.ts +15 -0
  150. package/src/modules/sso/backend/sso/config/new/page.tsx +463 -0
  151. package/src/modules/sso/data/entities.ts +240 -0
  152. package/src/modules/sso/data/validators.ts +140 -0
  153. package/src/modules/sso/di.ts +25 -0
  154. package/src/modules/sso/docs/entra-id-setup.md +281 -0
  155. package/src/modules/sso/docs/google-workspace-setup.md +174 -0
  156. package/src/modules/sso/docs/sso-overview.md +218 -0
  157. package/src/modules/sso/docs/sso-security-audit-2026-02-27.md +118 -0
  158. package/src/modules/sso/docs/zitadel-setup.md +195 -0
  159. package/src/modules/sso/events.ts +21 -0
  160. package/src/modules/sso/i18n/de.json +146 -0
  161. package/src/modules/sso/i18n/en.json +146 -0
  162. package/src/modules/sso/i18n/es.json +146 -0
  163. package/src/modules/sso/i18n/pl.json +146 -0
  164. package/src/modules/sso/index.ts +7 -0
  165. package/src/modules/sso/lib/domains.ts +31 -0
  166. package/src/modules/sso/lib/oidc-provider.ts +196 -0
  167. package/src/modules/sso/lib/registry.ts +13 -0
  168. package/src/modules/sso/lib/scim-filter.ts +62 -0
  169. package/src/modules/sso/lib/scim-mapper.ts +88 -0
  170. package/src/modules/sso/lib/scim-patch.ts +88 -0
  171. package/src/modules/sso/lib/scim-response.ts +40 -0
  172. package/src/modules/sso/lib/scim-utils.ts +5 -0
  173. package/src/modules/sso/lib/state-cookie.ts +79 -0
  174. package/src/modules/sso/lib/types.ts +50 -0
  175. package/src/modules/sso/migrations/.snapshot-open-mercato.json +912 -0
  176. package/src/modules/sso/migrations/Migration20260219000000_sso.ts +21 -0
  177. package/src/modules/sso/migrations/Migration20260222000000_sso_add_name.ts +13 -0
  178. package/src/modules/sso/migrations/Migration20260222000001_sso_partial_unique_org.ts +15 -0
  179. package/src/modules/sso/migrations/Migration20260223000000_scim_tables.ts +24 -0
  180. package/src/modules/sso/migrations/Migration20260224000000_sso_external_id.ts +15 -0
  181. package/src/modules/sso/migrations/Migration20260224100000_sso_role_grants.ts +18 -0
  182. package/src/modules/sso/migrations/Migration20260224200000_drop_default_role_id.ts +13 -0
  183. package/src/modules/sso/migrations/Migration20260225000000_sso_identities_partial_unique.ts +25 -0
  184. package/src/modules/sso/migrations/Migration20260305000000_sso_role_grants_org_id.ts +14 -0
  185. package/src/modules/sso/services/accountLinkingService.ts +386 -0
  186. package/src/modules/sso/services/hrdService.ts +22 -0
  187. package/src/modules/sso/services/scimService.ts +461 -0
  188. package/src/modules/sso/services/scimTokenService.ts +136 -0
  189. package/src/modules/sso/services/ssoConfigService.ts +337 -0
  190. package/src/modules/sso/services/ssoService.ts +167 -0
  191. package/src/modules/sso/setup.ts +56 -0
  192. package/src/modules/sso/subscribers/user-deleted-cleanup.ts +33 -0
  193. package/src/modules/sso/widgets/injection/login-sso/widget.client.tsx +130 -0
  194. package/src/modules/sso/widgets/injection/login-sso/widget.ts +16 -0
  195. package/src/modules/sso/widgets/injection-table.ts +12 -0
@@ -0,0 +1,146 @@
1
+ {
2
+ "common.activating": "Activando...",
3
+ "common.add": "Agregar",
4
+ "common.back": "Volver",
5
+ "common.cancel": "Cancelar",
6
+ "common.copied": "Copiado al portapapeles",
7
+ "common.copy": "Copiar",
8
+ "common.create": "Crear",
9
+ "common.creating": "Creando...",
10
+ "common.delete": "Eliminar",
11
+ "common.disabled": "Deshabilitado",
12
+ "common.dismiss": "Descartar",
13
+ "common.edit": "Editar",
14
+ "common.enabled": "Habilitado",
15
+ "common.loading": "Cargando...",
16
+ "common.next": "Siguiente",
17
+ "common.notFound": "No encontrado",
18
+ "common.remove": "Eliminar",
19
+ "common.save": "Guardar",
20
+ "common.saving": "Guardando...",
21
+ "settings.sections.auth": "Autenticación",
22
+ "sso.admin.action.activate": "Activar",
23
+ "sso.admin.action.deactivate": "Desactivar",
24
+ "sso.admin.action.test": "Verificar descubrimiento",
25
+ "sso.admin.activated": "Configuración SSO activada",
26
+ "sso.admin.activity.empty": "Aún no hay actividad de inicio de sesión SSO. La actividad aparecerá aquí cuando los usuarios comiencen a iniciar sesión a través de SSO.",
27
+ "sso.admin.banner.activateNow": "Activar ahora",
28
+ "sso.admin.banner.created": "Su configuración SSO ha sido creada. ¿Desea activarla ahora?",
29
+ "sso.admin.banner.notYet": "Aún no",
30
+ "sso.admin.column.created": "Creado",
31
+ "sso.admin.column.domains": "Dominios",
32
+ "sso.admin.column.name": "Nombre",
33
+ "sso.admin.column.protocol": "Protocolo",
34
+ "sso.admin.column.status": "Estado",
35
+ "sso.admin.create.title": "Configurar SSO",
36
+ "sso.admin.created": "Configuración SSO creada",
37
+ "sso.admin.deactivated": "Configuración SSO desactivada",
38
+ "sso.admin.delete.confirm": "¿Está seguro? Esto eliminará la configuración SSO. Los usuarios con identidades SSO vinculadas deberán usar inicio de sesión con contraseña.",
39
+ "sso.admin.delete.success": "Configuración SSO eliminada",
40
+ "sso.admin.delete.title": "Eliminar configuración SSO",
41
+ "sso.admin.detail.backToList": "Volver a SSO",
42
+ "sso.admin.detail.title": "Configuración SSO",
43
+ "sso.admin.domains.empty": "No hay dominios configurados. Agregue al menos un dominio antes de activar SSO.",
44
+ "sso.admin.empty.cta": "Configurar SSO",
45
+ "sso.admin.empty.description": "Configure el inicio de sesión único para permitir a sus usuarios autenticarse con su proveedor de identidad.",
46
+ "sso.admin.empty.title": "SSO no configurado",
47
+ "sso.admin.error.activationFailed": "Error al actualizar el estado de activación",
48
+ "sso.admin.error.alreadyExists": "Ya existe una configuración SSO para esta organización",
49
+ "sso.admin.error.createFailed": "Error al crear la configuración SSO",
50
+ "sso.admin.error.deleteActive": "No se puede eliminar una configuración SSO activa — desactívela primero",
51
+ "sso.admin.error.deleteFailed": "Error al eliminar la configuración SSO",
52
+ "sso.admin.error.domainAddFailed": "Error al agregar dominio",
53
+ "sso.admin.error.domainRemoveFailed": "Error al eliminar dominio",
54
+ "sso.admin.error.loadFailed": "Error al cargar la configuración SSO",
55
+ "sso.admin.error.noDomainsForActivation": "Agregue al menos un dominio de correo electrónico permitido antes de activar",
56
+ "sso.admin.error.saveFailed": "Error al guardar la configuración SSO",
57
+ "sso.admin.error.testFailed": "La prueba de conexión falló",
58
+ "sso.admin.field.autoLinkByEmail": "Vinculación automática por correo electrónico",
59
+ "sso.admin.field.autoLinkByEmailDesc": "Vincular automáticamente usuarios existentes por dirección de correo electrónico coincidente",
60
+ "sso.admin.field.changeSecret": "Cambiar",
61
+ "sso.admin.field.clientId": "ID de cliente",
62
+ "sso.admin.field.clientSecret": "Secreto de cliente",
63
+ "sso.admin.field.issuer": "URL del emisor",
64
+ "sso.admin.field.jitDisabledByScim": "No disponible — La sincronización de directorio SCIM está activa. Revoque los tokens SCIM para habilitar JIT.",
65
+ "sso.admin.field.jitEnabled": "Aprovisionamiento Just-in-Time",
66
+ "sso.admin.field.jitEnabledDesc": "Crear automáticamente cuentas de usuario en el primer inicio de sesión SSO",
67
+ "sso.admin.field.name": "Nombre de configuración",
68
+ "sso.admin.field.protocol": "Protocolo",
69
+ "sso.admin.field.secretPlaceholder": "Ingrese nuevo secreto para reemplazar el existente",
70
+ "sso.admin.field.secretRequired": "Ingrese el secreto de cliente",
71
+ "sso.admin.field.secretSet": "El secreto de cliente está configurado",
72
+ "sso.admin.new": "Nueva configuración SSO",
73
+ "sso.admin.roles.description": "Asigne nombres de roles de aplicación IdP a roles locales. En cada inicio de sesión SSO, los roles de origen SSO se sincronizan — los roles que el IdP ya no envía se eliminan, mientras que los roles asignados manualmente se conservan.",
74
+ "sso.admin.roles.empty": "No hay asignaciones de roles configuradas. Los nombres de roles del IdP se compararán directamente con los nombres de roles locales.",
75
+ "sso.admin.roles.error.duplicate": "Este rol de IdP ya está asignado",
76
+ "sso.admin.roles.error.emptyIdpRole": "El nombre del rol de IdP es obligatorio",
77
+ "sso.admin.roles.error.emptyLocalRole": "Seleccione un rol local",
78
+ "sso.admin.roles.error.saveFailed": "Error al guardar las asignaciones de roles",
79
+ "sso.admin.roles.idpRole": "Nombre del rol de IdP",
80
+ "sso.admin.roles.idpRolePlaceholder": "ej. OpenMercato.Admin",
81
+ "sso.admin.roles.localRole": "Rol local",
82
+ "sso.admin.roles.saved": "Asignaciones de roles guardadas",
83
+ "sso.admin.saved": "Configuración SSO guardada",
84
+ "sso.admin.scim.endpointCopied": "URL del endpoint SCIM copiada",
85
+ "sso.admin.scim.endpointUrl": "URL del endpoint SCIM",
86
+ "sso.admin.scim.error.createFailed": "Error al crear el token SCIM",
87
+ "sso.admin.scim.error.revokeFailed": "Error al revocar el token",
88
+ "sso.admin.scim.generateToken": "Generar token",
89
+ "sso.admin.scim.googleNotSupported": "Google Workspace no admite el aprovisionamiento SCIM. Los usuarios se aprovisionan mediante Just-In-Time (JIT) en el primer inicio de sesión.",
90
+ "sso.admin.scim.jitActiveWarning": "El aprovisionamiento SCIM no está disponible mientras JIT está habilitado. Deshabilite JIT en la pestaña General para configurar SCIM.",
91
+ "sso.admin.scim.log.error": "Error",
92
+ "sso.admin.scim.log.operation": "Operación",
93
+ "sso.admin.scim.log.resource": "Recurso",
94
+ "sso.admin.scim.log.status": "Estado",
95
+ "sso.admin.scim.log.time": "Hora",
96
+ "sso.admin.scim.noTokens": "El aprovisionamiento SCIM no está configurado. Genere un token bearer para permitir que su proveedor de identidad sincronice usuarios automáticamente.",
97
+ "sso.admin.scim.recentActivity": "Actividad de aprovisionamiento reciente",
98
+ "sso.admin.scim.revoke.action": "Revocar",
99
+ "sso.admin.scim.revoke.confirm": "¿Está seguro? Este token ya no autenticará solicitudes SCIM.",
100
+ "sso.admin.scim.revoke.title": "Revocar token",
101
+ "sso.admin.scim.revoked": "Token revocado",
102
+ "sso.admin.scim.tokenActive": "Activo",
103
+ "sso.admin.scim.tokenCopied": "Token copiado al portapapeles",
104
+ "sso.admin.scim.tokenCreated": "Su token SCIM ha sido creado. Cópielo ahora — no se mostrará de nuevo.",
105
+ "sso.admin.scim.tokenNamePlaceholder": "Nombre del token (ej., Entra ID Producción)",
106
+ "sso.admin.scim.tokenRevoked": "Revocado",
107
+ "sso.admin.scim.tokens": "Tokens bearer",
108
+ "sso.admin.search": "Buscar por nombre o emisor...",
109
+ "sso.admin.section.allowedDomains": "Dominios permitidos",
110
+ "sso.admin.section.oidcSettings": "Configuración OIDC",
111
+ "sso.admin.status.active": "Activo",
112
+ "sso.admin.status.inactive": "Inactivo",
113
+ "sso.admin.tab.activity": "Actividad",
114
+ "sso.admin.tab.domains": "Dominios",
115
+ "sso.admin.tab.general": "General",
116
+ "sso.admin.tab.roles": "Asignación de roles",
117
+ "sso.admin.tab.scim": "Aprovisionamiento",
118
+ "sso.admin.test.failed": "Descubrimiento fallido",
119
+ "sso.admin.test.success": "Descubrimiento exitoso — el emisor es accesible",
120
+ "sso.admin.title": "Inicio de sesión único",
121
+ "sso.admin.wizard.credentials.callbackUrl": "URI de redirección (copie a su IdP)",
122
+ "sso.admin.wizard.credentials.namePlaceholder": "ej., Zitadel Producción",
123
+ "sso.admin.wizard.credentials.title": "Credenciales OIDC",
124
+ "sso.admin.wizard.domain.duplicate": "Dominio ya agregado",
125
+ "sso.admin.wizard.domain.invalid": "Formato de dominio inválido",
126
+ "sso.admin.wizard.domain.limit": "Máximo 20 dominios por configuración",
127
+ "sso.admin.wizard.domains.description": "Los usuarios con direcciones de correo electrónico que coincidan con estos dominios serán redirigidos a su proveedor SSO.",
128
+ "sso.admin.wizard.domains.placeholder": "ejemplo.com",
129
+ "sso.admin.wizard.domains.title": "Dominios de correo electrónico permitidos",
130
+ "sso.admin.wizard.options.title": "Opciones",
131
+ "sso.admin.wizard.protocol.oidcDesc": "Funciona con Zitadel, Microsoft Entra ID, Google Workspace, Okta y más",
132
+ "sso.admin.wizard.protocol.samlDesc": "Próximamente",
133
+ "sso.admin.wizard.protocol.title": "Seleccionar protocolo",
134
+ "sso.admin.wizard.review.note": "La configuración se creará como inactiva. Puede activarla desde la página de detalle después de verificar que todo esté correcto.",
135
+ "sso.admin.wizard.review.save": "Crear configuración",
136
+ "sso.admin.wizard.review.testing": "Probando...",
137
+ "sso.admin.wizard.review.title": "Revisar y guardar",
138
+ "sso.login.continueWithSso": "Continuar con SSO",
139
+ "sso.login.errors.emailNotVerified": "Su dirección de correo electrónico no está verificada por el proveedor de identidad. Por favor verifique su correo electrónico e intente de nuevo.",
140
+ "sso.login.errors.failed": "El inicio de sesión SSO falló. Por favor intente de nuevo.",
141
+ "sso.login.errors.idpError": "El proveedor de identidad devolvió un error. Por favor intente de nuevo o contacte a su administrador.",
142
+ "sso.login.errors.missingConfig": "SSO no está configurado para esta cuenta.",
143
+ "sso.login.errors.missingParams": "La devolución de llamada SSO fue incompleta. Por favor intente de nuevo.",
144
+ "sso.login.errors.stateMissing": "La sesión SSO expiró. Por favor intente de nuevo.",
145
+ "sso.login.ssoEnabled": "SSO está habilitado para esta cuenta"
146
+ }
@@ -0,0 +1,146 @@
1
+ {
2
+ "common.activating": "Aktywowanie...",
3
+ "common.add": "Dodaj",
4
+ "common.back": "Wstecz",
5
+ "common.cancel": "Anuluj",
6
+ "common.copied": "Skopiowano do schowka",
7
+ "common.copy": "Kopiuj",
8
+ "common.create": "Utwórz",
9
+ "common.creating": "Tworzenie...",
10
+ "common.delete": "Usuń",
11
+ "common.disabled": "Wyłączone",
12
+ "common.dismiss": "Zamknij",
13
+ "common.edit": "Edytuj",
14
+ "common.enabled": "Włączone",
15
+ "common.loading": "Ładowanie...",
16
+ "common.next": "Dalej",
17
+ "common.notFound": "Nie znaleziono",
18
+ "common.remove": "Usuń",
19
+ "common.save": "Zapisz",
20
+ "common.saving": "Zapisywanie...",
21
+ "settings.sections.auth": "Autoryzacja",
22
+ "sso.admin.action.activate": "Aktywuj",
23
+ "sso.admin.action.deactivate": "Dezaktywuj",
24
+ "sso.admin.action.test": "Zweryfikuj Discovery",
25
+ "sso.admin.activated": "Konfiguracja SSO aktywowana",
26
+ "sso.admin.activity.empty": "Brak aktywności logowania SSO. Aktywność pojawi się tutaj, gdy użytkownicy zaczną logować się przez SSO.",
27
+ "sso.admin.banner.activateNow": "Aktywuj teraz",
28
+ "sso.admin.banner.created": "Konfiguracja SSO została utworzona. Czy chcesz ją teraz aktywować?",
29
+ "sso.admin.banner.notYet": "Jeszcze nie",
30
+ "sso.admin.column.created": "Utworzono",
31
+ "sso.admin.column.domains": "Domeny",
32
+ "sso.admin.column.name": "Nazwa",
33
+ "sso.admin.column.protocol": "Protokół",
34
+ "sso.admin.column.status": "Status",
35
+ "sso.admin.create.title": "Skonfiguruj SSO",
36
+ "sso.admin.created": "Konfiguracja SSO została utworzona",
37
+ "sso.admin.deactivated": "Konfiguracja SSO dezaktywowana",
38
+ "sso.admin.delete.confirm": "Czy na pewno? Spowoduje to usunięcie konfiguracji SSO. Użytkownicy z powiązanymi tożsamościami SSO będą musieli logować się hasłem.",
39
+ "sso.admin.delete.success": "Konfiguracja SSO została usunięta",
40
+ "sso.admin.delete.title": "Usuń konfigurację SSO",
41
+ "sso.admin.detail.backToList": "Powrót do SSO",
42
+ "sso.admin.detail.title": "Konfiguracja SSO",
43
+ "sso.admin.domains.empty": "Brak skonfigurowanych domen. Dodaj co najmniej jedną domenę przed aktywacją SSO.",
44
+ "sso.admin.empty.cta": "Skonfiguruj SSO",
45
+ "sso.admin.empty.description": "Skonfiguruj Single Sign-On, aby umożliwić użytkownikom logowanie przez dostawcę tożsamości.",
46
+ "sso.admin.empty.title": "Brak konfiguracji SSO",
47
+ "sso.admin.error.activationFailed": "Nie udało się zmienić statusu aktywacji",
48
+ "sso.admin.error.alreadyExists": "Konfiguracja SSO już istnieje dla tej organizacji",
49
+ "sso.admin.error.createFailed": "Nie udało się utworzyć konfiguracji SSO",
50
+ "sso.admin.error.deleteActive": "Nie można usunąć aktywnej konfiguracji SSO — najpierw ją dezaktywuj",
51
+ "sso.admin.error.deleteFailed": "Nie udało się usunąć konfiguracji SSO",
52
+ "sso.admin.error.domainAddFailed": "Nie udało się dodać domeny",
53
+ "sso.admin.error.domainRemoveFailed": "Nie udało się usunąć domeny",
54
+ "sso.admin.error.loadFailed": "Nie udało się załadować konfiguracji SSO",
55
+ "sso.admin.error.noDomainsForActivation": "Dodaj co najmniej jedną dozwoloną domenę e-mail przed aktywacją",
56
+ "sso.admin.error.saveFailed": "Nie udało się zapisać konfiguracji SSO",
57
+ "sso.admin.error.testFailed": "Test połączenia nie powiódł się",
58
+ "sso.admin.field.autoLinkByEmail": "Automatyczne łączenie po e-mailu",
59
+ "sso.admin.field.autoLinkByEmailDesc": "Automatycznie łącz istniejących użytkowników po adresie e-mail",
60
+ "sso.admin.field.changeSecret": "Zmień",
61
+ "sso.admin.field.clientId": "Client ID",
62
+ "sso.admin.field.clientSecret": "Client Secret",
63
+ "sso.admin.field.issuer": "Adres URL wystawcy",
64
+ "sso.admin.field.jitDisabledByScim": "Niedostępne — synchronizacja katalogów SCIM jest aktywna. Odwołaj tokeny SCIM, aby włączyć JIT.",
65
+ "sso.admin.field.jitEnabled": "Provisioning Just-in-Time",
66
+ "sso.admin.field.jitEnabledDesc": "Automatycznie twórz konta użytkowników przy pierwszym logowaniu SSO",
67
+ "sso.admin.field.name": "Nazwa konfiguracji",
68
+ "sso.admin.field.protocol": "Protokół",
69
+ "sso.admin.field.secretPlaceholder": "Wprowadź nowy secret, aby zastąpić istniejący",
70
+ "sso.admin.field.secretRequired": "Wprowadź client secret",
71
+ "sso.admin.field.secretSet": "Client secret jest skonfigurowany",
72
+ "sso.admin.new": "Nowa konfiguracja SSO",
73
+ "sso.admin.roles.description": "Mapuj nazwy ról IdP na lokalne role. Przy każdym logowaniu SSO role źródłowe SSO są synchronizowane — role nieotrzymane od IdP są usuwane, role przypisane ręcznie są zachowane.",
74
+ "sso.admin.roles.empty": "Brak skonfigurowanych mapowań ról. Nazwy ról IdP będą dopasowywane bezpośrednio do nazw lokalnych ról.",
75
+ "sso.admin.roles.error.duplicate": "Ta rola IdP jest już zmapowana",
76
+ "sso.admin.roles.error.emptyIdpRole": "Nazwa roli IdP jest wymagana",
77
+ "sso.admin.roles.error.emptyLocalRole": "Wybierz lokalną rolę",
78
+ "sso.admin.roles.error.saveFailed": "Nie udało się zapisać mapowań ról",
79
+ "sso.admin.roles.idpRole": "Nazwa roli IdP",
80
+ "sso.admin.roles.idpRolePlaceholder": "np. OpenMercato.Admin",
81
+ "sso.admin.roles.localRole": "Lokalna rola",
82
+ "sso.admin.roles.saved": "Mapowania ról zapisane",
83
+ "sso.admin.saved": "Konfiguracja SSO zapisana",
84
+ "sso.admin.scim.endpointCopied": "Adres URL endpointu SCIM skopiowany",
85
+ "sso.admin.scim.endpointUrl": "Adres URL endpointu SCIM",
86
+ "sso.admin.scim.error.createFailed": "Nie udało się utworzyć tokena SCIM",
87
+ "sso.admin.scim.error.revokeFailed": "Nie udało się odwołać tokena",
88
+ "sso.admin.scim.generateToken": "Wygeneruj token",
89
+ "sso.admin.scim.googleNotSupported": "Google Workspace nie obsługuje provisioningu SCIM. Użytkownicy są tworzeni przez Just-In-Time (JIT) przy pierwszym logowaniu.",
90
+ "sso.admin.scim.jitActiveWarning": "Provisioning SCIM jest niedostępny, gdy provisioning JIT jest włączony. Wyłącz JIT w zakładce Ogólne, aby skonfigurować SCIM.",
91
+ "sso.admin.scim.log.error": "Błąd",
92
+ "sso.admin.scim.log.operation": "Operacja",
93
+ "sso.admin.scim.log.resource": "Zasób",
94
+ "sso.admin.scim.log.status": "Status",
95
+ "sso.admin.scim.log.time": "Czas",
96
+ "sso.admin.scim.noTokens": "Provisioning SCIM nie jest skonfigurowany. Wygeneruj token bearer, aby umożliwić dostawcy tożsamości automatyczną synchronizację użytkowników.",
97
+ "sso.admin.scim.recentActivity": "Ostatnia aktywność provisioningu",
98
+ "sso.admin.scim.revoke.action": "Odwołaj",
99
+ "sso.admin.scim.revoke.confirm": "Czy na pewno? Ten token nie będzie już uwierzytelniał żądań SCIM.",
100
+ "sso.admin.scim.revoke.title": "Odwołaj token",
101
+ "sso.admin.scim.revoked": "Token odwołany",
102
+ "sso.admin.scim.tokenActive": "Aktywny",
103
+ "sso.admin.scim.tokenCopied": "Token skopiowany do schowka",
104
+ "sso.admin.scim.tokenCreated": "Twój token SCIM został utworzony. Skopiuj go teraz — nie będzie ponownie wyświetlony.",
105
+ "sso.admin.scim.tokenNamePlaceholder": "Nazwa tokena (np. Entra ID Produkcja)",
106
+ "sso.admin.scim.tokenRevoked": "Odwołany",
107
+ "sso.admin.scim.tokens": "Tokeny Bearer",
108
+ "sso.admin.search": "Szukaj po nazwie lub wystawcy...",
109
+ "sso.admin.section.allowedDomains": "Dozwolone domeny",
110
+ "sso.admin.section.oidcSettings": "Ustawienia OIDC",
111
+ "sso.admin.status.active": "Aktywny",
112
+ "sso.admin.status.inactive": "Nieaktywny",
113
+ "sso.admin.tab.activity": "Aktywność",
114
+ "sso.admin.tab.domains": "Domeny",
115
+ "sso.admin.tab.general": "Ogólne",
116
+ "sso.admin.tab.roles": "Mapowanie ról",
117
+ "sso.admin.tab.scim": "Provisioning",
118
+ "sso.admin.test.failed": "Weryfikacja Discovery nie powiodła się",
119
+ "sso.admin.test.success": "Weryfikacja Discovery udana — wystawca jest osiągalny",
120
+ "sso.admin.title": "Single Sign-On",
121
+ "sso.admin.wizard.credentials.callbackUrl": "Redirect URI (skopiuj do swojego IdP)",
122
+ "sso.admin.wizard.credentials.namePlaceholder": "np. Zitadel Produkcja",
123
+ "sso.admin.wizard.credentials.title": "Dane uwierzytelniające OIDC",
124
+ "sso.admin.wizard.domain.duplicate": "Domena już dodana",
125
+ "sso.admin.wizard.domain.invalid": "Nieprawidłowy format domeny",
126
+ "sso.admin.wizard.domain.limit": "Maksymalnie 20 domen na konfigurację",
127
+ "sso.admin.wizard.domains.description": "Użytkownicy z adresami e-mail pasującymi do tych domen będą przekierowywani do dostawcy SSO.",
128
+ "sso.admin.wizard.domains.placeholder": "example.com",
129
+ "sso.admin.wizard.domains.title": "Dozwolone domeny e-mail",
130
+ "sso.admin.wizard.options.title": "Opcje",
131
+ "sso.admin.wizard.protocol.oidcDesc": "Działa z Zitadel, Microsoft Entra ID, Google Workspace, Okta i innymi",
132
+ "sso.admin.wizard.protocol.samlDesc": "Wkrótce",
133
+ "sso.admin.wizard.protocol.title": "Wybierz protokół",
134
+ "sso.admin.wizard.review.note": "Konfiguracja zostanie utworzona jako nieaktywna. Możesz ją aktywować na stronie szczegółów po zweryfikowaniu poprawności ustawień.",
135
+ "sso.admin.wizard.review.save": "Utwórz konfigurację",
136
+ "sso.admin.wizard.review.testing": "Testowanie...",
137
+ "sso.admin.wizard.review.title": "Przegląd i zapis",
138
+ "sso.login.continueWithSso": "Kontynuuj przez SSO",
139
+ "sso.login.errors.emailNotVerified": "Twój adres e-mail nie został zweryfikowany przez dostawcę tożsamości. Zweryfikuj swój e-mail i spróbuj ponownie.",
140
+ "sso.login.errors.failed": "Logowanie SSO nie powiodło się. Spróbuj ponownie.",
141
+ "sso.login.errors.idpError": "Dostawca tożsamości zwrócił błąd. Spróbuj ponownie lub skontaktuj się z administratorem.",
142
+ "sso.login.errors.missingConfig": "SSO nie jest skonfigurowane dla tego konta.",
143
+ "sso.login.errors.missingParams": "Callback SSO był niekompletny. Spróbuj ponownie.",
144
+ "sso.login.errors.stateMissing": "Sesja SSO wygasła. Spróbuj ponownie.",
145
+ "sso.login.ssoEnabled": "SSO jest włączone dla tego konta"
146
+ }
@@ -0,0 +1,7 @@
1
+ export const metadata = {
2
+ id: 'sso',
3
+ version: '0.1.0',
4
+ enterprise: true,
5
+ } as const
6
+
7
+ export { features } from './acl'
@@ -0,0 +1,31 @@
1
+ const DOMAIN_REGEX = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$/
2
+
3
+ const MAX_DOMAINS_PER_CONFIG = 20
4
+
5
+ export function normalizeDomain(domain: string): string {
6
+ return domain.trim().toLowerCase()
7
+ }
8
+
9
+ export function validateDomain(domain: string): { valid: boolean; error?: string } {
10
+ const normalized = normalizeDomain(domain)
11
+
12
+ if (!normalized) return { valid: false, error: 'Domain cannot be empty' }
13
+ if (normalized.length > 253) return { valid: false, error: 'Domain exceeds maximum length of 253 characters' }
14
+ if (!DOMAIN_REGEX.test(normalized)) return { valid: false, error: 'Invalid domain format — only DNS hostnames are accepted' }
15
+ if (!normalized.includes('.')) return { valid: false, error: 'Domain must include at least one dot (e.g., example.com)' }
16
+
17
+ return { valid: true }
18
+ }
19
+
20
+ export function uniqueDomains(domains: string[]): string[] {
21
+ return [...new Set(domains.map(normalizeDomain).filter(Boolean))]
22
+ }
23
+
24
+ export function checkDomainLimit(currentCount: number, adding: number): { ok: boolean; error?: string } {
25
+ if (currentCount + adding > MAX_DOMAINS_PER_CONFIG) {
26
+ return { ok: false, error: `Maximum ${MAX_DOMAINS_PER_CONFIG} domains per SSO configuration` }
27
+ }
28
+ return { ok: true }
29
+ }
30
+
31
+ export { MAX_DOMAINS_PER_CONFIG }
@@ -0,0 +1,196 @@
1
+ import * as client from 'openid-client'
2
+ import type { SsoConfig } from '../data/entities'
3
+ import type { SsoIdentityPayload, SsoProtocolProvider } from './types'
4
+
5
+ export class OidcProvider implements SsoProtocolProvider {
6
+ readonly protocol = 'oidc' as const
7
+
8
+ async buildAuthUrl(
9
+ config: SsoConfig,
10
+ params: {
11
+ state: string
12
+ nonce: string
13
+ redirectUri: string
14
+ codeVerifier?: string
15
+ clientSecret?: string
16
+ },
17
+ ): Promise<string> {
18
+ const oidcConfig = await this.discover(config, params.clientSecret)
19
+
20
+ const codeChallenge = params.codeVerifier
21
+ ? await client.calculatePKCECodeChallenge(params.codeVerifier)
22
+ : undefined
23
+
24
+ const authUrl = client.buildAuthorizationUrl(oidcConfig, {
25
+ redirect_uri: params.redirectUri,
26
+ scope: 'openid email profile',
27
+ state: params.state,
28
+ nonce: params.nonce,
29
+ ...(codeChallenge
30
+ ? { code_challenge: codeChallenge, code_challenge_method: 'S256' }
31
+ : {}),
32
+ })
33
+
34
+ return authUrl.href
35
+ }
36
+
37
+ async handleCallback(
38
+ config: SsoConfig,
39
+ params: {
40
+ callbackParams: Record<string, string>
41
+ redirectUri: string
42
+ expectedState: string
43
+ expectedNonce: string
44
+ codeVerifier?: string
45
+ clientSecret?: string
46
+ },
47
+ ): Promise<SsoIdentityPayload> {
48
+ const oidcConfig = await this.discover(config, params.clientSecret)
49
+
50
+ const callbackUrl = new URL(params.redirectUri)
51
+ for (const [key, value] of Object.entries(params.callbackParams)) {
52
+ callbackUrl.searchParams.set(key, value)
53
+ }
54
+
55
+ const tokens = await client.authorizationCodeGrant(oidcConfig, callbackUrl, {
56
+ pkceCodeVerifier: params.codeVerifier,
57
+ expectedState: params.expectedState,
58
+ expectedNonce: params.expectedNonce,
59
+ })
60
+
61
+ const claims = tokens.claims()
62
+ if (!claims) {
63
+ throw new Error('No ID token claims received from IdP')
64
+ }
65
+
66
+ const mergedClaims = await mergeWithUserInfoClaims(oidcConfig, tokens, claims)
67
+
68
+ const subject = String(mergedClaims.sub ?? claims.sub ?? '')
69
+ const rawEmail = mergedClaims.email as string | undefined
70
+ const upnCandidate = (mergedClaims.upn ?? mergedClaims.unique_name) as string | undefined
71
+ const upnAsEmail = typeof upnCandidate === 'string' && upnCandidate.includes('@') ? upnCandidate : undefined
72
+ const email = rawEmail ?? upnAsEmail
73
+ if (!email) {
74
+ throw new Error('IdP did not return an email claim (checked: email, upn, unique_name)')
75
+ }
76
+
77
+ const emailVerified = mergedClaims.email_verified === true
78
+ const groups = extractIdentityGroups(mergedClaims)
79
+
80
+
81
+
82
+ return {
83
+ subject,
84
+ email,
85
+ emailVerified,
86
+ name: (mergedClaims.name as string) ?? undefined,
87
+ groups,
88
+ }
89
+ }
90
+
91
+ async validateConfig(
92
+ config: SsoConfig,
93
+ params?: { clientSecret?: string },
94
+ ): Promise<{ ok: boolean; error?: string }> {
95
+ try {
96
+ await this.discover(config, params?.clientSecret)
97
+ return { ok: true }
98
+ } catch (err) {
99
+ return {
100
+ ok: false,
101
+ error: err instanceof Error ? err.message : 'Discovery failed',
102
+ }
103
+ }
104
+ }
105
+
106
+ private async discover(
107
+ config: SsoConfig,
108
+ clientSecret?: string,
109
+ ): Promise<client.Configuration> {
110
+ if (!config.issuer) {
111
+ throw new Error('SSO config is missing issuer URL')
112
+ }
113
+ if (!config.clientId) {
114
+ throw new Error('SSO config is missing client ID')
115
+ }
116
+
117
+ return client.discovery(
118
+ new URL(config.issuer),
119
+ config.clientId,
120
+ clientSecret ?? undefined,
121
+ )
122
+ }
123
+ }
124
+
125
+ async function mergeWithUserInfoClaims(
126
+ oidcConfig: client.Configuration,
127
+ tokens: client.TokenEndpointResponse,
128
+ claims: Record<string, unknown>,
129
+ ): Promise<Record<string, unknown>> {
130
+ const accessToken = tokens.access_token
131
+ if (!accessToken) return claims
132
+
133
+ try {
134
+ const userInfo = await client.fetchUserInfo(
135
+ oidcConfig,
136
+ accessToken,
137
+ client.skipSubjectCheck,
138
+ )
139
+ return { ...(userInfo as Record<string, unknown>), ...claims }
140
+ } catch {
141
+ return claims
142
+ }
143
+ }
144
+
145
+ export function extractIdentityGroups(claims: Record<string, unknown>): string[] | undefined {
146
+ const groups = new Set<string>()
147
+
148
+ const add = (value: unknown) => {
149
+ for (const group of coerceClaimValues(value)) {
150
+ groups.add(group)
151
+ }
152
+ }
153
+
154
+ add(claims.groups)
155
+ add(claims.roles)
156
+ add(claims.role)
157
+
158
+ for (const [key, value] of Object.entries(claims)) {
159
+ if (!key.endsWith(':roles')) continue
160
+ add(value)
161
+ }
162
+
163
+ return groups.size > 0 ? Array.from(groups) : undefined
164
+ }
165
+
166
+ export function coerceClaimValues(value: unknown): string[] {
167
+ if (typeof value === 'string') {
168
+ const normalized = value.trim()
169
+ return normalized ? [normalized] : []
170
+ }
171
+
172
+ if (Array.isArray(value)) {
173
+ return value.flatMap((entry) => coerceClaimValues(entry))
174
+ }
175
+
176
+ if (value && typeof value === 'object') {
177
+ const entries = Object.entries(value as Record<string, unknown>)
178
+ const out = new Set<string>()
179
+ for (const [key, nested] of entries) {
180
+ const normalizedKey = key.trim()
181
+ if (normalizedKey) out.add(normalizedKey)
182
+ if (typeof nested === 'string') {
183
+ const normalizedNested = nested.trim()
184
+ if (normalizedNested) out.add(normalizedNested)
185
+ } else if (nested && typeof nested === 'object') {
186
+ const nestedName = (nested as Record<string, unknown>).name
187
+ if (typeof nestedName === 'string' && nestedName.trim()) {
188
+ out.add(nestedName.trim())
189
+ }
190
+ }
191
+ }
192
+ return Array.from(out)
193
+ }
194
+
195
+ return []
196
+ }
@@ -0,0 +1,13 @@
1
+ import type { SsoProtocolProvider } from './types'
2
+
3
+ export class SsoProviderRegistry {
4
+ private providers = new Map<string, SsoProtocolProvider>()
5
+
6
+ register(provider: SsoProtocolProvider): void {
7
+ this.providers.set(provider.protocol, provider)
8
+ }
9
+
10
+ resolve(protocol: string): SsoProtocolProvider | undefined {
11
+ return this.providers.get(protocol)
12
+ }
13
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Minimal SCIM filter parser supporting `eq` operator with `and` combinator.
3
+ * Supports: userName, externalId, displayName, active
4
+ */
5
+
6
+ export interface ScimFilterCondition {
7
+ attribute: string
8
+ value: string
9
+ }
10
+
11
+ export function parseScimFilter(filter: string | null | undefined): ScimFilterCondition[] {
12
+ if (!filter || !filter.trim()) return []
13
+
14
+ const conditions: ScimFilterCondition[] = []
15
+ const parts = filter.split(/\s+and\s+/i)
16
+
17
+ for (const part of parts) {
18
+ const match = part.trim().match(/^(\S+)\s+eq\s+"([^"]*)"$/i)
19
+ if (!match) continue
20
+
21
+ const [, attribute, value] = match
22
+ const normalizedAttr = attribute.toLowerCase()
23
+
24
+ const allowed = ['username', 'externalid', 'displayname', 'active']
25
+ if (!allowed.includes(normalizedAttr)) continue
26
+
27
+ conditions.push({ attribute: normalizedAttr, value })
28
+ }
29
+
30
+ return conditions
31
+ }
32
+
33
+ export function scimFilterToWhere(
34
+ conditions: ScimFilterCondition[],
35
+ ssoConfigId: string,
36
+ organizationId: string,
37
+ ): Record<string, unknown> {
38
+ const where: Record<string, unknown> = {
39
+ ssoConfigId,
40
+ organizationId,
41
+ deletedAt: null,
42
+ }
43
+
44
+ for (const { attribute, value } of conditions) {
45
+ switch (attribute) {
46
+ case 'username':
47
+ where.idpEmail = value
48
+ break
49
+ case 'externalid':
50
+ where.externalId = value
51
+ break
52
+ case 'displayname':
53
+ where.idpName = value
54
+ break
55
+ case 'active':
56
+ // Handled at application level (requires SsoUserDeactivation lookup)
57
+ break
58
+ }
59
+ }
60
+
61
+ return where
62
+ }