Compare commits

..

2 Commits

Author SHA1 Message Date
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
kawa 181c82d310 Document certificate (app-only) auth as primary per-profile path
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 11:38:24 +02:00
2 changed files with 67 additions and 15 deletions
+55 -3
View File
@@ -19,7 +19,7 @@ A web admin toolbox for Microsoft 365 / SharePoint Online, built with Blazor Ser
## Configuration ## Configuration
Authentication uses Microsoft OIDC (interactive sign-in) and, for scheduled reports, app-only certificate auth. 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`. Set these as environment variables (or in `appsettings.json` under the `Oidc` section). .NET maps `Section__Key` to `Section:Key`.
@@ -41,8 +41,37 @@ These are separate and registered on **different** Entra apps. Don't conflate th
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`). 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`. → 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 delegated SharePoint/Graph token for a client tenant. A PKCE public-client flow that uses **each connection profile's own `ClientId`/`TenantId`** — not the `Oidc__*` app. The callback for this flow is derived from `App__Domain` as `<domain>/connect/callback`; set `ClientConnect__RedirectUri` to override the full URL directly. 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.
→ 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. → 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. > **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.
@@ -132,3 +161,26 @@ Runs in `Development` mode — OIDC off, auto-login as Admin. No Entra config ne
## Tech stack ## Tech stack
.NET 10 · Blazor Server · Microsoft Graph SDK · PnP.Framework · Serilog · CsvHelper .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`.
+12 -12
View File
@@ -26,19 +26,19 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Azure.Identity" Version="1.14.1" /> <PackageReference Include="Azure.Identity" Version="1.21.0" />
<PackageReference Include="CsvHelper" Version="33.1.0" /> <PackageReference Include="CsvHelper" Version="33.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="10.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="10.0.9" />
<PackageReference Include="Microsoft.Graph" Version="5.74.0" /> <PackageReference Include="Microsoft.Graph" Version="6.2.0" />
<PackageReference Include="Microsoft.Kiota.Abstractions" Version="1.22.2" /> <PackageReference Include="Microsoft.Kiota.Abstractions" Version="2.0.0" />
<PackageReference Include="Microsoft.Kiota.Authentication.Azure" Version="1.22.2" /> <PackageReference Include="Microsoft.Kiota.Authentication.Azure" Version="2.0.0" />
<PackageReference Include="Microsoft.Kiota.Http.HttpClientLibrary" Version="1.22.2" /> <PackageReference Include="Microsoft.Kiota.Http.HttpClientLibrary" Version="2.0.0" />
<PackageReference Include="Microsoft.Kiota.Serialization.Form" Version="1.22.2" /> <PackageReference Include="Microsoft.Kiota.Serialization.Form" Version="2.0.0" />
<PackageReference Include="Microsoft.Kiota.Serialization.Json" Version="1.22.2" /> <PackageReference Include="Microsoft.Kiota.Serialization.Json" Version="2.0.0" />
<PackageReference Include="Microsoft.Kiota.Serialization.Multipart" Version="1.22.2" /> <PackageReference Include="Microsoft.Kiota.Serialization.Multipart" Version="2.0.0" />
<PackageReference Include="Microsoft.Kiota.Serialization.Text" Version="1.22.2" /> <PackageReference Include="Microsoft.Kiota.Serialization.Text" Version="2.0.0" />
<PackageReference Include="PnP.Framework" Version="1.18.0" /> <PackageReference Include="PnP.Framework" Version="1.19.0" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" /> <PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" /> <PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
</ItemGroup> </ItemGroup>