Files
claudetools/.claude/skills/remediation-tool/references/app-permissions-and-sharepoint.md
Mike Swanson 6f7f939a62 sync: auto-sync from GURU-5070 at 2026-07-01 09:32:17
Author: Mike Swanson
Machine: GURU-5070
Timestamp: 2026-07-01 09:32:17
2026-07-01 09:33:09 -07:00

6.8 KiB

App permissions (authoritative) + SharePoint access

Purpose: stop the recurring "I can't do this / access denied" friction. The ComputerGuru app suite has broad, working access across M365 — Graph, Exchange Online, Defender, AND SharePoint Online. When a call fails, the cause is almost always one of: wrong tier, wrong endpoint for the scope we hold, a not-consented tenant (AADSTS7000229), or the SharePoint secret-vs-cert gotcha below — NOT a genuine lack of access.

Discipline: before ever telling the user "the tool can't do X", verify against reality. Decode the token's roles claim and check it against this map:

TOK=$(bash scripts/get-token.sh <tenant> <tier>)
python - "$TOK" <<'PY'   # or: cut -d. -f2 | base64 -d | jq .roles
import sys,base64,json
p=sys.argv[1].split('.')[1]; p+='='*(-len(p)%4)
print(json.dumps(json.loads(base64.urlsafe_b64decode(p)).get('roles',[]),indent=2))
PY

Live-verified application-permission map (decoded 2026-07-01, Birth Biologic tenant)

Application (app-only) roles actually present in each app's token. Fetch live to confirm for a given tenant (consent can differ), but this is the baseline the apps are built to.

Tier (get-token.sh) App Resource Key application roles
investigator Security Investigator Graph Directory.Read.All, User.Read.All, AuditLog.Read.All, Application.Read.All, Organization.Read.All, Policy.Read.All, Sites.Read.All, Mail.Read, MailboxSettings.Read, BitlockerKey.Read.All, IdentityRiskyUser.ReadWrite.All, IdentityRiskEvent.ReadWrite.All, IdentityRiskyServicePrincipal.ReadWrite.All, IdentityRiskyAgent.ReadWrite.All, UserAuthenticationMethod.Read.All
investigator-exo Security Investigator Exchange Online EXO read (Get-InboxRule, Get-Mailbox, Get-*)
exchange-op Exchange Operator Exchange Online EXO write (Set-Mailbox, Remove-InboxRule, revoke sessions)
user-manager User Manager Graph Directory.ReadWrite.All, Group.ReadWrite.All, User.ReadWrite.All, Device.ReadWrite.All, User.RevokeSessions.All, UserAuthenticationMethod.ReadWrite.All, Organization.Read.All
tenant-admin Tenant Admin Graph Application.ReadWrite.All, AppRoleAssignment.ReadWrite.All, RoleManagement.ReadWrite.Directory, Directory.ReadWrite.All, Policy.ReadWrite.ConditionalAccess, Sites.FullControl.All, Sites.ReadWrite.All, User.ReadWrite.All, SecurityEvents.Read.All, Policy.Read.All, UserAuthenticationMethod.ReadWrite.All
tenant-admin Tenant Admin SharePoint Online Sites.FullControl.All (SharePoint resource 00000003-0000-0ff1-ce00-000000000000) — the SharePoint tiers use this
defender Defender Add-on Defender ATP MDE machine/alert actions (MDE-licensed tenants only)
intune-manager Intune Manager Graph DeviceManagement* (Intune)
mailbox ACG Mailbox Graph ACG-INTERNAL ONLY — Mail.ReadWrite/Send, Contacts.ReadWrite

Notes:

  • CA policies ARE manageable (tenant-admin holds Policy.ReadWrite.ConditionalAccess + the Conditional Access Administrator role) — report-only + break-glass exclusion first (see SKILL scope boundaries).
  • Directory settings (M365 Group creation restriction, etc.) are writable via user-manager (Directory.ReadWrite.All) or tenant-admin.

