feat: implement SSO group synchronization for SAML and OIDC

- Add is_group_sync_enabled column to auth_providers table
- Extract groups from SAML attributes (memberOf, groups, roles)
- Extract groups from OIDC claims (groups, roles)
- Implement case-insensitive group matching with auto-creation
- Sync user groups on each SSO login
- Ensure only one provider can have group sync enabled at a time
- Add group sync toggle to SAML and OIDC configuration forms
This commit is contained in:
Philipinho
2025-07-10 01:31:31 -07:00
parent 29388636bf
commit 837ab802a0
6 changed files with 49 additions and 1 deletions

View File

@ -16,6 +16,7 @@ const ssoSchema = z.object({
oidcClientSecret: z.string().min(1, "Client secret is required"),
isEnabled: z.boolean(),
allowSignup: z.boolean(),
isGroupSyncEnabled: z.boolean(),
});
type SSOFormValues = z.infer<typeof ssoSchema>;
@ -36,6 +37,7 @@ export function SsoOIDCForm({ provider, onClose }: SsoFormProps) {
oidcClientSecret: provider.oidcClientSecret || "",
isEnabled: provider.isEnabled,
allowSignup: provider.allowSignup,
isGroupSyncEnabled: provider.isGroupSyncEnabled || false,
},
validate: zodResolver(ssoSchema),
});
@ -67,6 +69,9 @@ export function SsoOIDCForm({ provider, onClose }: SsoFormProps) {
if (form.isDirty("allowSignup")) {
ssoData.allowSignup = values.allowSignup;
}
if (form.isDirty("isGroupSyncEnabled")) {
ssoData.isGroupSyncEnabled = values.isGroupSyncEnabled;
}
await updateSsoProviderMutation.mutateAsync(ssoData);
form.resetDirty();
@ -119,6 +124,15 @@ export function SsoOIDCForm({ provider, onClose }: SsoFormProps) {
/>
</Group>
<Group justify="space-between">
<div>{t("Group sync")}</div>
<Switch
className={classes.switch}
checked={form.values.isGroupSyncEnabled}
{...form.getInputProps("isGroupSyncEnabled")}
/>
</Group>
<Group justify="space-between">
<div>{t("Enabled")}</div>
<Switch