Skip to content

User Management

User management illustration

The SCF Controls Platform uses role-based access control to manage what users can do within the application. This guide covers user roles, permissions, and management tasks.

Before managing users:

  • A sign-in method must be enabled — OIDC single sign-on, the bundled Keycloak, or Google Sign-In (see Authentication)
  • You must have Admin role to manage users
  • Users must sign in at least once to appear in the system

The platform supports three roles with different permission levels:

Full access to all features

PermissionDescription
Manage users and rolesInvite users, assign roles, remove access
Invite new usersAdd users to the organisation
Remove usersRemove users from the organisation
Create, edit, delete controlsFull control management
Create, edit, delete evidenceFull evidence management
Manage tasks and assignmentsTask and assignment management
Create, edit, archive teamsDefine the organisation’s teams and their functions
Manage team membershipAdd and remove members, set primary and delegate
Set member typeRecord a member as internal or an external contractor
View all data and reportsAccess all dashboards and exports
Configure organisation settingsSystem configuration

Can edit content but not manage users

PermissionDescription
Create, edit, delete controlsFull control management
Create, edit, delete evidenceFull evidence management
Manage tasks and assignmentsTask and assignment management
Add comments and mentionsParticipate in discussions
View teams and membershipSee who owns what — read-only
View member typeSee which members are external contractors — read-only
View all data and reportsAccess dashboards and exports

Editors are ideal for GRC analysts who need to manage compliance content but should not administer users.

Read-only access

PermissionDescription
View controls and evidenceBrowse all content
View tasks and assignmentsSee task status
View teams and membershipSee who owns what — read-only
View member typeSee which members are external contractors — read-only
View reports and dashboardsAccess reports
Add commentsParticipate in discussions

Viewers can see everything but cannot modify content. Suitable for stakeholders who need visibility without editing capabilities.


  1. Sign in with an Admin account
  2. Click the Users icon in the sidebar (person icon)
  3. The User Management page displays all organisation members

The user list shows:

ColumnDescription
UserAvatar, name, and email from the sign-in profile (Google or your identity provider)
RoleCurrent assigned role (dropdown selector)
TypeInternal or Contractor — a dropdown for admins, plain text for everyone else. See Internal Members and External Contractors
JoinedDate the user joined the organisation
ActionsRemove button

People join an organisation by invitation. An admin sends the invite; the invitee accepts by opening the invitation link and signing in.

  1. Click the Invite User button
  2. Enter the user’s email address
  3. Select a role for the new user
  4. Set Employment TypeInternal - permanent staff or Contractor - works for you under a contract. It defaults to internal, travels with the invitation, and is applied to their membership when they accept
  5. Click Send Invite
  6. The invitee receives an email invitation to join. You can also copy the invitation link from the modal and send it yourself

What the invite does to their sign-in account

Section titled “What the invite does to their sign-in account”

This is the part that differs by deployment.

On the bundled Keycloak — the platform running docker compose --profile idp, with KC_ADMIN_USER, KC_ADMIN_PASSWORD and OIDC_DISCOVERY_URL / OIDC_ISSUER configured for the backend — the invite also creates the Keycloak account. The backend looks the email up in the realm: if it is absent it creates the user (username = the email address, enabled, email marked verified, required action Update Password) and sets a generated temporary password; if the user already exists in Keycloak nothing is changed and no password is set.

On a bring-your-own OIDC install, or a Google-only install — nothing changes. The platform does not create accounts in an external identity provider. Create the account in your IdP (or confirm the person has a Google account, on Google installs) and invite them here; the invite links that identity to the organisation when they first sign in.

Each outstanding invitation shows a badge saying which of the above applied:

BadgeMeaning
IdP accountAn account exists in the bundled Keycloak — created by the invite, or already there before it
No IdP accountThe bundled Keycloak is enabled, but no account exists for this email
External IdPProvisioning is disabled — a bring-your-own OIDC or Google install, where the IdP owns accounts

The invitee opens the invitation link — from the email, or the one you copied from the modal — and signs in. On OIDC deployments the invite page offers the identity provider’s Sign in button rather than Google; on Google deployments it offers Sign in with Google. They join with the role and employment type the invitation carried, and an admin can change both afterwards.

Cancelling an outstanding invitation removes the platform-side record. On the bundled Keycloak it also deletes the Keycloak account, but only when the invite created that account and the person has never signed in to the platform. In every other case — an identity that already existed, or somebody who has signed in at least once — the IdP account is left alone.