SharePoint Online — the two gotchas

  1. SharePoint app-only REQUIRES a certificate. A client_secret token is rejected with "Unsupported app only token" on every SharePoint endpoint (REST /_api/... and CSOM /_vti_bin/client.svc/ProcessQuery). The Tenant Admin app has a cert in the vault (cert_thumbprint_b64url + cert_private_key_pem_b64); get-token.sh forces cert for the SharePoint tiers automatically. If you hand-roll a token, use the cert (client_assertion), not the secret.
  2. The Graph endpoint GET /admin/sharepoint/settings needs SharePointTenantSettings.Read.All, which NONE of the apps currently hold — so that route returns accessDenied. That is NOT "no SharePoint access". Use the SharePoint CSOM/REST admin API (cert token, Sites.FullControl.All) instead — that path has full tenant-settings read/write. (If you'd rather use the Graph route, add SharePointTenantSettings.Read.All/.ReadWrite.All to the Tenant Admin app manifest and re-consent — patch-tenant-admin-manifest.sh + the tenant admin-consent URL.)

SharePoint tiers (get-token.sh)

# Tenant admin resource ("<name>-admin.sharepoint.com") — tenant settings, site provisioning
TOK=$(bash scripts/get-token.sh <tenant> sharepoint-admin)
# Content resource ("<name>.sharepoint.com") — site/list/file operations
TOK=$(bash scripts/get-token.sh <tenant> sharepoint)
# Host is auto-resolved via Graph /sites/root; override if needed:
SP_RESOURCE_ENV=contoso.sharepoint.com bash scripts/get-token.sh <tenant> sharepoint-admin

Read tenant settings (e.g. site-creation) via CSOM

ADMIN="https://<name>-admin.sharepoint.com"
TOK=$(bash scripts/get-token.sh <tenant> sharepoint-admin)
BODY='<Request xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009" SchemaVersion="15.0.0.0" LibraryVersion="16.0.0.0" ApplicationName="ACG"><Actions><ObjectPath Id="2" ObjectPathId="1"/><Query Id="3" ObjectPathId="1"><Query SelectAllProperties="true"><Properties/></Query></Query></Actions><ObjectPaths><Constructor Id="1" TypeId="{268004ae-ef6b-4e9b-8425-127220d84719}"/></ObjectPaths></Request>'
curl -s -X POST "$ADMIN/_vti_bin/client.svc/ProcessQuery" \
  -H "Authorization: Bearer $TOK" -H "Content-Type: text/xml" --data-binary "$BODY" \
  | jq -r '.[] | select(type=="object") | to_entries[]
           | select(.key|test("SiteCreation|SelfService|Sharing";"i")) | "\(.key) = \(.value)"'

Relevant tenant properties: SelfServiceSiteCreationDisabled (false = users CAN create sites), AllowClassicPublishingSiteCreation, SharingCapability. Writing a setting is the same CSOM surface with a SetProperty action (gate behind explicit YES, like any remediation write).

Modern site creation = M365 Group creation

A modern team/communication site creates an M365 Group, gated by the Entra directory setting Group.Unified -> EnableGroupCreation / GroupCreationAllowedGroupId. Read via Graph GET /groupSettings (empty result = defaults = anyone can create). Write via user-manager (Directory.ReadWrite.All): POST/PATCH /groupSettings using the Group.Unified template. So fully restricting employee site creation = (a) SelfServiceSiteCreationDisabled=true (CSOM) and (b) restrict Group.Unified group creation (Graph). Neither touches edit rights on an existing site.


First documented 2026-07-01 (Mike). Trigger: Syncro #32492 (Birth Biologic SharePoint site-creation lockdown) surfaced the secret-vs-cert + no-SharePoint-tier gaps. Added sharepoint/sharepoint-admin tiers to get-token.sh the same day.