kawa 9480e9537a Bump dependencies to latest and document them
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>
2026-06-26 11:44:48 +02:00
2026-06-02 10:56:03 +02:00
2026-06-02 10:56:03 +02:00
2026-06-02 10:56:03 +02:00

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.

  1. 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). When App__Domain is 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 URI https://your-host/signin-oidc under the Web platform. This app also needs the Graph permissions the audit/reporting features require: GroupMember.Read.All, Group.Read.All, User.Read.All.

  2. 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 the Oidc__* app. That fallback's callback is derived from App__Domain as <domain>/connect/callback; set ClientConnect__RedirectUri to 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-oidc redirect URI to be HTTPS — plain HTTP is allowed only for http://localhost, not a LAN host/IP. To run OIDC on a plain-HTTP LAN deployment, put the app behind an HTTPS-terminating reverse proxy: register https://your-host/signin-oidc, and the app honours X-Forwarded-Proto (see UseForwardedHeaders) to build the correct https redirect. 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__Domain so 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 the Host header makes the app 302 to the internal IP:port it 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.

S
Description
No description provided
Readme 1.2 MiB
Languages
C# 69.2%
HTML 26.4%
CSS 2.6%
JavaScript 1.1%
PowerShell 0.5%
Other 0.2%