There are 4 possible methods, outlined in the table below, for connecting to and displaying content from Microsoft 365 in Carousel Cloud.
Method |
Type of Account + Functionality |
Microsoft Permission |
Who Configures It? |
What the Carousel Cloud Bulletin Editor Sees |
|---|---|---|---|---|
1 |
|
|
|
|
2 |
|
|
|
|
3 |
|
|
|
|
4 |
|
|
|
|
The Carousel authentication types listed in methods 2-4 have no mode switch: the SharePoint Sites field scopes browsing when filled (method 3). If the SharePoint Sites field is left blank, Carousel tries the organization-wide listing and quietly degrades to manual-URL-only when the app can't enumerate sites (method 2 vs 4).
The same app registration can serve both files and calendars.
A note on the tooling used below:
Graph Explorer (https://aka.ms/ge) always runs delegated as whoever signs in. It is great for resolving site/file IDs and making admin grants, but it can never test what the org app itself can see — app-only access is verified with an app-only token (curl or
Connect-MgGraphwith client credentials).PowerShell commands assume
Install-Module PnP.PowerShell/Install-Module Microsoft.Graphwith-Scope CurrentUserhas been run once.
Method 1 — Personal Connected Account
Use Case: The default, lowest-friction path. The document owner signs in once with their own Microsoft account, and Carousel Cloud reads the file through that connection.
Configuration in Microsoft
Usually nothing. The user consents to Carousel's own multi-tenant Entra app at sign-in — no app registration, no admin.
Two exceptions:
Personal Microsoft accounts need no configuration.
-
Tenant restricts user consent (Entra ▸ Enterprise applications ▸ Consent and permissions): the sign-in ends in an approval prompt or an admin-consent error. The
admin can grant tenant-wide consent to Carousel's app when the prompt offers "Consent on behalf of your organization" — or the tenant is de facto an org-account customer (methods 2–4).
Configuration in Carousel Cloud
Open (or create) a Microsoft Word / Excel / PowerPoint bulletin.
In the Connected Account: dropdown under My Accounts, choose Connect Account — a Microsoft pop-up window walks the user through the connection and permissions granting process, then back to Carousel Cloud.
Choose the document from the searchable list. Save. Done — Carousel refreshes the render as the document changes.
Methods (2–4) – Framework for Organization-Wide Configuration
All three organization-wide methods (2-4) start with creating an Entra app registration and one Microsoft account connected in Carousel Cloud.
Complete the following instructions once.
Configuration in Microsoft
-
Complete the steps under the specific following article heading to Register an application with Microsoft Entra ID and create a service principal
A Redirect URI is not required during the app registration process
Record the Application (client) ID and Directory (tenant) ID.
-
Certificates & secrets: create a client secret (record the value immediately) — or, preferred by security teams, a certificate:
openssl req -x509 -newkey rsa:2048 -keyout carousel-graph.key -out carousel-graph.crt -days 730 -nodes -subj "/CN=Carousel Graph Access"
Upload
carousel-graph.crtunder Certificates; what gets pasted into Carousel iscarousel-graph.crtandcarousel-graph.keyconcatenated (both PEM blocks in
one paste). Carousel detects secret vs certificate by content and stores either encrypted at rest. -
In the new app registration you just created, click on:
API permissions
Add a permission
Microsoft Graph
Application permissions
Add the permission for your preferred method (next sections)
Grant admin consent
Note that the Status column must show a green checkmark
Configuration in Carousel Cloud
-
Navigate to Configure > System > Org Accounts and choose Microsoft Graph from the drop-down menu
-
From Entra ID, copy the Tenant ID, Client ID, and Client Secret or PEM Certificate data into these fields
-
To create a certificate valid for 2 years, use the following PowerShell command:
openssl req -x509 -newkey rsa:2048 -keyout carousel-graph.key -out carousel-graph.crt -days 730 -nodes -subj "/CN=Carousel Graph Access"
In Entra, upload the
carousel-graph.crtunder Certificates-
What gets pasted into Carousel is the
carousel-graph.crtandcarousel-graph.keyconcatenated (both PEM blocks in
one paste)
-
If this account will not be used to connect to your Exchange calendars, leave the User Email field blank
-
SharePoint Sites are only required if you intend to narrow the scope of the granted permissions
Click Save
Saving validates the credentials: Carousel acquires a token immediately, so a wrong client/tenant/secret fails at save time, and any URLs in SharePoint Sites are resolved through Graph (an ungranted site fails the save with a per-URL error).
Method 2 — Organizational Account that Browses Everything (Sites.Read.All)
Use Case: For organizations that don't want personal sign-ins, but aren't ‘locked down.’
Requires 1 permission grant, zero per-site configuration.
Configuration in Microsoft
In the app registration's API permissions, add
Sites.Read.All(Application, under Sites) and Grant admin consent.
That's the entire Microsoft setup — no PowerShell, no per-site grants. (Files.Read.All is not needed: it can read file content but cannot list sites, which is the call that drives browsing.)
Configuration in Carousel Cloud
-
A continuation of the previous section’s configuration instructions:
In the Microsoft Graph authentication, leave the SharePoint Sites field blank, and then save.
Press Verify Site Access - With no sites configured, it enumerates the organization sites the app can see (up to 50) and lists them.
An error at this point indicates the missing permission.
In your Microsoft bulletin, select the account under Organization Accounts — the picker lists matching files from every site, with Specific Document… still available.
Method 3 — Organizational Account Using Specific Sites (Sites.Selected)
Use Case: For tenants that forbid broad read scopes but keep signage content in known SharePoint sites. The app registration starts with access to nothing, and then each site is opened by an explicit admin grant.
Configuration in Microsoft
API permissions: add
Sites.Selected(Application, under Sites) + admin consent. By itself, this grants nothing. The next step is what opens specific sites.-
Grant the app registration Read on each site, by an admin, via PnP PowerShell:
Connect-PnPOnline -Url https://TENANT-admin.sharepoint.com -Interactive Grant-PnPAzureADAppSitePermission -AppId "<client-id>" -DisplayName "Carousel Digital Signage" -Site "https://TENANT.sharepoint.com/sites/YourSite" -Permissions Read
Repeat per site. Audit / revoke later with
Get-PnPAzureADAppSitePermission -Site <url>/Revoke-PnPAzureADAppSitePermission.Graph Explorer equivalent (sign in as an admin; consent
Sites.FullControl.Alldelegated in Graph Explorer's Modify permissions tab). Resolve the site ID, then post the grant. Note that site-level grants can take the pluralgrantedToIdentities:GET https://graph.microsoft.com/v1.0/sites/TENANT.sharepoint.com:/sites/YourSite?$select=id POST https://graph.microsoft.com/v1.0/sites/{siteId}/permissions { "roles": ["read"], "grantedToIdentities": [ { "application": { "id": "<client-id>", "displayName": "Carousel Digital Signage" } } ] }
Configuration in Carousel Cloud
-
A continuation of the Overview for Organization-Wide Configuration configuration instructions:
In the Microsoft Graph authentication, enter the granted site URLs, one per line, in the SharePoint Sites field.
Save - Every URL is resolved through Graph at save time, so a typo or a missing grant fails immediately with the offending URL.
Verify Site Access re-resolves the grants on demand (Graph has no "list my
Sites.Selectedgrants" API, so this resolution is the only visibility anyone gets).
In a Microsoft bulletin, select the organizational account — the picker lists files from only the configured site/s document libraries.
Method 4 — Organizational Account with Least Privilege (Per-File Grants)
Use Case: The most restrictive option where the app registration can read only the exact documents an admin has granted, one by one. There is nothing to browse — the Microsoft bulletin names itself by using the document URL.
Configuration in Microsoft
API permissions: add
Files.SelectedOperations.Selected(Application, under Files) + admin consent. By itself, this grants nothing.-
Grant the app read on the individual document. Microsoft Graph PowerShell, using the document's URL:
Connect-MgGraph -Scopes "Sites.FullControl.All" $url = "https://TENANT.sharepoint.com/sites/YourSite/Shared Documents/Flyer.docx" $bytes = [System.Text.Encoding]::UTF8.GetBytes($url) $token = "u!" + [Convert]::ToBase64String($bytes).TrimEnd("=").Replace("/", "_").Replace("+", "-") $item = Invoke-MgGraphRequest -Method GET -Uri "v1.0/shares/$token/driveItem?`$select=id,name,parentReference" $body = @{ roles = @("read"); grantedTo = @{ application = @{ id = "<client-id>"; displayName = "Carousel Digital Signage" } } } Invoke-MgGraphRequest -Method POST -Uri "v1.0/drives/$($item.parentReference.driveId)/items/$($item.id)/permissions" -Body $bodyPlease Note: Unlike the site-level granting, the item-level permissions POST takes a singular
grantedTo— sending the site-stylegrantedToIdentitiesarray fails withinvalidRequest.Graph Explorer Equivalent (admin sign-in,
Sites.FullControl.Alldelegated):GET https://graph.microsoft.com/v1.0/shares/u!<base64url-of-the-doc-url>/driveItem?$select=id,name,parentReference POST https://graph.microsoft.com/v1.0/drives/{driveId}/items/{itemId}/permissions { "roles": ["read"], "grantedTo": { "application": { "id": "<client-id>", "displayName": "Carousel Digital Signage" } } }Revoke later by listing
GET …/items/{itemId}/permissionsandDELETE-ing the app's permission entry.
Configuration in Carousel Cloud
-
A continuation of the Overview for Organization-Wide Configuration configuration instructions:
In the Microsoft Graph authentication, leave the SharePoint Sites field blank. A credentials-only account is valid — it exists to serve bulletins that name their documents directly.
Save
In a Microsoft bulletin, select the organizational account
The file picker contains a single entry: Specific Document… — choose it to reveal the Document URL: field.
Paste the document's link and click Load Document. Any link that points at the file works — a
Copy linksharing link or the address-bar URL — because access comes from the admin's grant, not the link.Success pins the document to the bulletin; an ungranted document fails right there with a clear error (that failure is the grant check).
Troubleshooting the Methods
Granted the permission, but the picker is still empty / Verify still fails: two caches are in play — Entra can take a few minutes to put a new app role into issued tokens, and Carousel caches the org-site/drive resolution (including a 403's empty result) for up to 10 minutes. Wait them out or restart the Carousel API.
Sites.Read.Allshows under API permissions, but app-only calls 403: it was added on the Delegated tab, or admin consent was never clicked. Both look plausible in the portal; the Status column's green check on the Application row is what counts.Graph Explorer works, but Carousel doesn't: Graph Explorer ran as you (delegated). Re-test with the app-only token from method 2's curl.
Site grant POST fails with
invalidRequest: you used singulargrantedToon a site (needsgrantedToIdentities) or the plural form on an item (needsgrantedTo).Personal sign-in ends in an admin-approval screen: the tenant restricts user consent — use an org method, or have the admin consent on behalf of the organization.
Excel file won't render sheets/charts: the workbook APIs are
.xlsx-only; legacy.xlsisn't supported (Word/PPT legacy binaries are fine — they go through PDF conversion).
Additional Notes
There is no file picker for the Selected methods because picking cannot grant access. Google pairs its Picker with the narrow
drive.filescope, so picking a file is the grant. Microsoft has no equivalent (investigated 2026-08-28): the OneDrive File Picker v8 is UI-only — it runs on the app's existing token and needs a broadFiles.Read.All-class scope just to function, so embedding it would change no permissions (this gap was publicized in 2025 security research, Oasis Security's "OneDrive File Picker flaw"). Microsoft's per-file mechanism is the*.Selectedfamily, and that grant is always an explicitPOST …/permissionsby an admin holdingSites.FullControl.All— no end user self-grant exists. Hence method 4's shape: admin grants the file, the bulletin names it by URL.These four methods are the only paths. A fifth "public file, no auth" method can't exist — there is no anonymous Microsoft Graph, and the V2 types render through Graph (PDF conversion, workbook APIs, thumbnails), so some credential always exists. The old V1 bulletin types (paste an "Anyone with the link" URL, screenshot the public Office web viewer) are the closest thing and are deprecated support workarounds: they lose PDF fidelity, page selection, and Excel sheet/chart selection, and security-conscious tenants disable anonymous links anyway.
Graph cannot enumerate
Sites.Selectedgrants. There is no "list my granted sites" call, which is why the granted URLs are configuration on the authentication, why resolving each URL doubles as grant verification, and why Verify Site Access is the only visibility anyone gets.*.Selectedpermissions grant nothing by themselves. ConsentingSites.SelectedorFiles.SelectedOperations.Selectedopens zero sites/files; the PowerShell/Graph
grant is the real authorization. ConverselySites.Read.Allneeds no grants at all.Links never carry access in the org methods. A pasted URL only identifies the document (via the
u!share-token encoding); whether Carousel can read it is decided entirely by the admin's grants. So "People with existing access" links from hardened tenants work fine, and forwarding a link grants nothing.One authentication serves files and calendars. The same MicrosoftGraph entry can also drive Event Display calendars (
Calendars.Readapplication + the User Email
field); files and calendars don't need separate app registrations.Personal accounts have a lifecycle risk org accounts don't: the connection breaks when its owner leaves or their token is revoked (see
wiki/concepts/concept-connected-account-revocation.md). Org accounts only break on secret/cert expiry — there are no expiry warnings today, so a lapsed credential surfaces as render/listing errors and a failed Verify Site Access.Fresh files appear at different speeds per method. MSA personal and all app-only listings read actual drive state via delta (a file created seconds ago shows up); M365 personal uses the search index, which can lag briefly for brand-new files.
Tuning knobs live in the options table:
API/MicrosoftOrgSiteLimit(org-wide site ceiling, default 50) andAPI/MicrosoftSharedDriveLimit(personal shared-drive ceiling, default 50). All Graph traffic is rate-limited per account key — app-only traffic is bucketed asapp:{authenticationId}so it never contends with personal connections.