diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c4d5fef..451a9665 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [2.12.0] - 2026-08-05 + ### Added - **`GET /api/version` reports the running build.** Returns `version`, `revision` (git SHA), `revisionShort`, `buildDate` and `environment`, so you can confirm what a deployment is actually serving with a single request. The image's OCI labels already carried this, but labels are only readable via `docker inspect` on the host — no help for checking an instance from outside, and absent entirely from locally-built images. CI now passes `BUILD_VERSION` / `BUILD_REVISION` / `BUILD_DATE` as Docker build args from the same metadata that produces the labels; builds without them report `unknown` rather than failing. The endpoint is anonymous by design (the repository is public, so the commit SHA is not sensitive, and no configuration or secret is exposed). - **Generic external SSO / OIDC login provider** ([#327](https://github.com/PGAN-Dev/PoracleWeb.NET/issues/327)): PoracleWeb can now delegate login to any external OAuth2/OpenID Connect provider, in addition to the built-in Discord and Telegram methods. This enables single sign-on — e.g. pointing PoracleWeb (`alerts.pogoalerts.net`) at the PogoAlerts OAuth2 server so a user who is already signed into the main site lands in PoracleWeb without re-authenticating — but it is fully **provider-agnostic**: any self-hoster can configure their own IdP. The implementation is a configurable twin of the existing Discord flow. Two new endpoints (`GET /api/auth/oidc/login` and `GET /api/auth/oidc/callback`) handle the authorization-code exchange with **PKCE** (state + verifier persisted in HttpOnly cookies, same CSRF protection as the Discord path), then read a configurable **identity claim** (default `discord_id`, falling back to the standard `sub`) from the provider's UserInfo response and look it up in the Poracle `human` table exactly as a direct Discord login would — so existing admin resolution (`GetRolesAsync`), Discord guild-role gating, and the per-user enable/disable all apply unchanged, and PoracleWeb still mints and validates **its own** JWT (no change to token issuance). Provider config (provider name, authorize/token/userinfo URLs, client id/secret, scopes, claim mapping, PKCE flag) comes from `OIDC_*` env vars / `appsettings` — the secret is never stored in the database — and `OIDC_ENABLED` is auto-inferred when the client id and three URLs are all present (same first-time-setup safeguard as Telegram). A separate `enable_oidc` site setting gives admins a runtime on/off toggle (Features → *External SSO* group on the admin settings page; carried by `SettingsMigrationService`), while admins can always log in even when it's disabled so they can re-enable it. The login page renders a "Sign in with {provider}" button (with the same disabled-by-admin hint pattern as Discord/Telegram) whenever the provider is configured, driven by a new `oidc` block on `GET /api/auth/providers`; a new `/auth/oidc/callback` route reuses the existing token-fragment callback handler. New `OIDC_*` keys documented in `.env.example`, new `AUTH.SIGN_IN_OIDC` / `AUTH.ERR_OIDC_*` and `ADMIN_SETTINGS.*_OIDC` / `GROUP_OIDC` i18n keys added to English (other locales fall back to English until translated). Backend tests cover the `providers` oidc block (configured / not-configured / admin-disabled) and the `/oidc/login` redirect (state + PKCE cookies, provider URL + params); frontend tests cover the OIDC button visibility and click delegation. Wiring ReactMap and the PogoAlerts main site to the same provider, and PogoAlerts-side cross-subdomain session cookies, are separate follow-up work. @@ -26,6 +28,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **Gym-picker images no longer send a `Referer` header to third-party hosts** ([#242](https://github.com/PGAN-Dev/PoracleWeb.NET/issues/242)): the gym picker renders two kinds of remote image — the scanner DB's `gym.url` photo (a Niantic CDN URL in stock Golbat/RDM deployments, though an operator can rewrite the column to point at a self-hosted mirror) and the team-icon fallback from `raw.githubusercontent.com`. Neither carried a referrer policy, so every image request told the remote host which PoracleWeb instance the user was browsing. All four `` tags in `gym-picker.component.html` now set `referrerpolicy="no-referrer"`. Modern browsers already default to `strict-origin-when-cross-origin`, so the pre-existing leak was the origin rather than the full URL — this closes the remainder. Presentation-only: no API, model, or scanner-query change, and `GymSearchResult.Url` still carries the raw scanner URL as before. The photo-proxy endpoint floated in the original issue was **not** implemented: server-side fetching of a URL supplied by a database PoracleWeb does not own would turn a passive disclosure into an authenticated outbound-request primitive from a host that can reach Poracle, Koji, Golbat, and both MySQL servers. A host allowlist applied at projection remains the cheaper option if a deployment ever needs the mirror case handled. - **App-wide `Referrer-Policy` tightened to `same-origin`, so no remote host learns the instance origin** ([#383](https://github.com/PGAN-Dev/PoracleWeb.NET/issues/383)): the per-element fix above covered the gym picker, but the same leak existed everywhere else the SPA loads a remote resource — uicons from `raw.githubusercontent.com` (`icon.service.ts`, operator-overridable, so possibly a self-hosted mirror) across the Pokémon/raid/egg/lure/invasion/gym/quick-pick lists and dialogs, Discord avatars from `cdn.discordapp.com`, and the Google Fonts stylesheets in `index.html`. Each request disclosed the origin of the PoracleWeb instance being browsed, which for a private or invite-only deployment is the part worth withholding. The security-headers middleware previously sent `strict-origin-when-cross-origin` (the browser default, which sends the origin cross-origin); it now sends `same-origin` — full referrer within the site, nothing at all to third parties — fixing every case in one place rather than annotating tags individually. `no-referrer` was considered and rejected: `AuthController` reads the `Referer` header on `DiscordLogin`, the OIDC login path, and OIDC RP-initiated logout to recover which frontend origin the user came from, validate it against the configured CORS origins, and redirect back there after the provider callback — blanking the same-origin referrer would degrade all three to this host's own origin and bounce users to the wrong place. The header values moved out of the inline lambda in `Program.cs` into a `SecurityHeaders` class so they're assertable without booting the app; the CSP is carried over byte-identical (a test pins it against the original literal). Tests cover the policy value, a guard that it never becomes `no-referrer` or any of the origin-leaking values, and the previously untested `AuthController` origin recovery it depends on (allowed referer honored, disallowed and non-absolute referers rejected, absent referer falling back to self). The per-element `referrerpolicy` attributes from #242 are left in place as defence-in-depth. +### Dependencies +- Bump Microsoft.AspNetCore.Authentication.JwtBearer and 6 others ([#363](https://github.com/PGAN-Dev/PoracleWeb.NET/pull/363)) +- Bump Microsoft.EntityFrameworkCore and 4 others ([#366](https://github.com/PGAN-Dev/PoracleWeb.NET/pull/366)) +- Bump Microsoft.NET.Test.Sdk from 18.6.0 to 18.8.1 ([#365](https://github.com/PGAN-Dev/PoracleWeb.NET/pull/365)) +- Bump jest-preset-angular ([#344](https://github.com/PGAN-Dev/PoracleWeb.NET/pull/344)) +- Bump the angular group across 1 directory with 13 updates ([#338](https://github.com/PGAN-Dev/PoracleWeb.NET/pull/338)) +- Bump the angular group across 1 directory with 9 updates ([#359](https://github.com/PGAN-Dev/PoracleWeb.NET/pull/359)) +- Bump the eslint group across 1 directory with 5 updates ([#349](https://github.com/PGAN-Dev/PoracleWeb.NET/pull/349)) +- Upgrade @ngx-translate to v18 and migrate off TranslateModule ([#377](https://github.com/PGAN-Dev/PoracleWeb.NET/pull/377)) + ## [2.11.1] - 2026-06-05 ### Fixed @@ -607,7 +619,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Rate limiting (per-IP) on auth endpoints - Docker deployment with Watchtower auto-updates -[Unreleased]: https://github.com/PGAN-Dev/PoracleWeb.NET/compare/v2.11.1...HEAD +[Unreleased]: https://github.com/PGAN-Dev/PoracleWeb.NET/compare/v2.12.0...HEAD +[2.12.0]: https://github.com/PGAN-Dev/PoracleWeb.NET/compare/v2.11.1...v2.12.0 [2.11.1]: https://github.com/PGAN-Dev/PoracleWeb.NET/compare/v2.11.0...v2.11.1 [2.11.0]: https://github.com/PGAN-Dev/PoracleWeb.NET/compare/v2.10.0...v2.11.0 [2.10.0]: https://github.com/PGAN-Dev/PoracleWeb.NET/compare/v2.9.0...v2.10.0