What's Changed
- Adopt secure development process and repository hygiene by @iderex in #3
- Bump softprops/action-gh-release from 50195ba7f6f93d1ac97ba8332a178e008ad176aa to 132dbbba49e22bd4837f09b46d32a8edaf6baa58 by @dependabot[bot] in #11
- Bump Kevinjil/jellyfin-plugin-repo-action from 0.4.0 to 0.4.3 by @dependabot[bot] in #4
- Bump creyD/prettier_action from 4.3 to 4.6 by @dependabot[bot] in #5
- Bump actions/checkout from 2 to 7 by @dependabot[bot] in #6
- Bump actions/setup-dotnet from 1 to 5 by @dependabot[bot] in #7
- Bump actions/download-artifact from 5.0.0 to 8.0.1 by @dependabot[bot] in #8
- Bump actions/upload-artifact from 4.6.2 to 7.0.1 by @dependabot[bot] in #9
- Bump fjogeleit/yaml-update-action from 0.10.0 to 0.17.0 by @dependabot[bot] in #10
- Bump Duende.IdentityModel.OidcClient from 6.0.1 to 7.1.0 by @dependabot[bot] in #12
- Bump Jellyfin.Model from 10.11.0 to 10.11.11 by @dependabot[bot] in #14
- Bump System.Security.Cryptography.Xml from 9.0.13 to 10.0.9 by @dependabot[bot] in #16
- Bump Newtonsoft.Json from 13.0.3 to 13.0.4 by @dependabot[bot] in #15
- Add xUnit test foundation with SAML validation regression tests by @iderex in #18
- Remove unconfigured Black Duck security scan workflow by @iderex with @Copilot in #19
- Fix thread-unsafe OpenID state store by @iderex in #21
- Remove unconfigured APIsec scan workflow by @iderex in #23
- Harden SAML assertion time validation (fail-closed) by @iderex in #25
- Bind SSO logins to existing accounts fail-closed by @iderex in #27
- Constrain server-side avatar fetch to public http(s) targets by @iderex in #29
- Validate SAML assertion AudienceRestriction by @iderex in #31
- Enforce one-time use of SAML assertions (replay protection) by @iderex in #33
- Harden the avatar fetch from Copilot review feedback by @iderex in #35
- Polish SAML validation and fix relayState log forging by @iderex in #37
- Add least-privilege permissions to CI workflows by @iderex in #39
- Pin third-party GitHub Actions to commit SHAs by @iderex in #41
- Tidy the account-link switch and exception (Copilot review) by @iderex in #43
- Harden the SAML test factory and OpenID state add (Copilot review) by @iderex in #45
- Rework the README into a truthful landing page by @iderex in #47
- Link the wiki from the README by @iderex in #48
- Refine the README AI note and name the sibling project by @iderex in #50
- Serialize plugin-configuration read-modify-writes by @iderex in #52
- Add a vulnerable-dependency CI gate by @iderex in #54
- Enforce the SAML login role allow-list at the session-minting endpoint by @iderex in #55
- Bind OpenID authorize state to its provider and make it single-use by @iderex in #56
- Verify Jellyfin compatibility in CI (ABI consistency + packaging) by @iderex in #58
- Require modern signature algorithms in SAML validation by @iderex in #59
- Add Copilot custom instructions for code review by @iderex in #62
- Resolve SonarCloud findings across web assets and the compat script by @iderex in #64
- Remove stray comment token breaking the checkbox-wrapper rule by @iderex in #67
- Use optional chaining in the server-response filter by @iderex in #65
- Scope SonarCloud analysis to source, excluding img assets by @iderex in #71
- Build the account-linking DOM without HTML interpolation by @iderex in #68
- Document the mandatory two-reviewer PR process in CODEOWNERS by @iderex in #72
- Run SonarCloud analysis in CI instead of Automatic Analysis by @iderex in #73
- Import test coverage into the SonarCloud analysis by @iderex in #74
- Fix delete-button contrast and drop dead filter-bubble CSS by @iderex in #75
- Fail closed on a missing user and namespace SerializableDictionary by @iderex in #77
- Hoist and time-box the role-claim split regex by @iderex in #78
- Encode the auth-completion data value as a JS string literal by @iderex in #80
- Build auth-page URLs from JSON-encoded constants, not raw interpolation by @iderex in #81
- Remove unused injected dependencies from SSOViewsController by @iderex in #82
- Tidy pure helpers: LINQ filters, member order, argument guards by @iderex in #83
- Tidy Saml.cs: static helper, LINQ audience check, dead comments, guard by @iderex in #84
- Split the avatar SSRF range checks and guard Generator's baseUrl by @iderex in #85
- Extract the OIDC role-claim parsing into a tested pure helper by @iderex in #86
- Mechanical tidy of SSOController (behavior-neutral) by @iderex in #87
- Use structured logging templates in two controller log calls by @iderex in #88
- Extract the OID role-to-privilege mapping into a tested pure helper by @iderex in #90
- Extract the SAML role-to-privilege mapping into a tested pure helper by @iderex in #91
- Use Length instead of Any() for the role-claim segment guard by @iderex in #92
- Group Authenticate's parameters into a SessionParameters object by @iderex in #93
- Extract OID authorize-state derivation into a pure builder by @iderex in #94
- Extract SAML authorize-state derivation into a pure builder by @iderex in #96
- Deduplicate the OIDC client construction by @iderex in #97
- Drop the Copilot review step from the CODEOWNERS process note by @iderex in #100
- Add CodeRabbit review configuration by @iderex in #102
- Add a dependency-review PR gate by @iderex in #103
- Enable the .NET security analyzers as a standing in-build reviewer by @iderex in #107
- Lint the PR merge commit instead of checking out head_ref by @iderex in #108
- Use logical OR in the SAML Live TV grant merge by @iderex in #109
- Split the OIDC authorize-state derivation into focused helpers by @iderex in #110
- Bump setup-java and cache actions in the SonarCloud workflow by @iderex in #111
- Fail closed when a login resolves no identity by @iderex in #112
- Deduplicate the denial message and drop null-guards the belt made dead by @iderex in #113
- Rebuild the OID callback redirect_uri from the callback's own route by @iderex in #114
- Remove the vestigial F# Lib project by @iderex in #116
- Harden the NuGet supply chain: source mapping, lock files, locked-mode CI by @iderex in #118
- Prohibit DTD processing when parsing the SAML response (XXE / DoS) by @iderex in #121
- Set defensive response headers on the rendered auth page by @iderex in #124
- Add FsCheck property tests over the pure login-decision helpers by @iderex in #127
- Make string comparisons explicitly ordinal on the auth path by @iderex in #130
- Add a structured SSO audit log by @iderex in #132
- Add a pre-alpha production-use warning to the top of the README by @iderex in #180
- Fix CONTRIBUTING.md drift and document XXE/state-replay hardening by @iderex in #183
- Validate the OpenID Connect id_token by @iderex in #182
- Reflect the id_token validation in the README and fix a config key typo by @iderex in #184
- Key the OpenID account link on the stable sub claim by @iderex in #185
- Bind SAML responses to this SP: Recipient and InResponseTo by @iderex in #187
- Preserve server-managed canonical links across config saves by @iderex in #188
- Make SSO account adoption atomic by @iderex in #190
- Add opt-in per-client rate limiting to the anonymous SSO endpoints by @iderex in #193
- Make the OpenID client secret write-only in JSON responses by @iderex in #197
- Harden SAML signature processing against XML-signature-wrapping by @iderex in #198
- Surface and audit-log the insecure OpenID discovery toggles by @iderex in #200
- Record the client IP for SSO logins in Jellyfin's activity log by @iderex in #201
- Pin the OpenID state single-use / invalidate-on-consumption invariant by @iderex in #202
- Add a canonical external base-URL override by @iderex in #203
- Reject malformed SAML callback input with a clean 400, not a 500 by @iderex in #205
- Reject a non-form POST to the SAML ACS with a clean 400 by @iderex in #207
- Reject an unloadable SAML certificate instead of 500-ing every callback by @iderex in #208
- Validate the RFC 9207 authorization-response issuer by @iderex in #209
- Harden and speed up the CI/CD workflows by @iderex in #223
- Harden the SSO login-completion path (fail-closed robustness) by @iderex in #224
- Add a 7-day Dependabot cooldown for nuget and github-actions by @iderex in #225
- Escape media-folder Name/Id in the config-page folder list by @iderex in #227
- Clarify the ExtractRoles fail-closed comment for mixed role arrays by @iderex in #228
- Restrict avatar content types to a raster allow-list by @iderex in #229
- Consume the SAML replay cache in the account-linking callback by @iderex in #230
- Make Unregister actually revoke SSO access, persist, and null-guard by @iderex in #231
- Check PKCE (S256) support at OpenID discovery by @iderex in #233
- Add a CI gate that rejects Trojan Source Unicode by @iderex in #250
- Cap the SAML response size before base64 decode and DOM parse by @iderex in #254
- Cap and throttle the OIDC authorize-state store by @iderex in #255
- Take the config lock for login-path provider-dictionary reads by @iderex in #256
- Remove dead upstream AspNetSaml surface from Saml.cs by @iderex in #257
- Serve embedded view assets with an ETag for 304 revalidation by @iderex in #258
- Dead-code sweep: unused constructors, redundant defaults, debug logging by @iderex in #259
- Package the SAML crypto runtime DLLs (fix packaged SAML login) by @iderex in #260
- Migrate the injected login JS off the deprecated X-Emby-Authorization header by @iderex in #261
- Add a zizmor CI gate over the GitHub Actions workflows by @iderex in #262
- Add a nonce-based Content-Security-Policy to the auth page by @iderex in #264
- Skip zizmor SARIF upload on fork and Dependabot PRs by @iderex in #265
- Reset SSO config toggles when switching providers by @iderex in #266
- Expand the public AI-and-contributions note by @iderex in #268
- Make the CanonicalLinks getter self-healing by @iderex in #269
- Replace KeyNotFoundException control flow in the provider lookup with a null check by @iderex in #270
- Extract GetRequestBase's URL derivation into CanonicalBaseUrl.Resolve by @iderex in #271
- Elide the default port against the effective scheme, not the request scheme by @iderex in #273
- Point the plugin manifest and docs at this repository by @iderex in #274
- Add explicit assertions to the does-not-throw tests by @iderex in #275
- Document the real build, test, and branch flow in CONTRIBUTING by @iderex in #276
- Verify the plugin builds against the declared targetAbi floor in CI by @iderex in #277
- Record the enabled repository security controls in SECURITY.md by @iderex in #278
- Add an in-process SSOController test harness and first endpoint tests by @iderex in #279
- Cover disabled providers, state validation, and the read endpoints by @iderex in #280
- Cover the provider-delete endpoints and the rate-limit 429 by @iderex in #281
- Cover the provider-add endpoints and canonical-link preservation by @iderex in #282
- Cover the enabled SAML challenge, the Unregister guard, and the provider lists by @iderex in #283
- Introduce the In-Development → Full Release maturity ladder in the docs by @iderex in #285
- Cover the Unregister happy path and the canonical-link endpoints by @iderex in #286
- Cover the canonical-link add/delete outcomes by @iderex in #287
- Cover the SAML challenge linking, solicited-only, and rate-limit branches by @iderex in #288
- Cover the OpenID challenge PKCE-discovery fail-closed gate (#141) by @iderex in #290
- Point config page help links at this repo's wiki by @iderex in #293
- Point config page roadmap link at this project's board by @iderex in #295
- Make the OpenID authorize-state store test-resettable by @iderex in #292
- Add a Login Flow wiki page and link it from the README by @iderex in #296
- Cover the OpenID auth callback (OidAuth) by @iderex in #299
- Correct misleading config doc-comments for NewPath and FolderRoleMap by @iderex in #300
- Cover the SAML auth callback (SamlAuth) by @iderex in #301
- Dedupe the challenge NewPath logic and correct misleading doc-comments by @iderex in #302
- Cover the canonical-link add success paths and per-user link queries by @iderex in #304
- Cover the SAML assertion-consumer callback (SamlPost) by @iderex in #306
- Deduplicate device-name logic onto WebResponse.cs by @iderex in #308
- Add a bounded throttle-engaged observability signal to the rate limiter by @iderex in #307
- Consolidate shared SamlConfig/OidConfig members into ProviderConfigBase by @iderex in #309
- Add OpenSSF Scorecard supply-chain self-audit workflow by @iderex in #311
- Add rel="noopener noreferrer" to the config page Help link by @iderex in #314
- Cover the OpenID redirect callback token-exchange path (OidPost) by @iderex in #313
- Pin the 2025-2026 SAML attack shapes with characterization tests by @iderex in #312
- Retarget linking page help links off archived 9p4 upstream by @iderex in #315
- Key IPv4-in-IPv6 transition rate-limit sources on the embedded IPv4 by @iderex in #317
- Add Opengrep gate for greppable repo invariants by @iderex in #316
- Order TryExtractEmbeddedIPv4 among the internal members by @iderex in #321
- Add architecture-conformance fitness tests as a mandatory per-PR check by @iderex in #322
- Tighten the architecture-conformance rules (close the #322 review gaps) by @iderex in #323
- Rename the SAML core types and extract the controller data records (#318 step 1a) by @iderex in #324
- Extract the provider-scoped one-time-use key into ProviderScopedKey (#318 step 2) by @iderex in #325
- Add rel="noopener noreferrer" to the linking.html Help button by @iderex in #330
- List Scorecard and Opengrep in the security policy's repository controls by @iderex in #332
- Add a CRLF/pretty-printed signed-SAML interop regression test by @iderex in #333
- Extract the once-per-interval throttle into IntervalGate by @iderex in #331
- Adopt IntervalGate at the three remaining throttle sites by @iderex in #335
- Extract the SSO URL construction into SsoUrlBuilder by @iderex in #339
- Correct the Keycloak SAML assertion URL in providers.md by @iderex in #340
- Consolidate the OpenID authorize-state store into OidcStateStore by @iderex in #342
- Reject linking through a disabled provider by @iderex in #345
- Introduce LoginOutcome and LoginStatusMapper by @iderex in #347
- Make client-caused login rejections 4xx, not 500 by @iderex in #349
- Extract the config boundary into ProviderConfigStore by @iderex in #348
- State that development is currently stopped in the README by @iderex in #351
- Semi-halt notice, maintainership handover, iderex-only merge gate, and drop the public AI claim by @iderex in #352
- Extract the login-path account linking into CanonicalLinkService by @TheRealStroopwafel in #353
- State plainly that review is amongst AI-driven #decided against it and revert by @TheRealStroopwafel in #355
- Remove the AI note from the public docs by @TheRealStroopwafel in #357
- List the full maintainer team in CODEOWNERS by @TheRealStroopwafel in #356
- Reject URI-reserved characters in new provider names by @rekamer in #359
- Gate the legacy-link migration behind AllowExistingAccountLink by @shippingToken in #358
- Move the manual link/unlink admin surface into CanonicalLinkService by @TheRealStroopwafel in #373
- Extract the SSRF-safe avatar fetch into AvatarService by @TheRealStroopwafel in #375
- Reject control characters in new provider names by @rekamer in #376
- Route server-managed re-injection through ServerManagedFields.Preserve by @shippingToken in #387
- Centralize outbound HTTP client creation in SsoHttp by @TheRealStroopwafel in #379
- Extract session minting into SessionMinter by @rekamer in #390
- Write the avatar before touching the user's profile-image record by @iderex in #399
- Rework the README status notes by @iderex in #398
- Reject a provider disabled mid-flight on every grant path by @iderex in #404
- Make the SonarCloud job the required gate instead of the app status by @iderex in #405
- Normalize null OidScopes so the challenge does not 500 by @iderex in #408
- Reject a null provider body at the Add endpoints by @iderex in #409
- Add a controller socket/DNS tripwire to the conformance rules by @iderex in #406
- Match the challenge route segment exactly, not as a substring by @iderex in #410
- Persist the default provider in the single session-mint write by @iderex in #413
- Bind the OpenID authorize state to the initiating browser by @iderex in #416
- Bind the SP-initiated SAML login to the initiating browser by @iderex in #417
- Make the avatar fetch path unit-testable behind a handler seam by @iderex in #418
- Store the avatar with a real dotted file extension by @iderex in #419
- Reuse the resolved subject for the OpenID sub fallback by @iderex in #420
- Flatten OidPost's provider guard and drop dead link-helper attributes by @iderex in #421
- Define the SAML algorithm allow-list predicate once by @iderex in #422
- Fold the SAML XML loaders into SamlResponse construction by @iderex in #423
- Collapse the per-mode twin blocks over ProviderConfigBase by @iderex in #424
- Compact the helpers: record struct, dead guards, constant parameter by @iderex in #425
- Remove the CodeRabbit and Copilot review integrations by @iderex in #426
- Bound the in-flight login state stores per client by @iderex in #427
- Unify the two role-privilege mappers behind ProviderConfigBase by @iderex in #428
- Strip the dead browser-detection code from the auth page script by @iderex in #429
- Document the admin-UI dev loop and the vendored view-asset provenance by @iderex in #430
- Make the provider form's save contract explicit and drop the dead sso-json path by @iderex in #431
- Drop blank scope elements in BuildScopeString by @iderex in #433
- Require the legacy link's target to still bear the presented name before following it by @iderex in #435
- Anchor OidcCallbackPath.RedirectSegment to the route suffix by @iderex in #434
- Reuse the challenge's validated discovery metadata at the OpenID callback by @iderex in #437
- Characterize the OIDC callback against COAT and session fixation by @iderex in #438
- Re-check the identity link before minting an SSO session by @iderex in #439
- Tighten SAML assertion conformance: signatures, audience, bearer by @iderex in #441
- Update ChallengePath cross-reference now that RedirectSegment is suffix-anchored by @iderex in #442
- Harden the controller link-map conformance scan against split and rename by @iderex in #443
- Serialize the per-user avatar store against concurrent logins by @iderex in #445
- Reject unsafe usernames when building the avatar profile path by @iderex in #459
- Harden the OIDC RFC 9207 authorization-response issuer check by @iderex in #446
- Document the SAML audience validation options in providers.md by @iderex in #454
- Add a positive-control sentinel to the socket/DNS conformance scan by @iderex in #462
- Remove SonarCloud from the repo; confirm CodeRabbit/Copilot are gone by @iderex in #463
- Gate account adoption on admin status and a verified claim (#218) by @iderex in #464
- Harden IntervalGate against stale-sample re-admission by @iderex in #465
- Collapse repeated PluginPageInfo construction in SSOPlugin by @iderex in #466
- Revoke active tokens when unregistering a user by @iderex in #467
- Bound SamlReplayCache with a hard cap and throttled prune by @iderex in #469
- Add an AI-assisted, human-owned disclosure to the README by @iderex in #472
- Add an in-repo architecture / login-flow map by @iderex in #475
- Compact admin-UI JS in linking.js and config.js by @iderex in #476
- Fold legacy-link re-key into one authoritative migration transaction by @iderex in #477
- Trim redundant per-login work in the avatar fetch by @iderex in #478
- Document remaining OpenID config keys and multi-restriction SAML audience semantics by @iderex in #481
- Enable TreatWarningsAsErrors as a project default by @iderex in #482
- Surface AllowExistingAccountLink in the admin provider form by @iderex in #484
- Swap the OpenID authorize state atomically instead of promoting it in place by @iderex in #485
- Bump NSubstitute to 6.0.0 by @iderex in #487
- Surface RequireVerifiedEmailForAdoption in the admin provider form by @iderex in #488
- Correct stale config.xml-only claim for two adoption flags by @iderex in #490
- Sign outgoing SAML AuthnRequests (HTTP-Redirect binding) by @iderex in #492
- Make the null-config-maps preservation contract explicit (S2699) by @iderex in #494
- Introduce VerifiedIdentity as the session-mint keystone by @iderex in #495
- Extract the shared login-completion tail into a flow service by @iderex in #497
- Extract the OpenID discovery-facts cache into OidcDiscoveryCache by @iderex in #498
- Route rate-limit rejection through LoginOutcome.Throttled by @iderex in #499
- Extract the OpenID login flow into OidcLoginService by @iderex in #500
- Extract the SAML login flow into SamlLoginService by @iderex in #501
- Move the shared rate-limit gate off SSOController into the Shared tier by @iderex in #503
- Parse the oid/saml mode token once at the controller boundary by @iderex in #504
- Sweep contributor-clarity traps: callback naming, dead accessor, IP-classifier home, asset casing by @iderex in #506
- Split OidcIdTokenValidator into named validation sub-checks by @iderex in #512
- Throttle the pending-legacy-link warning through a shared IntervalGate by @iderex in #513
- Rate-limit the authenticated link/unlink admin endpoints by @iderex in #514
- Support ECDSA service-provider signing keys for outgoing SAML requests by @iderex in #515
- Name ECDSA in the two SP signing-key rejection messages by @iderex in #519
- Rate-limit the Unregister admin endpoint by @iderex in #520
- Extract a dedicated SAML assertion-validator type by @iderex in #521
- Add opt-in verified-email gate for OpenID logins by @iderex in #522
- Derive a real device name for the SSO linking view by @iderex in #523
- Add Kanidm to the tested-providers index in providers.md by @iderex in #511
- Surface RequireVerifiedEmailForLogin in the admin provider form by @iderex in #525
- Address zizmor pedantic workflow code-smells by @iderex in #527
- Replace the SAML assertion browser round-trip with a one-time outcome token by @iderex in #531
- Force-refresh the SSO avatar when the on-disk file is missing by @iderex in #530
- Stop offering disabled providers on the linking page and surface link rejections by @iderex in #535
- Correct stale config.xml-only claim for RequireVerifiedEmailForLogin by @iderex in #533
- Surface a throttled cap-refusal capacity warning from SamlReplayCache by @iderex in #537
- Single-source the shared authorize-state privilege merge by @iderex in #545
- Share the route-suffix reader between ChallengePath and OidcCallbackPath by @iderex in #543
- Bound the avatar store-lock acquire wait with a timeout by @iderex in #544
- Revoke tokens on last-link unlink; document per-provider disable non-revoke by @iderex in #546
- Document anonymous-by-design posture of the GetNames endpoints by @iderex in #547
- Source OIDC discovery facts from the login's own discovery response by @iderex in #548
- Generalize the shared 429 body wording by @iderex in #551
- Shorten the avatar store-lock acquire timeout to 3s by @iderex in #552
- Encrypt provider secrets at rest (AES-256-GCM envelope, transparent migration) by @iderex in #549
- Harden SecretStore key handling: orphan-prevention and atomic 0600 temp key by @iderex in #553
- Bind OpenID canonical links to the discovered issuer by @iderex in #554
- Prepare the 4.1.0.0 release by @iderex in #555
- Publish the release from a version-tag push by @iderex in #557
- Make EnabledProviderNames predicate null-safe by @iderex in #559
- Widen dotnet/prettier pull_request triggers to all branches by @iderex in #561
- Reserve the SAML outcome-store slot before the replay consume by @iderex in #563
New Contributors
- @dependabot[bot] made their first contribution in #11
- @iderex with @Copilot made their first contribution in #19
- @TheRealStroopwafel made their first contribution in #353
- @rekamer made their first contribution in #359
- @shippingToken made their first contribution in #358
Full Changelog: v4.0.0.4...v4.1.0.0