Keycloak accounts the platform creates or deletes are recorded in the audit log under entity type idp_user.

  1. Find the user in the list
  2. Click the role dropdown next to their name
  3. Select the new role (Admin, Editor, or Viewer)
  4. The change is saved automatically

The role change takes effect immediately on the user’s next action.

Every membership carries a type alongside its role: the person is either internal to your organisation or an external contractor. It exists to answer a question auditors ask directly and most control registers cannot — is any of this owned by somebody who does not work here?

  1. Open User Management from the sidebar (Operations group)
  2. Find the person in the user list
  3. Choose Internal or Contractor from the dropdown in the Type column on their row
  4. The change saves immediately

Only Admins can change a member’s type, the same as changing a role. Editors and Viewers still see the value — it renders as plain text in the same column rather than disappearing, so the badges elsewhere in the platform do not look as though they came from nowhere. They simply cannot change it.

Type and role are independent. Changing one leaves the other exactly as it was — setting somebody to external contractor does not touch their role, and changing their role does not touch their type. They are two separate decisions about the same membership, and the platform treats them that way.

That independence matters most when two admins are working at once. Changing somebody’s type sends only the type, so it cannot overwrite a role another admin changed a moment earlier — your screen being slightly out of date does not silently undo their work.

Every change is written to the Audit Log (Admin group in the sidebar): who was changed, from which type to which, by whom, and when. Each field is logged separately, so changing somebody’s role and type together produces two entries rather than one. Contractor involvement is exactly the kind of fact an auditor asks to see the history of rather than the current state of, and the log is what answers “was this control owned by a contractor in March?” after the engagement has ended and the record has been tidied.

You do not have to wait until somebody has accepted. The Invite User dialog carries the type alongside the role, so an admin decides both in the same breath — and the type is applied to the membership at the moment the person accepts. A contractor is recorded as one from their first sign-in rather than after somebody notices and goes back to fix it.

An invitation that says nothing means internal, in exactly the way an invitation that says nothing about a role means Viewer. The default is a default, not a statement — it is what the platform assumes in the absence of a decision, and it is why every membership that predates this feature is internal too.

Admins can see what a pending invitation will produce — the list of outstanding invitations shows the type alongside the role, so an invitation sent last week does not have to be taken on trust. The invitee does not see it before accepting; the page they land on names the role they are being offered and nothing about employment type.

Nothing about it is permanent. Any admin can change a member’s type from their row at any time after they have joined, so getting it wrong at invite time costs nothing: somebody invited as internal who turns out to be a contractor is one dropdown away from correct, and the correction is written to the audit log like any other change. Invite on your best understanding and adjust when you know better — that is cheaper, and more honest, than leaving the field blank in your head until somebody asks.

Every membership that already existed when this feature was installed became internal.

Nothing was inferred. Not from email domains, not from the free-text owner labels on controls, not from any other field — no rule anywhere on the platform reads a value and decides somebody is a contractor. That is deliberate: an email domain is a lousy proxy for an employment relationship in both directions, and an organisation guessing wrongly at scale would produce a contractor report that looks authoritative and is not.

Organisations state their own contractors, one membership at a time. So the first thing to do after upgrading is a pass down the user list marking the people who actually are external. Until you do, the report below is accurate about what you have recorded, and silent about what you have not.

The badge reads Contractor, and it renders wherever that person appears as an owner, an assignee, or a team member — the user list, team membership tables, the pending-invitation list, and the assignment surfaces on controls and evidence items. Anyone in the organisation can see it; it is not an admin-only view. Hovering it repeats what it means and what it does not: a label only — it grants and restricts nothing.

Internal members get no badge at all. The absence of one is the ordinary case, not a claim that somebody is on the payroll — an “Internal” badge on every row would be noise that makes the one row that matters harder to find.

In dropdown menus, where a badge cannot be drawn, the person’s name carries the same word as a suffix instead — Ana Ruiz (Contractor). Same word, same meaning, so the two surfaces cannot drift into looking like two different claims about the same person.

The point is that contractor involvement is visible at the moment it matters rather than only in a report somebody has to think to run. Naming an external contractor as a team’s primary is allowed, and plenty of organisations do it deliberately — but the badge is on screen while that choice is being made, so it is a decision rather than an oversight.

