# LEAP Calendar Sync Permission Investigation — slarionova@grabblaw.com **Date (UTC):** 2026-05-04 **Tenant:** grabblaw.com (`032b383e-96e4-491b-880d-3fd3295672c3`) — Grabb & Durando, P.C. **User:** Svetlana Larionova (`slarionova@grabblaw.com`, `affab40c-5535-4c1a-9a78-a2eda1a4a3b7`) **Issue:** "Not able to add calendar event from Leap to M365 — no admin permissions" **Investigator:** Howard Enos (read-only Graph via Security Investigator app) --- ## TL;DR **She does NOT need an M365 admin role.** The error is a misnamed OAuth consent block — Leap's sign-in app asks for scopes (Mail.ReadWrite, Mail.Send, Files.ReadWrite.All) that the tenant's user-consent policy classifies as "high-risk" and reserves for admin approval. Five of her co-workers (4 with no admin roles at all, 1 with User Administrator) already have these grants because an admin previously approved them per-user. She just needs the same per-user approval — not an elevated role. **Recommended fix:** Have Svetlana run through Leap's M365 connect again and click "Request approval from your administrator." Then approve the request in Entra → Enterprise Applications → Admin consent requests, scoped to her user only. --- ## Findings ### 1. User account is healthy - Account enabled: `true` - License: `O365_BUSINESS_PREMIUM` (Exchange, SharePoint, Teams all enabled) - Mailbox settings reachable (English-US, MST timezone, autoreply off) - Group memberships: only `Grabb & Durando, P.C.` distribution group - **Directory roles: NONE** (correctly, no admin role) - Existing OAuth grants: **0** ### 2. Two LEAP service principals are registered in this tenant | App ID | Display | Role | Consent state | |---|---|---|---| | `5602fc50-4c30-4faa-a595-e5a0f15d2cce` | LEAP (service) | App-only/daemon — runs as the app, not a user | Tenant-wide app-permission consent already granted (Calendars/Mail/Files via Application roles on Graph + EXO + SharePoint) | | `a7d19842-33e2-457b-a399-d4e6ec010f0a` | LEAP (delegated) | User sign-in — runs as the signed-in user | Per-user (`consentType=Principal`) grants for 5 users only | ### 3. The 5 users who already have working Leap calendar sync | User | Admin role? | |---|---| | jsosa@grabblaw.com (Jeannette Sosa) | None | | rpesqueira@grabblaw.com (Reyna Pesqueira) | None | | avazquez@grabblaw.com (Ana Vazquez) | None | | yheredia@grabblaw.com (Yvette Heredia) | None | | jwilliams@grabblaw.com (Jeff Williams) | User Administrator (incidental) | Each holds a `Principal`-type OAuth grant on the LEAP delegated app with the full scope set: ``` Calendars.Read Calendars.ReadWrite Mail.Read Mail.ReadWrite Mail.Send Contacts.Read Tasks.Read Tasks.ReadWrite OnlineMeetings.ReadWrite ChannelMessage.Send ChannelMessage.Edit Chat.Create Chat.ReadWrite ChatMessage.Send Files.ReadWrite.All User.Read User.ReadBasic.All offline_access email profile openid ``` The per-user grant is what makes Leap → calendar work. **No admin role is required to hold the grant.** ### 4. Tenant user-consent policy `policies/authorizationPolicy` shows: - `permissionGrantPoliciesAssigned`: - `ManagePermissionGrantsForSelf.microsoft-user-default-recommended` - `ManagePermissionGrantsForSelf.microsoft-user-default-allow-consent-apps` Under the **recommended** baseline, users may self-consent to apps from verified publishers, but only for "low-risk" delegated permissions. `Mail.ReadWrite`, `Mail.Send`, `Files.ReadWrite.All` are explicitly classified as **not** low-risk → admin approval required. This is what produces the "no admin permissions" message in Leap. ### 5. Why she's the only one stuck She was hired/onboarded after the previous batch of 5 users was approved. The other consents were granted point-in-time (per-user), so a new user has to go through the same approval again. This is not a policy regression — it's the steady-state pattern in this tenant. --- ## Recommended Fix (least-privilege) ### Option A — Admin Consent Request (preferred, matches existing pattern) 1. Have Svetlana sign in to Leap, click **Connect to Microsoft 365 / Enable calendar sync**. 2. When she hits the "approval required" page, she clicks **Request approval from your administrator** and submits a short justification. 3. The admin (`sysadmin@grabblaw.com` or the `guru@grabblaw.com` Global Admin) approves at: `https://entra.microsoft.com → Identity → Applications → Enterprise applications → Admin consent requests` 4. Choose **Approve for this user only** (this matches what the other 5 employees have). 5. Leap calendar sync starts working immediately. Net effect: a single new `oauth2PermissionGrant` row tied to her object ID with `consentType=Principal`. No role change. No tenant-wide impact. ### Option B — Switch to tenant-wide admin consent (broader, easier going forward) If new hires keep tripping over this, an admin can grant the LEAP delegated app **tenant-wide** consent once: `https://entra.microsoft.com → Enterprise applications → LEAP (appId a7d19842-33e2-457b-a399-d4e6ec010f0a) → Permissions → Grant admin consent for Grabb & Durando` Trade-off: ALL users get those scopes automatically (including new hires). The existing pattern in this tenant is per-user, so this is a deliberate change in posture — not a fix. Worth considering if Leap is the firm's standard practice management tool and everyone needs it. ### What NOT to do - Do **not** assign her any directory role (Global Admin, Exchange Admin, etc.). It would not fix this — the error is OAuth consent, not RBAC. A user with no admin role can hold the grant, as 4 of the 5 working users prove. --- ## Evidence Artifacts Raw JSON in `/tmp/remediation-tool/032b383e-96e4-491b-880d-3fd3295672c3/sla-check/`: - `user.json` — slarionova profile + license - `memberOf.json`, `transitive.json` — group/role membership (no admin roles) - `grants.json` — her OAuth grants (empty) - `sp-leap.json` — both LEAP SPs found in tenant - `sp-{spid}-grants.json` (via direct query) — current consent state on each LEAP SP - `skus.json` — license definitions - `ga-role.json`, `exo-role.json` — directory role lookups ## Tools used - App tier: `investigator` (Graph read-only) — `bfbc12a4-f0dd-4e12-b06d-997e7271e10c` - Auth: cert (client_assertion JWT) via `get-token.sh` - No write actions performed.