Skip to content

Configuring SSO Roles

How to map your IdP groups to CampusCore roles so that signed-in users automatically get the right access. Run through this once when standing up SSO for a new institution, then revisit when their group structure changes.

What you need from the institution

Before you start, get from the institution's IT contact:

  1. The exact name of the IdP claim that contains group/role membership. Common names: groups, member_of, roles, urn:oid:1.3.6.1.4.1.5923.1.5.1.1. Microsoft Entra (Azure AD) typically uses groups. Active Directory Federation Services often uses member_of with LDAP DNs.
  2. The exact group values they want to map to CampusCore roles. For LDAP-style providers this means full DNs like CN=Advisors,OU=Staff,DC=vsu,DC=edu. For OIDC providers this is usually a short name like advisors.
  3. Whether their group values are case-sensitive. Active Directory groups are case-insensitive; OIDC group names are usually case-sensitive.

Step 1 — Configure the SSO claim mapping

  1. Log into Django admin at https://<deployment>/cc_admin/django/ as a CampusCore platform admin (superuser).
  2. Open SSO Providers → click the institution's provider.
  3. Scroll to the Map your IdP claims to CampusCore fields section.
  4. Fill in the structured claim columns. Each value is the claim name on the IdP side. The table below is the complete, fixed list of claims CampusCore reads. Institutions choose which claims to release, never which fields exist. For the identity fields (email, first name, last name), blank means the protocol's standard claim names - only fill a column when the institution's IdP uses a non-standard name.
CampusCore field What it's for Example values
Email claim The user's email address. Blank uses the standard names. mail, EmailAddress (SAML); email (OIDC)
External ID claim SAML only - optional. The assertion attribute holding the stable subject ID. Blank links accounts by the NameID. Set before go-live: changing it on a provider with existing users changes how their accounts are linked. OIDC always links by the sub claim. urn:oasis:names:tc:SAML:attribute:subject-id, oid
First name claim Optional. Blank uses the standard names. givenName, firstName (SAML); given_name (OIDC)
Last name claim Optional. Blank uses the standard names. sn, lastName (SAML); family_name (OIDC)
Groups/roles claim Where to look for the user's groups. Leave blank to disable role sync from this provider. groups, member_of, roles
Department claim Optional. Copied to the user's profile on every login. department, ou
Student ID claim Optional. Copied to the user's profile on every login. employeeNumber, student_id
  1. (Optional) Set Default role to whichever CampusCore role users should get when they sign in but don't match any group mapping. Leave it blank to grant nothing to unmatched users - the right choice when, say, alum accounts also sign in through the same IdP.

  2. Save.

Step 2 - Create the group mappings on the Roles page

The mappings are institution self-service: any workspace admin creates them in the app, role by role.

  1. In the app, open Settings → Roles & Permissions.
  2. Expand the role you want the institution's group to grant, and click Map an IdP group.
  3. Provider: The institution's SSO provider. A provider without a groups claim shows an inline warning - go back to Step 1 if you see it.
  4. IdP group: The exact value the IdP returns. Paste it verbatim from what the institution gave you.
  5. Match mode: Pick Exact match for case-sensitive providers (most OIDC). Pick Case-insensitive for Active Directory and other case-insensitive providers.
  6. One IdP group can map to multiple roles (add the same group under each role), and multiple IdP groups can map to the same role (add several rules under one role) - useful when, e.g., both advising_freshman and advising_senior AD groups should grant advisor.
  7. A role flagged Unassigned clears the moment it gets a rule or a manual grant.

The same rules are also editable as a set on the provider's form under Settings → Single Sign-On; the Roles page is the role-centric view of the same table. Deleting a rule that is a role's last way of being assigned, while enabled features are scoped to that role, asks for an explicit confirmation naming the affected features.

Step 3 - Verify with a test user

  1. Find a real user in one of the mapped groups and have them log in.
  2. Open Settings → User Management and search for their email; the role should appear on their row with a "Synced from SSO" tooltip.
  3. Open Settings → Roles & Permissions and expand the role: the member counts show the new SSO member, and the mapping is listed under the provider.
  4. Open Settings → Audit Log and look for the most recent role_sync entry for that user - it shows the added/removed role IDs.

Common gotchas

  • The user logs in but no roles get assigned. Most often the Groups/roles claim is misspelled or the institution sends groups under a nested key. CampusCore looks for the claim in the OIDC userinfo/id_token payload, at the top level, and under SAML attributes/saml_attributes, so make sure the configured claim name matches one of those. Check the AuditLog for a role_sync entry with no_mappings_matched — that means CampusCore saw groups but none matched a mapping.
  • Active Directory DN strings get split into multiple groups. They shouldn't — RoleSyncService detects = in the string and treats it as a single LDAP DN. If you see multiple roles being granted from one DN, file a bug.
  • A user's roles disappear after a known-good login. The IdP might be returning an empty groups array. CampusCore deliberately does NOT wipe roles when the assertion's groups claim is empty (this would lock people out during transient IdP bugs). Check the IdP-side group membership.
  • You add a new mapping but a user doesn't get the new role. Roles only sync at login time. The user has to log out and log back in.
  • A platform admin grants a role manually and the next SSO login wipes it. Manual grants are preserved across SSO syncs as long as they were created with source='manual' (which the Django admin form does automatically). If you're scripting it, set source='manual' explicitly.
  • SAML logins start failing with "signature validation failed" after the institution rotates its IdP certificate. For providers configured with a metadata URL, CampusCore caches the IdP metadata (including the signing cert) for 15 minutes, so a rotation self-heals within that window. To pick it up immediately, re-save the provider, or select it in Django admin and run the "Refresh IdP metadata" action. Providers configured with a pasted certificate need the new cert entered by hand.

Reference: how role sync runs under the hood

On every successful SSO login, CampusCore's user_logged_in signal receiver calls RoleSyncService.sync_user_roles_from_sso. The service:

  1. Reads claim_groups from the SSO provider config.
  2. Extracts the group values from the assertion, normalizing across four shapes (list of strings, comma-separated string, Azure-AD-style list of dicts, LDAP DN string).
  3. Looks up matching SSOGroupMapping rows.
  4. Computes the diff against the user's existing source='sso' UserRole rows.
  5. Applies the diff under select_for_update so concurrent logins don't race.
  6. Logs a role_sync audit entry containing the SSOGroupMapping IDs (never the raw group strings — avoids PII leak).
  7. Cycles the session key on role change.

If anything fails, the failure is logged but the login is allowed to proceed. A broken role sync must never lock users out.

For full design details see docs/project/rbac-and-feature-flags.md.