OIDC: Update README.md #5334

Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
Michael Mayer 2025-11-27 11:52:54 +01:00
parent 12e06fe7d3
commit 9d378f2041

View file

@ -1,6 +1,6 @@
## PhotoPrism — OIDC Integration
**Last Updated:** November 23, 2025
**Last Updated:** November 27, 2025
### Overview
@ -53,18 +53,79 @@
### Security Group Extension for Entra ID
- [x] Parse `groups` claim (string GUIDs) from ID/Access tokens and map to roles using a configurable mapping, mirroring LDAP group handling.
- [x] Detect `_claim_names.groups` overage markers; optionally fetch memberships from Microsoft Graph (`/me/transitiveMemberOf`) behind a feature flag and enforced timeouts.
- [x] Keep `roles`/`wids` (app and directory roles) separate from security groups; document precedence.
- [x] Add unit tests with signed JWT fixtures covering: direct `groups` array, overage marker, and no-groups fallback.
- [x] Expose config knobs (e.g., `AuthOIDCGroupsToRoles`, Graph fetch enable/disable, request timeout) and surface them in CLI/`options.yml` reports.
- [ ] Add integration doc/tests for Entra app registration requirements (`groupMembershipClaims=SecurityGroup|All|ApplicationGroup`) and token size limits (~200 groups).
- [ ] Update Pro parity notes so LDAP and OIDC group mappings share helpers and behavior.
The following features are supported by the current implementation:
> **Note:** Entra ID security groups are only supported in PhotoPrism® Pro.
- Reads security groups from the `groups` claim in ID or access tokens; accepts GUIDs or names (case-insensitive, sanitized via `NormalizeGroupID`).
- Optional required membership: `--oidc-group` (or `PHOTOPRISM_OIDC_GROUP`) lists one or more groups that must be present; login is rejected if none match. If the token signals overage via `_claim_names.groups` and contains no groups, login is denied with an audit entry explaining that membership could not be validated.
- Group-to-role mapping: `--oidc-group-role` (`GROUP=ROLE`, repeatable) assigns the first matching role; falls back to `--oidc-role` (default `guest`) when no mapping matches.
- Keeps app/directory roles (`roles`, `wids`) separate from security groups to avoid accidental privilege escalation.
- Claim name is configurable via `--oidc-group-claim` (default `groups`).
Configuration options:
- `--oidc-group-claim` / `PHOTOPRISM_OIDC_GROUP_CLAIM`: claim to read (default `groups`).
- `--oidc-group` / `PHOTOPRISM_OIDC_GROUP`: comma- or multi-flag list of groups required for login (IDs or names accepted, normalized to lowercase alphanumerics/hyphen/underscore).
- `--oidc-group-role` / `PHOTOPRISM_OIDC_GROUP_ROLE`: mapping `GROUP=ROLE` (roles: `admin|manager|user|contributor|viewer|guest|none`). First match wins.
- `--oidc-role` / `PHOTOPRISM_OIDC_ROLE`: fallback role if no group mapping matches (defaults to `guest`).
Integration Guide for Entra ID:
1. Register an app in Microsoft Entra ID (v2) or reuse your existing PhotoPrism registration. Note the tenant ID and the application (client) ID.
2. Redirect URI: add `https://app.localssl.dev/api/v1/oidc/redirect` (for Traefik) or `http://localhost:2342/api/v1/oidc/redirect` for local dev.
3. Token configuration → **Add optional claim****Token type** = ID (and Access if you prefer) → **Groups** → choose **Security groups**.
4. Under “Emit groups as”, pick **Group name** (cloud-only) or **sAMAccountName** / **DNSDomainName\sAMAccountName** for synced AD; this makes tokens carry human-friendly names instead of GUIDs.
5. If you keep **Group ID**, leave PhotoPrism config in GUID mode; if you emit names, set `PHOTOPRISM_OIDC_GROUP` / `PHOTOPRISM_OIDC_GROUP_ROLE` to those names (lowercase in config for consistency).
6. Grant admin consent for the chosen scopes (at minimum `openid profile email`, plus `offline_access` if you need refresh tokens).
7. Configure PhotoPrism (example `.env-oidc` with placeholder secrets):
```
PHOTOPRISM_OIDC_URI="https://login.microsoftonline.com/f8b10857-a7f2-49ba-b73c-6f619715f574/v2.0"
PHOTOPRISM_OIDC_CLIENT="11111111-2222-3333-4444-555555555555"
PHOTOPRISM_OIDC_SECRET="asecure-random-oidc-client-secret"
PHOTOPRISM_OIDC_GROUP_CLAIM="groups"
PHOTOPRISM_OIDC_GROUP="photoprism-admins, photoprism-users" # names or GUIDs
PHOTOPRISM_OIDC_GROUP_ROLE="photoprism-admins=admin, photoprism-users=user"
```
8. Restart PhotoPrism; on login the service will:
- Read groups from ID token, then fall back to userinfo if absent.
- Deny login if required groups are configured but none are present (and overage is signaled).
- Apply the first matching group→role mapping; otherwise assign the fallback role.
Please note:
- Entra ID security groups are only supported in PhotoPrism® Pro.
- If tokens still contain GUIDs, revisit Token configuration → Groups and change “Emit groups as” to a name format; reissue tokens by signing out/in. Names must be unique in your tenant for deterministic mapping.
- Overage: when the `_claim_names.groups` marker is present and no groups are in the token, PhotoPrism cannot validate membership and will block login if `oidc-group` is set. (Graph-based resolution is described in the next section but is not yet implemented.)
- For mixed environments, you can supply both names and GUIDs in `oidc-group` / `oidc-group-role`; all entries are normalized and deduplicated.
#### Microsoft Graph API
Future enhancements may include translating Entra security group GUIDs to display names, as well as fetching full membership lists when tokens omit groups:
- Resolve GUID → display name so `--oidc-group` / `--oidc-group-role` can use human-friendly group names while still matching IDs.
- Fetch memberships via Microsoft Graph when `_claim_names.groups` signals overage or when the token only carries IDs.
- Deduplicate and merge token groups with Graph results; continue to fall back gracefully if Graph is unavailable.
Implementation outline:
- Config: add flags/options such as `oidc-graph-lookup` (enable), `oidc-graph-timeout` (default ~35s), `oidc-graph-mode` (`client` for Client Credentials, `obo` for On-Behalf-Of), and optional scope override (default `https://graph.microsoft.com/.default`). Surface in flags, reports, and `options.yml`.
- Token acquisition:
- Client Credentials flow using the existing OIDC client ID/secret against the tenant token endpoint with Graph scope; requires admin-consented Application permission `Group.Read.All`.
- On-Behalf-Of flow exchanging the user access token plus the same secret; requires Delegated `Group.Read.All` consent.
- Graph calls:
- Prefer a single batch or `/v1.0/me/transitiveMemberOf?$select=id,displayName` to retrieve security groups; filter to `@odata.type` that ends with `group`.
- Optionally fall back to `/v1.0/groups/{id}?$select=id,displayName` when only a few IDs need resolution.
- Processing: normalize both `id` and `displayName`, cache GUID→name mappings with a short TTL, merge into the existing group set, then apply required-group and group→role mapping logic.
- Testing: add httptest fixtures for token exchange and Graph responses, covering timeouts, 401/403, and partial data.
Impact:
- Allows administrators to configure PhotoPrism with recognizable group names instead of GUIDs.
- Makes log/debug output more readable and reduces reliance on Azure portal lookups for GUIDs.
- Provides a path to honor group-based access when tokens exceed size limits and omit groups by default.
### Documentation & References
- Microsoft Entra ID: https://www.microsoft.com/en-us/security/business/identity-access/microsoft-entra-id
- Microsoft Entra group claims: https://learn.microsoft.com/en-us/entra/identity-platform/access-token-claims-reference#groups-claim
- Group overage handling: https://learn.microsoft.com/en-us/entra/identity-platform/howto-add-app-roles-in-azure-ad-apps#group-overage-and-_claim_names
- Token customization guidance: https://learn.microsoft.com/en-us/entra/architecture/customize-tokens