Files
SharepointToolbox-Web/README.md
T
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

187 lines
10 KiB
Markdown

# 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](#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](#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.
```bash
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`](.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)
```bash
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:
```yaml
environment:
- ASPNETCORE_ENVIRONMENT=Production
- DataFolder=/data
- Oidc__TenantId=...
- Oidc__ClientId=...
- Oidc__ClientSecret=...
- ClientConnect__RedirectUri=https://your-host/connect/callback
```
Plain Docker (no compose):
```bash
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](https://dotnet.microsoft.com/download).
```bash
# 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
```bash
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`](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`.