@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.
- package/dist/index.js +1 -1
- package/dist/index.js.map +2 -2
- package/dist/modules/sso/acl.js +11 -0
- package/dist/modules/sso/acl.js.map +7 -0
- package/dist/modules/sso/api/admin-context.js +27 -0
- package/dist/modules/sso/api/admin-context.js.map +7 -0
- package/dist/modules/sso/api/callback/oidc/route.js +103 -0
- package/dist/modules/sso/api/callback/oidc/route.js.map +7 -0
- package/dist/modules/sso/api/config/[id]/activate/route.js +49 -0
- package/dist/modules/sso/api/config/[id]/activate/route.js.map +7 -0
- package/dist/modules/sso/api/config/[id]/domains/route.js +96 -0
- package/dist/modules/sso/api/config/[id]/domains/route.js.map +7 -0
- package/dist/modules/sso/api/config/[id]/route.js +103 -0
- package/dist/modules/sso/api/config/[id]/route.js.map +7 -0
- package/dist/modules/sso/api/config/[id]/test/route.js +41 -0
- package/dist/modules/sso/api/config/[id]/test/route.js.map +7 -0
- package/dist/modules/sso/api/config/route.js +83 -0
- package/dist/modules/sso/api/config/route.js.map +7 -0
- package/dist/modules/sso/api/error-handler.js +28 -0
- package/dist/modules/sso/api/error-handler.js.map +7 -0
- package/dist/modules/sso/api/hrd/route.js +52 -0
- package/dist/modules/sso/api/hrd/route.js.map +7 -0
- package/dist/modules/sso/api/initiate/route.js +66 -0
- package/dist/modules/sso/api/initiate/route.js.map +7 -0
- package/dist/modules/sso/api/scim/context.js +68 -0
- package/dist/modules/sso/api/scim/context.js.map +7 -0
- package/dist/modules/sso/api/scim/logs/route.js +65 -0
- package/dist/modules/sso/api/scim/logs/route.js.map +7 -0
- package/dist/modules/sso/api/scim/tokens/[id]/route.js +42 -0
- package/dist/modules/sso/api/scim/tokens/[id]/route.js.map +7 -0
- package/dist/modules/sso/api/scim/tokens/route.js +83 -0
- package/dist/modules/sso/api/scim/tokens/route.js.map +7 -0
- package/dist/modules/sso/api/scim/v2/ServiceProviderConfig/route.js +42 -0
- package/dist/modules/sso/api/scim/v2/ServiceProviderConfig/route.js.map +7 -0
- package/dist/modules/sso/api/scim/v2/Users/[id]/route.js +94 -0
- package/dist/modules/sso/api/scim/v2/Users/[id]/route.js.map +7 -0
- package/dist/modules/sso/api/scim/v2/Users/route.js +86 -0
- package/dist/modules/sso/api/scim/v2/Users/route.js.map +7 -0
- package/dist/modules/sso/backend/page.js +173 -0
- package/dist/modules/sso/backend/page.js.map +7 -0
- package/dist/modules/sso/backend/page.meta.js +31 -0
- package/dist/modules/sso/backend/page.meta.js.map +7 -0
- package/dist/modules/sso/backend/sso/config/[id]/page.js +749 -0
- package/dist/modules/sso/backend/sso/config/[id]/page.js.map +7 -0
- package/dist/modules/sso/backend/sso/config/[id]/page.meta.js +19 -0
- package/dist/modules/sso/backend/sso/config/[id]/page.meta.js.map +7 -0
- package/dist/modules/sso/backend/sso/config/new/page.js +381 -0
- package/dist/modules/sso/backend/sso/config/new/page.js.map +7 -0
- package/dist/modules/sso/backend/sso/config/new/page.meta.js +19 -0
- package/dist/modules/sso/backend/sso/config/new/page.meta.js.map +7 -0
- package/dist/modules/sso/data/entities.js +299 -0
- package/dist/modules/sso/data/entities.js.map +7 -0
- package/dist/modules/sso/data/validators.js +114 -0
- package/dist/modules/sso/data/validators.js.map +7 -0
- package/dist/modules/sso/di.js +26 -0
- package/dist/modules/sso/di.js.map +7 -0
- package/dist/modules/sso/events.js +24 -0
- package/dist/modules/sso/events.js.map +7 -0
- package/dist/modules/sso/i18n/de.json +146 -0
- package/dist/modules/sso/i18n/en.json +146 -0
- package/dist/modules/sso/i18n/es.json +146 -0
- package/dist/modules/sso/i18n/pl.json +146 -0
- package/dist/modules/sso/index.js +11 -0
- package/dist/modules/sso/index.js.map +7 -0
- package/dist/modules/sso/lib/domains.js +30 -0
- package/dist/modules/sso/lib/domains.js.map +7 -0
- package/dist/modules/sso/lib/oidc-provider.js +140 -0
- package/dist/modules/sso/lib/oidc-provider.js.map +7 -0
- package/dist/modules/sso/lib/registry.js +15 -0
- package/dist/modules/sso/lib/registry.js.map +7 -0
- package/dist/modules/sso/lib/scim-filter.js +43 -0
- package/dist/modules/sso/lib/scim-filter.js.map +7 -0
- package/dist/modules/sso/lib/scim-mapper.js +49 -0
- package/dist/modules/sso/lib/scim-mapper.js.map +7 -0
- package/dist/modules/sso/lib/scim-patch.js +63 -0
- package/dist/modules/sso/lib/scim-patch.js.map +7 -0
- package/dist/modules/sso/lib/scim-response.js +34 -0
- package/dist/modules/sso/lib/scim-response.js.map +7 -0
- package/dist/modules/sso/lib/scim-utils.js +9 -0
- package/dist/modules/sso/lib/scim-utils.js.map +7 -0
- package/dist/modules/sso/lib/state-cookie.js +67 -0
- package/dist/modules/sso/lib/state-cookie.js.map +7 -0
- package/dist/modules/sso/lib/types.js +1 -0
- package/dist/modules/sso/lib/types.js.map +7 -0
- package/dist/modules/sso/migrations/Migration20260219000000_sso.js +20 -0
- package/dist/modules/sso/migrations/Migration20260219000000_sso.js.map +7 -0
- package/dist/modules/sso/migrations/Migration20260222000000_sso_add_name.js +13 -0
- package/dist/modules/sso/migrations/Migration20260222000000_sso_add_name.js.map +7 -0
- package/dist/modules/sso/migrations/Migration20260222000001_sso_partial_unique_org.js +15 -0
- package/dist/modules/sso/migrations/Migration20260222000001_sso_partial_unique_org.js.map +7 -0
- package/dist/modules/sso/migrations/Migration20260223000000_scim_tables.js +22 -0
- package/dist/modules/sso/migrations/Migration20260223000000_scim_tables.js.map +7 -0
- package/dist/modules/sso/migrations/Migration20260224000000_sso_external_id.js +15 -0
- package/dist/modules/sso/migrations/Migration20260224000000_sso_external_id.js.map +7 -0
- package/dist/modules/sso/migrations/Migration20260224100000_sso_role_grants.js +17 -0
- package/dist/modules/sso/migrations/Migration20260224100000_sso_role_grants.js.map +7 -0
- package/dist/modules/sso/migrations/Migration20260224200000_drop_default_role_id.js +13 -0
- package/dist/modules/sso/migrations/Migration20260224200000_drop_default_role_id.js.map +7 -0
- package/dist/modules/sso/migrations/Migration20260225000000_sso_identities_partial_unique.js +23 -0
- package/dist/modules/sso/migrations/Migration20260225000000_sso_identities_partial_unique.js.map +7 -0
- package/dist/modules/sso/migrations/Migration20260305000000_sso_role_grants_org_id.js +14 -0
- package/dist/modules/sso/migrations/Migration20260305000000_sso_role_grants_org_id.js.map +7 -0
- package/dist/modules/sso/services/accountLinkingService.js +298 -0
- package/dist/modules/sso/services/accountLinkingService.js.map +7 -0
- package/dist/modules/sso/services/hrdService.js +18 -0
- package/dist/modules/sso/services/hrdService.js.map +7 -0
- package/dist/modules/sso/services/scimService.js +372 -0
- package/dist/modules/sso/services/scimService.js.map +7 -0
- package/dist/modules/sso/services/scimTokenService.js +94 -0
- package/dist/modules/sso/services/scimTokenService.js.map +7 -0
- package/dist/modules/sso/services/ssoConfigService.js +254 -0
- package/dist/modules/sso/services/ssoConfigService.js.map +7 -0
- package/dist/modules/sso/services/ssoService.js +125 -0
- package/dist/modules/sso/services/ssoService.js.map +7 -0
- package/dist/modules/sso/setup.js +47 -0
- package/dist/modules/sso/setup.js.map +7 -0
- package/dist/modules/sso/subscribers/user-deleted-cleanup.js +21 -0
- package/dist/modules/sso/subscribers/user-deleted-cleanup.js.map +7 -0
- package/dist/modules/sso/widgets/injection/login-sso/widget.client.js +106 -0
- package/dist/modules/sso/widgets/injection/login-sso/widget.client.js.map +7 -0
- package/dist/modules/sso/widgets/injection/login-sso/widget.js +16 -0
- package/dist/modules/sso/widgets/injection/login-sso/widget.js.map +7 -0
- package/dist/modules/sso/widgets/injection-table.js +14 -0
- package/dist/modules/sso/widgets/injection-table.js.map +7 -0
- package/package.json +5 -4
- package/src/index.ts +1 -1
- package/src/modules/sso/acl.ts +7 -0
- package/src/modules/sso/api/admin-context.ts +36 -0
- package/src/modules/sso/api/callback/oidc/route.ts +115 -0
- package/src/modules/sso/api/config/[id]/activate/route.ts +53 -0
- package/src/modules/sso/api/config/[id]/domains/route.ts +107 -0
- package/src/modules/sso/api/config/[id]/route.ts +114 -0
- package/src/modules/sso/api/config/[id]/test/route.ts +44 -0
- package/src/modules/sso/api/config/route.ts +88 -0
- package/src/modules/sso/api/error-handler.ts +36 -0
- package/src/modules/sso/api/hrd/route.ts +55 -0
- package/src/modules/sso/api/initiate/route.ts +70 -0
- package/src/modules/sso/api/scim/context.ts +85 -0
- package/src/modules/sso/api/scim/logs/route.ts +69 -0
- package/src/modules/sso/api/scim/tokens/[id]/route.ts +45 -0
- package/src/modules/sso/api/scim/tokens/route.ts +89 -0
- package/src/modules/sso/api/scim/v2/ServiceProviderConfig/route.ts +40 -0
- package/src/modules/sso/api/scim/v2/Users/[id]/route.ts +103 -0
- package/src/modules/sso/api/scim/v2/Users/route.ts +94 -0
- package/src/modules/sso/backend/page.meta.ts +29 -0
- package/src/modules/sso/backend/page.tsx +232 -0
- package/src/modules/sso/backend/sso/config/[id]/page.meta.ts +15 -0
- package/src/modules/sso/backend/sso/config/[id]/page.tsx +1024 -0
- package/src/modules/sso/backend/sso/config/new/page.meta.ts +15 -0
- package/src/modules/sso/backend/sso/config/new/page.tsx +463 -0
- package/src/modules/sso/data/entities.ts +240 -0
- package/src/modules/sso/data/validators.ts +140 -0
- package/src/modules/sso/di.ts +25 -0
- package/src/modules/sso/docs/entra-id-setup.md +281 -0
- package/src/modules/sso/docs/google-workspace-setup.md +174 -0
- package/src/modules/sso/docs/sso-overview.md +218 -0
- package/src/modules/sso/docs/sso-security-audit-2026-02-27.md +118 -0
- package/src/modules/sso/docs/zitadel-setup.md +195 -0
- package/src/modules/sso/events.ts +21 -0
- package/src/modules/sso/i18n/de.json +146 -0
- package/src/modules/sso/i18n/en.json +146 -0
- package/src/modules/sso/i18n/es.json +146 -0
- package/src/modules/sso/i18n/pl.json +146 -0
- package/src/modules/sso/index.ts +7 -0
- package/src/modules/sso/lib/domains.ts +31 -0
- package/src/modules/sso/lib/oidc-provider.ts +196 -0
- package/src/modules/sso/lib/registry.ts +13 -0
- package/src/modules/sso/lib/scim-filter.ts +62 -0
- package/src/modules/sso/lib/scim-mapper.ts +88 -0
- package/src/modules/sso/lib/scim-patch.ts +88 -0
- package/src/modules/sso/lib/scim-response.ts +40 -0
- package/src/modules/sso/lib/scim-utils.ts +5 -0
- package/src/modules/sso/lib/state-cookie.ts +79 -0
- package/src/modules/sso/lib/types.ts +50 -0
- package/src/modules/sso/migrations/.snapshot-open-mercato.json +912 -0
- package/src/modules/sso/migrations/Migration20260219000000_sso.ts +21 -0
- package/src/modules/sso/migrations/Migration20260222000000_sso_add_name.ts +13 -0
- package/src/modules/sso/migrations/Migration20260222000001_sso_partial_unique_org.ts +15 -0
- package/src/modules/sso/migrations/Migration20260223000000_scim_tables.ts +24 -0
- package/src/modules/sso/migrations/Migration20260224000000_sso_external_id.ts +15 -0
- package/src/modules/sso/migrations/Migration20260224100000_sso_role_grants.ts +18 -0
- package/src/modules/sso/migrations/Migration20260224200000_drop_default_role_id.ts +13 -0
- package/src/modules/sso/migrations/Migration20260225000000_sso_identities_partial_unique.ts +25 -0
- package/src/modules/sso/migrations/Migration20260305000000_sso_role_grants_org_id.ts +14 -0
- package/src/modules/sso/services/accountLinkingService.ts +386 -0
- package/src/modules/sso/services/hrdService.ts +22 -0
- package/src/modules/sso/services/scimService.ts +461 -0
- package/src/modules/sso/services/scimTokenService.ts +136 -0
- package/src/modules/sso/services/ssoConfigService.ts +337 -0
- package/src/modules/sso/services/ssoService.ts +167 -0
- package/src/modules/sso/setup.ts +56 -0
- package/src/modules/sso/subscribers/user-deleted-cleanup.ts +33 -0
- package/src/modules/sso/widgets/injection/login-sso/widget.client.tsx +130 -0
- package/src/modules/sso/widgets/injection/login-sso/widget.ts +16 -0
- 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,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
|
+
}
|