Update 13 NuGet packages, including major bumps: Microsoft.Graph 5.74.0 -> 6.2.0, Microsoft.Kiota.* 1.22.2 -> 2.0.0, and Serilog.AspNetCore 9.0.0 -> 10.0.0. The set is internally consistent (Graph 6.2.0 -> Graph.Core 4.0.1 -> Kiota 2.0.0) and builds clean with no code changes. Add a Dependencies table to the README listing each package, its pinned version, and purpose. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
10 KiB
SharePoint Toolbox
A web admin toolbox for Microsoft 365 / SharePoint Online, built with Blazor Server (.NET 10) and Microsoft Graph.
Features
- Site management — bulk site creation, folder-structure provisioning, templates
- Members & permissions — bulk member add, permission inspection
- Content tools — search, duplicate finder, file transfer, storage usage, version cleanup
- Reporting — on-demand reports, scheduled reports (unattended via app-only cert auth)
- Auditing — tenant-wide user-access audit (SP + M365/AAD group expansion)
- Directory — user directory browsing
- Multi-tenant via connection profiles. EN / FR localization.
Requirements
- An Entra ID (Azure AD) app registration — see Configuration
- Docker, or the .NET 10 SDK for bare-metal
Configuration
Signing into the toolbox itself uses Microsoft OIDC (or a local account). Per-profile SharePoint/Graph access uses certificate (app-only) auth as the primary path — the same app identity drives both technician work and scheduled reports, so technicians never sign in per profile. Profiles without a certificate fall back to an interactive per-profile sign-in.
Set these as environment variables (or in appsettings.json under the Oidc section). .NET maps Section__Key to Section:Key.
| Variable | Description |
|---|---|
Oidc__TenantId |
Entra tenant GUID |
Oidc__ClientId |
App registration client ID |
Oidc__ClientSecret |
App registration client secret |
App__Domain |
Public domain the app is reached at, e.g. sptb.example.com or https://sptb.example.com (scheme defaults to https). Pins the OIDC sign-in redirect (/signin-oidc) and derives the SharePoint-connect redirect URI. |
DataFolder |
Persistent data path (default /data) |
ASPNETCORE_ENVIRONMENT |
Must be Production to enable OIDC |
In
Development, OIDC is disabled — the app uses a cookie-only auto-login (hardcoded Admin) for local work.
Two distinct OAuth flows — two redirect URIs
These are separate and registered on different Entra apps. Don't conflate them.
-
App sign-in (OIDC). Logging into the toolbox itself via "Sign in with Microsoft". Uses the
Oidc__*app above. Callback path is the framework default/signin-oidc(not configurable here). WhenApp__Domainis set, the redirect is pinned to<domain>/signin-oidc; otherwise it's derived from the request host (X-Forwarded-Host/Host). → On this app registration, add redirect URIhttps://your-host/signin-oidcunder the Web platform. This app also needs the Graph permissions the audit/reporting features require:GroupMember.Read.All,Group.Read.All,User.Read.All. -
SharePoint connect (per-profile). Getting a SharePoint/Graph token for a client tenant. The primary path is certificate (app-only) auth (see below), which needs no redirect URI. Profiles without a certificate fall back to an interactive PKCE public-client flow that uses each connection profile's own
ClientId/TenantId— not theOidc__*app. That fallback's callback is derived fromApp__Domainas<domain>/connect/callback; setClientConnect__RedirectUrito override the full URL directly. → Only when using the interactive fallback: on each client-tenant profile's app registration, add that callback value (e.g.https://your-host/connect/callback) under the Mobile and desktop / public client platform. See Per-profile app registration permissions below for the API permissions it needs.
Per-profile app registration permissions
The Register App action in a profile provisions this registration for you (single tenant, public client) and grants org-wide admin consent automatically. The list below documents what it creates, for manual registration or auditing.
Per-profile access uses certificate (app-only) auth as the primary path: the app identity drives both interactive technician work and unattended features (scheduled reports, tenant-wide audits), so technicians never sign in per profile. The cert is provisioned alongside the registration and attached as a sign-in credential. The registration grants application permissions for that app-only auth; it also keeps delegated scopes for the interactive connect flow that profiles without a certificate fall back to.
Application permissions (app-only certificate auth — primary):
| API | Permission | Used for |
|---|---|---|
| Microsoft Graph | User.Read.All |
Look up users (unattended) |
| Microsoft Graph | Group.ReadWrite.All |
Read group members; add members/owners (unattended) |
| Microsoft Graph | Directory.Read.All |
Expand M365/AAD group membership in the user-access audit |
| Microsoft Graph | Sites.FullControl.All |
Site operations (unattended) |
| Microsoft Graph | Mail.Send |
Send emailed scheduled reports |
| SharePoint | Sites.FullControl.All |
App-only CSOM |
Delegated permissions (interactive connect flow — only for profiles without a certificate):
| API | Permission | Used for |
|---|---|---|
| Microsoft Graph | User.Read |
Signed-in user's basic profile |
| Microsoft Graph | User.Read.All |
Look up users by email/UPN |
| Microsoft Graph | Group.ReadWrite.All |
Read group members; add members/owners |
| Microsoft Graph | Sites.Read.All |
Resolve a site's groupId from its siteId |
| SharePoint | AllSites.FullControl |
CSOM — site permissions, content, admin operations |
All permissions above require admin consent.
HTTPS note. The sign-in app is a confidential (Web) client, so Entra requires its
/signin-oidcredirect URI to be HTTPS — plain HTTP is allowed only forhttp://localhost, not a LAN host/IP. To run OIDC on a plain-HTTP LAN deployment, put the app behind an HTTPS-terminating reverse proxy: registerhttps://your-host/signin-oidc, and the app honoursX-Forwarded-Proto(seeUseForwardedHeaders) to build the correcthttpsredirect. Without a proxy, OIDC sign-in won't work over a non-localhost HTTP host — use the local email/password login instead.
Reverse-proxy host. Set
App__Domainso the app builds every redirect (cookie login, OIDC) against the public domain regardless of what host the proxy forwards. Without it, a proxy that doesn't forward theHostheader makes the app 302 to the internalIP:portit actually received.
Persistent state (profiles, settings, templates, logs, exports, certs) lives in DataFolder.
Installation — Docker (prebuilt image)
Pulls the published image from the Gitea registry — no local build needed.
cp .env.example .env # then edit .env with your OIDC values
docker compose -f docker-compose.prebuilt.yml pull
docker compose -f docker-compose.prebuilt.yml up -d
The compose file reads config from .env (see .env.example). Pin a
version with SPTB_TAG, e.g. SPTB_TAG=v1.2.0 in .env. Don't quote values — the
list form embeds literal quotes and breaks OIDC discovery.
Installation — Docker (build locally)
docker compose up -d --build
App listens on http://localhost:8080. Data persists in the sptb-data volume.
Set your OIDC values in docker-compose.yml under environment:, or pass an env file:
environment:
- ASPNETCORE_ENVIRONMENT=Production
- DataFolder=/data
- Oidc__TenantId=...
- Oidc__ClientId=...
- Oidc__ClientSecret=...
- ClientConnect__RedirectUri=https://your-host/connect/callback
Plain Docker (no compose):
docker build -t sptb-web .
docker run -d -p 8080:8080 \
-v sptb-data:/data \
-e ASPNETCORE_ENVIRONMENT=Production \
-e Oidc__TenantId=... \
-e Oidc__ClientId=... \
-e Oidc__ClientSecret=... \
-e ClientConnect__RedirectUri=https://your-host/connect/callback \
sptb-web
Installation — Bare metal
Requires the .NET 10 SDK.
# Restore + build
dotnet restore
dotnet publish -c Release -o ./publish
# Configure (PowerShell example)
$env:ASPNETCORE_ENVIRONMENT = "Production"
$env:DataFolder = "C:\sptb-data"
$env:Oidc__TenantId = "..."
$env:Oidc__ClientId = "..."
$env:Oidc__ClientSecret = "..."
$env:ClientConnect__RedirectUri = "https://your-host/connect/callback"
# Run
dotnet ./publish/SharepointToolbox.Web.dll
By default it listens on the Kestrel port (http://localhost:5000). Override with ASPNETCORE_URLS, e.g. http://+:8080.
Local development
dotnet run
Runs in Development mode — OIDC off, auto-login as Admin. No Entra config needed.
Tech stack
.NET 10 · Blazor Server · Microsoft Graph SDK · PnP.Framework · Serilog · CsvHelper
Dependencies
NuGet packages and their pinned versions (see SharepointToolbox.Web.csproj). Last reviewed 2026-06-26.
| Package | Version | Purpose |
|---|---|---|
Azure.Identity |
1.21.0 | Entra ID token credentials — app-only certificate and client-secret auth |
CsvHelper |
33.1.0 | CSV parsing for bulk import/export (sites, members, folder structures) |
Microsoft.AspNetCore.Authentication.OpenIdConnect |
10.0.9 | OIDC "Sign in with Microsoft" app sign-in |
Microsoft.Graph |
6.2.0 | Microsoft Graph SDK — users, groups, sites, mail |
Microsoft.Kiota.Abstractions |
2.0.0 | Graph SDK request runtime (abstractions) |
Microsoft.Kiota.Authentication.Azure |
2.0.0 | Graph SDK Azure auth provider |
Microsoft.Kiota.Http.HttpClientLibrary |
2.0.0 | Graph SDK HTTP transport |
Microsoft.Kiota.Serialization.Form |
2.0.0 | Graph SDK form serialization |
Microsoft.Kiota.Serialization.Json |
2.0.0 | Graph SDK JSON serialization |
Microsoft.Kiota.Serialization.Multipart |
2.0.0 | Graph SDK multipart serialization |
Microsoft.Kiota.Serialization.Text |
2.0.0 | Graph SDK text serialization |
PnP.Framework |
1.19.0 | SharePoint CSOM — site permissions, content, admin operations |
Serilog.AspNetCore |
10.0.0 | Structured logging integration |
Serilog.Sinks.File |
7.0.0 | Rolling-file log sink |
To check for newer releases: dotnet list package --outdated.