Controls and evidence can be filtered to those whose accountable team’s primary owner is an external contractor.

That sentence is precise, and each qualifier is doing work:

  • The accountable team — not merely an assigned one. A control may be owned by several teams (see Owning teams), but exactly one of them can be accountable, and that is the team whose name is on the control when it fails. A contractor team consulted on a control is not the same finding as a contractor team answerable for it.
  • That team’s primary — not any member of it. The primary is the single named owner; contractors elsewhere in the team are contributing, not accountable.

It is deliberately not “any member of any assigned team is a contractor”. A contractor sitting in a team that was consulted on a control is not answerable for that control, and counting them would overstate contractor exposure in the exact report somebody reaches for in order to size it.

So an item is not returned when it has no team assignment at all, when its owning teams include none marked accountable, or when the accountable team has no primary. All three hops are required.

The filter offers three choices — All Owner Types, Contractor-owned and Internally owned — so it answers the opposite question just as readily: what are our own staff accountable for? On the controls list it sits inside the ⚙ Filters dropdown with the team and function filters and counts towards the badge on the button; on the evidence list it sits on the filter bar beside them.

It is a lens, not a restriction. Appearing in this report changes nothing about what anybody can see or do — it is there so that “is any of this owned by a contractor?” has an answer you can produce in the meeting rather than reconstruct afterwards.

To remove a user from the organisation:

  1. Find the user in the list
  2. Click the Remove button (trash icon)
  3. Confirm the deletion when prompted

Choose roles based on user responsibilities:

User TypeRecommended RoleRationale
GRC Program ManagerAdminNeeds to manage team and all features
GRC AnalystEditorDay-to-day compliance work
IT Security StaffEditorImplements and updates controls
Department ManagerViewerReviews compliance status
Auditor (internal)ViewerReviews evidence and reports
Executive StakeholderViewerDashboard access only

The User Management page includes an expandable Role Permissions Reference panel:

  1. Click Role Permissions Reference to expand
  2. View detailed permissions for each role
  3. Use this as a quick reference when assigning roles

Teams answer a question every audit eventually asks and most control registers cannot: who owns this, and who covers for them? An organisation defines its own teams, maps each one to a business function, and names the people on it. The team is a durable record — it outlives the individuals on it, so ownership history survives staff changes.

A function is a business capability that compliance work can belong to — Security Operations, Legal, Finance, and so on. The platform ships a fixed list of fourteen, identical in every organisation and every deployment:

KeyName
governance_risk_complianceGovernance, Risk & Compliance
security_operationsSecurity Operations
security_engineeringSecurity Engineering
it_operationsIT Operations
software_engineeringSoftware Engineering / DevSecOps
identity_access_managementIdentity & Access Management
data_privacyData Protection & Privacy
human_resourcesHuman Resources
legalLegal
financeFinance
procurement_vendor_managementProcurement & Vendor Management
facilities_physical_securityFacilities & Physical Security
business_continuityBusiness Continuity & Resilience
executive_leadershipExecutive Leadership

Functions are read-only. You cannot add, rename or remove them from the application — the list is seeded by the platform, and extending it is a platform release decision rather than an organisation setting. That is deliberate: because the list is shared, “Security Operations” means the same thing in every organisation and in every environment, so anything built on top of it stays portable.

Your own vocabulary lives in team names, not function names. If your organisation calls it “Blue Team”, create a team called Blue Team and map it to Security Operations. A team that spans responsibilities can serve more than one function.

  1. Open User Management from the sidebar (Operations group)
  2. Find the Team Management card, alongside the user list
  3. Click New Team
  4. Enter a name — it must be unique within your organisation
  5. Add one or more functions the team delivers. Pick each one from the Add a business function… list; it appears as a chip below, with an × to take it off again. The first function added is the team’s primary grouping. Functions that are no longer active are listed but cannot be added.
  6. Add an optional description — what this team is actually responsible for
  7. Click Create

Creating, editing and archiving teams requires the Admin role. Any organisation member can view teams and their membership.

Each team serves one or more functions, and a function may be delivered by several teams. This lets both cross-functional and federated organisations describe themselves honestly without inventing functions or duplicate teams:

TeamFunction
EMEA Security OperationsSecurity Operations
APAC Security OperationsSecurity Operations
Group Privacy OfficeData Protection & Privacy
Platform EngineeringSoftware Engineering / DevSecOps; Security Engineering

