docs: Clarify groups_attribute placement for SAML group sync

What does this MR do?

GitLab reads groups_attribute at the provider level of gitlab_rails['omniauth_providers'], as a sibling of name and label. Placed inside the provider's args hash it is never read, so SAML group sync does not run. Nothing surfaces the mistake: users sign in normally, the assertion still carries the groups, and no error or warning is written.

The documentation does not state the rule. doc/integration/saml.md says to "add a groups_attribute: element to your SAML settings", which does not distinguish the provider level from args. doc/user/group/saml_sso/group_sync.md shows a correct example but does not name the rule the example embodies, so a reader adapting their own configuration has nothing to check against. There is no troubleshooting entry for the resulting symptom.

This MR:

  • Adds a placement callout in doc/integration/saml.md where groups_attribute is introduced, covering the rest of the provider-level family too (required_groups, external_groups, admin_groups, auditor_groups).
  • Names the placement rule beside the existing GitLab Self-Managed configuration example in doc/user/group/saml_sso/group_sync.md.
  • Adds a troubleshooting entry in doc/user/group/saml_sso/troubleshooting.md for the symptom "group links configured, assertion carries the groups, nothing syncs", with the cause, the fix, and a log check to confirm the fix took.

Why the behavior is silent

On master:

  • lib/gitlab/auth/saml/config.rb resolves the setting as def groups; options[:groups_attribute]; end, where options is Gitlab::Auth::OAuth::Provider.config_for(@provider), the provider hash itself. A key inside args is therefore unreachable.
  • ee/lib/ee/gitlab/auth/saml/config.rb gates group_sync_enabled? on groups.present?.
  • ee/lib/gitlab/auth/saml/membership_updater.rb enqueues Auth::SamlGroupSyncWorker only when that gate passes. There is no else branch and no log line, so a misplaced key produces no output at all.

Reproduction

The behavior follows from the master source quoted above and does not turn on a release. I also confirmed it by measurement, on a GitLab 19.x Enterprise Edition Linux package installation with an Ultimate license, against a single SAML provider emitting a multi-valued groups attribute, with seven SAML group links configured across several top-level groups.

Configuration that does not work:

gitlab_rails['omniauth_providers'] = [
  {
    name: 'saml',
    label: 'Example SSO',
    args: {
      assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
      idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8',
      idp_sso_target_url: 'https://idp.example.com/protocol/saml',
      issuer: 'https://gitlab.example.com',
      name_identifier_format: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
      groups_attribute: 'groups'
    }
  }
]

Observed: sign-in succeeds and creates the account. The assertion carries the groups attribute with the expected values. audit_json.log records the system_access sign-in entry and contains no Auth::SamlGroupSyncWorker entries. Group memberships are unchanged. No error is shown to the user and no warning is logged.

Configuration that works, differing only in where groups_attribute sits:

gitlab_rails['omniauth_providers'] = [
  {
    name: 'saml',
    label: 'Example SSO',
    groups_attribute: 'groups',
    args: {
      assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
      idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8',
      idp_sso_target_url: 'https://idp.example.com/protocol/saml',
      issuer: 'https://gitlab.example.com',
      name_identifier_format: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'
    }
  }
]

Observed after sudo gitlab-ctl reconfigure and the same sign-in: memberships appear exactly as the group links define them, Auth::SamlGroupSyncWorker entries appear in audit_json.log, and a later group removal at the identity provider propagates to GitLab membership removal at the next sign-in.

Related to #534702 Related to #388185

#388185 proposes defaulting the value so the setting is harder to get wrong, and states that groups_attribute "can be misplaced". This MR documents the current behavior and does not close that proposal.

Author's checklist

If you are a GitLab team member and only adding documentation, do not add any of the following labels:

  • ~"frontend"
  • ~"backend"
  • ~"type::bug"
  • ~"database"

These labels cause the MR to be added to code verification QA issues.

Reviewer's checklist

Documentation-related MRs should be reviewed by a Technical Writer for a non-blocking review, based on Documentation Guidelines and the Style Guide.

If you aren't sure which tech writer to ask, use roulette or ask in the #docs Slack channel.

  • If the content requires it, ensure the information is reviewed by a subject matter expert.
  • Technical writer review items:
    • Ensure docs metadata is present and up-to-date.
    • Ensure the appropriate labels are added to this MR.
    • Ensure a release milestone is set.
    • If relevant to this MR, ensure content topic type principles are in use, including:
      • The headings should be something you'd do a Google search for. Instead of Default behavior, say something like Default behavior when you close an issue.
      • The headings (other than the page title) should be active. Instead of Configuring GDK, say something like Configure GDK.
      • Any task steps should be written as a numbered list.
      • If the content still needs to be edited for topic types, you can create a follow-up issue with the docs-technical-debt label.
  • Review by assigned maintainer, who can always request/require the reviews above. Maintainer's review can occur before or after a technical writer review.

AI-Generated Content Disclosure: This MR was prepared with assistance from Claude Code. Every factual claim was verified against master source, both configurations were confirmed on a GitLab Self-Managed instance, and the wording was checked against the documentation style guide.

Merge request reports

Loading