Both security operations teams map to the same function, so a report grouped by function still sees one Security Operations capability, while day-to-day ownership stays with the right regional team.

A team’s functions can be changed later if you reorganise. Expand the team and use the same chips: add from the list, remove with the ×. Nothing is sent while you edit — Save functions applies the change and Cancel puts it back — so a mis-click costs nothing. At least one function must remain, so the × on the last chip is disabled; to swap a team’s only function, add the replacement first, then remove the old one. If a team still holds a function that has since been retired, that chip stays visible, marked (inactive), and can still be removed. The team, its members and its history stay intact.

Membership Roles: Primary, Delegate, Member

Section titled “Membership Roles: Primary, Delegate, Member”

Every membership carries exactly one role:

RoleLimit per teamMeaning
PrimaryAt most oneThe single named owner. The person answerable for the team’s work.
DelegateAt most oneThe deputy. Covers when the primary is unavailable.
MemberNo limitContributes to the team’s work; not individually accountable for it.

Why a primary and a delegate? Accountability that names one person is clear but fragile — that person takes leave, changes role, or leaves the company, and the ownership record quietly becomes wrong at exactly the moment it matters. The delegate is the documented answer to “and if they are not available?”. Without it, coverage lives in somebody’s head and cannot be shown to an auditor, who will ask both questions: who owns this, and who covers for them.

These roles are properties of the membership, not of the person. The same user can be the primary of one team and an ordinary member of another. Nothing about the primary role follows a person around the platform, and nothing about it changes what they are permitted to do — see the note at the top of this section.

  1. Open the team from the Team Management card
  2. Click Add Member
  3. Choose a user from your organisation’s existing members
  4. Choose their role — primary, delegate or member
  5. Save

To change a role, edit the membership and pick a different role. To remove someone from a team, remove the membership — this affects that team only and does not touch their organisation account or any other team.

Only users who are already members of your organisation can be added to a team. If someone is missing from the picker, add them to the organisation first (see Inviting Users).

Because a team can have at most one primary and at most one delegate, assigning a second one is rejected. Change or remove the existing holder first, then assign the new one.

Members who are external contractors carry a contractor badge on their row here, primary and delegate included. The badge changes nothing about the membership — the role means exactly what it means for anyone else — but it puts the fact on screen while you are deciding who is answerable for the team, rather than in a report somebody runs later.

A team with no members, or with members but no primary, is perfectly legal. The platform shows a warning badge rather than blocking you.

That is intentional. Half-formed teams are a normal step in setting the structure up — you sketch the shape of the organisation first and fill in the names as you confirm them. Blocking on a complete team would only push people into inventing placeholder owners, which is worse than an honest gap. Treat the badges as a to-do list: a team without a primary has nobody answerable for it, and that is worth resolving before an audit rather than during one.

Remove the person from the organisation as normal, from the user list. Their team memberships go with them — all of them, across every team, automatically. This is enforced in the database itself rather than by application cleanup, so there is no path by which a departed user is left sitting in a team.

What survives is everything that should: the teams themselves, their names, their functions, their remaining members, and the audit record of who was on them and when.

If the person was a team’s primary, that team is now without one and shows its warning badge — a visible prompt to name a successor rather than a silent gap.

Deleting a team archives it. The team is marked inactive; it is never destroyed.

  1. Open the team from the Team Management card
  2. Click Delete
  3. Confirm

Archived teams drop out of the default team list and can be shown again with the include-inactive option. Their name stays reserved, so recreating a team with the same name is not possible while the archived one exists — rename or reactivate instead.

Archiving rather than deleting exists so that history keeps resolving. A team named in March’s audit trail must still resolve to a real team in December, long after the reorganisation that retired it.

Teams can be assigned to individual controls and evidence items, with one team marked accountable for each. That is described in Control Management — the part that concerns administrators is what those assignments do when the team structure changes underneath them.

Archiving a team does not release its work. Archiving marks the team inactive; it does not delete it, and every control and evidence item that named it still names it afterwards. That is the point — a control does not silently become unowned because somebody tidied the team list. Before archiving a team that owns work, reassign that work to the team taking it over. Otherwise the ownership record is intact but points at a team nobody is staffing, which is worse than an obvious gap because it does not look like one.

Removing a user does not change any assignment. Assignments name teams, not people, so a departure removes that person’s memberships and leaves every control and evidence item owned exactly as before. The team is now short a person — and short a primary, if it was theirs — but nothing became unowned.

Assignments cannot cross organisations. A control can only be assigned a team from its own organisation, and that is enforced by the database rather than by the application, so there is no sequence of API calls that produces a cross-tenant assignment.

Every team and membership change is recorded in the Audit Log (Admin group in the sidebar): team created, renamed, remapped to a different function or archived; member added, role changed or removed — each with the user who did it and when.

Team assignments are recorded there too: a team given ownership of a control or evidence item, made accountable, stood down, or removed. When accountability moves from one team to another, both halves are logged — the promotion and the demotion it caused — so “who was accountable for this in March?” has an answer even after several handovers.

This is what makes the ownership record defensible rather than merely current. “Who owned this in March?” has an answer, and it is not somebody’s memory.


Admins and Editors can assign controls to users:

  1. Navigate to Control Scoping
  2. Select a control
  3. Click Assign in the detail panel
  4. Select a user from the dropdown
  5. Click Save

Assigned users receive visibility into their responsibilities but assignments don’t restrict editing.

Similar to controls, evidence items can be assigned:

  1. Navigate to the Evidence Workspace
  2. Select an evidence item
  3. Set the Owner field
  4. Click Save

Tasks support explicit assignment:

  1. Create or edit a task
  2. Set Assigned To field
  3. User sees task in My Tasks view

  1. Start with Viewer — Assign Viewer initially, upgrade as needed
  2. Limit Admins — Keep Admin count minimal (2-3 per organisation)
  3. Match responsibilities — Align roles with job functions
  4. Review periodically — Audit user roles quarterly
  1. Onboarding — Provide new users with role-appropriate training
  2. Role changes — Document when and why roles change
  3. Offboarding — Remove users promptly when they leave
  1. Assign appropriate roles — Roles are enforced, so the role you assign is the access you grant
  2. Audit trail — All actions are logged with user attribution
  3. Least privilege — Give users minimum access they’ll need

  1. Check the badge on their pending invite. No IdP account on a bundled-Keycloak install means KC_ADMIN_USER / KC_ADMIN_PASSWORD were not configured for the backend when they were invited — cancel the invite and re-invite, or create the user in the Keycloak admin console. External IdP means the account has to exist in your own IdP
  2. Verify the sign-in method itself is working — see Authentication and, for SSO deployments, Identity Provider
  3. Have the user clear their browser cache and try again
  4. Check they are signing in with the correct account for that identity provider
  1. Navigate to User Management
  2. Find the user in the list
  3. Use the role dropdown to change their role
  4. Change takes effect immediately

Users must sign in at least once to appear in User Management. If a user has signed in but doesn’t appear:

  1. Check browser console for authentication errors
  2. Have the user sign out and sign in again
  3. Ensure the user signed into the correct organisation
  1. Check spam/junk folder
  2. Verify email address was entered correctly
  3. Try re-sending the invitation
  4. Contact support if issues persist

The platform supports multiple users working simultaneously with automatic data synchronisation.

Three mechanisms:

MechanismBehaviour
Change pollingEvery 20 seconds, asks the server whether anything changed. Does not refetch data — it only raises the “Updates available” flag
Focus refetchSwitching back to the tab refetches the queries currently on screen
Manual refreshThe refresh button in the header, or the R key. This is the only thing that reloads everything
  • “Updated 3m ago” — time since your last refresh
  • “Updates available”, with a dot — the poll found a change

For Real-Time Collaboration:

  1. Switch away and back to the tab to refetch what is on screen
  2. Refresh before making critical changes — the button, or R
  3. Watch for “Updates available”: it means someone else has changed something you may be looking at a stale copy of

For Team Workflows:

  1. Coordinate with team — Use Slack/Teams to communicate who’s editing what
  2. Work in different domains — Multiple users can edit different control domains simultaneously
  3. Refresh before bulk operations — Click refresh, then select all/deselect all

The platform uses last-write-wins strategy:

  • Simple and works well for most GRC workflows
  • If two users edit the same control simultaneously, the last save wins
  • The 20-second change poll surfaces a conflict quickly, though it is up to you to act on it

For a compliance tool, this approach is typically sufficient since:

  • Controls are usually edited by different teams
  • Changes are incremental rather than wholesale replacements
  • Users can coordinate through comments and assignments